public void Action_WithOneDeletedEntry_ShouldNotCountThatDeletedEntryOnSecondRun() { // Setup var lfProj = _lfProj; sutFdoToMongo.Run(lfProj); Guid entryGuid = Guid.Parse(TestEntryGuidStr); LfLexEntry entry = _conn.GetLfLexEntryByGuid(entryGuid); entry.IsDeleted = true; _conn.UpdateMockLfLexEntry(entry); // Exercise sutMongoToFdo.Run(lfProj); // Verify Assert.That(_counts.Added, Is.EqualTo(0)); Assert.That(_counts.Modified, Is.EqualTo(0)); Assert.That(_counts.Deleted, Is.EqualTo(1)); Assert.That(LfMergeBridgeServices.FormatCommitMessageForLfMerge(_counts.Added, _counts.Modified, _counts.Deleted), Is.EqualTo("Language Forge: 1 entry deleted")); // Exercise again sutMongoToFdo.Run(lfProj); // Verify zero on second run Assert.That(_counts.Added, Is.EqualTo(0)); Assert.That(_counts.Modified, Is.EqualTo(0)); Assert.That(_counts.Deleted, Is.EqualTo(0)); Assert.That(LfMergeBridgeServices.FormatCommitMessageForLfMerge(_counts.Added, _counts.Modified, _counts.Deleted), Is.EqualTo("Language Forge S/R")); }
private bool SyncResultedInError(ILfProject project, string syncResult, string errorString, ProcessingState.SendReceiveStates?newState = null) { var line = LfMergeBridgeServices.GetLineContaining(syncResult, errorString); if (!string.IsNullOrEmpty(line)) { if (line.Contains("Exception")) { IEnumerable <string> stackTrace = LfMergeBridgeServices.GetLineAndStackTraceContaining(syncResult, errorString); Logger.Error(String.Join(Environment.NewLine, stackTrace)); // We want entire stack trace logged as a single log entry, so don't use Logger.LogMany() } else { Logger.Error(line); } if (newState.HasValue) { if (newState.Value == ProcessingState.SendReceiveStates.HOLD) { // When going on hold, do so via the new PutOnHold() function so we record an error message project.State.PutOnHold("Error during synchronize of {0}: {1}", project.ProjectCode, line); } else { project.State.SRState = newState.Value; } } return(true); } return(false); }
public void Action_WithTwoDeletedEntries_ShouldCountTwoDeleted() { // Setup var lfProj = _lfProj; sutFdoToMongo.Run(lfProj); Guid entryGuid = Guid.Parse(TestEntryGuidStr); LfLexEntry entry = _conn.GetLfLexEntryByGuid(entryGuid); entry.IsDeleted = true; _conn.UpdateMockLfLexEntry(entry); Guid kenGuid = Guid.Parse(KenEntryGuidStr); entry = _conn.GetLfLexEntryByGuid(kenGuid); entry.IsDeleted = true; _conn.UpdateMockLfLexEntry(entry); // Exercise sutMongoToFdo.Run(lfProj); // Verify Assert.That(_counts.Added, Is.EqualTo(0)); Assert.That(_counts.Modified, Is.EqualTo(0)); Assert.That(_counts.Deleted, Is.EqualTo(2)); Assert.That(LfMergeBridgeServices.FormatCommitMessageForLfMerge(_counts.Added, _counts.Modified, _counts.Deleted), Is.EqualTo("Language Forge: 2 entries deleted")); }
public void Action_WithOneModifiedEntry_ShouldCountOneModified() { // Setup var lfProj = _lfProj; sutFdoToMongo.Run(lfProj); Guid entryGuid = Guid.Parse(TestEntryGuidStr); LfLexEntry entry = _conn.GetLfLexEntryByGuid(entryGuid); FdoCache cache = lfProj.FieldWorksProject.Cache; string vernacularWS = cache.LanguageProject.DefaultVernacularWritingSystem.Id; string changedLexeme = "modified lexeme for this test"; entry.Lexeme = LfMultiText.FromSingleStringMapping(vernacularWS, changedLexeme); entry.AuthorInfo = new LfAuthorInfo(); entry.AuthorInfo.ModifiedDate = DateTime.UtcNow; _conn.UpdateMockLfLexEntry(entry); // Exercise sutMongoToFdo.Run(lfProj); // Verify Assert.That(_counts.Added, Is.EqualTo(0)); Assert.That(_counts.Modified, Is.EqualTo(1)); Assert.That(_counts.Deleted, Is.EqualTo(0)); Assert.That(LfMergeBridgeServices.FormatCommitMessageForLfMerge(_counts.Added, _counts.Modified, _counts.Deleted), Is.EqualTo("Language Forge: 1 entry modified")); }
public void Action_WithOneNewEntry_ShouldCountOneAdded() { // Setup var lfProj = _lfProj; LfLexEntry newEntry = new LfLexEntry(); newEntry.Guid = Guid.NewGuid(); FdoCache cache = lfProj.FieldWorksProject.Cache; string vernacularWS = cache.LanguageProject.DefaultVernacularWritingSystem.Id; string newLexeme = "new lexeme for this test"; newEntry.Lexeme = LfMultiText.FromSingleStringMapping(vernacularWS, newLexeme); newEntry.AuthorInfo = new LfAuthorInfo(); newEntry.AuthorInfo.CreatedDate = DateTime.UtcNow; newEntry.AuthorInfo.ModifiedDate = newEntry.AuthorInfo.CreatedDate; _conn.UpdateMockLfLexEntry(newEntry); // Exercise sutMongoToFdo.Run(lfProj); // Verify Assert.That(_counts.Added, Is.EqualTo(1)); Assert.That(_counts.Modified, Is.EqualTo(0)); Assert.That(_counts.Deleted, Is.EqualTo(0)); Assert.That(LfMergeBridgeServices.FormatCommitMessageForLfMerge(_counts.Added, _counts.Modified, _counts.Deleted), Is.EqualTo("Language Forge: 1 entry added")); }
public void Action_RunTwiceWithOneNewEntryEachTime_ShouldCountTwoAddedInTotal() { // Setup var lfProj = _lfProj; LfLexEntry newEntry = new LfLexEntry(); newEntry.Guid = Guid.NewGuid(); LcmCache cache = lfProj.FieldWorksProject.Cache; string vernacularWS = cache.LanguageProject.DefaultVernacularWritingSystem.Id; string newLexeme = "new lexeme for this test"; newEntry.Lexeme = LfMultiText.FromSingleStringMapping(vernacularWS, newLexeme); newEntry.AuthorInfo = new LfAuthorInfo(); newEntry.AuthorInfo.CreatedDate = DateTime.UtcNow; newEntry.AuthorInfo.ModifiedDate = newEntry.AuthorInfo.CreatedDate; _conn.UpdateMockLfLexEntry(newEntry); // Exercise SutMongoToLcm.Run(lfProj); // Verify Assert.That(_counts.Added, Is.EqualTo(1)); Assert.That(_counts.Modified, Is.EqualTo(0)); Assert.That(_counts.Deleted, Is.EqualTo(0)); Assert.That(LfMergeBridgeServices.FormatCommitMessageForLfMerge(_counts.Added, _counts.Modified, _counts.Deleted), Is.EqualTo("Language Forge: 1 entry added")); // Setup second run newEntry = new LfLexEntry(); newEntry.Guid = Guid.NewGuid(); newLexeme = "second new lexeme for this test"; newEntry.Lexeme = LfMultiText.FromSingleStringMapping(vernacularWS, newLexeme); newEntry.AuthorInfo = new LfAuthorInfo(); newEntry.AuthorInfo.CreatedDate = DateTime.UtcNow; newEntry.AuthorInfo.ModifiedDate = newEntry.AuthorInfo.CreatedDate; _conn.UpdateMockLfLexEntry(newEntry); // Exercise SutMongoToLcm.Run(lfProj); // Verify Assert.That(_counts.Added, Is.EqualTo(1)); // Modified and Deleted shouldn't have changed, but check Added first // since that's the main point of this test Assert.That(_counts.Modified, Is.EqualTo(0)); Assert.That(_counts.Deleted, Is.EqualTo(0)); Assert.That(LfMergeBridgeServices.FormatCommitMessageForLfMerge(_counts.Added, _counts.Modified, _counts.Deleted), Is.EqualTo("Language Forge: 1 entry added")); }
public void Action_RunTwiceWithTheSameEntryModifiedEachTime_ShouldCountTwoModifiedInTotal() { // Setup var lfProj = _lfProj; sutFdoToMongo.Run(lfProj); Guid entryGuid = Guid.Parse(TestEntryGuidStr); LfLexEntry entry = _conn.GetLfLexEntryByGuid(entryGuid); FdoCache cache = lfProj.FieldWorksProject.Cache; string vernacularWS = cache.LanguageProject.DefaultVernacularWritingSystem.Id; string changedLexeme = "modified lexeme for this test"; entry.Lexeme = LfMultiText.FromSingleStringMapping(vernacularWS, changedLexeme); entry.AuthorInfo = new LfAuthorInfo(); entry.AuthorInfo.ModifiedDate = DateTime.UtcNow; _conn.UpdateMockLfLexEntry(entry); // Exercise sutMongoToFdo.Run(lfProj); // Verify Assert.That(_counts.Added, Is.EqualTo(0)); Assert.That(_counts.Modified, Is.EqualTo(1)); Assert.That(_counts.Deleted, Is.EqualTo(0)); Assert.That(LfMergeBridgeServices.FormatCommitMessageForLfMerge(_counts.Added, _counts.Modified, _counts.Deleted), Is.EqualTo("Language Forge: 1 entry modified")); // Setup second run string changedLexeme2 = "second modified lexeme for this test"; entry.Lexeme = LfMultiText.FromSingleStringMapping(vernacularWS, changedLexeme2); entry.AuthorInfo = new LfAuthorInfo(); entry.AuthorInfo.ModifiedDate = DateTime.UtcNow; _conn.UpdateMockLfLexEntry(entry); // Exercise second run sutMongoToFdo.Run(lfProj); // Verify second run Assert.That(_counts.Modified, Is.EqualTo(1)); // Added and Deleted shouldn't have changed, but check Modified first // since that's the main point of this test Assert.That(_counts.Added, Is.EqualTo(0)); Assert.That(_counts.Deleted, Is.EqualTo(0)); Assert.That(LfMergeBridgeServices.FormatCommitMessageForLfMerge(_counts.Added, _counts.Modified, _counts.Deleted), Is.EqualTo("Language Forge: 1 entry modified")); }
public static string GetPrefixedStringFromLfMergeBridgeOutput(string lfMergeBridgeOutput, string prefix) { if (string.IsNullOrEmpty(prefix) || string.IsNullOrEmpty(lfMergeBridgeOutput)) { return(string.Empty); } string result = LfMergeBridgeServices.GetLineContaining(lfMergeBridgeOutput, prefix); if (result.StartsWith(prefix)) { return(result.Substring(prefix.Length)); } else { return(string.Empty); // If the "prefix" wasn't actually a prefix, this wasn't the string we wanted. } }
public void Action_WithNoChangesFromMongo_ShouldCountZeroChanges() { // Setup var lfProj = _lfProj; FdoCache cache = lfProj.FieldWorksProject.Cache; // Exercise sutMongoToFdo.Run(lfProj); // Verify Assert.That(_counts.Added, Is.EqualTo(0)); Assert.That(_counts.Modified, Is.EqualTo(0)); Assert.That(_counts.Deleted, Is.EqualTo(0)); Assert.That(LfMergeBridgeServices.FormatCommitMessageForLfMerge(_counts.Added, _counts.Modified, _counts.Deleted), Is.EqualTo("Language Forge S/R")); }
private bool CloneResultedInError(ILfProject project, string cloneResult, string errorString, bool isRecoverableError, ProcessingState.ErrorCodes errorCode) { var line = LfMergeBridgeServices.GetLineContaining(cloneResult, errorString); if (string.IsNullOrEmpty(line)) { return(false); } var errorType = isRecoverableError ? "Recoverable error" : "Error"; Logger.Error("{2} during initial clone of {0}: {1}", project.ProjectCode, line, errorType); project.State.SetErrorState(isRecoverableError ? ProcessingState.SendReceiveStates.ERROR : ProcessingState.SendReceiveStates.HOLD, errorCode, "{2} during initial clone of {0}: {1}", project.ProjectCode, line, errorType); return(true); }
public void Action_RunTwiceWithTheSameEntryDeletedEachTime_ShouldCountJustOneDeletedInTotal() { // Setup var lfProj = _lfProj; sutFdoToMongo.Run(lfProj); Guid entryGuid = Guid.Parse(TestEntryGuidStr); LfLexEntry entry = _conn.GetLfLexEntryByGuid(entryGuid); entry.IsDeleted = true; _conn.UpdateMockLfLexEntry(entry); // Exercise sutMongoToFdo.Run(lfProj); // Verify Assert.That(_counts.Added, Is.EqualTo(0)); Assert.That(_counts.Modified, Is.EqualTo(0)); Assert.That(_counts.Deleted, Is.EqualTo(1)); Assert.That(LfMergeBridgeServices.FormatCommitMessageForLfMerge(_counts.Added, _counts.Modified, _counts.Deleted), Is.EqualTo("Language Forge: 1 entry deleted")); entry = _conn.GetLfLexEntryByGuid(entryGuid); entry.IsDeleted = true; _conn.UpdateMockLfLexEntry(entry); // Exercise second run sutMongoToFdo.Run(lfProj); // Verify second run Assert.That(_counts.Deleted, Is.EqualTo(0)); // Added and Modified shouldn't have changed either, but check Deleted first // since that's the main point of this test Assert.That(_counts.Added, Is.EqualTo(0)); Assert.That(_counts.Modified, Is.EqualTo(0)); Assert.That(LfMergeBridgeServices.FormatCommitMessageForLfMerge(_counts.Added, _counts.Modified, _counts.Deleted), Is.EqualTo("Language Forge S/R")); }
/// <summary> /// Ensures a Send/Receive project from Language Depot is properly /// cloned into the WebWork directory for LfMerge.Core. /// A project will be cloned if: /// 1) The LfMerge state file doesn't exist OR /// 2) The current LfMerge state is ProcessingState.SendReceiveStates.CLONING OR /// 3) The project directory is empty /// </summary> /// <param name="project">LF Project.</param> protected override void DoRun(ILfProject project) { try { // Check if an initial clone needs to be performed if (File.Exists(Settings.GetStateFileName(project.ProjectCode)) && project.State.SRState != ProcessingState.SendReceiveStates.CLONING && File.Exists(project.FwDataPath)) { return; } var cloneLocation = project.ProjectDir; if (RepoAlreadyExists(cloneLocation)) { Logger.Notice("Repairing clone of project {0}", project.ProjectCode); } else { Logger.Notice("Initial clone for project {0}", project.ProjectCode); } project.State.SRState = ProcessingState.SendReceiveStates.CLONING; string cloneResult; if (!CloneRepo(project, cloneLocation, out cloneResult)) { Logger.Error(cloneResult); return; } if (CloneResultedInError(project, cloneResult, "clone is not a FLEx project", true, ProcessingState.ErrorCodes.NoFlexProject) || CloneResultedInError(project, cloneResult, "new repository with no commits", true, ProcessingState.ErrorCodes.EmptyProject) || CloneResultedInError(project, cloneResult, "clone has higher model", true, ProcessingState.ErrorCodes.ProjectTooNew) || CloneResultedInError(project, cloneResult, "LfMergeBridge starting S/R handler from directory", false, ProcessingState.ErrorCodes.Unspecified)) { return; } string line = LfMergeBridgeServices.GetLineContaining(cloneResult, "no such branch"); if (!string.IsNullOrEmpty(line)) { const string modelString = "Highest available model '"; var index = line.IndexOf(modelString, StringComparison.Ordinal); if (index < 0) { ReportNoSuchBranchFailure(project, cloneLocation, cloneResult, line, ProcessingState.ErrorCodes.UnspecifiedBranchError); return; } var cloneModelVersion = line.Substring(index + modelString.Length, 7); if (int.Parse(cloneModelVersion) < int.Parse(MagicStrings.MinimalModelVersion)) { ReportNoSuchBranchFailure(project, cloneLocation, cloneResult, line, ProcessingState.ErrorCodes.ProjectTooOld); Logger.Error("Error during initial clone of '{0}': " + "clone model version '{1}' less than minimal supported model version '{2}'.", project.ProjectCode, cloneModelVersion, MagicStrings.MinimalModelVersion); return; } Logger.Info(line); ChorusHelper.SetModelVersion(cloneModelVersion); } else { ChorusHelper.SetModelVersion(FdoCache.ModelVersion); line = LfMergeBridgeServices.GetLineContaining(cloneResult, "new clone created on branch"); Require.That(!string.IsNullOrEmpty(line), "Looks like the clone was not successful, but we didn't get an understandable error"); // verify clone path GetActualClonePath(cloneLocation, line); if (MongoProjectHasUserDataOrHasBeenSynced()) { // If the local Mercurial repo was deleted but the Mongo database is still there, // then there might be data in Mongo that we still need, in which case we should NOT // skip the syncing step. So do nothing, so that we'll fall through to the SYNCING state. } else { InitialTransferToMongoAfterClone(project); Logger.Notice("Initial clone completed; setting state to CLONED"); project.State.SRState = ProcessingState.SendReceiveStates.CLONED; } } } catch (Exception e) { switch (e.GetType().Name) { case "ArgumentOutOfRangeException": if (e.Message == "Cannot update to any branch.") { project.State.SetErrorState(ProcessingState.SendReceiveStates.ERROR, ProcessingState.ErrorCodes.UnspecifiedBranchError, "Error during initial clone of {0}: {1}", project.ProjectCode, e); return; } break; case "RepositoryAuthorizationException": Logger.Error("Initial clone of {0}: authorization exception", project.ProjectCode); project.State.SetErrorState(ProcessingState.SendReceiveStates.ERROR, ProcessingState.ErrorCodes.Unauthorized, "Error during initial clone of {0}: authorization exception from remote repository", project.ProjectCode); return; } Logger.Error("Got {0} exception trying to clone {1}: {2}", e.GetType(), project.ProjectCode, e.Message); throw; } }
protected override void DoRun(ILfProject project) { using (MainClass.Container.BeginLifetimeScope()) { var transferAction = GetAction(ActionNames.TransferMongoToLcm); transferAction.Run(project); int entriesAdded = 0, entriesModified = 0, entriesDeleted = 0; // Need to (safely) cast to TransferMongoToLcmAction to get the entry counts var transferMongoToLcmAction = transferAction as TransferMongoToLcmAction; if (transferMongoToLcmAction != null) { entriesAdded = transferMongoToLcmAction.EntryCounts.Added; entriesModified = transferMongoToLcmAction.EntryCounts.Modified; entriesDeleted = transferMongoToLcmAction.EntryCounts.Deleted; } Logger.Debug("About to dispose FW project {0}", project.ProjectCode); LanguageForgeProject.DisposeFwProject(project); Logger.Debug("Successfully disposed FW project {0}", project.ProjectCode); Logger.Notice("Syncing"); string commitMessage = LfMergeBridgeServices.FormatCommitMessageForLfMerge(entriesAdded, entriesModified, entriesDeleted); if (commitMessage == null) // Shouldn't happen, but be careful anyway { commitMessage = "Language Forge Send/Receive"; // Desperate fallback } var chorusHelper = MainClass.Container.Resolve <ChorusHelper>(); // Call into LF Bridge to do the work. string syncResult; var options = new Dictionary <string, string> { { "fullPathToProject", project.ProjectDir }, { "fwdataFilename", project.FwDataPath }, { "fdoDataModelVersion", LcmCache.ModelVersion.ToString() }, { "languageDepotRepoName", "Language Depot" }, { "languageDepotRepoUri", chorusHelper.GetSyncUri(project) }, { "commitMessage", commitMessage } }; try { if (!LfMergeBridge.LfMergeBridge.Execute("Language_Forge_Send_Receive", Progress, options, out syncResult)) { Logger.Error(syncResult); return; } } catch (System.FormatException e) { if (e.StackTrace.Contains("System.Int32.Parse")) { ChorusHelper.SetModelVersion(MagicStrings.MinimalModelVersionForNewBranchFormat); return; } else { throw; } } const string cannotCommitCurrentBranch = "Cannot commit to current branch '"; var line = LfMergeBridgeServices.GetLineContaining(syncResult, cannotCommitCurrentBranch); if (!string.IsNullOrEmpty(line)) { var index = line.IndexOf(cannotCommitCurrentBranch, StringComparison.Ordinal); Require.That(index >= 0); var modelVersion = int.Parse(line.Substring(index + cannotCommitCurrentBranch.Length, 7)); if (modelVersion > MagicStrings.MaximalModelVersion) { // Chorus changed model versions to 75#####.xxxxxxx where xxxxxxx is the old-style model version modelVersion = int.Parse(line.Substring(index + cannotCommitCurrentBranch.Length + 8, 7)); } if (modelVersion < MagicStrings.MinimalModelVersion) { SyncResultedInError(project, syncResult, cannotCommitCurrentBranch, ProcessingState.SendReceiveStates.HOLD); Logger.Error("Error during sync of '{0}': " + "clone model version '{1}' less than minimal supported model version '{2}'.", project.ProjectCode, modelVersion, MagicStrings.MinimalModelVersion); return; } ChorusHelper.SetModelVersion(modelVersion); return; } const string pulledHigherModel = "pulled a higher model '"; line = LfMergeBridgeServices.GetLineContaining(syncResult, pulledHigherModel); if (!string.IsNullOrEmpty(line)) { var index = line.IndexOf(pulledHigherModel, StringComparison.Ordinal); Require.That(index >= 0); var modelVersion = int.Parse(line.Substring(index + pulledHigherModel.Length, 7)); ChorusHelper.SetModelVersion(modelVersion); // The .hg branch has a higher model version than the .fwdata file. We allow // data migrations and try again. Logger.Notice("Allow data migration for project '{0}' to migrate to model version '{1}'", project.ProjectCode, modelVersion); FwProject.AllowDataMigration = true; return; } if (SyncResultedInError(project, syncResult, "Cannot create a repository at this point in LF development.", ProcessingState.SendReceiveStates.HOLD) || // REVIEW: should we set the state to HOLD if we don't have previous commits? SyncResultedInError(project, syncResult, "Cannot do first commit.", ProcessingState.SendReceiveStates.HOLD) || SyncResultedInError(project, syncResult, "Sync failure:")) { return; } line = LfMergeBridgeServices.GetLineContaining(syncResult, "No changes from others"); if (!string.IsNullOrEmpty(line)) { Logger.Notice(line); // We still need to transfer back to Mongo to delete any entries marked for deletion } else { // LfMergeBridge has code to detect when we got changes from others. However, // that code never executes because it does a pull before calling synchronizer.SyncNow() // so that syncResults.DidGetChangesFromOthers never gets set. It doesn't // matter to us because we always do a transfer to mongodb. line = LfMergeBridgeServices.GetLineContaining(syncResult, "Received changes from others"); if (string.IsNullOrEmpty(line)) { // Hmm. Bad news. Must have been some kind of problem down there. Logger.Error("Unhandled sync failure. Result we got was: {0}", syncResult); return; } Logger.Notice(line); } IAction transferLcmToMongoAction = GetAction(ActionNames.TransferLcmToMongo); if (transferLcmToMongoAction == null) { Logger.Error("Failed to run TransferLcmToMongo action: GetAction returned null"); return; } transferLcmToMongoAction.Run(project); } }