Verifier.cs :  » Installers-Generators » WiX » Microsoft » Tools » WindowsInstallerXml » Test » 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 » Test » Verifier.cs
//-------------------------------------------------------------------------------------------------
// <copyright file="Verifier.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>
//      Contains methods for verification
// </summary>
//-------------------------------------------------------------------------------------------------

namespace Microsoft.Tools.WindowsInstallerXml.Test{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Globalization;
    using System.IO;
    using System.Reflection;
    using System.Text;
    using System.Xml;
    using System.Xml.XPath;

    using DTF = Microsoft.Deployment.WindowsInstaller;
    using Microsoft.Tools.WindowsInstallerXml;
    using Microsoft.Tools.WindowsInstallerXml.Msi;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    /// <summary>
    /// Contains methods for test verification
    /// </summary>
    public static class Verifier
    {
        /// <summary>
        /// Verifies the value returned by an MSI query
        /// </summary>
        /// <param name="msi">The path to an MSI</param>
        /// <param name="query">An MSI query</param>
        /// <param name="expectedValue">The string that the query is expected to return</param>
        /// <remarks>
        /// Only works for queries that return a single value (ie. not a query that returns a row or set of rows)
        /// </remarks>
        public static void VerifyQuery(string msi, string query, string expectedValue)
        {
            string queryResult = Verifier.Query(msi, query);
            Assert.AreEqual(expectedValue, queryResult, "The query '{0}' on '{1}' did not return the expected results", query, msi);
        }

        /// <summary>
        /// Query an MSI table for a single value
        /// </summary>
        /// <param name="msi">The path to an MSI</param>
        /// <param name="sql">An MSI query</param>
        /// <returns>The results as a string or null if no results are returned</returns>
        /// <remarks>
        /// Returns the value of the first field in the first record
        /// </remarks>
        public static string Query(string msi, string query)
        {
            string result = null;

            using (Database database = new Database(msi, OpenDatabase.ReadOnly))
            {
                using (View view = database.OpenExecuteView(query))
                {
                    Record record = view.Fetch();

                    if (null != record)
                    {
                        result = Convert.ToString(record.GetString(1));
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// Verifies the codepage of a database
        /// </summary>
        /// <param name="msi">The path to the database</param>
        /// <param name="expectedCodepage">The expected codepage</param>
        public static void VerifyDatabaseCodepage(string msi, int expectedCodepage)
        {
            int actualCodepage = Verifier.GetDatabaseCodepage(msi);
            Assert.AreEqual(expectedCodepage, actualCodepage, "The codepage for {0} does not match the expected codepage", msi);
        }

        /// <summary>
        /// Gets the codepage of a database
        /// </summary>
        /// <param name="msi">The path to the database</param>
        /// <returns>The codepage if it was found, or -1 if it could not be found</returns>
        /// <remarks>
        /// Most of this code was copied from Unbinder.UnbindDatabase() in wix.csproj
        /// </remarks>
        public static int GetDatabaseCodepage(string msi)
        {
            int codepage = -1;

            Database database = new Database(msi, OpenDatabase.ReadOnly);

            string codepageIdt = String.Concat(Path.GetTempFileName(), ".idt");
            database.Export("_ForceCodepage", Path.GetDirectoryName(codepageIdt), Path.GetFileName(codepageIdt));
            using (StreamReader sr = File.OpenText(codepageIdt))
            {
                string line;

                while (null != (line = sr.ReadLine()))
                {
                    string[] data = line.Split('\t');

                    if (2 == data.Length)
                    {
                        codepage = Convert.ToInt32(data[0], CultureInfo.InvariantCulture);
                    }
                }
            }

            return codepage;
        }


        /// <summary>
        /// Verifies that the value of a summary information stream property matches the expected value
        /// </summary>
        /// <param name="msi">The MSI to verify</param>
        /// <param name="propertyIndex">The summary information stream property Id of the property to verify</param>
        /// <param name="expectedValue">The expected value</param>
        public static void VerifySummaryInformationProperty(string msi, int propertyIndex, string expectedValue)
        {
            string actualValue = Verifier.GetSummaryInformationProperty(msi, propertyIndex);
            Assert.AreEqual(expectedValue, actualValue, "The expected summary information property does not match the actual value");
        }

        /// <summary>
        /// Gets the value of a summary information stream property
        /// </summary>
        /// <param name="msi">The MSI to get the property from</param>
        /// <param name="propertyIndex">The summary information stream property Id of the property to get</param>
        /// <returns>Summary information stream property</returns>
        /// <remarks>
        /// This method reflects on wix.dll to use some of its internal methods for getting Summary Information data
        /// </remarks>
        public static string GetSummaryInformationProperty(string msi, int propertyIndex)
        {
            // Load the wix.dll assembly
            string wixDllLocation = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "wix.dll");
            Assembly wix = Assembly.LoadFile(wixDllLocation);

            // Find the SummaryInformation type
            string summaryInformationTypeName = "Microsoft.Tools.WindowsInstallerXml.Msi.SummaryInformation";
            Type summaryInformationType = wix.GetType(summaryInformationTypeName);
            if (null == summaryInformationType)
            {
                throw new NullReferenceException(String.Format("The Type {0} could not be found in {1}", summaryInformationTypeName, wixDllLocation));
            }

            // Find the SummaryInformation.GetProperty method
            BindingFlags getPropertyBindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
            string getPropertyMethodName = "GetProperty";
            MethodInfo getPropertyMethod = summaryInformationType.GetMethod(getPropertyMethodName, getPropertyBindingFlags);
            if (null == getPropertyMethod)
            {
                throw new NullReferenceException(String.Format("The Method {0} could not be found in {1}", getPropertyMethodName, summaryInformationTypeName));
            }

            // Create an instance of a SummaryInformation object
            Object[] constructorArguments = { msi };
            BindingFlags constructorBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
            Object instance = wix.CreateInstance(summaryInformationTypeName, false, constructorBindingFlags, null, constructorArguments, CultureInfo.InvariantCulture, null);

            // Call the SummaryInformation.GetProperty method
            Object[] arguments = { propertyIndex };
            string value = (string)getPropertyMethod.Invoke(instance, arguments);

            return value;
        }

        /// <summary>
        /// Assert that two files do not contain any differences
        /// </summary>
        /// <param name="expectedResult">The expected result file.</param>
        /// <param name="actualResult">The actual result file.</param>
        public static void VerifyResults(string expectedResult, string actualResult)
        {
            Verifier.VerifyResults(expectedResult, actualResult, (string[])null);
        }

        /// <summary>
        /// Assert that two files do not contain any differences
        /// </summary>
        /// <param name="expectedResult">The expected result file.</param>
        /// <param name="actualResult">The actual result file.</param>
        /// <param name="table">The table to compare</param>
        public static void VerifyResults(string expectedResult, string actualResult, string table)
        {
            Verifier.VerifyResults(expectedResult, actualResult, new string[] { table });
        }

        /// <summary>
        /// Assert that two files do not contain any differences
        /// </summary>
        /// <param name="expectedResult">The expected result file.</param>
        /// <param name="actualResult">The actual result file.</param>
        /// <param name="tables">The list of tables to compare</param>
        public static void VerifyResults(string expectedResult, string actualResult, params string[] tables)
        {
            ArrayList differences = Verifier.CompareResults(expectedResult, actualResult, tables);

            if (0 != differences.Count)
            {
                foreach (string difference in differences)
                {
                    Console.WriteLine(difference);
                }

                Assert.Fail("Expected output '{0}' did not match actual output '{1}'", expectedResult, actualResult);
            }
        }

        /// <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)
        {
            return Verifier.CompareResults(expectedResult, actualResult, null);
        }

        /// <summary>
        /// Compare two result files.
        /// </summary>
        /// <param name="expectedResult">The expected result file.</param>
        /// <param name="actualResult">The actual result file.</param>
        /// <param name="tables">The list of tables to compare</param>
        /// <returns>Any differences found.</returns>
        public static ArrayList CompareResults(string expectedResult, string actualResult, params string[] tables)
        {
            ArrayList differences = new ArrayList();
            Output targetOutput;
            Output updatedOutput;
            expectedResult = Environment.ExpandEnvironmentVariables(expectedResult);
            actualResult = Environment.ExpandEnvironmentVariables(actualResult);

            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);
            }
            
            differences.AddRange(CompareOutput(targetOutput, updatedOutput, tables));

            // 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 = Verifier.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>
        /// Verifies that a given table  exists in the msi
        /// </summary>
        /// <param name="msi">The MSI to verify</param>
        /// <param name="tableName">The Name of the table to check for</param>
        public static void VerifyTableExists(string msi, string tableName)
        {
            bool tableExists = Verifier.CheckTableExists(msi, tableName);
            Assert.IsTrue(tableExists, "Table '{0}' does not exist in msi '{1}'. It was expected to exist.", tableName, msi);
        }

        /// <summary>
        /// Verifies that a given table does not exist in the msi
        /// </summary>
        /// <param name="msi">The MSI to verify</param>
        /// <param name="tableName">The Name of the table to check for</param>
        public static void VerifyNotTableExists(string msi, string tableName)
        {
            bool tableExists = Verifier.CheckTableExists(msi, tableName);
            Assert.IsFalse(tableExists, "Table '{0}' exists in msi '{1}. It was NOT expected to exist.", tableName, msi);
        }

        /// <summary>
        /// Checks if a given table exists in the msi
        /// </summary>
        /// <param name="msi">The MSI to verify</param>
        /// <param name="tableName">The Name of the table to check for</param>
        /// <returns>True if the table exists in the msi, false otherwise</returns>
        public static bool CheckTableExists(string msi, string tableName)
        {
            bool tableExists = false;

            using (DTF.Database database = new DTF.Database(msi, DTF.DatabaseOpenMode.ReadOnly))
            {
                tableExists = database.Tables.Contains(tableName);
            }

            return tableExists;
        }

        /// <summary>
        /// Query XML for a node list.
        /// </summary>
        /// <param name="xmlPath">Path to XML.</param>
        /// <param name="xpathQuery">XPath Query.</param>
        /// <param name="nsm">A NameSpaceManager.</param>
        /// <returns>List of XML Nodes.</returns>
        public static XmlNodeList QueryXML(string xmlPath, string xpathQuery, XmlNamespaceManager nsm)
        {
            // Load the xml document
            XmlDocument xmldoc = new XmlDocument();
            xmldoc.Load(xmlPath);

            XmlNodeList nodeList = xmldoc.SelectNodes(xpathQuery, nsm);

            return nodeList;
        }

        /// <summary>
        /// Query wixlib file.
        /// </summary>
        /// <param name="wixLibPath">Path to wixlib file.</param>
        /// <param name="xpathQuery">XPath Query.</param>
        /// <returns>List of XmlNodes.</returns>
        public static XmlNodeList QueryWixLib(string wixLibPath, string xpathQuery)
        {
            XmlNamespaceManager xmlNamespaceManager = new XmlNamespaceManager(new NameTable());
            xmlNamespaceManager.AddNamespace("wix", "http://schemas.microsoft.com/wix/2006/objects");
            xmlNamespaceManager.AddNamespace("lib", "http://schemas.microsoft.com/wix/2006/libraries");
            xmlNamespaceManager.AddNamespace("loc", "http://schemas.microsoft.com/wix/2006/localization");
            XmlNodeList nodeList = Verifier.QueryXML(wixLibPath, xpathQuery, xmlNamespaceManager);
            return nodeList;
        }

        /// <summary>
        /// Query wixobj file.
        /// </summary>
        /// <param name="wixobjPath">Path to wixobj file.</param>
        /// <param name="xpathQuery">XPath Query.</param>
        /// <returns>List of XmlNodes.</returns>
        public static XmlNodeList QueryWixobj(string wixobjPath, string xpathQuery)
        {
            XmlNamespaceManager xmlNamespaceManager = new XmlNamespaceManager(new NameTable());
            xmlNamespaceManager.AddNamespace("wix", "http://schemas.microsoft.com/wix/2006/objects");
            XmlNodeList nodeList = Verifier.QueryXML(wixobjPath, xpathQuery, xmlNamespaceManager);
            return nodeList;
        }

        /// <summary>
        /// Verification of wixlib for a localization string.
        /// </summary>
        /// <param name="libraryFile">Path to wix library file.</param>
        /// <param name="culture">culture to verify</param>
        /// <param name="stringId">string looking for</param>
        /// <param name="expectedValue">Expected value.</param>
        public static void VerifyWixLibLocString(string libraryFile, string culture, string stringId, string expectedValue)
        {
            string xpathQuery = String.Format(@" /lib:wixLibrary/loc:WixLocalization[@Culture='{0}']/loc:String[@Id='{1}']", culture, stringId);
            XmlNodeList stringNode = Verifier.QueryWixLib(libraryFile, xpathQuery);
            Assert.AreEqual(1, stringNode.Count, "Expected 1 node to be returned");
            Assert.IsNotNull(stringNode[0].InnerText, "String is Null");

            string actualValue = stringNode[0].InnerText;
            Assert.AreEqual(expectedValue, actualValue, "Unexpected value for Loc String: '{0}'  with Cutlure: '{1}'", stringId, culture);
        }

        /// <summary>
        /// Verification of wixlib for Property table.
        /// </summary>
        /// <param name="libraryFile">Path to wix library file.</param>
        /// <param name="propertyName">Name of property.</param>
        /// <param name="expectedValue">Expected value.</param>
        /// <remarks>The function checks against the first node, if multiple nodes have the same ID in diffrent sections</remarks>
        public static void VerifyWixLibProperty(string libraryFile, string propertyName, string expectedValue)
        {
            string xpathQuery = String.Format(@"/lib:wixLibrary/wix:section/wix:table[@name='Property']/wix:row/wix:field[text()='{0}']", propertyName);
            XmlNodeList propertyNode = Verifier.QueryWixLib(libraryFile, xpathQuery);
            Assert.IsTrue(propertyNode.Count > 0, "Expected at least 1 node to be returned");
            Assert.IsNotNull(propertyNode[0].NextSibling, "Property's sibling does not exist");

            string actualValue = propertyNode[0].NextSibling.InnerText;
            Assert.AreEqual(expectedValue, actualValue, "Unexpected value for Property {0}", propertyName);
        }

        /// <summary>
        /// Verification of wixobj for Property table.
        /// </summary>
        /// <param name="outputFile">Path to output file.</param>
        /// <param name="propertyName">Name of property.</param>
        /// <param name="expectedValue">Expected value.</param>
        public static void VerifyWixObjProperty(string outputFile, string propertyName, string expectedValue)
        {
            string xpathQuery = String.Format(@"//wix:wixObject/wix:section/wix:table[@name='Property']/wix:row/wix:field[text()='{0}']", propertyName);
            XmlNodeList propertyNode = Verifier.QueryWixobj(outputFile, xpathQuery);
            Assert.AreEqual(1, propertyNode.Count, "Expected 1 node to be returned");
            Assert.IsNotNull(propertyNode[0].NextSibling, "Property's sibling does not exist");

            string actualValue = propertyNode[0].NextSibling.InnerText;
            Assert.AreEqual(expectedValue, actualValue, "Unexpected value for Property {0}", propertyName);
        }

        /// <summary>
        /// Compare two Outputs
        /// </summary>
        /// <param name="targetOutput">The expected output</param>
        /// <param name="updatedOutput">The actual output</param>
        /// <returns>Any differences found.</returns>
        private static ArrayList CompareOutput(Output targetOutput, Output updatedOutput)
        {
            return Verifier.CompareOutput(targetOutput, updatedOutput, null);
        }

        /// <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>
        /// 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;
        }
    }
}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.