CompareUnit.cs :  » Installers-Generators » WiX » Microsoft » Tools » WindowsInstallerXml » Unit » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » Installers Generators » WiX 
WiX » Microsoft » Tools » WindowsInstallerXml » Unit » CompareUnit.cs
//-------------------------------------------------------------------------------------------------
// <copyright file="CompareUnit.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
//    
//    The use and distribution terms for this software are covered by the
//    Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
//    which can be found in the file CPL.TXT at the root of this distribution.
//    By using this software in any fashion, you are agreeing to be bound by
//    the terms of this license.
//    
//    You must not remove this notice, or any other, from this software.
// </copyright>
// 
// <summary>
// Compares to results files as part of a Windows Installer XML WixUnit test.
// </summary>
//-------------------------------------------------------------------------------------------------

namespace Microsoft.Tools.WindowsInstallerXml.Unit{
    using System;
    using System.Collections;
    using System.Globalization;
    using System.IO;
    using System.Text;
    using System.Threading;
    using System.Xml;

    using Microsoft.Tools.WindowsInstallerXml.Unit;

    /// <summary>
    /// Compares to results files as part of a Windows Installer XML WixUnit test.
    /// </summary>
    internal class CompareUnit
    {
        /// <summary>
        /// A mutex for a Torch process
        /// </summary>
        private static Mutex mutex = new Mutex(false, "CompareUnit");

        /// <summary>
        /// Private constructor to prevent instantiation of static class.
        /// </summary>
        private CompareUnit()
        {
        }

        /// <summary>
        /// Run a Compare unit test.
        /// </summary>
        /// <param name="element">The unit test element.</param>
        /// <param name="previousUnitResults">The previous unit test results.</param>
        /// <param name="update">Indicates whether to give the user the option to fix a failing test.</param>
        /// <param name="args">The command arguments passed to WixUnit.</param>
        public static void RunUnitTest(XmlElement element, UnitResults previousUnitResults, bool update, ICommandArgs args)
        {
            string file1 = Environment.ExpandEnvironmentVariables(element.GetAttribute("File1"));
            string file2 = Environment.ExpandEnvironmentVariables(element.GetAttribute("File2"));
            string testName = element.ParentNode.Attributes["Name"].Value;

            // Check the results
            ArrayList differences = CompareUnit.CompareResults(file1, file2, testName, update);
            previousUnitResults.Errors.AddRange(differences);
            previousUnitResults.Output.AddRange(differences);
        }

        /// <summary>
        /// Compare two result files and update the expected result if specified
        /// </summary>
        /// <param name="expectedResult">The expected result file.</param>
        /// <param name="actualResult">The actual result file.</param>
        /// <param name="testName">The name of the test.</param>
        /// <param name="update">If true, update the expected result with the actual result.</param>
        /// <returns>Any differences found</returns>
        public static ArrayList CompareResults(string expectedResult, string actualResult, string testName, bool update)
        {
            ArrayList differences = CompareUnit.CompareResults(expectedResult, actualResult);

            // Update the test
            if (0 < differences.Count && update)
            {
                bool isUpdated = CompareUnit.UpdateTest(expectedResult, actualResult, testName, differences);

                if (isUpdated)
                {
                    // CompareResults again to verify that there are now no differences
                    differences = CompareResults(expectedResult, actualResult);
                }
            }

            return differences;
        }

        /// <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;

                // don't allow concurrent calls to Unbind
                try
                {
                    mutex.WaitOne();
                    targetOutput = unbinder.Unbind(expectedResult, outputType, null);
                    updatedOutput = unbinder.Unbind(actualResult, outputType, null);
                }
                finally
                {
                    mutex.ReleaseMutex();
                }

            }
            else
            {
                targetOutput = Output.Load(expectedResult, false, false);
                updatedOutput = Output.Load(actualResult, false, false);
            }

            differences.AddRange(CompareOutput(targetOutput, updatedOutput));

            // If the Output type is a Patch, then compare the patch's transforms
            if (outputType == OutputType.Patch)
            {
                // Compare transforms
                foreach (SubStorage targetTransform in targetOutput.SubStorages)
                {
                    SubStorage updatedTransform = null;

                    // Find the same transform in the other patch
                    foreach (SubStorage transform in updatedOutput.SubStorages)
                    {
                        if (transform.Name == targetTransform.Name)
                        {
                            updatedTransform = transform;
                            break;
                        }
                    }

                    if (null != updatedTransform)
                    {
                        // Both patch's have this transform
                        ArrayList transformDifferences = CompareUnit.CompareOutput(targetTransform.Data, updatedTransform.Data);

                        // add a description of the transforms being compared
                        if (0 < transformDifferences.Count)
                        {
                            transformDifferences.Insert(0, String.Concat("Differences found while comparing the transform ", targetTransform.Name, " from the two patches"));
                            differences.AddRange(transformDifferences);
                        }
                    }
                    else
                    {
                        differences.Add(String.Format("The {0} tranform has been dropped", targetTransform.Name));
                    }
                }

                // Check if the updated patch has had transforms added
                foreach (SubStorage updatedTransform in updatedOutput.SubStorages)
                {
                    SubStorage targetTransform = null;
                    foreach (SubStorage transform in targetOutput.SubStorages)
                    {
                        if (transform.Name == updatedTransform.Name)
                        {
                            targetTransform = transform;
                            break;
                        }
                    }

                    if (targetTransform == null)
                    {
                        differences.Add(String.Format("The {0} tranform has been added", updatedTransform.Name));
                    }
                }
            }

            // 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>
        /// 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>
        /// Determines if the given row can be ignored when comparing results.
        /// </summary>
        /// <param name="row">The row to check.</param>
        /// <returns>True if the row can be ignored; otherwise, false.</returns>
        private static bool Ignore(Row row)
        {
            if ("_SummaryInformation" == row.Table.Name)
            {
                // check timestamp and version-dependent fields
                switch ((int)row[0])
                {
                    case 9:
                    case 12:
                    case 13:
                    case 18:
                        return true;
                }
            }
            else if ("Property" == row.Table.Name)
            {
                switch ((string)row[0])
                {
                    case "ProductCode":
                        return true;
                }
            }
            else if ("MsiPatchMetadata" == row.Table.Name)
            {
                switch (row.GetPrimaryKey('/'))
                {
                    case "/CreationTimeUTC":
                        return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Fix a failed test by replacing the expected file with the actual file
        /// </summary>
        /// <param name="expectedFile">The expected file</param>
        /// <param name="actualFile">The actual file</param>
        /// <param name="testName">The test name</param>
        /// <param name="differences">The list of differences between to files</param>
        /// <returns>True if the user chose to update the test, false otherwise</returns>
        private static bool UpdateTest(string expectedFile, string actualFile, string testName, ArrayList differences)
        {
            Console.WriteLine();
            Console.WriteLine(String.Concat("Test Name: ", testName));

            // Print the differences that were found
            foreach (string line in differences)
            {
                Console.WriteLine(line);
            }

            Console.WriteLine();
            Console.Write("Do you wish to update the test to use the actual file '{0}' as the expected '{1}' (y/n)? ", actualFile, expectedFile);
            string answer = Console.ReadLine();

            if (answer.Equals("y", StringComparison.InvariantCultureIgnoreCase) || answer.Equals("yes", StringComparison.InvariantCultureIgnoreCase))
            {
                // sd edit the expected file
                ArrayList output = ToolUtility.RunTool("sd.exe", String.Concat("edit ", expectedFile));

                // Print the sd edit output
                foreach (string line in output)
                {
                    Console.WriteLine(line);
                }

                File.Copy(actualFile, expectedFile, true);
                return true;
            }
            else
            {
                Console.WriteLine("The test was not updated");
                return false;
            }
        }
    }
}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.