public void Action_NoDataChanged_ShouldUpdatePictures() { // Setup var lfProject = _lfProj; IEnumerable <LfLexEntry> receivedData = _conn.GetLfLexEntries(); int originalNumPictures = receivedData.Count(e => ((e.Senses.Count > 0) && (e.Senses[0].Pictures.Count > 0))); Assert.That(originalNumPictures, Is.EqualTo(0)); // Exercise sutFdoToMongo.Run(lfProject); // Verify LF project now contains the 4 FDO pictures (1 externally linked, 3 internal) receivedData = _conn.GetLfLexEntries(); int newNumPictures = receivedData.Count(e => ((e.Senses.Count > 0) && (e.Senses[0].Pictures.Count > 0))); Assert.That(newNumPictures, Is.EqualTo(4)); LfLexEntry subEntry = receivedData.FirstOrDefault(e => e.Guid.ToString() == TestSubEntryGuidStr); Assert.That(subEntry, Is.Not.Null); Assert.That(subEntry.Senses[0].Pictures[0].FileName, Is.EqualTo("TestImage.tif")); LfLexEntry kenEntry = receivedData.FirstOrDefault(e => e.Guid.ToString() == KenEntryGuidStr); Assert.That(kenEntry, Is.Not.Null); Assert.That(kenEntry.Senses[0].Pictures[0].FileName, Is.EqualTo("F:\\src\\xForge\\web-languageforge\\test\\php\\common\\TestImage.jpg")); }
public void SynchronizeAction_LFDataChanged_GlossChanged() { // Setup TestEnvironment.CopyFwProjectTo(testProjectCode, _lDSettings.WebWorkDirectory); _lfProject.IsInitialClone = true; _transferFdoToMongo.Run(_lfProject); IEnumerable <LfLexEntry> originalMongoData = _mongoConnection.GetLfLexEntries(); LfLexEntry lfEntry = originalMongoData.First(e => e.Guid == _testEntryGuid); string unchangedGloss = lfEntry.Senses[0].Gloss["en"].Value; string lfChangedGloss = unchangedGloss + " - changed in LF"; lfEntry.Senses[0].Gloss["en"].Value = lfChangedGloss; _mongoConnection.UpdateRecord(_lfProject, lfEntry); _lDProject = new LanguageDepotMock(testProjectCode, _lDSettings); var lDcache = _lDProject.FieldWorksProject.Cache; var lDFdoEntry = lDcache.ServiceLocator.GetObject(_testEntryGuid) as ILexEntry; Assert.That(lDFdoEntry, Is.Not.Null); Assert.That(lDFdoEntry.SensesOS.Count, Is.EqualTo(2)); Assert.That(lDFdoEntry.SensesOS[0].Gloss.AnalysisDefaultWritingSystem.Text, Is.EqualTo(unchangedGloss)); // Exercise var sutSynchronize = new SynchronizeAction(_env.Settings, _env.Logger); sutSynchronize.Run(_lfProject); // Verify Assert.That(GetGlossFromFdo(_testEntryGuid, 2), Is.EqualTo(lfChangedGloss)); Assert.That(GetGlossFromMongoDb(_testEntryGuid, expectedDeletedEntries: 0), Is.EqualTo(lfChangedGloss)); Assert.That(GetGlossFromLanguageDepot(_testEntryGuid, 2), Is.EqualTo(lfChangedGloss)); }
public void Action_NoDataChanged_ShouldUpdatePictures() { // Setup var lfProject = _lfProj; IEnumerable <LfLexEntry> receivedData = _conn.GetLfLexEntries(); int originalNumPictures = receivedData.Count(e => ((e.Senses.Count > 0) && (e.Senses[0].Pictures.Count > 0))); Assert.That(originalNumPictures, Is.EqualTo(0)); // Exercise SutLcmToMongo.Run(lfProject); // Verify LF project now contains the 4 LCM pictures (1 externally linked, 3 internal) receivedData = _conn.GetLfLexEntries(); int newNumPictures = receivedData.Count(e => ((e.Senses.Count > 0) && (e.Senses[0].Pictures.Count > 0))); Assert.That(newNumPictures, Is.EqualTo(4)); LfLexEntry subEntry = receivedData.FirstOrDefault(e => e.Guid.ToString() == TestSubEntryGuidStr); Assert.That(subEntry, Is.Not.Null); Assert.That(subEntry.Senses[0].Pictures[0].FileName, Is.EqualTo("TestImage.tif")); LfLexEntry kenEntry = receivedData.FirstOrDefault(e => e.Guid.ToString() == KenEntryGuidStr); Assert.That(kenEntry, Is.Not.Null); Assert.That(kenEntry.Senses[0].Pictures[0].FileName, Is.EqualTo(string.Format( "F:{0}src{0}xForge{0}web-languageforge{0}test{0}php{0}common{0}TestImage.jpg", Path.DirectorySeparatorChar))); }
public void Run_CustomMultiListRefTest(int whichSense, params string[] desiredKeys) { // Setup var lfProj = _lfProj; sutFdoToMongo.Run(lfProj); Guid entryGuid = Guid.Parse(TestEntryGuidStr); LfLexEntry entry = _conn.GetLfLexEntryByGuid(entryGuid); LfSense sense = entry.Senses[whichSense]; SetCustomMultiOptionList(sense, "customField_senses_Cust_Multi_ListRef", desiredKeys); entry.AuthorInfo = new LfAuthorInfo(); entry.AuthorInfo.ModifiedDate = DateTime.UtcNow; _conn.UpdateMockLfLexEntry(entry); // Exercise sutMongoToFdo.Run(lfProj); // Verify FdoCache cache = _cache; var fdoEntry = cache.ServiceLocator.GetObject(entryGuid) as ILexEntry; Assert.IsNotNull(fdoEntry); Assert.That(cache.ServiceLocator.MetaDataCache.FieldExists(LexSenseTags.kClassName, "Cust Multi ListRef", false), "LexSense should have the Cust Multi ListRef field in our test data."); int fieldId = cache.ServiceLocator.MetaDataCache.GetFieldId(LexSenseTags.kClassName, "Cust Multi ListRef", false); ILexSense fdoSense = fdoEntry.SensesOS[whichSense]; IEnumerable <string> fdoAbbrevs = GetFdoAbbrevsForField(fdoSense, fieldId); Assert.That(fdoAbbrevs, Is.EquivalentTo(desiredKeys)); }
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")); }
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 SynchronizeAction_LFDataDeleted_EntryRemoved() { // Setup TestEnvironment.CopyFwProjectTo(testProjectCode, _lDSettings.WebWorkDirectory); _lfProject.IsInitialClone = true; _transferFdoToMongo.Run(_lfProject); IEnumerable <LfLexEntry> originalMongoData = _mongoConnection.GetLfLexEntries(); LfLexEntry lfEntry = originalMongoData.First(e => e.Guid == _testEntryGuid); DateTime originalLfDateModified = lfEntry.DateModified; string unchangedGloss = lfEntry.Senses[0].Gloss["en"].Value; // Don't use _mongoConnection.RemoveRecord to delete the entry. LF uses the "IsDeleted" field lfEntry.IsDeleted = true; // The LF PHP code would have updated DateModified when it deleted the record, so simulate that here lfEntry.DateModified = DateTime.UtcNow; _mongoConnection.UpdateRecord(_lfProject, lfEntry); IEnumerable <LfLexEntry> updatedMongoData = _mongoConnection.GetLfLexEntries(); Assert.That(updatedMongoData.First(e => e.Guid == _testEntryGuid).IsDeleted, Is.True); _lDProject = new LanguageDepotMock(testProjectCode, _lDSettings); var lDcache = _lDProject.FieldWorksProject.Cache; var lDFdoEntry = lDcache.ServiceLocator.GetObject(_testEntryGuid) as ILexEntry; Assert.That(lDFdoEntry.SensesOS[0].Gloss.AnalysisDefaultWritingSystem.Text, Is.EqualTo(unchangedGloss)); DateTime originalLdDateModified = lDFdoEntry.DateModified; // Exercise var sutSynchronize = new SynchronizeAction(_env.Settings, _env.Logger); var timeBeforeRun = DateTime.UtcNow; sutSynchronize.Run(_lfProject); // Verify IEnumerable <LfLexEntry> receivedMongoData = _mongoConnection.GetLfLexEntries(); Assert.That(receivedMongoData, Is.Not.Null); Assert.That(receivedMongoData, Is.Not.Empty); // Deleting entries in LF should *not* remove them, just set the isDeleted flag Assert.That(receivedMongoData.Count(), Is.EqualTo(originalNumOfFdoEntries)); var entry = receivedMongoData.FirstOrDefault(e => e.Guid == _testEntryGuid); Assert.That(entry, Is.Not.Null); Assert.That(entry.IsDeleted, Is.EqualTo(true)); DateTime updatedLfDateModified = entry.DateModified; Assert.That(updatedLfDateModified, Is.GreaterThan(originalLfDateModified)); Assert.That(updatedLfDateModified, Is.GreaterThan(originalLdDateModified)); var cache = _lfProject.FieldWorksProject.Cache; Assert.That(() => cache.ServiceLocator.GetObject(_testEntryGuid), Throws.InstanceOf <KeyNotFoundException>()); Assert.That(_mongoConnection.GetLastSyncedDate(_lfProject), Is.GreaterThanOrEqualTo(timeBeforeRun)); }
/// <summary> /// Return a name suitable for logging from an entry /// </summary> /// <returns>The lexeme(s), if present, otherwise something suitable.</returns> /// <param name="lfEntry">LF entry we want to write about in the log.</param> public static string EntryNameForDebugging(LfLexEntry lfEntry) { if (lfEntry == null) { return("<null entry>"); } if (lfEntry.Lexeme == null || lfEntry.Lexeme.Values == null) { return("<null lexeme>"); } return(String.Join(", ", lfEntry.Lexeme.Values.Where(x => x != null && !x.IsEmpty).Select(x => x.Value))); }
public void SynchronizeAction_LFEntryDeletedLDDataChanged_LDWins() { // Setup TestEnvironment.CopyFwProjectTo(modifiedTestProjectCode, _lDSettings.WebWorkDirectory); Directory.Move(Path.Combine(_lDSettings.WebWorkDirectory, modifiedTestProjectCode), LanguageDepotMock.ProjectFolderPath); _lfProject.IsInitialClone = true; _transferFdoToMongo.Run(_lfProject); IEnumerable <LfLexEntry> originalMongoData = _mongoConnection.GetLfLexEntries(); LfLexEntry lfEntry = originalMongoData.First(e => e.Guid == _testEntryGuid); DateTime originalLfDateModified = lfEntry.DateModified; string unchangedGloss = lfEntry.Senses[0].Gloss["en"].Value; string fwChangedGloss = unchangedGloss + " - changed in FW"; // Don't use _mongoConnection.RemoveRecord to delete the entry. LF uses the "IsDeleted" field lfEntry.IsDeleted = true; var modificationTimestamp = DateTime.UtcNow; lfEntry.AuthorInfo.ModifiedDate = modificationTimestamp; _mongoConnection.UpdateRecord(_lfProject, lfEntry); IEnumerable <LfLexEntry> updatedMongoData = _mongoConnection.GetLfLexEntries(); Assert.That(updatedMongoData.Count(), Is.EqualTo(originalNumOfFdoEntries)); Assert.That(updatedMongoData.First(e => e.Guid == _testEntryGuid).IsDeleted, Is.True); _lDProject = new LanguageDepotMock(testProjectCode, _lDSettings); var lDcache = _lDProject.FieldWorksProject.Cache; var lDFdoEntry = lDcache.ServiceLocator.GetObject(_testEntryGuid) as ILexEntry; Assert.That(lDFdoEntry.SensesOS[0].Gloss.AnalysisDefaultWritingSystem.Text, Is.EqualTo(fwChangedGloss)); DateTime originalLdDateModified = lDFdoEntry.DateModified; // Exercise var sutSynchronize = new SynchronizeAction(_env.Settings, _env.Logger); var timeBeforeRun = DateTime.UtcNow; sutSynchronize.Run(_lfProject); // Verify LD modified entry remains and LF marks not deleted Assert.That(GetGlossFromMongoDb(_testEntryGuid), Is.EqualTo(fwChangedGloss)); Assert.That(GetGlossFromLanguageDepot(_testEntryGuid, 2), Is.EqualTo(fwChangedGloss)); LfLexEntry updatedLfEntry = _mongoConnection.GetLfLexEntries().First(e => e.Guid == _testEntryGuid); Assert.That(updatedLfEntry.IsDeleted, Is.False); DateTime updatedLfDateModified = updatedLfEntry.DateModified; Assert.That(updatedLfDateModified, Is.GreaterThan(originalLfDateModified)); Assert.That(updatedLfDateModified, Is.GreaterThan(originalLdDateModified)); Assert.That(updatedLfDateModified, Is.GreaterThan(modificationTimestamp)); Assert.That(_mongoConnection.GetLastSyncedDate(_lfProject), Is.GreaterThanOrEqualTo(timeBeforeRun)); }
public void RunConversion() { Logger.Notice("FdoToMongo: Converting lexicon for project {0}", LfProject.ProjectCode); ILexEntryRepository repo = GetInstance <ILexEntryRepository>(); if (repo == null) { Logger.Error("Can't find LexEntry repository for FieldWorks project {0}", LfProject.ProjectCode); return; } // Custom field configuration AND view configuration should all be set at once Dictionary <string, LfConfigFieldBase> lfCustomFieldList = new Dictionary <string, LfConfigFieldBase>(); Dictionary <string, string> lfCustomFieldTypes = new Dictionary <string, string>(); _convertCustomField.WriteCustomFieldConfig(lfCustomFieldList, lfCustomFieldTypes); Connection.SetCustomFieldConfig(LfProject, lfCustomFieldList); _convertCustomField.CreateCustomFieldsConfigViews(LfProject, lfCustomFieldList, lfCustomFieldTypes); Dictionary <Guid, DateTime> previousModificationDates = Connection.GetAllModifiedDatesForEntries(LfProject); int i = 1; foreach (ILexEntry fdoEntry in repo.AllInstances()) { bool createdEntry = false; DateTime previousDateModified; if (!previousModificationDates.TryGetValue(fdoEntry.Guid, out previousDateModified)) { // Looks like this entry is new in FDO createdEntry = true; previousDateModified = DateTime.MinValue; // Ensure it will seem modified when comparing it later } // Remember that FDO's DateModified is stored in local time for some incomprehensible reason... if (!createdEntry && previousDateModified.ToLocalTime() == fdoEntry.DateModified) { // Hasn't been modified since last time: just skip this record entirely continue; } LfLexEntry lfEntry = FdoLexEntryToLfLexEntry(fdoEntry); lfEntry.IsDeleted = false; Logger.Info("{3} - FdoToMongo: {0} LfEntry {1} ({2})", createdEntry ? "Created" : "Modified", lfEntry.Guid, ConvertUtilities.EntryNameForDebugging(lfEntry), i++); Connection.UpdateRecord(LfProject, lfEntry); } LfProject.IsInitialClone = false; RemoveMongoEntriesDeletedInFdo(); // Logger.Debug("Running FtMComments, should see comments show up below:"); var commCvtr = new ConvertFdoToMongoComments(Connection, LfProject, Logger, Progress, ProjectRecordFactory); commCvtr.RunConversion(); }
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 SynchronizeAction_LFDataChangedLDEntryDeleted_LFWins() { // Setup TestEnvironment.CopyFwProjectTo(modifiedTestProjectCode, _lDSettings.WebWorkDirectory); Directory.Move(Path.Combine(_lDSettings.WebWorkDirectory, modifiedTestProjectCode), LanguageDepotMock.ProjectFolderPath); _lfProject.IsInitialClone = true; _transferFdoToMongo.Run(_lfProject); IEnumerable <LfLexEntry> originalMongoData = _mongoConnection.GetLfLexEntries(); LfLexEntry lfEntry = originalMongoData.First(e => e.Guid == _testDeletedEntryGuid); DateTime originalLfDateModified = lfEntry.DateModified; Assert.That(lfEntry.Senses.Count, Is.EqualTo(1)); const string lfCreatedGloss = "new English gloss - added in LF"; const string fwChangedGloss = "English gloss - changed in FW"; // LF adds a gloss to the entry that LD is deleting lfEntry.Senses[0].Gloss = LfMultiText.FromSingleStringMapping("en", lfCreatedGloss); lfEntry.AuthorInfo.ModifiedDate = DateTime.UtcNow; _mongoConnection.UpdateRecord(_lfProject, lfEntry); _lDProject = new LanguageDepotMock(testProjectCode, _lDSettings); var lDcache = _lDProject.FieldWorksProject.Cache; Assert.That(() => lDcache.ServiceLocator.GetObject(_testDeletedEntryGuid), Throws.InstanceOf <KeyNotFoundException>()); var lDFdoEntry = lDcache.ServiceLocator.GetObject(_testEntryGuid) as ILexEntry; Assert.That(lDFdoEntry.SensesOS[0].Gloss.AnalysisDefaultWritingSystem.Text, Is.EqualTo(fwChangedGloss)); DateTime originalLdDateModified = lDFdoEntry.DateModified; // Exercise var sutSynchronize = new SynchronizeAction(_env.Settings, _env.Logger); var timeBeforeRun = DateTime.UtcNow; sutSynchronize.Run(_lfProject); // Verify modified LF entry wins Assert.That(GetGlossFromMongoDb(_testDeletedEntryGuid, originalNumOfFdoEntries + 1, 0), Is.EqualTo(lfCreatedGloss)); Assert.That(GetGlossFromMongoDb(_testEntryGuid, originalNumOfFdoEntries + 1, 0), Is.EqualTo(fwChangedGloss)); Assert.That(GetGlossFromLanguageDepot(_testDeletedEntryGuid, 1), Is.EqualTo(lfCreatedGloss)); LfLexEntry updatedLfEntry = _mongoConnection.GetLfLexEntries().First(e => e.Guid == _testEntryGuid); DateTime updatedLfDateModified = updatedLfEntry.DateModified; Assert.That(updatedLfDateModified, Is.GreaterThan(originalLfDateModified)); Assert.That(updatedLfDateModified, Is.GreaterThan(originalLdDateModified)); Assert.That(_mongoConnection.GetLastSyncedDate(_lfProject), Is.GreaterThanOrEqualTo(timeBeforeRun)); }
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 void SynchronizeAction_LFDataChangedLDDataChanged_LFWins() { //Setup TestEnvironment.CopyFwProjectTo(modifiedTestProjectCode, _lDSettings.WebWorkDirectory); Directory.Move(Path.Combine(_lDSettings.WebWorkDirectory, modifiedTestProjectCode), LanguageDepotMock.ProjectFolderPath); _lfProject.IsInitialClone = true; _transferFdoToMongo.Run(_lfProject); IEnumerable <LfLexEntry> originalMongoData = _mongoConnection.GetLfLexEntries(); LfLexEntry lfEntry = originalMongoData.First(e => e.Guid == _testEntryGuid); DateTime originalLfDateModified = lfEntry.DateModified; DateTime originalLfAuthorInfoModifiedDate = lfEntry.AuthorInfo.ModifiedDate; string unchangedGloss = lfEntry.Senses[0].Gloss["en"].Value; string fwChangedGloss = unchangedGloss + " - changed in FW"; string lfChangedGloss = unchangedGloss + " - changed in LF"; lfEntry.Senses[0].Gloss["en"].Value = lfChangedGloss; lfEntry.AuthorInfo.ModifiedDate = DateTime.UtcNow; _mongoConnection.UpdateRecord(_lfProject, lfEntry); _lDProject = new LanguageDepotMock(testProjectCode, _lDSettings); var lDcache = _lDProject.FieldWorksProject.Cache; var lDFdoEntry = lDcache.ServiceLocator.GetObject(_testEntryGuid) as ILexEntry; Assert.That(lDFdoEntry.SensesOS[0].Gloss.AnalysisDefaultWritingSystem.Text, Is.EqualTo(fwChangedGloss)); DateTime originalLdDateModified = lDFdoEntry.DateModified; // Exercise var sutSynchronize = new SynchronizeAction(_env.Settings, _env.Logger); var timeBeforeRun = DateTime.UtcNow; sutSynchronize.Run(_lfProject); // Verify Assert.That(GetGlossFromMongoDb(_testEntryGuid), Is.EqualTo(lfChangedGloss)); LfLexEntry updatedLfEntry = _mongoConnection.GetLfLexEntries().First(e => e.Guid == _testEntryGuid); DateTime updatedLfDateModified = updatedLfEntry.DateModified; DateTime updatedLfAuthorInfoModifiedDate = updatedLfEntry.AuthorInfo.ModifiedDate; // LF had the same data previously; however it's a merge conflict so DateModified // got updated Assert.That(updatedLfDateModified, Is.GreaterThan(originalLfDateModified)); // But the FDO modified date (AuthorInfo.ModifiedDate in LF) should be updated. Assert.That(updatedLfAuthorInfoModifiedDate, Is.GreaterThan(originalLfAuthorInfoModifiedDate)); Assert.That(updatedLfDateModified, Is.GreaterThan(originalLdDateModified)); Assert.That(_mongoConnection.GetLastSyncedDate(_lfProject), Is.GreaterThanOrEqualTo(timeBeforeRun)); }
public void SynchronizeAction_LDDataChanged_GlossChanged() { // Setup TestEnvironment.CopyFwProjectTo(modifiedTestProjectCode, _lDSettings.WebWorkDirectory); Directory.Move(Path.Combine(_lDSettings.WebWorkDirectory, modifiedTestProjectCode), LanguageDepotMock.ProjectFolderPath); _lfProject.IsInitialClone = true; _transferFdoToMongo.Run(_lfProject); IEnumerable <LfLexEntry> originalMongoData = _mongoConnection.GetLfLexEntries(); LfLexEntry lfEntry = originalMongoData.First(e => e.Guid == _testEntryGuid); string unchangedGloss = lfEntry.Senses[0].Gloss["en"].Value; string ldChangedGloss = unchangedGloss + " - changed in FW"; lfEntry = originalMongoData.First(e => e.Guid == _testDeletedEntryGuid); Assert.That(lfEntry.Lexeme["qaa-x-kal"].Value, Is.EqualTo("ken")); int createdEntryCount = originalMongoData.Count(e => e.Guid == _testCreatedEntryGuid); Assert.That(createdEntryCount, Is.EqualTo(0)); _lDProject = new LanguageDepotMock(testProjectCode, _lDSettings); var lDcache = _lDProject.FieldWorksProject.Cache; var lDFdoEntry = lDcache.ServiceLocator.GetObject(_testEntryGuid) as ILexEntry; Assert.That(lDFdoEntry.SensesOS[0].Gloss.AnalysisDefaultWritingSystem.Text, Is.EqualTo(ldChangedGloss)); // Exercise var sutSynchronize = new SynchronizeAction(_env.Settings, _env.Logger); sutSynchronize.Run(_lfProject); // Verify Assert.That(GetGlossFromFdo(_testEntryGuid, 2), Is.EqualTo(ldChangedGloss)); Assert.That(GetGlossFromMongoDb(_testEntryGuid), Is.EqualTo(ldChangedGloss)); IEnumerable <LfLexEntry> receivedMongoData = _mongoConnection.GetLfLexEntries(); lfEntry = receivedMongoData.First(e => e.Guid == _testCreatedEntryGuid); Assert.That(lfEntry.Lexeme["qaa-x-kal"].Value, Is.EqualTo("Ira")); int deletedEntryCount = receivedMongoData.Count(e => e.Guid == _testDeletedEntryGuid && !e.IsDeleted); Assert.That(deletedEntryCount, Is.EqualTo(0)); Assert.That(GetGlossFromLanguageDepot(_testEntryGuid, 2), Is.EqualTo(ldChangedGloss)); }
public void Action_NoDataChanged_ShouldUpdateLexemes() { // Setup var lfProject = _lfProj; // Exercise SutLcmToMongo.Run(lfProject); // Verify string[] searchOrder = new string[] { "en", "fr" }; string expectedLexeme = "zitʰɛstmen"; IEnumerable <LfLexEntry> receivedData = _conn.GetLfLexEntries(); Assert.That(receivedData, Is.Not.Null); Assert.That(receivedData, Is.Not.Empty); LfLexEntry entry = receivedData.FirstOrDefault(e => e.Guid.ToString() == TestEntryGuidStr); Assert.That(entry, Is.Not.Null); Assert.That(entry.Lexeme.BestString(searchOrder), Is.EqualTo(expectedLexeme)); }
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")); }
public void SynchronizeAction_NoChangedData_GlossUnchanged() { // Setup TestEnvironment.CopyFwProjectTo(testProjectCode, _lDSettings.WebWorkDirectory); _lfProject.IsInitialClone = true; _transferFdoToMongo.Run(_lfProject); // Exercise var sutSynchronize = new SynchronizeAction(_env.Settings, _env.Logger); sutSynchronize.Run(_lfProject); // Verify IEnumerable <LfLexEntry> receivedMongoData = _mongoConnection.GetLfLexEntries(); Assert.That(receivedMongoData, Is.Not.Null); Assert.That(receivedMongoData, Is.Not.Empty); Assert.That(receivedMongoData.Count(), Is.EqualTo(originalNumOfFdoEntries)); LfLexEntry lfEntry = receivedMongoData.First(e => e.Guid == _testEntryGuid); Assert.That(lfEntry.Senses[0].Gloss["en"].Value, Is.EqualTo("English gloss")); }
public void Action_IsInitialClone_ShouldUpdateDates() { // Setup var lfProject = _lfProj; lfProject.IsInitialClone = true; // Exercise SutLcmToMongo.Run(lfProject); // Verify IEnumerable <LfLexEntry> receivedData = _conn.GetLfLexEntries(); Assert.That(receivedData, Is.Not.Null); Assert.That(receivedData, Is.Not.Empty); LfLexEntry entry = receivedData.FirstOrDefault(e => e.Guid.ToString() == TestEntryGuidStr); Assert.That(entry, Is.Not.Null); Assert.That(entry.DateCreated, Is.EqualTo(DateTime.UtcNow).Within(5).Seconds); Assert.That(entry.DateModified, Is.EqualTo(DateTime.UtcNow).Within(5).Seconds); Assert.That(entry.AuthorInfo.CreatedDate, Is.EqualTo(DateTime.Parse("2004-10-19 02:42:02.903"))); Assert.That(entry.AuthorInfo.ModifiedDate, Is.EqualTo(DateTime.Parse("2016-02-25 03:51:29.404"))); }
/// <summary> /// Convert FDO lex entry to LF lex entry. /// </summary> /// <returns>LF entry /// <param name="fdoEntry">Fdo entry.</param> private LfLexEntry FdoLexEntryToLfLexEntry(ILexEntry fdoEntry) { if (fdoEntry == null) { return(null); } ILgWritingSystem AnalysisWritingSystem = ServiceLocator.LanguageProject.DefaultAnalysisWritingSystem; ILgWritingSystem VernacularWritingSystem = ServiceLocator.LanguageProject.DefaultVernacularWritingSystem; var lfEntry = new LfLexEntry(); IMoForm fdoLexeme = fdoEntry.LexemeFormOA; if (fdoLexeme == null) { lfEntry.Lexeme = null; } else { lfEntry.Lexeme = ToMultiText(fdoLexeme.Form); } // Other fields of fdoLexeme (AllomorphEnvironments, LiftResidue, MorphTypeRA, etc.) not mapped // Fields below in alphabetical order by ILexSense property, except for Lexeme foreach (IMoForm allomorph in fdoEntry.AlternateFormsOS) { // Do nothing; LanguageForge doesn't currently handle allomorphs, so we don't convert them } lfEntry.EntryBibliography = ToMultiText(fdoEntry.Bibliography); // TODO: Consider whether to use fdoEntry.CitationFormWithAffixType instead // (which would produce "-s" instead of "s" for the English plural suffix, for instance) lfEntry.CitationForm = ToMultiText(fdoEntry.CitationForm); lfEntry.Note = ToMultiText(fdoEntry.Comment); // DateModified and DateCreated can be confusing, because LF and FDO are doing two different // things with them. In FDO, there is just one DateModified and one DateCreated; simple. But // in LF, there is an AuthorInfo record as well, which contains its own ModifiedDate and CreatedDate // fields. (Note the word order: there's LfEntry.DateCreated, and LfEntry.AuthorInfo.CreatedDate). // The conversion we have chosen to use is: AuthorInfo will correspond to FDO. So FDO.DateCreated // becomes AuthorInfo.CreatedDate, and FDO.DateModified becomes AuthorInfo.ModifiedDate. The two // fields on the LF entry will instead refer to when the *Mongo record* was created or modified, // and the LfEntry.DateCreated and LfEntry.DateModified fields will never be put into FDO. var now = DateTime.UtcNow; if (LfProject.IsInitialClone) { lfEntry.DateCreated = now; } // LanguageForge needs this modified to know there is changed data lfEntry.DateModified = now; if (lfEntry.AuthorInfo == null) { lfEntry.AuthorInfo = new LfAuthorInfo(); } lfEntry.AuthorInfo.CreatedByUserRef = null; lfEntry.AuthorInfo.CreatedDate = fdoEntry.DateCreated.ToUniversalTime(); lfEntry.AuthorInfo.ModifiedByUserRef = null; lfEntry.AuthorInfo.ModifiedDate = fdoEntry.DateModified.ToUniversalTime(); #if DBVERSION_7000068 ILexEtymology fdoEtymology = fdoEntry.EtymologyOA; #else // TODO: Once LF's data model is updated from a single etymology to an array, // convert all of them instead of just the first. E.g., // foreach (ILexEtymology fdoEtymology in fdoEntry.EtymologyOS) { ... } ILexEtymology fdoEtymology = null; if (fdoEntry.EtymologyOS.Count > 0) { fdoEtymology = fdoEntry.EtymologyOS.First(); } #endif if (fdoEtymology != null) { lfEntry.Etymology = ToMultiText(fdoEtymology.Form); lfEntry.EtymologyComment = ToMultiText(fdoEtymology.Comment); lfEntry.EtymologyGloss = ToMultiText(fdoEtymology.Gloss); #if DBVERSION_7000068 lfEntry.EtymologySource = LfMultiText.FromSingleStringMapping(AnalysisWritingSystem.Id, fdoEtymology.Source); #else lfEntry.EtymologySource = ToMultiText(fdoEtymology.LanguageNotes); #endif // fdoEtymology.LiftResidue not mapped } lfEntry.Guid = fdoEntry.Guid; if (fdoEntry.LIFTid == null) { lfEntry.LiftId = null; } else { lfEntry.LiftId = fdoEntry.LIFTid.Normalize(System.Text.NormalizationForm.FormC); // Because LIFT files on disk are NFC and we need to make sure LiftIDs match those on disk } lfEntry.LiteralMeaning = ToMultiText(fdoEntry.LiteralMeaning); if (fdoEntry.PrimaryMorphType != null) { lfEntry.MorphologyType = fdoEntry.PrimaryMorphType.NameHierarchyString; } // TODO: Once LF's data model is updated from a single pronunciation to an array of pronunciations, convert all of them instead of just the first. E.g., //foreach (ILexPronunciation fdoPronunciation in fdoEntry.PronunciationsOS) { ... } if (fdoEntry.PronunciationsOS.Count > 0) { ILexPronunciation fdoPronunciation = fdoEntry.PronunciationsOS.First(); lfEntry.Pronunciation = ToMultiText(fdoPronunciation.Form); lfEntry.CvPattern = LfMultiText.FromSingleITsString(fdoPronunciation.CVPattern, ServiceLocator.WritingSystemFactory); lfEntry.Tone = LfMultiText.FromSingleITsString(fdoPronunciation.Tone, ServiceLocator.WritingSystemFactory); // TODO: Map fdoPronunciation.MediaFilesOS properly (converting video to sound files if necessary) lfEntry.Location = ToStringField(LocationListCode, fdoPronunciation.LocationRA); } lfEntry.EntryRestrictions = ToMultiText(fdoEntry.Restrictions); if (lfEntry.Senses == null) // Shouldn't happen, but let's be careful { lfEntry.Senses = new List <LfSense>(); } lfEntry.Senses.AddRange(fdoEntry.SensesOS.Select(FdoSenseToLfSense)); lfEntry.SummaryDefinition = ToMultiText(fdoEntry.SummaryDefinition); BsonDocument customFieldsAndGuids = _convertCustomField.GetCustomFieldsForThisCmObject(fdoEntry, "entry", ListConverters); BsonDocument customFieldsBson = customFieldsAndGuids["customFields"].AsBsonDocument; BsonDocument customFieldGuids = customFieldsAndGuids["customFieldGuids"].AsBsonDocument; lfEntry.CustomFields = customFieldsBson; lfEntry.CustomFieldGuids = customFieldGuids; return(lfEntry); /* Fields not mapped because it doesn't make sense to map them (e.g., Hvo, backreferences, etc): * fdoEntry.ComplexFormEntries; * fdoEntry.ComplexFormEntryRefs; * fdoEntry.ComplexFormsNotSubentries; * fdoEntry.EntryRefsOS; * fdoEntry.HasMoreThanOneSense; * fdoEntry.HeadWord; // Read-only virtual property * fdoEntry.IsMorphTypesMixed; // Read-only property * fdoEntry.LexEntryReferences; * fdoEntry.MainEntriesOrSensesRS; * fdoEntry.MinimalLexReferences; * fdoEntry.MorphoSyntaxAnalysesOC; * fdoEntry.MorphTypes; * fdoEntry.NumberOfSensesForEntry; * fdoEntry.PicturesOfSenses; * */ /* Fields that would make sense to map, but that we don't because LF doesn't handle them (e.g., allomorphs): * fdoEntry.AllAllomorphs; // LF doesn't handle allomorphs, so skip all allomorph-related fields * fdoEntry.AlternateFormsOS; * fdoEntry.CitationFormWithAffixType; // Citation form already mapped * fdoEntry.DoNotPublishInRC; * fdoEntry.DoNotShowMainEntryInRC; * fdoEntry.DoNotUseForParsing; * fdoEntry.HomographForm; * fdoEntry.HomographFormKey; * fdoEntry.HomographNumber; * fdoEntry.ImportResidue; * fdoEntry.LiftResidue; * fdoEntry.PronunciationsOS * fdoEntry.PublishAsMinorEntry; * fdoEntry.PublishIn; * fdoEntry.ShowMainEntryIn; * fdoEntry.Subentries; * fdoEntry.VariantEntryRefs; * fdoEntry.VariantFormEntries; * fdoEntry.VisibleComplexFormBackRefs; * fdoEntry.VisibleComplexFormEntries; * fdoEntry.VisibleVariantEntryRefs; * */ }
public bool UpdateRecord(ILfProject project, LfLexEntry data) { _storedLfLexEntries[data.Guid ?? Guid.Empty] = DeepCopy(data); return(true); }
public void UpdateMockLfLexEntry(LfLexEntry mockData) { Guid guid = mockData.Guid ?? Guid.Empty; _storedLfLexEntries[guid] = DeepCopy(mockData); }
public void UpdateMockLfLexEntry(BsonDocument mockData) { LfLexEntry data = BsonSerializer.Deserialize <LfLexEntry>(mockData); UpdateMockLfLexEntry(data); }
public void SynchronizeAction_CustomReferenceAtomicField_DoesNotThrowExceptionDuringSync() { // Setup // Buggy code path needs us to change the field writing system to a "magic" ws (it's 0 in the original data/testlangproj project) var lcmMetaData = _lfProject.FieldWorksProject.Cache.MetaDataCacheAccessor as SIL.LCModel.Infrastructure.IFwMetaDataCacheManaged; int listRef_flid = lcmMetaData.GetFieldIds().FirstOrDefault(flid => lcmMetaData.GetFieldLabel(flid) == "Cust Single ListRef"); Assert.AreNotEqual(0, listRef_flid, "Cust Single ListRef field not found in test data"); string fieldLabel = lcmMetaData.GetFieldLabel(listRef_flid); string fieldHelp = lcmMetaData.GetFieldHelp(listRef_flid); int wsid = SIL.LCModel.DomainServices.WritingSystemServices.kwsAnal; lcmMetaData.UpdateCustomField(listRef_flid, fieldHelp, wsid, fieldLabel); TestEnvironment.CopyFwProjectTo(testProjectCode, _lDSettings.WebWorkDirectory); _lfProject.IsInitialClone = true; _transferLcmToMongo.Run(_lfProject); // To look at Mongo optionlist before test runs, uncomment this block // var x = _mongoConnection.GetLfOptionLists().FirstOrDefault(l => l.Code == "domain-type"); // if (x != null) { // foreach (LfOptionListItem item in x.Items) { // Console.WriteLine($"{item.Guid} ({item.Key}) => {item.Value}"); // } // } // Buggy code path requires that there not be a GUID in the Mongo data IEnumerable <LfLexEntry> originalMongoData = _mongoConnection.GetLfLexEntries(); LfLexEntry lfEntry = originalMongoData.First(e => e.Guid == _testEntryGuid); lfEntry.CustomFieldGuids.Remove("customField_entry_Cust_Single_ListRef"); DateTime originalLfDateModified = lfEntry.DateModified; DateTime originalLfAuthorInfoModifiedDate = lfEntry.AuthorInfo.ModifiedDate; lfEntry.AuthorInfo.ModifiedDate = DateTime.UtcNow; _mongoConnection.UpdateRecord(_lfProject, lfEntry); _lDProject = new LanguageDepotMock(testProjectCode, _lDSettings); var lDcache = _lDProject.FieldWorksProject.Cache; var lDLcmEntry = lDcache.ServiceLocator.GetObject(_testEntryGuid) as ILexEntry; var data = (SIL.LCModel.Application.ISilDataAccessManaged)lDcache.DomainDataByFlid; int ownedHvo = data.get_ObjectProp(lDLcmEntry.Hvo, listRef_flid); Assert.AreNotEqual(0, ownedHvo, "Custom field value in test data was invalid during setup"); Assert.IsTrue(data.get_IsValidObject(ownedHvo), "Custom field value in test data was invalid during setup"); ICmObject referencedObject = lDcache.GetAtomicPropObject(ownedHvo); Assert.IsNotNull(referencedObject, "Custom field in test data referenced invalid CmObject during setup"); DateTime originalLdDateModified = lDLcmEntry.DateModified; // Exercise var sutSynchronize = new SynchronizeAction(_env.Settings, _env.Logger); var timeBeforeRun = DateTime.UtcNow; sutSynchronize.Run(_lfProject); // Verify LfLexEntry updatedLfEntry = _mongoConnection.GetLfLexEntries().First(e => e.Guid == _testEntryGuid); var updatedLcmEntry = lDcache.ServiceLocator.GetObject(_testEntryGuid) as ILexEntry; ownedHvo = data.get_ObjectProp(updatedLcmEntry.Hvo, listRef_flid); Assert.AreNotEqual(0, ownedHvo, "Custom field value in test data was invalid after running sync"); Assert.IsTrue(data.get_IsValidObject(ownedHvo), "Custom field value in test data was invalid after running sync"); referencedObject = lDcache.GetAtomicPropObject(ownedHvo); Assert.IsNotNull(referencedObject, "Custom field in test data referenced invalid CmObject after running sync"); var poss = referencedObject as ICmPossibility; // TODO: Write another test to check on the abbrev hierarchy, because we may have a bug here (LfMerge not doing correct optionlist keys for hierarchical items) // Console.WriteLine($"Abbrev hierarchy: {poss.AbbrevHierarchyString}"); Assert.IsNotNull(poss, "Custom field value in test data did not reference a CmPossibility object after running sync"); }