Beispiel #1
0
        /// <summary>
        /// Run a Torch 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 arguments = element.GetAttribute("Arguments");
            string expectedErrors = element.GetAttribute("ExpectedErrors");
            string expectedWarnings = element.GetAttribute("ExpectedWarnings");
            string extensions = element.GetAttribute("Extensions");
            string outputFile = element.GetAttribute("OutputFile");
            string targetDatabase = element.GetAttribute("TargetDatabase");
            string tempDirectory = element.GetAttribute("TempDirectory");
            string testName = element.ParentNode.Attributes["Name"].Value;
            string toolsDirectory = element.GetAttribute("ToolsDirectory");
            string updatedDatabase = element.GetAttribute("UpdatedDatabase");
            bool usePreviousOutput = ("true" == element.GetAttribute("UsePreviousOutput"));
            bool verifyTransform = ("true" == element.GetAttribute("VerifyTransform"));

            string toolFile = Path.Combine(toolsDirectory, "torch.exe");
            StringBuilder commandLine = new StringBuilder(arguments);

            // handle extensions
            if (!String.IsNullOrEmpty(extensions))
            {
                foreach (string extension in extensions.Split(';'))
                {
                    commandLine.AppendFormat(" -ext \"{0}\"", extension);
                }
            }

            // handle wixunit arguments
            if (args.NoTidy)
            {
                commandLine.Append(" -notidy");
            }

            // handle any previous outputs
            if (0 < previousUnitResults.OutputFiles.Count && usePreviousOutput)
            {
                commandLine.AppendFormat(" \"{0}\"", previousUnitResults.OutputFiles[0]);
                previousUnitResults.OutputFiles.Clear();
            }
            else // diff database files to create transform
            {
                commandLine.AppendFormat(" \"{0}\" \"{1}\"", targetDatabase, updatedDatabase);
            }

            if (null == outputFile || String.Empty == outputFile)
            {
                outputFile = Path.Combine(tempDirectory, "transform.mst");
            }
            commandLine.AppendFormat(" -out \"{0}\"", outputFile);
            previousUnitResults.OutputFiles.Add(outputFile);

            // run the tool
            ArrayList output = ToolUtility.RunTool(toolFile, commandLine.ToString());
            previousUnitResults.Errors.AddRange(ToolUtility.GetErrors(output, expectedErrors, expectedWarnings));
            previousUnitResults.Output.AddRange(output);

            // check the results
            if (verifyTransform && 0 == expectedErrors.Length && 0 == previousUnitResults.Errors.Count)
            {
                string actualDatabase = Path.Combine(tempDirectory, String.Concat(Guid.NewGuid(), ".msi"));
                File.Copy(targetDatabase, actualDatabase);
                File.SetAttributes(actualDatabase, File.GetAttributes(actualDatabase) & ~FileAttributes.ReadOnly);
                using (Database database = new Database(actualDatabase, OpenDatabase.Direct))
                {
                    // use transform validation bits set in the transform (if any; defaults to None).
                    database.ApplyTransform(outputFile);
                    database.Commit();
                }

                // check the output file
                ArrayList differences = CompareUnit.CompareResults(updatedDatabase, actualDatabase, testName, update);
                previousUnitResults.Errors.AddRange(differences);
                previousUnitResults.Output.AddRange(differences);
            }
        }
Beispiel #2
0
        /// <summary>
        /// Unbind an MSI transform file.
        /// </summary>
        /// <param name="transformFile">The transform file.</param>
        /// <param name="exportBasePath">The path where files should be exported.</param>
        /// <returns>The unbound transform.</returns>
        private Output UnbindTransform(string transformFile, string exportBasePath)
        {
            Output transform = new Output(SourceLineNumberCollection.FromFileName(transformFile));
            transform.Type = OutputType.Transform;

            // get the summary information table
            using (SummaryInformation summaryInformation = new SummaryInformation(transformFile))
            {
                Table table = transform.Tables.EnsureTable(null, this.tableDefinitions["_SummaryInformation"]);

                for (int i = 1; 19 >= i; i++)
                {
                    string value = summaryInformation.GetProperty(i);

                    if (0 < value.Length)
                    {
                        Row row = table.CreateRow(transform.SourceLineNumbers);
                        row[0] = i;
                        row[1] = value;
                    }
                }
            }

            // create a schema msi which hopefully matches the table schemas in the transform
            Output schemaOutput = new Output(null);
            string msiDatabaseFile = Path.Combine(this.tempFiles.BasePath, "schema.msi");
            foreach (TableDefinition tableDefinition in this.tableDefinitions)
            {
                // skip unreal tables and the Patch table
                if (!tableDefinition.IsUnreal && "Patch" != tableDefinition.Name)
                {
                    schemaOutput.EnsureTable(tableDefinition);
                }
            }

            Hashtable addedRows = new Hashtable();
            Table transformViewTable;

            // bind the schema msi
            using (Binder binder = new Binder())
            {
                binder.SuppressAddingValidationRows = true;
                binder.WixVariableResolver = new WixVariableResolver();
                binder.GenerateDatabase(schemaOutput, msiDatabaseFile, true, false);

                // apply the transform to the database and retrieve the modifications
                using (Database msiDatabase = new Database(msiDatabaseFile, OpenDatabase.Transact))
                {
                    // apply the transform with the ViewTransform option to collect all the modifications
                    msiDatabase.ApplyTransform(transformFile, TransformErrorConditions.All | TransformErrorConditions.ViewTransform);

                    // unbind the database
                    Output transformViewOutput = this.UnbindDatabase(msiDatabaseFile, msiDatabase, OutputType.Product, exportBasePath, true);

                    // index the added and possibly modified rows (added rows may also appears as modified rows)
                    transformViewTable = transformViewOutput.Tables["_TransformView"];
                    Hashtable modifiedRows = new Hashtable();
                    foreach (Row row in transformViewTable.Rows)
                    {
                        string tableName = (string) row[0];
                        string columnName = (string) row[1];
                        string primaryKeys = (string) row[2];

                        if ("INSERT" == columnName)
                        {
                            string index = String.Concat(tableName, ':', primaryKeys);

                            addedRows.Add(index, null);
                        }
                        else if ("CREATE" != columnName && "DELETE" != columnName && "DROP" != columnName && null != primaryKeys) // modified row
                        {
                            string index = String.Concat(tableName, ':', primaryKeys);

                            modifiedRows[index] = row;
                        }
                    }

                    // create placeholder rows for modified rows to make the transform insert the updated values when its applied
                    foreach (Row row in modifiedRows.Values)
                    {
                        string tableName = (string) row[0];
                        string columnName = (string) row[1];
                        string primaryKeys = (string) row[2];

                        string index = String.Concat(tableName, ':', primaryKeys);

                        // ignore information for added rows
                        if (!addedRows.Contains(index))
                        {
                            Table table = schemaOutput.Tables[tableName];
                            this.CreateRow(table, primaryKeys, true);
                        }
                    }
                }

                // re-bind the schema output with the placeholder rows
                binder.GenerateDatabase(schemaOutput, msiDatabaseFile, true, false);
            }

            // apply the transform to the database and retrieve the modifications
            using (Database msiDatabase = new Database(msiDatabaseFile, OpenDatabase.Transact))
            {
                try
                {
                    // apply the transform
                    msiDatabase.ApplyTransform(transformFile, TransformErrorConditions.All);

                    // commit the database to guard against weird errors with streams
                    msiDatabase.Commit();
                }
                catch (Win32Exception ex)
                {
                    if (0x65B == ex.NativeErrorCode)
                    {
                        // this commonly happens when the transform was built
                        // against a database schema different from the internal
                        // table definitions
                        throw new WixException(WixErrors.TransformSchemaMismatch());
                    }
                }

                // unbind the database
                Output output = this.UnbindDatabase(msiDatabaseFile, msiDatabase, OutputType.Product, exportBasePath, true);

                // index all the rows to easily find modified rows
                Hashtable rows = new Hashtable();
                foreach (Table table in output.Tables)
                {
                    foreach (Row row in table.Rows)
                    {
                        rows.Add(String.Concat(table.Name, ':', row.GetPrimaryKey('\t', " ")), row);
                    }
                }

                // process the _TransformView rows into transform rows
                foreach (Row row in transformViewTable.Rows)
                {
                    string tableName = (string)row[0];
                    string columnName = (string)row[1];
                    string primaryKeys = (string)row[2];

                    Table table = transform.Tables.EnsureTable(null, this.tableDefinitions[tableName]);

                    if ("CREATE" == columnName) // added table
                    {
                        table.Operation = TableOperation.Add;
                    }
                    else if ("DELETE" == columnName) // deleted row
                    {
                        Row deletedRow = this.CreateRow(table, primaryKeys, false);
                        deletedRow.Operation = RowOperation.Delete;
                    }
                    else if ("DROP" == columnName) // dropped table
                    {
                        table.Operation = TableOperation.Drop;
                    }
                    else if ("INSERT" == columnName) // added row
                    {
                        string index = String.Concat(tableName, ':', primaryKeys);
                        Row addedRow = (Row)rows[index];
                        addedRow.Operation = RowOperation.Add;
                        table.Rows.Add(addedRow);
                    }
                    else if (null != primaryKeys) // modified row
                    {
                        string index = String.Concat(tableName, ':', primaryKeys);

                        // the _TransformView table includes information for added rows
                        // that looks like modified rows so it sometimes needs to be ignored
                        if (!addedRows.Contains(index))
                        {
                            Row modifiedRow = (Row)rows[index];

                            // mark the field as modified
                            int indexOfModifiedValue = modifiedRow.TableDefinition.Columns.IndexOf(columnName);
                            modifiedRow.Fields[indexOfModifiedValue].Modified = true;

                            // move the modified row into the transform the first time its encountered
                            if (RowOperation.None == modifiedRow.Operation)
                            {
                                modifiedRow.Operation = RowOperation.Modify;
                                table.Rows.Add(modifiedRow);
                            }
                        }
                    }
                    else // added column
                    {
                        table.Definition.Columns[columnName].Added = true;
                    }
                }
            }

            return transform;
        }