public void Add(WitDiffPair pair) { m_witDiffPairs.Add(pair); switch (pair.DiffType) { case WitDiffType.AttachmentCount: case WitDiffType.AttachmentMismatch: case WitDiffType.AttachmentMissing: AttachmentMismatchCount++; break; case WitDiffType.DataMismatch: case WitDiffType.DefinitionMismatch: case WitDiffType.InvalidDefinition: ContentMismatchCount++; break; case WitDiffType.LinkCount: case WitDiffType.LinkMismatch: case WitDiffType.LinkMisssing: LinkMismatchCount++; break; case WitDiffType.NotMirrored: MissingWorkItemCount++; break; default: Debug.Fail("Unexpected WitDiffType: " + pair.DiffType.ToString()); break; } }
private bool HandleDifference(WitDiffPair diffPair, bool forceDiff) { // First check if the difference is a false alarm because a work item has been modified on the server since the last sync point bool ignoreDiff = false; if (!forceDiff) { ignoreDiff = IgnoreDifference(diffPair); } if (!ignoreDiff) { m_diffResult.Add(diffPair); } return(ignoreDiff); }
private bool IgnoreDifference(WitDiffPair diffPair) { // TODO: This assumes the HighWaterMark value for all work items is a date time // Should pass HighWaterMark instead? DateTime sourceHighWaterMarkTime = DateTime.MinValue; if (m_latestSyncPoint != null && !DateTime.TryParse(m_latestSyncPoint.SourceHighWaterMarkValue, out sourceHighWaterMarkTime)) { // There is no sync point for the session or we can't parse the DateTime value, so we need to assume the difference cannot be ignored return(false); } if (diffPair.Side1DiffItem != null && diffPair.Side1DiffItem.HasBeenModifiedSince(sourceHighWaterMarkTime)) { string side2workItemId = (diffPair.Side2DiffItem == null) ? MigrationToolkitResources.WitDiffUnknownWorkItemId : diffPair.Side2DiffItem.WorkItemId; m_serverDiffEngine.LogInfo(String.Format(CultureInfo.InvariantCulture, MigrationToolkitResources.WitDiffIngoringDiff1, diffPair.Side1DiffItem.WorkItemId, diffPair.Side1Name, side2workItemId, diffPair.Side2Name)); return(true); } MigrationItemId lastMigratedTargetItemId = new MigrationItemId(); lastMigratedTargetItemId.ItemId = m_latestSyncPoint.LastMigratedTargetItemId; lastMigratedTargetItemId.ItemVersion = m_latestSyncPoint.LastMigratedTargetItemVersion; if (diffPair.Side2DiffItem != null && diffPair.Side2DiffItem.HasBeenModifiedMoreRecentlyThan(lastMigratedTargetItemId)) { string side1workItemId = (diffPair.Side1DiffItem == null) ? MigrationToolkitResources.WitDiffUnknownWorkItemId : diffPair.Side1DiffItem.WorkItemId; m_serverDiffEngine.LogInfo(String.Format(CultureInfo.InvariantCulture, MigrationToolkitResources.WitDiffIngoringDiff2, side1workItemId, diffPair.Side1Name, diffPair.Side2DiffItem.WorkItemId, diffPair.Side2Name)); return(true); } return(false); }
/// <summary> /// Handle a difference found between two work items; a difference does not necessarily cause the entire /// diff operation to result in a difference, because we want to compare the contents at the time of the /// last sync point (when one direction of the migration was completed) and not reported differences caused /// by changes to work items since then that have not been sync'd to the other side. /// </summary> /// <param name="diffPair">A DiffPair object describing the difference just identified</param> /// <returns>True if differences betweeen the two work items in the pair should be ignored</returns> private bool HandleDifference(WitDiffPair diffPair) { return(HandleDifference(diffPair, false)); }
private void CompareWorkItemsFromOneSideToTheOther( string side1FilterString, string side1MigrationSourceId, MigrationSource side1MigrationSource, MigrationSource side2MigrationSource, IWITDiffProvider side1DiffProvider, IWITDiffProvider side2DiffProvider, string side1QueryCondition, HashSet <string> moreSide1FieldNamesToIgnore, HashSet <string> moreSide2FieldNamesToIgnore) { // Only left query condition specified side1DiffProvider.InitializeForDiff(side1FilterString, !m_serverDiffEngine.NoContentComparison); side2DiffProvider.InitializeForDiff(string.Empty, !m_serverDiffEngine.NoContentComparison); if (m_serverDiffEngine.NoContentComparison) { m_serverDiffEngine.LogVerbose(String.Format(CultureInfo.InvariantCulture, MigrationToolkitResources.WitDiffNoContentComparison)); } foreach (IWITDiffItem side1WitDiffItem in side1DiffProvider.GetWITDiffItems(side1QueryCondition)) { bool skipCurrentWorkItem = false; bool fieldValuesMismatch = false; Guid side1MigrationSourceGuid = new Guid(side1MigrationSourceId); IWITDiffItem side2WitDiffItem = null; string side2WorkItemId = TranslationService.TryGetTargetItemId(side1WitDiffItem.WorkItemId, side1MigrationSourceGuid); if (!string.IsNullOrEmpty(side2WorkItemId)) { side2WitDiffItem = side2DiffProvider.GetWITDiffItem(side2WorkItemId); } if (side2WitDiffItem == null) { HandleDifference(new WitDiffPair(WitDiffType.NotMirrored, side1MigrationSource.FriendlyName, side1WitDiffItem, side2MigrationSource.FriendlyName, side2WitDiffItem)); continue; } if (m_serverDiffEngine.NoContentComparison) { /* Uncomment for debugging * m_serverDiffEngine.LogVerbose(String.Format(CultureInfo.InvariantCulture, "Corresponding work item with Id {0} found on '{1}' for work item with Id {2} on '{3}", * side2WorkItemId, side2MigrationSource.FriendlyName, side1WitDiffItem.WorkItemId, side1MigrationSource.FriendlyName)); */ } else { TranslationService.MapWorkItemTypeFieldValues(side1WitDiffItem.WorkItemId, side1WitDiffItem.WorkItemDetails, side1MigrationSourceGuid); XmlElement side1RootElement = side1WitDiffItem.WorkItemDetails.DocumentElement; XmlElement side2RootElement = side2WitDiffItem.WorkItemDetails.DocumentElement; HashSet <string> side2AttributesFound = new HashSet <string>(); foreach (XmlAttribute side1Attribute in side1RootElement.Attributes) { if (side1DiffProvider.IgnoreFieldInComparison(side1Attribute.Name) || side2DiffProvider.IgnoreFieldInComparison(side1Attribute.Name) || moreSide1FieldNamesToIgnore.Contains(side1Attribute.Name) || moreSide2FieldNamesToIgnore.Contains(side1Attribute.Name)) { continue; } /* Uncomment for debugging * m_serverDiffEngine.LogVerbose(String.Format(CultureInfo.InvariantCulture, * "Comparing field '{0}' from source '{1}'", * side1Attribute.Name, side1MigrationSource.FriendlyName)); */ string side2AttributeValue = side2RootElement.GetAttribute(side1Attribute.Name); if (string.IsNullOrEmpty(side2AttributeValue)) { WitDiffPair diffPair = new WitDiffPair(WitDiffType.DefinitionMismatch, side1MigrationSource.FriendlyName, side1WitDiffItem, side2MigrationSource.FriendlyName, side2WitDiffItem); diffPair.AddMissingField(new WitDiffMissingField(side1Attribute.Name, side1MigrationSource.FriendlyName)); skipCurrentWorkItem = HandleDifference(diffPair); break; } side2AttributesFound.Add(side1Attribute.Name); if (!string.Equals(side1Attribute.Value, side2AttributeValue, StringComparison.OrdinalIgnoreCase)) { WitDiffPair diffPair = new WitDiffPair(WitDiffType.DataMismatch, side1MigrationSource.FriendlyName, side1WitDiffItem, side2MigrationSource.FriendlyName, side2WitDiffItem); diffPair.AddMismatchField(new WitDiffField(side1Attribute.Name, side1Attribute.Value, side2AttributeValue)); skipCurrentWorkItem = HandleDifference(diffPair); break; } else { /* Uncomment for debugging * m_serverDiffEngine.LogVerbose(String.Format(CultureInfo.InvariantCulture, "Match: The header field '{0}' for work item {1} has value '{2}' on both sides", * side1Attribute.Name, side1WitDiffItem.WorkItemId, side1Attribute.Value)); */ } } if (skipCurrentWorkItem) { continue; } foreach (XmlAttribute side2Attribute in side2RootElement.Attributes) { if (!side1DiffProvider.IgnoreFieldInComparison(side2Attribute.Name) && !side2DiffProvider.IgnoreFieldInComparison(side2Attribute.Name) && !moreSide1FieldNamesToIgnore.Contains(side2Attribute.Name) && !moreSide2FieldNamesToIgnore.Contains(side2Attribute.Name) && !side2AttributesFound.Contains(side2Attribute.Name)) { WitDiffPair diffPair = new WitDiffPair(WitDiffType.DefinitionMismatch, side1MigrationSource.FriendlyName, side1WitDiffItem, side2MigrationSource.FriendlyName, side2WitDiffItem); diffPair.AddMissingField(new WitDiffMissingField(side2Attribute.Name, side2MigrationSource.FriendlyName)); skipCurrentWorkItem = HandleDifference(diffPair); break; } } if (skipCurrentWorkItem) { continue; } XmlNodeList side1Columns = side1RootElement.SelectNodes("/WorkItemChanges/Columns/Column"); if (null == side1Columns) { m_diffResult.ProcessingErrors.Add(String.Format(CultureInfo.InvariantCulture, ServerDiffResources.InvalidXMLDocumentForDiffItem, side1MigrationSource.FriendlyName)); continue; } XmlNodeList side2Columns = side2RootElement.SelectNodes("/WorkItemChanges/Columns/Column"); if (null == side2Columns) { m_diffResult.ProcessingErrors.Add(String.Format(CultureInfo.InvariantCulture, ServerDiffResources.InvalidXMLDocumentForDiffItem, side2MigrationSource.FriendlyName)); continue; } Dictionary <string, XmlNode> side2ColumnsByReferenceName = new Dictionary <string, XmlNode>(); foreach (XmlNode side2Column in side2Columns) { string referenceName = GetReferenceNameFromFieldColumn(side2Column); if (referenceName != null) { if (!side2ColumnsByReferenceName.ContainsKey(referenceName)) { side2ColumnsByReferenceName.Add(referenceName, side2Column); } } } for (int i = 0; i < side1Columns.Count; i++) { XmlNode side1Column = side1Columns[i]; string side1ReferenceName = GetReferenceNameFromFieldColumn(side1Column); if (side1ReferenceName == null) { WitDiffPair diffPair = new WitDiffPair(WitDiffType.InvalidDefinition, side1MigrationSource.FriendlyName, side1WitDiffItem); skipCurrentWorkItem = HandleDifference(diffPair, true); break; } XmlNode side2Column; if (!side2ColumnsByReferenceName.TryGetValue(side1ReferenceName, out side2Column)) { WitDiffPair diffPair = new WitDiffPair(WitDiffType.DefinitionMismatch, side1MigrationSource.FriendlyName, side1WitDiffItem, side2MigrationSource.FriendlyName, side2WitDiffItem); diffPair.AddMissingField(new WitDiffMissingField(side1ReferenceName, side1MigrationSource.FriendlyName)); skipCurrentWorkItem = HandleDifference(diffPair, true); break; } string side2ReferenceName = GetReferenceNameFromFieldColumn(side2Column); // Remove the side2Column from the Dictionary side2ColumnsByReferenceName.Remove(side2ReferenceName); string fieldDisplayName = null; const string c_displayName = "DisplayName"; XmlAttribute side1DisplayName = side1Column.Attributes[c_displayName]; if (side1DisplayName != null && !string.IsNullOrEmpty(side1DisplayName.Value)) { fieldDisplayName = side1DisplayName.Value; } else { fieldDisplayName = side1ReferenceName; } if (!side1DiffProvider.IgnoreFieldInComparison(side1ReferenceName) && !side2DiffProvider.IgnoreFieldInComparison(side1ReferenceName) && !moreSide1FieldNamesToIgnore.Contains(side1ReferenceName) && !moreSide2FieldNamesToIgnore.Contains(side1ReferenceName)) { if (!side1Column.HasChildNodes) { m_diffResult.ProcessingErrors.Add(String.Format(MigrationToolkitResources.WitDiffWorkItemDescriptionMissingChildNodes, side1WitDiffItem.WorkItemId, side1MigrationSource.FriendlyName, side1Column.Name)); continue; } if (!side2Column.HasChildNodes) { m_diffResult.ProcessingErrors.Add(String.Format(MigrationToolkitResources.WitDiffWorkItemDescriptionMissingChildNodes, side2WitDiffItem.WorkItemId, side2MigrationSource.FriendlyName, side2Column.Name)); continue; } XmlNode side1ValueNode = side1Column.FirstChild; XmlNode side2ValueNode = side2Column.FirstChild; if (!string.Equals(side1ValueNode.InnerText.Replace("\r\n", "\n"), side2ValueNode.InnerText.Replace("\r\n", "\n"), StringComparison.OrdinalIgnoreCase)) { WitDiffPair diffPair = new WitDiffPair(WitDiffType.DataMismatch, side1MigrationSource.FriendlyName, side1WitDiffItem, side2MigrationSource.FriendlyName, side2WitDiffItem); diffPair.AddMismatchField(new WitDiffField(fieldDisplayName, side1ValueNode.InnerText, side2ValueNode.InnerText)); skipCurrentWorkItem = HandleDifference(diffPair); // If there's a data mismatch, continue to compare fields to report additional data mismatches // unless HandleDifference returns true to skip the entire work item if (skipCurrentWorkItem) { break; } fieldValuesMismatch = true; } else { /* Uncomment for debugging * m_serverDiffEngine.LogVerbose(String.Format(CultureInfo.InvariantCulture, "Match: The field '{0}' for work item {1} has value '{2}' on both sides", * fieldDisplayName, side1WitDiffItem.WorkItemId, side1ValueNode.InnerText)); */ } } } bool attachmentsMismatch = false; bool linksMismatch = false; if (!skipCurrentWorkItem) { // Compare Attachments if (side1WitDiffItem.Attachments.Count != side2WitDiffItem.Attachments.Count) { WitDiffPair diffPair = new WitDiffPair(WitDiffType.AttachmentCount, side1MigrationSource.FriendlyName, side1WitDiffItem, side2MigrationSource.FriendlyName, side2WitDiffItem); skipCurrentWorkItem = HandleDifference(diffPair); attachmentsMismatch = true; } else { Dictionary <string, IMigrationFileAttachment> side2AttachmentsById = new Dictionary <string, IMigrationFileAttachment>(); foreach (IMigrationFileAttachment side2Attachment in side2WitDiffItem.Attachments) { string attachmentId = GetAttachmentId(side2Attachment); if (!side2AttachmentsById.ContainsKey(attachmentId)) { side2AttachmentsById.Add(attachmentId, side2Attachment); } } HashSet <string> side1AttachmentIds = new HashSet <string>(); foreach (IMigrationFileAttachment side1Attachment in side1WitDiffItem.Attachments) { string side1AttachmentId = GetAttachmentId(side1Attachment); if (side1AttachmentIds.Contains(side1AttachmentId)) { // This is a duplicate attachment; continue to ignore the duplicate continue; } side1AttachmentIds.Add(side1AttachmentId); IMigrationFileAttachment side2Attachment; if (side2AttachmentsById.TryGetValue(side1AttachmentId, out side2Attachment)) { side2AttachmentsById.Remove(side1AttachmentId); WitDiffAttachment diffAttachment; if (!AttachmentsMatch(side1Attachment, side2Attachment, out diffAttachment)) { WitDiffPair diffPair = new WitDiffPair(WitDiffType.AttachmentMismatch, side1MigrationSource.FriendlyName, side1WitDiffItem, side2MigrationSource.FriendlyName, side2WitDiffItem); diffPair.AddMistmatchedAttachment(diffAttachment); skipCurrentWorkItem = HandleDifference(diffPair); attachmentsMismatch = true; } } else { WitDiffPair diffPair = new WitDiffPair(WitDiffType.AttachmentMissing, side1MigrationSource.FriendlyName, side1WitDiffItem, side2MigrationSource.FriendlyName, side2WitDiffItem); diffPair.AddMissingAttachment(side1Attachment.Name); skipCurrentWorkItem = HandleDifference(diffPair); attachmentsMismatch = true; } if (skipCurrentWorkItem) { break; } } if (!skipCurrentWorkItem) { // Any attachments still in side2AttachmentsByKey were not in side1 foreach (IMigrationFileAttachment side2Attachment in side2AttachmentsById.Values) { WitDiffPair diffPair = new WitDiffPair(WitDiffType.AttachmentMissing, side1MigrationSource.FriendlyName, side1WitDiffItem, side2MigrationSource.FriendlyName, side2WitDiffItem); diffPair.AddMissingAttachment(side2Attachment.Name); skipCurrentWorkItem = HandleDifference(diffPair); attachmentsMismatch = true; } } } } if (!skipCurrentWorkItem) { // Compare links if (side1WitDiffItem.Links.Count != side2WitDiffItem.Links.Count) { WitDiffPair diffPair = new WitDiffPair(WitDiffType.LinkCount, side1MigrationSource.FriendlyName, side1WitDiffItem, side2MigrationSource.FriendlyName, side2WitDiffItem); skipCurrentWorkItem = HandleDifference(diffPair); linksMismatch = true; } /* Commenting out the detailed comparison of each link for now as it does not work because the link artifact URIs * need to be translated. This requires some refactoring of translation methods current embedded in the LinkEngine * that are not easily used outside the context of a full running migration/sync session. * else * { * Dictionary<string, ILink> side2LinksById = new Dictionary<string, ILink>(); * foreach (ILink side2Link in side2WitDiffItem.Links) * { * string side2LinkId = GetLinkId(side2Link); * if (!side2LinksById.ContainsKey(side2LinkId)) * { * side2LinksById.Add(side2LinkId, side2Link); * } * } * foreach (ILink side1Link in side1WitDiffItem.Links) * { * ILink side2Link; * string side1LinkId = GetLinkId(side1Link); * if (side2LinksById.TryGetValue(side1LinkId, out side2Link)) * { * side2LinksById.Remove(side1LinkId); * WitDiffLink diffLink; * if (!LinksMatch(new Guid(side1MigrationSource.InternalUniqueId), side1Link, new Guid(side2MigrationSource.InternalUniqueId), side2Link, out diffLink)) * { * WitDiffPair diffPair = new WitDiffPair(WitDiffType.LinkMismatch, * side1MigrationSource.FriendlyName, side1WitDiffItem, side2MigrationSource.FriendlyName, side2WitDiffItem); * diffPair.AddMistmatchedLink(diffLink); * skipCurrentWorkItem = HandleDifference(diffPair); * linksMismatch = true; * break; * } * * } * else * { * WitDiffPair diffPair = new WitDiffPair(WitDiffType.LinkMisssing, * side1MigrationSource.FriendlyName, side1WitDiffItem, side2MigrationSource.FriendlyName, side2WitDiffItem); * diffPair.AddMissingLink(side1LinkId); * skipCurrentWorkItem = HandleDifference(diffPair); * linksMismatch = true; * } * } * * if (!skipCurrentWorkItem) * { * // Any links still in side2LinksById were not in side1 * foreach (ILink side2link in side2LinksById.Values) * { * WitDiffPair diffPair = new WitDiffPair(WitDiffType.LinkMisssing, * side1MigrationSource.FriendlyName, side1WitDiffItem, side2MigrationSource.FriendlyName, side2WitDiffItem); * diffPair.AddMissingLink(GetLinkId(side2link)); * skipCurrentWorkItem = HandleDifference(diffPair); * linksMismatch = true; * } * } * } */ } if (skipCurrentWorkItem || fieldValuesMismatch || attachmentsMismatch || linksMismatch) { continue; } m_serverDiffEngine.LogVerbose(String.Format(CultureInfo.InvariantCulture, MigrationToolkitResources.WitDiffWorkItemsMatch, side1WitDiffItem.WorkItemId, side1MigrationSource.FriendlyName, side2WorkItemId, side2MigrationSource.FriendlyName)); } } }
public void LogDifference(WitDiffPair diffPair) { string side1WorkItemId = diffPair.Side1DiffItem == null ? string.Empty : diffPair.Side1DiffItem.WorkItemId; string side2WorkItemId = diffPair.Side2DiffItem == null ? string.Empty : diffPair.Side2DiffItem.WorkItemId; m_serverDiffEngine.LogError(String.Format(CultureInfo.InvariantCulture, MigrationToolkitResources.WitDiffResultWorkItemMismatch, side1WorkItemId, diffPair.Side1Name, side2WorkItemId, diffPair.Side2Name, diffPair.DiffType.ToString())); if (diffPair.MissingFields.Count > 0) { m_serverDiffEngine.LogError(MigrationToolkitResources.WitDiffResultWorkItemFieldMissingHeader); foreach (WitDiffMissingField missingField in diffPair.MissingFields) { m_serverDiffEngine.LogError(String.Format(CultureInfo.InvariantCulture, MigrationToolkitResources.WitDiffResultWorkItemFieldMissing, missingField.FieldName, missingField.SourceName)); } } if (diffPair.DiffFields.Count > 0) { m_serverDiffEngine.LogError(MigrationToolkitResources.WitDiffResultWorkItemFieldDiffHeader); foreach (WitDiffField diffField in diffPair.DiffFields) { m_serverDiffEngine.LogError(String.Format(CultureInfo.InvariantCulture, MigrationToolkitResources.WitDiffResultWorkItemFieldDiffDetail, diffField.FieldName, diffField.SourceValue, diffField.TargetValue)); } } if (diffPair.MissingAttachments.Count > 0) { m_serverDiffEngine.LogError(MigrationToolkitResources.WitDiffResultWorkItemAttachmentMissingHeader); foreach (string missingAttachment in diffPair.MissingAttachments) { m_serverDiffEngine.LogError(String.Format(CultureInfo.InvariantCulture, MigrationToolkitResources.WitDiffResultWorkItemAttachmentMissingDetail, missingAttachment)); } } if (diffPair.DiffAttachments.Count > 0) { m_serverDiffEngine.LogError(MigrationToolkitResources.WitDiffResultWorkItemAttachmentDiffHeader); foreach (WitDiffAttachment diffAttachment in diffPair.DiffAttachments) { m_serverDiffEngine.LogError(String.Format(CultureInfo.InvariantCulture, MigrationToolkitResources.WitDiffResultWorkItemAttachmentDiffDetail, diffAttachment.SourceAttachmentName, diffAttachment.FieldName, diffAttachment.SourceValue, diffAttachment.TargetValue)); } } if (diffPair.DiffLinks.Count > 0) { m_serverDiffEngine.LogError(MigrationToolkitResources.WitDiffResultWorkItemLinkDiffHeader); foreach (WitDiffLink diffLink in diffPair.DiffLinks) { m_serverDiffEngine.LogError(String.Format(CultureInfo.InvariantCulture, MigrationToolkitResources.WitDiffResultWorkItemLinkDiffDetail, diffLink.FieldName, diffLink.SourceValue, diffLink.TargetValue)); } } if (diffPair.DiffType == WitDiffType.LinkCount) { m_serverDiffEngine.LogError(String.Format(CultureInfo.InvariantCulture, MigrationToolkitResources.WitDiffResultWorkItemLinkCountDiffDetail, diffPair.Side1DiffItem.LinkCount, diffPair.Side2DiffItem.LinkCount)); foreach (string linkUri in diffPair.Side1DiffItem.LinkUris) { m_serverDiffEngine.LogError(String.Format(CultureInfo.InvariantCulture, "Work Item {0} has link to {1}", diffPair.Side1DiffItem.WorkItemId, linkUri)); } foreach (string linkUri in diffPair.Side2DiffItem.LinkUris) { m_serverDiffEngine.LogError(String.Format(CultureInfo.InvariantCulture, "Work Item {0} has link to {1}", diffPair.Side2DiffItem.WorkItemId, linkUri)); } } }