public void EqualBigDataTest() { //Arrange string left = _big1; string right = _big1; //Act DiffResult result = _sut.Diff(left, right); //Assert Assert.IsNotNull(result); Assert.AreEqual("equal", result.Description); Assert.IsNull(result.Locations); }
protected IEnumerable <BaseCommand> GetCommands <T>( DtoMetadataCache cache, T oldDto, T newDto, int expectedDifferenceCount, int expectedOperationCount, int expectedInsertOperations, int expectedUpdateOperations, int expectedDeleteOperations, int expectedCommandCount, int expectedInsertCommands, int expectedUpdateCommands, int expectedDeleteCommands, bool assertOnCounts = true) { var differ = new Differ(cache); var differences = differ.Diff(oldDto, newDto); if (assertOnCounts) { Assert.AreEqual(expectedDifferenceCount, differences.Count(), "Unexpected number of differences."); } var operationBuilder = new OperationBuilder(); var operations = operationBuilder.Build(differences); var commandBuilder = new CommandBuilder(); var commands = commandBuilder.Coalesce(operations); if (assertOnCounts) { Assert.AreEqual(expectedOperationCount, operations.Count(), "Unexpected number of operations."); var counts = CountItemsByType(operations); CheckCount(counts, typeof(InsertOperation), expectedInsertOperations); CheckCount(counts, typeof(UpdateOperation), expectedUpdateOperations); CheckCount(counts, typeof(DeleteOperation), expectedDeleteOperations); Assert.AreEqual(expectedCommandCount, commands.Count(), "Unexpected number of commands."); counts = CountItemsByType(commands); CheckCount(counts, typeof(InsertCommand), expectedInsertCommands); CheckCount(counts, typeof(UpdateCommand), expectedUpdateCommands); CheckCount(counts, typeof(DeleteCommand), expectedDeleteCommands); } var scriptBuilder = new ScriptBuilder(cache); var transactionScript = scriptBuilder.Build(commands); Assert.IsNotNull(transactionScript, "#badtimes - null transaction script"); Assert.IsTrue(transactionScript.Count > 0, "Should be at least one script."); foreach (var script in transactionScript) { Assert.IsTrue(script.Buffer.Length > 0, "#badtimes - empty transaction script"); } CheckNoReferenceTypesInParameters(transactionScript); return(commands); }
public string DiffTest(string left, string right, bool NoOrderInBasicTypeValueJArray = false) { Differ differ = new Differ(NoOrderInBasicTypeValueJArray); JArray r = differ.Diff(left, right); string s = JsonConvert.SerializeObject(r); Console.WriteLine(s); return(s); }
public void diff_does_not_drill_past_simple_save_ignore_where_differences_beneath() { var dao1 = CreateApplicationForUpdateWithAcquiringOffer(); var dao2 = CreateApplicationForUpdateWithAcquiringOffer(); dao2.Locations[0].Opportunities[0].CurrentOffer.AcquiringOffer.TypeOfTransaction = new TypeOfTransactionLutDao() { FieldItemKey = 0, FieldItem = new FieldItemLutDao() { FieldItemKey = 0, }, TypeOfTransactionEnumKey = OppTypeOfTransactionEnum.None }; var differ = new Differ(new DtoMetadataCache()); var diffs = differ.Diff(dao1, dao2); Assert.AreEqual(0, diffs.Count, "Should not have found any differences."); }
public void diff_shows_differences_between_nested_objects() { var dao1 = CreateAcquiringOfferTrnDao(); var dao2 = CreateAcquiringOfferTrnDao(); dao2.TypeOfTransaction = new TypeOfTransactionLutDao() { FieldItemKey = 0, FieldItem = new FieldItemLutDao() { FieldItemKey = 0, }, TypeOfTransactionEnumKey = OppTypeOfTransactionEnum.None }; var differ = new Differ(new DtoMetadataCache()); var diffs = differ.Diff(dao1, dao2); Assert.IsTrue(diffs.Count > 0, "Should have found some differences."); }
/// <summary> /// Main running method for the application. /// </summary> /// <param name="args">Commandline arguments to the application.</param> /// <returns>Returns the application error code.</returns> private int Run(string[] args) { Microsoft.Tools.WindowsInstallerXml.Binder binder = null; Differ differ = null; Unbinder unbinder = null; TempFileCollection tempFileCollection = null; try { // parse the command line this.ParseCommandLine(args); // validate the inputs if (this.xmlInputs && this.adminImage) { this.messageHandler.Display(this, WixErrors.IllegalCommandlineArgumentCombination("a", "xi")); this.showHelp = true; } string[] allValidExtensions = new string[] { wixMstExtension, wixOutExtension, wixPdbExtension, msiExtension }; string[] expectedSingleInputExtensions = new string[] { wixMstExtension, wixOutExtension }; string[] expectedDoubleInputXmlExtensions = new string[] { wixOutExtension, wixPdbExtension }; string[] expectedDoubleInputMsiExtensions = new string[] { msiExtension }; // Validate that all inputs have the correct extension and we dont have too many inputs. if (1 == this.inputFiles.Count) { string inputFile = this.inputFiles[0]; bool hasValidExtension = false; foreach (string extension in expectedSingleInputExtensions) { if (String.Equals(Path.GetExtension(inputFile), extension, StringComparison.OrdinalIgnoreCase)) { hasValidExtension = true; break; } } if (!hasValidExtension) { bool missingInput = false; // Check if its using an extension that could be valid in other scenarios. foreach (string validExtension in allValidExtensions) { if (String.Equals(Path.GetExtension(inputFile), validExtension, StringComparison.OrdinalIgnoreCase)) { this.messageHandler.Display(this, WixErrors.WrongFileExtensionForNumberOfInputs(Path.GetExtension(inputFile), inputFile)); missingInput = true; break; } } if (!missingInput) { this.messageHandler.Display(this, WixErrors.UnexpectedFileExtension(inputFile, String.Join(", ", expectedSingleInputExtensions))); } } } else if (2 == this.inputFiles.Count) { foreach (string inputFile in inputFiles) { bool hasValidExtension = false; string[] expectedExtensions = allValidExtensions; if (this.xmlInputs) { foreach (string extension in expectedDoubleInputXmlExtensions) { if (String.Equals(Path.GetExtension(inputFile), extension, StringComparison.OrdinalIgnoreCase)) { hasValidExtension = true; expectedExtensions = expectedDoubleInputXmlExtensions; break; } } } else { foreach (string extension in expectedDoubleInputMsiExtensions) { if (String.Equals(Path.GetExtension(inputFile), extension, StringComparison.OrdinalIgnoreCase)) { hasValidExtension = true; expectedExtensions = expectedDoubleInputMsiExtensions; break; } } } if (!hasValidExtension) { this.messageHandler.Display(this, WixErrors.UnexpectedFileExtension(inputFile, String.Join(", ", expectedExtensions))); } } } else { this.showHelp = true; } // exit if there was an error parsing the command line or with a file extension (otherwise the logo appears after error messages) if (this.messageHandler.EncounteredError) { return(this.messageHandler.LastErrorNumber); } if (null == this.outputFile) { this.showHelp = true; } if (this.showLogo) { AppCommon.DisplayToolHeader(); } if (this.showHelp) { Console.WriteLine(TorchStrings.HelpMessage); AppCommon.DisplayToolFooter(); return(this.messageHandler.LastErrorNumber); } foreach (string parameter in this.invalidArgs) { this.messageHandler.Display(this, WixWarnings.UnsupportedCommandLineArgument(parameter)); } this.invalidArgs = null; binder = new Microsoft.Tools.WindowsInstallerXml.Binder(); differ = new Differ(); unbinder = new Unbinder(); // load any extensions foreach (string extension in this.extensionList) { WixExtension wixExtension = WixExtension.Load(extension); unbinder.AddExtension(wixExtension); binder.AddExtension(wixExtension); differ.AddExtension(wixExtension); } binder.Message += new MessageEventHandler(this.messageHandler.Display); differ.Message += new MessageEventHandler(this.messageHandler.Display); unbinder.Message += new MessageEventHandler(this.messageHandler.Display); binder.TempFilesLocation = Environment.GetEnvironmentVariable("WIX_TEMP"); unbinder.TempFilesLocation = Environment.GetEnvironmentVariable("WIX_TEMP"); tempFileCollection = new TempFileCollection(Environment.GetEnvironmentVariable("WIX_TEMP")); binder.WixVariableResolver = new WixVariableResolver(); differ.PreserveUnchangedRows = this.preserveUnchangedRows; differ.ShowPedanticMessages = this.showPedanticMessages; unbinder.SuppressExtractCabinets = true; unbinder.IsAdminImage = this.adminImage; if (null == this.exportBasePath) { this.exportBasePath = tempFileCollection.BasePath; } // load and process the inputs Output transform; if (1 == this.inputFiles.Count) { transform = Output.Load(this.inputFiles[0], false, false); if (OutputType.Transform != transform.Type) { this.messageHandler.Display(this, WixErrors.InvalidWixTransform(this.inputFiles[0])); return(this.messageHandler.LastErrorNumber); } } else // 2 inputs { Output targetOutput; Output updatedOutput; if (this.xmlInputs) { // load the target database if (String.Equals(Path.GetExtension(inputFiles[0]), wixPdbExtension, StringComparison.OrdinalIgnoreCase)) { Pdb targetPdb = Pdb.Load(this.inputFiles[0], false, false); targetOutput = targetPdb.Output; } else { targetOutput = Output.Load(this.inputFiles[0], false, false); } // load the updated database if (String.Equals(Path.GetExtension(inputFiles[1]), wixPdbExtension, StringComparison.OrdinalIgnoreCase)) { Pdb updatedPdb = Pdb.Load(this.inputFiles[1], false, false); updatedOutput = updatedPdb.Output; } else { updatedOutput = Output.Load(this.inputFiles[1], false, false); } this.xmlOutput = true; } else { // load the target database targetOutput = unbinder.Unbind(this.inputFiles[0], OutputType.Product, Path.Combine(this.exportBasePath, "targetBinaries")); // load the updated database updatedOutput = unbinder.Unbind(this.inputFiles[1], OutputType.Product, Path.Combine(this.exportBasePath, "updatedBinaries")); } // diff the target and updated databases transform = differ.Diff(targetOutput, updatedOutput, this.validationFlags); if (null == transform.Tables || 0 >= transform.Tables.Count) { throw new WixException(WixErrors.NoDifferencesInTransform(transform.SourceLineNumbers)); } } // output the transform if (null != transform) { // If either the user selected xml output or gave xml input, save as xml output. // With xml inputs, many funtions of the binder have not been performed on the inputs (ie. file sequencing). This results in bad IDT files which cannot be put in a transform. if (this.xmlOutput) { transform.Save(this.outputFile, null, null, tempFileCollection.BasePath); } else { binder.Bind(transform, this.outputFile); } } } catch (WixException we) { if (we is WixInvalidIdtException) { // make sure the IDT files stay around this.tidy = false; } this.messageHandler.Display(this, we.Error); } catch (Exception e) { // make sure the files stay around for debugging this.tidy = false; this.messageHandler.Display(this, WixErrors.UnexpectedException(e.Message, e.GetType().ToString(), e.StackTrace)); if (e is NullReferenceException || e is SEHException) { throw; } } finally { if (null != binder) { if (this.tidy) { if (!binder.DeleteTempFiles()) { Console.WriteLine(TorchStrings.WAR_FailedToDeleteTempDir, binder.TempFilesLocation); } } else { Console.WriteLine(TorchStrings.INF_BinderTempDirLocatedAt, binder.TempFilesLocation); } } if (null != unbinder) { if (this.tidy) { if (!unbinder.DeleteTempFiles()) { Console.WriteLine(TorchStrings.WAR_FailedToDeleteTempDir, binder.TempFilesLocation); } } else { Console.WriteLine(TorchStrings.INF_UnbinderTempDirLocatedAt, binder.TempFilesLocation); } } if (null != tempFileCollection) { if (this.tidy) { try { Directory.Delete(tempFileCollection.BasePath, true); } catch (DirectoryNotFoundException) { // if the path doesn't exist, then there is nothing for us to worry about } catch { Console.WriteLine(TorchStrings.WAR_FailedToDeleteTempDir, tempFileCollection.BasePath); } } else { Console.WriteLine(TorchStrings.INF_TorchTempDirLocatedAt, tempFileCollection.BasePath); } } } return(this.messageHandler.LastErrorNumber); }
/// <summary> /// Compare two Outputs /// </summary> /// <param name="targetOutput"></param> /// <param name="updatedOutput"></param> /// <returns>Any differences found.</returns> private static ArrayList CompareOutput(Output targetOutput, Output updatedOutput) { ArrayList differences = new ArrayList(); Differ differ = new Differ(); differ.SuppressKeepingSpecialRows = true; Output transform = differ.Diff(targetOutput, updatedOutput); foreach (Table table in transform.Tables) { switch (table.Operation) { case TableOperation.Add: differences.Add(String.Format(CultureInfo.InvariantCulture, "The {0} table has been added.", table.Name)); break; case TableOperation.Drop: differences.Add(String.Format(CultureInfo.InvariantCulture, "The {0} table has been dropped.", table.Name)); continue; } // index the target rows for better error messages Hashtable targetRows = new Hashtable(); Table targetTable = targetOutput.Tables[table.Name]; if (null != targetTable) { foreach (Row row in targetTable.Rows) { string primaryKey = row.GetPrimaryKey('/'); // only index rows with primary keys since these are the ones that can be modified if (null != primaryKey) { targetRows.Add(primaryKey, row); } } } foreach (Row row in table.Rows) { switch (row.Operation) { case RowOperation.Add: differences.Add(String.Format(CultureInfo.InvariantCulture, "The {0} table, row '{1}' has been added.", table.Name, row.ToString())); break; case RowOperation.Delete: differences.Add(String.Format(CultureInfo.InvariantCulture, "The {0} table, row '{1}' has been deleted.", table.Name, row.ToString())); break; case RowOperation.Modify: if (!Ignore(row)) { string primaryKey = row.GetPrimaryKey('/'); Row targetRow = (Row)targetRows[primaryKey]; differences.Add(String.Format(CultureInfo.InvariantCulture, "The {0} table, row '{1}' has changed to '{2}'.", table.Name, targetRow.ToString(), row.ToString())); } break; default: throw new InvalidOperationException("Unknown diff row."); } } } return differences; }
/// <summary> /// Compare two Outputs /// </summary> /// <param name="targetOutput">The expected output</param> /// <param name="updatedOutput">The actual output</param> /// <param name="tables">The list of tables to compare</param> /// <returns>Any differences found.</returns> private static ArrayList CompareOutput(Output targetOutput, Output updatedOutput, params string[] tables) { ArrayList differences = new ArrayList(); Differ differ = new Differ(); differ.SuppressKeepingSpecialRows = true; Output transform = differ.Diff(targetOutput, updatedOutput); foreach (Table table in transform.Tables) { if (null != tables && -1 == Array.IndexOf(tables, table.Name)) { // Skip this table continue; } switch (table.Operation) { case TableOperation.Add: differences.Add(String.Format(CultureInfo.InvariantCulture, "The {0} table has been added.", table.Name)); break; case TableOperation.Drop: differences.Add(String.Format(CultureInfo.InvariantCulture, "The {0} table has been dropped.", table.Name)); continue; } // index the target rows for better error messages Hashtable targetRows = new Hashtable(); Table targetTable = targetOutput.Tables[table.Name]; if (null != targetTable) { foreach (Row row in targetTable.Rows) { string primaryKey = row.GetPrimaryKey('/'); // only index rows with primary keys since these are the ones that can be modified if (null != primaryKey) { targetRows.Add(primaryKey, row); } } } foreach (Row row in table.Rows) { switch (row.Operation) { case RowOperation.Add: differences.Add(String.Format(CultureInfo.InvariantCulture, "The {0} table, row '{1}' has been added.", table.Name, row.ToString())); break; case RowOperation.Delete: differences.Add(String.Format(CultureInfo.InvariantCulture, "The {0} table, row '{1}' has been deleted.", table.Name, row.ToString())); break; case RowOperation.Modify: if (!Verifier.Ignore(row)) { string primaryKey = row.GetPrimaryKey('/'); Row targetRow = (Row)targetRows[primaryKey]; differences.Add(String.Format(CultureInfo.InvariantCulture, "The {0} table, row '{1}' has changed to '{2}'.", table.Name, targetRow.ToString(), row.ToString())); } break; default: throw new InvalidOperationException("Unknown diff row."); } } } return(differences); }
/// <summary> /// Main running method for the application. /// </summary> /// <param name="args">Commandline arguments to the application.</param> /// <returns>Returns the application error code.</returns> private int Run(string[] args) { Microsoft.Tools.WindowsInstallerXml.Binder binder = null; Differ differ = null; Unbinder unbinder = null; TempFileCollection tempFileCollection = null; try { // parse the command line this.ParseCommandLine(args); // validate the inputs if (this.xmlInputs && this.adminImage) { this.messageHandler.Display(this, WixErrors.IllegalCommandlineArgumentCombination("a", "xi")); this.showHelp = true; } string[] allValidExtensions = new string[] { wixMstExtension, wixOutExtension, wixPdbExtension, msiExtension }; string[] expectedSingleInputExtensions = new string[] { wixMstExtension, wixOutExtension }; string[] expectedDoubleInputXmlExtensions = new string[] { wixOutExtension, wixPdbExtension }; string[] expectedDoubleInputMsiExtensions = new string[] { msiExtension }; // Validate that all inputs have the correct extension and we dont have too many inputs. if (1 == this.inputFiles.Count) { string inputFile = this.inputFiles[0]; bool hasValidExtension = false; foreach (string extension in expectedSingleInputExtensions) { if (String.Equals(Path.GetExtension(inputFile), extension, StringComparison.OrdinalIgnoreCase)) { hasValidExtension = true; break; } } if (!hasValidExtension) { bool missingInput = false; // Check if its using an extension that could be valid in other scenarios. foreach (string validExtension in allValidExtensions) { if (String.Equals(Path.GetExtension(inputFile), validExtension, StringComparison.OrdinalIgnoreCase)) { this.messageHandler.Display(this, WixErrors.WrongFileExtensionForNumberOfInputs(Path.GetExtension(inputFile), inputFile)); missingInput = true; break; } } if (!missingInput) { this.messageHandler.Display(this, WixErrors.UnexpectedFileExtension(inputFile, String.Join(", ", expectedSingleInputExtensions))); } } } else if (2 == this.inputFiles.Count) { foreach (string inputFile in inputFiles) { bool hasValidExtension = false; string[] expectedExtensions = allValidExtensions; if (this.xmlInputs) { foreach (string extension in expectedDoubleInputXmlExtensions) { if (String.Equals(Path.GetExtension(inputFile), extension, StringComparison.OrdinalIgnoreCase)) { hasValidExtension = true; expectedExtensions = expectedDoubleInputXmlExtensions; break; } } } else { foreach (string extension in expectedDoubleInputMsiExtensions) { if (String.Equals(Path.GetExtension(inputFile), extension, StringComparison.OrdinalIgnoreCase)) { hasValidExtension = true; expectedExtensions = expectedDoubleInputMsiExtensions; break; } } } if (!hasValidExtension) { this.messageHandler.Display(this, WixErrors.UnexpectedFileExtension(inputFile, String.Join(", ", expectedExtensions))); } } } else { this.showHelp = true; } // exit if there was an error parsing the command line or with a file extension (otherwise the logo appears after error messages) if (this.messageHandler.EncounteredError) { return this.messageHandler.LastErrorNumber; } if (null == this.outputFile) { this.showHelp = true; } if (this.showLogo) { AppCommon.DisplayToolHeader(); } if (this.showHelp) { Console.WriteLine(TorchStrings.HelpMessage); AppCommon.DisplayToolFooter(); return this.messageHandler.LastErrorNumber; } foreach (string parameter in this.invalidArgs) { this.messageHandler.Display(this, WixWarnings.UnsupportedCommandLineArgument(parameter)); } this.invalidArgs = null; binder = new Microsoft.Tools.WindowsInstallerXml.Binder(); differ = new Differ(); unbinder = new Unbinder(); // load any extensions foreach (string extension in this.extensionList) { WixExtension wixExtension = WixExtension.Load(extension); unbinder.AddExtension(wixExtension); binder.AddExtension(wixExtension); differ.AddExtension(wixExtension); } binder.Message += new MessageEventHandler(this.messageHandler.Display); differ.Message += new MessageEventHandler(this.messageHandler.Display); unbinder.Message += new MessageEventHandler(this.messageHandler.Display); binder.TempFilesLocation = Environment.GetEnvironmentVariable("WIX_TEMP"); unbinder.TempFilesLocation = Environment.GetEnvironmentVariable("WIX_TEMP"); tempFileCollection = new TempFileCollection(Environment.GetEnvironmentVariable("WIX_TEMP")); binder.WixVariableResolver = new WixVariableResolver(); differ.PreserveUnchangedRows = this.preserveUnchangedRows; differ.ShowPedanticMessages = this.showPedanticMessages; unbinder.SuppressExtractCabinets = true; unbinder.IsAdminImage = this.adminImage; if (null == this.exportBasePath) { this.exportBasePath = tempFileCollection.BasePath; } // load and process the inputs Output transform; if (1 == this.inputFiles.Count) { transform = Output.Load(this.inputFiles[0], false, false); if (OutputType.Transform != transform.Type) { this.messageHandler.Display(this, WixErrors.InvalidWixTransform(this.inputFiles[0])); return this.messageHandler.LastErrorNumber; } } else // 2 inputs { Output targetOutput; Output updatedOutput; if (this.xmlInputs) { // load the target database if (String.Equals(Path.GetExtension(inputFiles[0]), wixPdbExtension, StringComparison.OrdinalIgnoreCase)) { Pdb targetPdb = Pdb.Load(this.inputFiles[0], false, false); targetOutput = targetPdb.Output; } else { targetOutput = Output.Load(this.inputFiles[0], false, false); } // load the updated database if (String.Equals(Path.GetExtension(inputFiles[1]), wixPdbExtension, StringComparison.OrdinalIgnoreCase)) { Pdb updatedPdb = Pdb.Load(this.inputFiles[1], false, false); updatedOutput = updatedPdb.Output; } else { updatedOutput = Output.Load(this.inputFiles[1], false, false); } this.xmlOutput = true; } else { // load the target database targetOutput = unbinder.Unbind(this.inputFiles[0], OutputType.Product, Path.Combine(this.exportBasePath, "targetBinaries")); // load the updated database updatedOutput = unbinder.Unbind(this.inputFiles[1], OutputType.Product, Path.Combine(this.exportBasePath, "updatedBinaries")); } // diff the target and updated databases transform = differ.Diff(targetOutput, updatedOutput, this.validationFlags); if (null == transform.Tables || 0 >= transform.Tables.Count) { throw new WixException(WixErrors.NoDifferencesInTransform(transform.SourceLineNumbers)); } } // output the transform if (null != transform) { // If either the user selected xml output or gave xml input, save as xml output. // With xml inputs, many funtions of the binder have not been performed on the inputs (ie. file sequencing). This results in bad IDT files which cannot be put in a transform. if (this.xmlOutput) { transform.Save(this.outputFile, null, null, tempFileCollection.BasePath); } else { binder.Bind(transform, this.outputFile); } } } catch (WixException we) { if (we is WixInvalidIdtException) { // make sure the IDT files stay around this.tidy = false; } this.messageHandler.Display(this, we.Error); } catch (Exception e) { // make sure the files stay around for debugging this.tidy = false; this.messageHandler.Display(this, WixErrors.UnexpectedException(e.Message, e.GetType().ToString(), e.StackTrace)); if (e is NullReferenceException || e is SEHException) { throw; } } finally { if (null != binder) { if (this.tidy) { if (!binder.DeleteTempFiles()) { Console.WriteLine(TorchStrings.WAR_FailedToDeleteTempDir, binder.TempFilesLocation); } } else { Console.WriteLine(TorchStrings.INF_BinderTempDirLocatedAt, binder.TempFilesLocation); } } if (null != unbinder) { if (this.tidy) { if (!unbinder.DeleteTempFiles()) { Console.WriteLine(TorchStrings.WAR_FailedToDeleteTempDir, binder.TempFilesLocation); } } else { Console.WriteLine(TorchStrings.INF_UnbinderTempDirLocatedAt, binder.TempFilesLocation); } } if (null != tempFileCollection) { if (this.tidy) { try { Directory.Delete(tempFileCollection.BasePath, true); } catch (DirectoryNotFoundException) { // if the path doesn't exist, then there is nothing for us to worry about } catch { Console.WriteLine(TorchStrings.WAR_FailedToDeleteTempDir, tempFileCollection.BasePath); } } else { Console.WriteLine(TorchStrings.INF_TorchTempDirLocatedAt, tempFileCollection.BasePath); } } } return this.messageHandler.LastErrorNumber; }
/// <summary> /// Compare two result files. /// </summary> /// <param name="expectedResult">The expected result file.</param> /// <param name="actualResult">The actual result file.</param> /// <returns>Any differences found.</returns> public static ArrayList CompareResults(string expectedResult, string actualResult) { ArrayList differences = new ArrayList(); Output targetOutput; Output updatedOutput; OutputType outputType; string extension = Path.GetExtension(expectedResult); if (String.Compare(extension, ".msi", true, CultureInfo.InvariantCulture) == 0) { outputType = OutputType.Product; } else if (String.Compare(extension, ".msm", true, CultureInfo.InvariantCulture) == 0) { outputType = OutputType.Module; } else if (String.Compare(extension, ".msp", true, CultureInfo.InvariantCulture) == 0) { outputType = OutputType.Patch; } else if (String.Compare(extension, ".mst", true, CultureInfo.InvariantCulture) == 0) { outputType = OutputType.Transform; } else if (String.Compare(extension, ".pcp", true, CultureInfo.InvariantCulture) == 0) { outputType = OutputType.PatchCreation; } else if (String.Compare(extension, ".wixout", true, CultureInfo.InvariantCulture) == 0) { outputType = OutputType.Unknown; } else { throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Cannot determine the type of msi database file based on file extension '{0}'.", extension)); } if (outputType != OutputType.Unknown) { Unbinder unbinder = new Unbinder(); unbinder.SuppressDemodularization = true; targetOutput = unbinder.Unbind(expectedResult, outputType, null); updatedOutput = unbinder.Unbind(actualResult, outputType, null); } else { targetOutput = Output.Load(expectedResult, false, false); updatedOutput = Output.Load(actualResult, false, false); } Differ differ = new Differ(); differ.SuppressKeepingSpecialRows = true; Output transform = differ.Diff(targetOutput, updatedOutput); foreach (Table table in transform.Tables) { switch (table.Operation) { case TableOperation.Add: differences.Add(String.Format(CultureInfo.InvariantCulture, "The {0} table has been added.", table.Name)); break; case TableOperation.Drop: differences.Add(String.Format(CultureInfo.InvariantCulture, "The {0} table has been dropped.", table.Name)); continue; } // index the target rows for better error messages Hashtable targetRows = new Hashtable(); Table targetTable = targetOutput.Tables[table.Name]; if (null != targetTable) { foreach (Row row in targetTable.Rows) { string primaryKey = row.GetPrimaryKey('/'); // only index rows with primary keys since these are the ones that can be modified if (null != primaryKey) { targetRows.Add(primaryKey, row); } } } foreach (Row row in table.Rows) { switch (row.Operation) { case RowOperation.Add: differences.Add(String.Format(CultureInfo.InvariantCulture, "The {0} table, row '{1}' has been added.", table.Name, row.ToString())); break; case RowOperation.Delete: differences.Add(String.Format(CultureInfo.InvariantCulture, "The {0} table, row '{1}' has been deleted.", table.Name, row.ToString())); break; case RowOperation.Modify: if (("_SummaryInformation" != table.Name || (9 != (int)row[0] && 12 != (int)row[0] && 13 != (int)row[0] && 18 != (int)row[0])) && ("Property" != table.Name || "ProductCode" != (string)row[0])) { string primaryKey = row.GetPrimaryKey('/'); Row targetRow = (Row)targetRows[primaryKey]; differences.Add(String.Format(CultureInfo.InvariantCulture, "The {0} table, row '{1}' has changed to '{2}'.", table.Name, targetRow.ToString(), row.ToString())); } break; default: throw new InvalidOperationException("Unknown diff row."); } } } // add a description of the files being compared if (0 < differences.Count) { differences.Insert(0, "Differences found while comparing:"); differences.Insert(1, expectedResult); differences.Insert(2, actualResult); } return(differences); }
/// <summary> /// Main running method for the application. /// </summary> /// <param name="args">Commandline arguments to the application.</param> /// <returns>Returns the application error code.</returns> private int Run(string[] args) { Microsoft.Tools.WindowsInstallerXml.Binder binder = null; Differ differ = null; Unbinder unbinder = null; TempFileCollection tempFileCollection = null; try { // parse the command line this.ParseCommandLine(args); // exit if there was an error parsing the command line (otherwise the logo appears after error messages) if (this.messageHandler.EncounteredError) { return(this.messageHandler.LastErrorNumber); } // validate the inputs if (1 == this.inputFiles.Count) { // Validate that if its a single input, it is a wixout to be converted to an mst. if (0 != String.Compare(Path.GetExtension(this.inputFiles[0]), ".wixout", true, CultureInfo.InvariantCulture)) { this.showHelp = true; } } else if (2 == this.inputFiles.Count) { string expectedExtension = ".msi"; if (this.xmlInputs) { expectedExtension = ".wixout"; } // Validate that all inputs have the correct extension foreach (string inputFile in inputFiles) { if (0 != String.Compare(Path.GetExtension(inputFile), expectedExtension, true, CultureInfo.InvariantCulture)) { this.messageHandler.Display(this, WixErrors.UnexpectedFileExtension(this.inputFiles[0], expectedExtension)); this.showHelp = true; } } } else { this.showHelp = true; } if (null == this.outputFile) { this.showHelp = true; } if (this.showLogo) { Assembly torchAssembly = Assembly.GetExecutingAssembly(); Console.WriteLine("Microsoft (R) Windows Installer Xml Transform Builder Version {0}", torchAssembly.GetName().Version.ToString()); Console.WriteLine("Copyright (C) Microsoft Corporation 2003. All rights reserved.\n"); Console.WriteLine(); } if (this.showHelp) { Console.WriteLine(" usage: torch.exe [-?] [options] targetInput updatedInput -out outputFile"); Console.WriteLine(); Console.WriteLine(" -nologo skip printing logo information"); Console.WriteLine(" -notidy do not delete temporary files (useful for debugging)"); Console.WriteLine(" -p preserve unmodified content in the output"); Console.WriteLine(" -sw<N> suppress warning with specific message ID"); Console.WriteLine(" -v verbose output"); Console.WriteLine(" -wx treat warnings as errors"); Console.WriteLine(" -xi input xml instead of MSI format"); Console.WriteLine(" -xo output xml instead of MST format (set by default if -xi is present"); Console.WriteLine(" -? this help information"); Console.WriteLine(); Console.WriteLine("Environment variables:"); Console.WriteLine(" WIX_TEMP overrides the temporary directory used for cab extraction, binary extraction, ..."); Console.WriteLine(); Console.WriteLine("Common extensions:"); Console.WriteLine(" .wxi - Windows installer Xml Include file"); Console.WriteLine(" .wxl - Windows installer Xml Localization file"); Console.WriteLine(" .wxs - Windows installer Xml Source file"); Console.WriteLine(" .wixlib - Windows installer Xml Library file (in XML format)"); Console.WriteLine(" .wixobj - Windows installer Xml Object file (in XML format)"); Console.WriteLine(" .wixout - Windows installer Xml Output file (in XML format)"); Console.WriteLine(); Console.WriteLine(" .msi - Windows installer Product Database"); Console.WriteLine(" .msm - Windows installer Merge Module"); Console.WriteLine(" .msp - Windows installer Patch"); Console.WriteLine(" .mst - Windows installer Transform"); Console.WriteLine(" .pcp - Windows installer Patch Creation Package"); Console.WriteLine(); Console.WriteLine("For more information see: http://wix.sourceforge.net"); return(this.messageHandler.LastErrorNumber); } binder = new Microsoft.Tools.WindowsInstallerXml.Binder(); differ = new Differ(); unbinder = new Unbinder(); binder.Message += new MessageEventHandler(this.messageHandler.Display); differ.Message += new MessageEventHandler(this.messageHandler.Display); unbinder.Message += new MessageEventHandler(this.messageHandler.Display); binder.TempFilesLocation = Environment.GetEnvironmentVariable("WIX_TEMP"); unbinder.TempFilesLocation = Environment.GetEnvironmentVariable("WIX_TEMP"); tempFileCollection = new TempFileCollection(Environment.GetEnvironmentVariable("WIX_TEMP")); binder.WixVariableResolver = new WixVariableResolver(); differ.PreserveUnchangedRows = this.preserveUnchangedRows; unbinder.SuppressExtractCabinets = true; // load and process the inputs Output transform; if (1 == this.inputFiles.Count) { transform = Output.Load(this.inputFiles[0], false, false); if (OutputType.Transform != transform.Type) { this.messageHandler.Display(this, WixErrors.InvalidWixTransform(this.inputFiles[0])); return(this.messageHandler.LastErrorNumber); } } else // 2 inputs { Output targetOutput; Output updatedOutput; if (this.xmlInputs) { // load the target database targetOutput = Output.Load(this.inputFiles[0], false, false); // load the updated database updatedOutput = Output.Load(this.inputFiles[1], false, false); } else { // load the target database targetOutput = unbinder.Unbind(this.inputFiles[0], OutputType.Product, Path.Combine(tempFileCollection.BasePath, "targetBinaries")); // load the updated database updatedOutput = unbinder.Unbind(this.inputFiles[1], OutputType.Product, Path.Combine(tempFileCollection.BasePath, "updatedBinaries")); } // diff the target and updated databases transform = differ.Diff(targetOutput, updatedOutput); if (null == transform.Tables || 0 >= transform.Tables.Count) { throw new WixException(WixErrors.NoDifferencesInTransform(transform.SourceLineNumbers)); } } // output the transform if (null != transform) { // If either the user selected xml output or gave xml input, save as xml output. // With xml inputs, many funtions of the binder have not been performed on the inputs (ie. file sequencing). This results in bad IDT files which cannot be put in a transform. if (this.xmlOutput || this.xmlInputs) { transform.Save(this.outputFile, null, null, null); } else { binder.Bind(transform, this.outputFile); } } } catch (WixException we) { if (we is WixInvalidIdtException) { // make sure the IDT files stay around this.tidy = false; } this.messageHandler.Display(this, we.Error); } catch (Exception e) { // make sure the files stay around for debugging this.tidy = false; this.messageHandler.Display(this, WixErrors.UnexpectedException(e.Message, e.GetType().ToString(), e.StackTrace)); if (e is NullReferenceException || e is SEHException) { throw; } } finally { if (null != binder) { if (this.tidy) { if (!binder.DeleteTempFiles()) { Console.WriteLine("Warning, failed to delete temporary directory: {0}", binder.TempFilesLocation); } } else { Console.WriteLine("Binder temporary directory located at '{0}'.", binder.TempFilesLocation); } } if (null != unbinder) { if (this.tidy) { if (!unbinder.DeleteTempFiles()) { Console.WriteLine("Warning, failed to delete temporary directory: {0}", binder.TempFilesLocation); } } else { Console.WriteLine("Unbinder temporary directory located at '{0}'.", binder.TempFilesLocation); } } if (null != tempFileCollection) { if (this.tidy) { try { Directory.Delete(tempFileCollection.BasePath, true); } catch { Console.WriteLine("Warning, failed to delete temporary directory: {0}", tempFileCollection.BasePath); } } else { Console.WriteLine("Torch temporary directory located at '{0}'.", tempFileCollection.BasePath); } } } return(this.messageHandler.LastErrorNumber); }