From cff729a33833a963bbaeca9def11b7c09f8c2c0b Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Thu, 9 Feb 2023 13:59:15 -0800 Subject: Improve instance transforms Fix bug where the string `UpgradeCode` was being used instead of the GUID for the UpgradeCode in an instance transform. Also handle the case where the instance transform's Property does or does not exist when creating the transform. Fixes 7193 --- src/internal/WixInternal.TestSupport/Query.cs | 127 +++++++++++++-------- .../Bind/BindTransformCommand.cs | 2 +- .../Bind/CreateInstanceTransformsCommand.cs | 43 +++---- .../InstanceTransformFixture.cs | 103 +++++++++++++++++ .../WixToolsetTest.CoreIntegration/MsiFixture.cs | 64 +---------- .../TestData/InstanceTransform/Package.wxs | 6 +- 6 files changed, 202 insertions(+), 143 deletions(-) create mode 100644 src/wix/test/WixToolsetTest.CoreIntegration/InstanceTransformFixture.cs (limited to 'src') diff --git a/src/internal/WixInternal.TestSupport/Query.cs b/src/internal/WixInternal.TestSupport/Query.cs index 38f5df64..4b396454 100644 --- a/src/internal/WixInternal.TestSupport/Query.cs +++ b/src/internal/WixInternal.TestSupport/Query.cs @@ -26,6 +26,20 @@ namespace WixInternal.TestSupport return results.ToArray(); } + public static string[] QueryDatabase(Database db, string[] tables) + { + var results = new List(); + var resultsByTable = QueryDatabaseByTable(db, tables); + var sortedTables = tables.ToList(); + sortedTables.Sort(); + foreach (var tableName in sortedTables) + { + var rows = resultsByTable[tableName]; + rows?.ForEach(r => results.Add($"{tableName}:{r}")); + } + return results.ToArray(); + } + /// /// Returns rows from requested tables formatted to facilitate testing. /// If the table did not exist in the database, its list will be null. @@ -39,68 +53,89 @@ namespace WixInternal.TestSupport if (tables?.Length > 0) { - var sb = new StringBuilder(); using (var db = new Database(path)) { - foreach (var table in tables) + results = QueryDatabaseByTable(db, tables); + } + } + + return results; + } + + /// + /// Returns rows from requested tables formatted to facilitate testing. + /// If the table did not exist in the database, its list will be null. + /// + /// + /// + /// + public static Dictionary> QueryDatabaseByTable(Database db, string[] tables) + { + var results = new Dictionary>(); + + if (tables?.Length > 0) + { + var sb = new StringBuilder(); + + foreach (var table in tables) + { + if (table == "_SummaryInformation") { - if (table == "_SummaryInformation") - { - var entries = new List(); - results.Add(table, entries); - - entries.Add($"Title\t{db.SummaryInfo.Title}"); - entries.Add($"Subject\t{db.SummaryInfo.Subject}"); - entries.Add($"Author\t{db.SummaryInfo.Author}"); - entries.Add($"Keywords\t{db.SummaryInfo.Keywords}"); - entries.Add($"Comments\t{db.SummaryInfo.Comments}"); - entries.Add($"Template\t{db.SummaryInfo.Template}"); - entries.Add($"CodePage\t{db.SummaryInfo.CodePage}"); - entries.Add($"PageCount\t{db.SummaryInfo.PageCount}"); - entries.Add($"WordCount\t{db.SummaryInfo.WordCount}"); - entries.Add($"CharacterCount\t{db.SummaryInfo.CharacterCount}"); - entries.Add($"Security\t{db.SummaryInfo.Security}"); - - continue; - } + var entries = new List(); + results.Add(table, entries); + + entries.Add($"Title\t{db.SummaryInfo.Title}"); + entries.Add($"Subject\t{db.SummaryInfo.Subject}"); + entries.Add($"Author\t{db.SummaryInfo.Author}"); + entries.Add($"Keywords\t{db.SummaryInfo.Keywords}"); + entries.Add($"Comments\t{db.SummaryInfo.Comments}"); + entries.Add($"Template\t{db.SummaryInfo.Template}"); + entries.Add($"CodePage\t{db.SummaryInfo.CodePage}"); + entries.Add($"PageCount\t{db.SummaryInfo.PageCount}"); + entries.Add($"WordCount\t{db.SummaryInfo.WordCount}"); + entries.Add($"CharacterCount\t{db.SummaryInfo.CharacterCount}"); + entries.Add($"Security\t{db.SummaryInfo.Security}"); + + continue; + } - if (!db.IsTablePersistent(table)) - { - results.Add(table, null); - continue; - } + if (!db.IsTablePersistent(table)) + { + results.Add(table, null); + continue; + } - var rows = new List(); - results.Add(table, rows); + var rows = new List(); + results.Add(table, rows); - using (var view = db.OpenView("SELECT * FROM `{0}`", table)) + using (var view = db.OpenView("SELECT * FROM `{0}`", table)) + { + view.Execute(); + + Record record; + while ((record = view.Fetch()) != null) { - view.Execute(); + sb.Clear(); - Record record; - while ((record = view.Fetch()) != null) + using (record) { - sb.Clear(); - - using (record) + for (var i = 0; i < record.FieldCount; ++i) { - for (var i = 0; i < record.FieldCount; ++i) + if (i > 0) { - if (i > 0) - { - sb.Append("\t"); - } - - sb.Append(record[i + 1]?.ToString()); + sb.Append("\t"); } - } - rows.Add(sb.ToString()); + sb.Append(record[i + 1]?.ToString()); + } } + + rows.Add(sb.ToString()); } - rows.Sort(); } - } + + rows.Sort(); + } } return results; diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs index b61247a5..95cc18cd 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs @@ -76,7 +76,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind } else if ("UpgradeCode" == id) { - updatedUpgradeCode = id; + updatedUpgradeCode = row.FieldAsString(1); propertyTable.Rows.RemoveAt(i); } } diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateInstanceTransformsCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateInstanceTransformsCommand.cs index 1d480250..9e5eb04e 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateInstanceTransformsCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateInstanceTransformsCommand.cs @@ -41,43 +41,27 @@ namespace WixToolset.Core.WindowsInstaller.Bind if (wixInstanceTransformsSymbols.Any()) { - string targetProductCode = null; - string targetUpgradeCode = null; - string targetProductVersion = null; - var targetSummaryInformationTable = this.Output.Tables["_SummaryInformation"]; - var targetPropertyTable = this.Output.Tables["Property"]; + var targetPropertyRows = new RowDictionary(this.Output.Tables["Property"]); + PropertyRow propertyRow = null; // Get the data from target database - foreach (var propertyRow in targetPropertyTable.Rows) - { - if ("ProductCode" == (string)propertyRow[0]) - { - targetProductCode = (string)propertyRow[1]; - } - else if ("ProductVersion" == (string)propertyRow[0]) - { - targetProductVersion = (string)propertyRow[1]; - } - else if ("UpgradeCode" == (string)propertyRow[0]) - { - targetUpgradeCode = (string)propertyRow[1]; - } - } + var targetProductCode = targetPropertyRows.TryGetValue("ProductCode", out propertyRow) ? propertyRow.Value : null; + var targetProductVersion = targetPropertyRows.TryGetValue("ProductVersion", out propertyRow) ? propertyRow.Value : null; + var targetUpgradeCode = targetPropertyRows.TryGetValue("UpgradeCode", out propertyRow) ? propertyRow.Value : null; // Index the Instance Component Rows, we'll get the Components rows from the real Component table. - var targetInstanceComponentTable = this.Section.Symbols.OfType(); - var instanceComponentGuids = targetInstanceComponentTable.ToDictionary(t => t.Id.Id, t => (ComponentRow)null); + var instanceComponentSymbols = this.Section.Symbols.OfType().ToDictionary(t => t.Id.Id, t => (ComponentRow)null); - if (instanceComponentGuids.Any()) + if (instanceComponentSymbols.Any()) { var targetComponentTable = this.Output.Tables["Component"]; foreach (ComponentRow componentRow in targetComponentTable.Rows) { var component = (string)componentRow[0]; - if (instanceComponentGuids.ContainsKey(component)) + if (instanceComponentSymbols.ContainsKey(component)) { - instanceComponentGuids[component] = componentRow; + instanceComponentSymbols[component] = componentRow; } } } @@ -123,9 +107,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind productCodeRow[0] = "ProductCode"; productCodeRow[1] = productCode; - // Change the instance property + // Set the instance property. If the property exists in the MSI already, mark the row as modified in the transform. + // Otherwise, we need to mark the row as an add. var instanceIdRow = propertyTable.CreateRow(instanceSymbol.SourceLineNumbers); - instanceIdRow.Operation = RowOperation.Modify; + instanceIdRow.Operation = targetPropertyRows.ContainsKey(instanceSymbol.PropertyId) ? RowOperation.Modify : RowOperation.Add; instanceIdRow.Fields[1].Modified = true; instanceIdRow[0] = instanceSymbol.PropertyId; instanceIdRow[1] = instanceId; @@ -192,10 +177,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind } // If there are instance Components generate new GUIDs for them. - if (0 < instanceComponentGuids.Count) + if (0 < instanceComponentSymbols.Count) { var componentTable = instanceTransform.EnsureTable(this.TableDefinitions["Component"]); - foreach (var targetComponentRow in instanceComponentGuids.Values) + foreach (var targetComponentRow in instanceComponentSymbols.Values) { var guid = targetComponentRow.Guid; if (!String.IsNullOrEmpty(guid)) diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/InstanceTransformFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/InstanceTransformFixture.cs new file mode 100644 index 00000000..2485aaf8 --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/InstanceTransformFixture.cs @@ -0,0 +1,103 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace WixToolsetTest.CoreIntegration +{ + using System; + using System.IO; + using System.Linq; + using WixInternal.Core.TestPackage; + using WixInternal.TestSupport; + using WixToolset.Data.WindowsInstaller; + using WixToolset.Dtf.WindowsInstaller; + using Xunit; + + public class InstanceTransformFixture + { + [Fact] + public void CanBuildInstanceTransform() + { + var folder = TestData.Get("TestData", "InstanceTransform"); + + using (var fs = new DisposableFileSystem()) + { + var intermediateFolder = fs.GetFolder(); + var msiPath = Path.Combine(intermediateFolder, "bin", "test.msi"); + var wixpdbPath = Path.Combine(intermediateFolder, "bin", "test.wixpdb"); + var mstPath = Path.Combine(intermediateFolder, "iii.mst"); + + var result = WixRunner.Execute(new[] + { + "build", + Path.Combine(folder, "Package.wxs"), + Path.Combine(folder, "PackageComponents.wxs"), + "-loc", Path.Combine(folder, "Package.en-us.wxl"), + "-bindpath", Path.Combine(folder, "data"), + "-intermediateFolder", intermediateFolder, + "-o", msiPath + }); + + result.AssertSuccess(); + + var output = WindowsInstallerData.Load(wixpdbPath, false); + var substorage = output.SubStorages.Single(); + Assert.Equal("I1", substorage.Name); + + var data = substorage.Data; + WixAssert.CompareLineByLine(new[] + { + "_SummaryInformation", + "Property", + "Upgrade" + }, data.Tables.Select(t => t.Name).ToArray()); + + WixAssert.CompareLineByLine(new[] + { + "INSTANCEPROPERTY\tI1", + "ProductName\tMsiPackage (Instance 1)", + }, JoinRows(data.Tables["Property"])); + + WixAssert.CompareLineByLine(new[] + { + "{22222222-2222-2222-2222-222222222222}\t\t1.0.0.0\t1033\t1\t\tWIX_UPGRADE_DETECTED", + "{11111111-1111-1111-1111-111111111111}\t\t1.0.0.0\t1033\t1\t0\t0", + "{22222222-2222-2222-2222-222222222222}\t1.0.0.0\t\t1033\t2\t\tWIX_DOWNGRADE_DETECTED", + "{11111111-1111-1111-1111-111111111111}\t1.0.0.0\t\t1033\t2\t0\t0", + }, JoinRows(data.Tables["Upgrade"])); + + var names = Query.GetSubStorageNames(msiPath); + Query.ExtractSubStorage(msiPath, "I1", mstPath); + + using (var db = new Database(msiPath, DatabaseOpenMode.Transact)) + { + db.ApplyTransform(mstPath); + + var results = Query.QueryDatabase(db, new[] { "Property", "Upgrade" }); + var resultsWithoutProductCode = results.Where(s => !s.StartsWith("Property:ProductCode\t{")).ToArray(); + WixAssert.CompareLineByLine(new[] + { + "Property:ALLUSERS\t1", + "Property:INSTANCEPROPERTY\tI1", + "Property:Manufacturer\tExample Corporation", + "Property:ProductLanguage\t1033", + "Property:ProductName\tMsiPackage (Instance 1)", + "Property:ProductVersion\t1.0.0.0", + "Property:SecureCustomProperties\tINSTANCEPROPERTY;WIX_DOWNGRADE_DETECTED;WIX_UPGRADE_DETECTED", + "Property:UpgradeCode\t{22222222-2222-2222-2222-222222222222}", + "Upgrade:{22222222-2222-2222-2222-222222222222}\t\t1.0.0.0\t1033\t1\t\tWIX_UPGRADE_DETECTED", + "Upgrade:{22222222-2222-2222-2222-222222222222}\t1.0.0.0\t\t1033\t2\t\tWIX_DOWNGRADE_DETECTED", + }, resultsWithoutProductCode); + } + } + } + + private static string[] JoinRows(Table table) + { + return table.Rows.Select(r => JoinFields(r.Fields)).ToArray(); + + static string JoinFields(Field[] fields) + { + return String.Join('\t', fields.Select(f => f.ToString())); + } + } + } +} diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/MsiFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/MsiFixture.cs index a7dbe542..0dd6d75f 100644 --- a/src/wix/test/WixToolsetTest.CoreIntegration/MsiFixture.cs +++ b/src/wix/test/WixToolsetTest.CoreIntegration/MsiFixture.cs @@ -2,12 +2,10 @@ namespace WixToolsetTest.CoreIntegration { - using System; using System.IO; using System.Linq; - using Example.Extension; - using WixInternal.TestSupport; using WixInternal.Core.TestPackage; + using WixInternal.TestSupport; using WixToolset.Data; using WixToolset.Data.Symbols; using WixToolset.Data.WindowsInstaller; @@ -598,56 +596,6 @@ namespace WixToolsetTest.CoreIntegration } } - [Fact] - public void CanBuildInstanceTransform() - { - var folder = TestData.Get(@"TestData\InstanceTransform"); - - using (var fs = new DisposableFileSystem()) - { - var intermediateFolder = fs.GetFolder(); - - var result = WixRunner.Execute(new[] - { - "build", - Path.Combine(folder, "Package.wxs"), - Path.Combine(folder, "PackageComponents.wxs"), - "-loc", Path.Combine(folder, "Package.en-us.wxl"), - "-bindpath", Path.Combine(folder, "data"), - "-intermediateFolder", intermediateFolder, - "-o", Path.Combine(intermediateFolder, @"bin\test.msi") - }); - - result.AssertSuccess(); - - var output = WindowsInstallerData.Load(Path.Combine(intermediateFolder, @"bin\test.wixpdb"), false); - var substorage = output.SubStorages.Single(); - Assert.Equal("I1", substorage.Name); - - var data = substorage.Data; - WixAssert.CompareLineByLine(new[] - { - "_SummaryInformation", - "Property", - "Upgrade" - }, data.Tables.Select(t => t.Name).ToArray()); - - WixAssert.CompareLineByLine(new[] - { - "INSTANCEPROPERTY\tI1", - "ProductName\tMsiPackage (Instance 1)", - }, JoinRows(data.Tables["Property"])); - - WixAssert.CompareLineByLine(new[] - { - "{047730A5-30FE-4A62-A520-DA9381B8226A}\t\t1.0.0.0\t1033\t1\t\tWIX_UPGRADE_DETECTED", - "{047730A5-30FE-4A62-A520-DA9381B8226A}\t\t1.0.0.0\t1033\t1\t0\t0", - "{047730A5-30FE-4A62-A520-DA9381B8226A}\t1.0.0.0\t\t1033\t2\t\tWIX_DOWNGRADE_DETECTED", - "{047730A5-30FE-4A62-A520-DA9381B8226A}\t1.0.0.0\t\t1033\t2\t0\t0" - }, JoinRows(data.Tables["Upgrade"])); - } - } - [Fact] public void FailsBuildAtBindTimeForMissingEnsureTable() { @@ -680,15 +628,5 @@ namespace WixToolsetTest.CoreIntegration Assert.False(File.Exists(msiPath)); } } - - private static string[] JoinRows(Table table) - { - return table.Rows.Select(r => JoinFields(r.Fields)).ToArray(); - - static string JoinFields(Field[] fields) - { - return String.Join('\t', fields.Select(f => f.ToString())); - } - } } } diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/InstanceTransform/Package.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/InstanceTransform/Package.wxs index 7826d673..f60149a2 100644 --- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/InstanceTransform/Package.wxs +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/InstanceTransform/Package.wxs @@ -1,13 +1,11 @@ - - - + - + -- cgit v1.2.3-55-g6feb