From 913b6238417dceeb8440315e4669990756d17655 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Tue, 19 Jul 2022 15:17:10 -0500 Subject: Add WixInternalUIBootstrapperApplication as a new built-in BA. Implements 6835 --- src/test/burn/TestData/Templates/Bundle.wxs | 4 + src/test/burn/TestData/TestData.proj | 2 +- .../ArchSpecificBundle/ArchSpecificBundle.wixproj | 17 ++ .../ArchSpecificBundle/ArchSpecificBundle.wxs | 13 ++ .../WixIuiBaTests/ArchSpecificBundle/Bundle.wxs | 63 ++++++++ .../burn/TestData/WixIuiBaTests/DtfSamples.sln | 28 ++++ .../WixIuiBaTests/EmbeddedUI/EmbeddedUI.config | 10 ++ .../WixIuiBaTests/EmbeddedUI/EmbeddedUI.csproj | 24 +++ .../EmbeddedUI/InstallProgressCounter.cs | 174 +++++++++++++++++++++ .../WixIuiBaTests/EmbeddedUI/SampleEmbeddedUI.cs | 141 +++++++++++++++++ .../WixIuiBaTests/EmbeddedUI/SetupWizard.xaml | 19 +++ .../WixIuiBaTests/EmbeddedUI/SetupWizard.xaml.cs | 154 ++++++++++++++++++ .../WixIuiBaTests/EmbeddedUIBundle/Bundle.wxs | 63 ++++++++ .../EmbeddedUIBundle/EmbeddedUIBundle.wixproj | 15 ++ .../EmbeddedUIBundle/EmbeddedUIBundle.wxs | 11 ++ .../EmbeddedUIPackage/EmbeddedUIPackage.wixproj | 16 ++ .../EmbeddedUIPackage/EmbeddedUIPackage.wxs | 13 ++ .../WixIuiBaTests/InternalUIBundle/Bundle.wxs | 63 ++++++++ .../InternalUIBundle/InternalUIBundle.wixproj | 14 ++ .../InternalUIBundle/InternalUIBundle.wxs | 10 ++ .../InternalUIPackage/InternalUIPackage.wixproj | 16 ++ .../InternalUIPackage/InternalUIPackage.wxs | 24 +++ .../InternalUIarm64Package.wixproj | 14 ++ .../InternalUIarm64Package.wxs | 9 ++ .../InternalUIx64Package.wixproj | 14 ++ .../InternalUIx64Package/InternalUIx64Package.wxs | 9 ++ .../InternalUIx86Package.wixproj | 14 ++ .../InternalUIx86Package/InternalUIx86Package.wxs | 9 ++ .../WixIuiBaTests/ManagedCA/CustomAction.config | 10 ++ .../WixIuiBaTests/ManagedCA/ManagedCA.csproj | 16 ++ .../TestData/WixIuiBaTests/ManagedCA/SampleCA.cs | 125 +++++++++++++++ .../WixIuiBaTests/ManagedCA/testsub/testfile.txt | 1 + src/test/burn/WixTestTools/PackageVerifier.cs | 18 ++- .../burn/WixToolsetTest.BurnE2E/WixIuiBaTests.cs | 144 +++++++++++++++++ src/test/dtf/Directory.Build.props | 11 -- src/test/dtf/Directory.Build.targets | 6 - src/test/dtf/DtfE2ETests.sln | 28 ---- src/test/dtf/EmbeddedUI/EmbeddedUI.config | 10 -- src/test/dtf/EmbeddedUI/EmbeddedUI.csproj | 24 --- src/test/dtf/EmbeddedUI/InstallProgressCounter.cs | 174 --------------------- src/test/dtf/EmbeddedUI/SampleEmbeddedUI.cs | 141 ----------------- src/test/dtf/EmbeddedUI/SetupWizard.xaml | 19 --- src/test/dtf/EmbeddedUI/SetupWizard.xaml.cs | 154 ------------------ src/test/dtf/SampleCA/CustomAction.config | 10 -- src/test/dtf/SampleCA/SampleCA.cs | 125 --------------- src/test/dtf/SampleCA/SampleCA.csproj | 16 -- src/test/dtf/SampleCA/testsub/testfile.txt | 1 - src/test/test.cmd | 2 - 48 files changed, 1272 insertions(+), 726 deletions(-) create mode 100644 src/test/burn/TestData/WixIuiBaTests/ArchSpecificBundle/ArchSpecificBundle.wixproj create mode 100644 src/test/burn/TestData/WixIuiBaTests/ArchSpecificBundle/ArchSpecificBundle.wxs create mode 100644 src/test/burn/TestData/WixIuiBaTests/ArchSpecificBundle/Bundle.wxs create mode 100644 src/test/burn/TestData/WixIuiBaTests/DtfSamples.sln create mode 100644 src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/EmbeddedUI.config create mode 100644 src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/EmbeddedUI.csproj create mode 100644 src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/InstallProgressCounter.cs create mode 100644 src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/SampleEmbeddedUI.cs create mode 100644 src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/SetupWizard.xaml create mode 100644 src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/SetupWizard.xaml.cs create mode 100644 src/test/burn/TestData/WixIuiBaTests/EmbeddedUIBundle/Bundle.wxs create mode 100644 src/test/burn/TestData/WixIuiBaTests/EmbeddedUIBundle/EmbeddedUIBundle.wixproj create mode 100644 src/test/burn/TestData/WixIuiBaTests/EmbeddedUIBundle/EmbeddedUIBundle.wxs create mode 100644 src/test/burn/TestData/WixIuiBaTests/EmbeddedUIPackage/EmbeddedUIPackage.wixproj create mode 100644 src/test/burn/TestData/WixIuiBaTests/EmbeddedUIPackage/EmbeddedUIPackage.wxs create mode 100644 src/test/burn/TestData/WixIuiBaTests/InternalUIBundle/Bundle.wxs create mode 100644 src/test/burn/TestData/WixIuiBaTests/InternalUIBundle/InternalUIBundle.wixproj create mode 100644 src/test/burn/TestData/WixIuiBaTests/InternalUIBundle/InternalUIBundle.wxs create mode 100644 src/test/burn/TestData/WixIuiBaTests/InternalUIPackage/InternalUIPackage.wixproj create mode 100644 src/test/burn/TestData/WixIuiBaTests/InternalUIPackage/InternalUIPackage.wxs create mode 100644 src/test/burn/TestData/WixIuiBaTests/InternalUIarm64Package/InternalUIarm64Package.wixproj create mode 100644 src/test/burn/TestData/WixIuiBaTests/InternalUIarm64Package/InternalUIarm64Package.wxs create mode 100644 src/test/burn/TestData/WixIuiBaTests/InternalUIx64Package/InternalUIx64Package.wixproj create mode 100644 src/test/burn/TestData/WixIuiBaTests/InternalUIx64Package/InternalUIx64Package.wxs create mode 100644 src/test/burn/TestData/WixIuiBaTests/InternalUIx86Package/InternalUIx86Package.wixproj create mode 100644 src/test/burn/TestData/WixIuiBaTests/InternalUIx86Package/InternalUIx86Package.wxs create mode 100644 src/test/burn/TestData/WixIuiBaTests/ManagedCA/CustomAction.config create mode 100644 src/test/burn/TestData/WixIuiBaTests/ManagedCA/ManagedCA.csproj create mode 100644 src/test/burn/TestData/WixIuiBaTests/ManagedCA/SampleCA.cs create mode 100644 src/test/burn/TestData/WixIuiBaTests/ManagedCA/testsub/testfile.txt create mode 100644 src/test/burn/WixToolsetTest.BurnE2E/WixIuiBaTests.cs delete mode 100644 src/test/dtf/Directory.Build.props delete mode 100644 src/test/dtf/Directory.Build.targets delete mode 100644 src/test/dtf/DtfE2ETests.sln delete mode 100644 src/test/dtf/EmbeddedUI/EmbeddedUI.config delete mode 100644 src/test/dtf/EmbeddedUI/EmbeddedUI.csproj delete mode 100644 src/test/dtf/EmbeddedUI/InstallProgressCounter.cs delete mode 100644 src/test/dtf/EmbeddedUI/SampleEmbeddedUI.cs delete mode 100644 src/test/dtf/EmbeddedUI/SetupWizard.xaml delete mode 100644 src/test/dtf/EmbeddedUI/SetupWizard.xaml.cs delete mode 100644 src/test/dtf/SampleCA/CustomAction.config delete mode 100644 src/test/dtf/SampleCA/SampleCA.cs delete mode 100644 src/test/dtf/SampleCA/SampleCA.csproj delete mode 100644 src/test/dtf/SampleCA/testsub/testfile.txt (limited to 'src/test') diff --git a/src/test/burn/TestData/Templates/Bundle.wxs b/src/test/burn/TestData/Templates/Bundle.wxs index 612e67f5..c55f67a7 100644 --- a/src/test/burn/TestData/Templates/Bundle.wxs +++ b/src/test/burn/TestData/Templates/Bundle.wxs @@ -33,6 +33,10 @@ + + + + diff --git a/src/test/burn/TestData/TestData.proj b/src/test/burn/TestData/TestData.proj index 27bfb02b..0f7cbee9 100644 --- a/src/test/burn/TestData/TestData.proj +++ b/src/test/burn/TestData/TestData.proj @@ -4,7 +4,7 @@ - + diff --git a/src/test/burn/TestData/WixIuiBaTests/ArchSpecificBundle/ArchSpecificBundle.wixproj b/src/test/burn/TestData/WixIuiBaTests/ArchSpecificBundle/ArchSpecificBundle.wixproj new file mode 100644 index 00000000..9e3cba8b --- /dev/null +++ b/src/test/burn/TestData/WixIuiBaTests/ArchSpecificBundle/ArchSpecificBundle.wixproj @@ -0,0 +1,17 @@ + + + + Bundle + iui + {22B5ADAF-74D3-424A-8D32-9901FCF97E6D} + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/burn/TestData/WixIuiBaTests/ArchSpecificBundle/ArchSpecificBundle.wxs b/src/test/burn/TestData/WixIuiBaTests/ArchSpecificBundle/ArchSpecificBundle.wxs new file mode 100644 index 00000000..71b8665f --- /dev/null +++ b/src/test/burn/TestData/WixIuiBaTests/ArchSpecificBundle/ArchSpecificBundle.wxs @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/burn/TestData/WixIuiBaTests/ArchSpecificBundle/Bundle.wxs b/src/test/burn/TestData/WixIuiBaTests/ArchSpecificBundle/Bundle.wxs new file mode 100644 index 00000000..195c159e --- /dev/null +++ b/src/test/burn/TestData/WixIuiBaTests/ArchSpecificBundle/Bundle.wxs @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/burn/TestData/WixIuiBaTests/DtfSamples.sln b/src/test/burn/TestData/WixIuiBaTests/DtfSamples.sln new file mode 100644 index 00000000..1e6c2082 --- /dev/null +++ b/src/test/burn/TestData/WixIuiBaTests/DtfSamples.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EmbeddedUI", "EmbeddedUI\EmbeddedUI.csproj", "{864B8C50-7895-4485-AC89-900D86FD8C0D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ManagedCA", "ManagedCA\ManagedCA.csproj", "{8F53B9CC-6FBE-493D-9C9A-09B2AD578CE7}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {864B8C50-7895-4485-AC89-900D86FD8C0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {864B8C50-7895-4485-AC89-900D86FD8C0D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {864B8C50-7895-4485-AC89-900D86FD8C0D}.Release|Any CPU.ActiveCfg = Debug|Any CPU + {864B8C50-7895-4485-AC89-900D86FD8C0D}.Release|Any CPU.Build.0 = Debug|Any CPU + {8F53B9CC-6FBE-493D-9C9A-09B2AD578CE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8F53B9CC-6FBE-493D-9C9A-09B2AD578CE7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8F53B9CC-6FBE-493D-9C9A-09B2AD578CE7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8F53B9CC-6FBE-493D-9C9A-09B2AD578CE7}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/EmbeddedUI.config b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/EmbeddedUI.config new file mode 100644 index 00000000..700aff6f --- /dev/null +++ b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/EmbeddedUI.config @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/EmbeddedUI.csproj b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/EmbeddedUI.csproj new file mode 100644 index 00000000..a6339220 --- /dev/null +++ b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/EmbeddedUI.csproj @@ -0,0 +1,24 @@ + + + net35 + Sample managed embedded external UI + true + + + + + + + + + + + + + + + + + + + diff --git a/src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/InstallProgressCounter.cs b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/InstallProgressCounter.cs new file mode 100644 index 00000000..3d75081c --- /dev/null +++ b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/InstallProgressCounter.cs @@ -0,0 +1,174 @@ +namespace WixToolset.Samples.EmbeddedUI +{ + using System; + using WixToolset.Dtf.WindowsInstaller; + + /// + /// Tracks MSI progress messages and converts them to usable progress. + /// + public class InstallProgressCounter + { + private int total; + private int completed; + private int step; + private bool moveForward; + private bool enableActionData; + private int progressPhase; + private double scriptPhaseWeight; + + public InstallProgressCounter() : this(0.3) + { + } + + public InstallProgressCounter(double scriptPhaseWeight) + { + if (!(0 <= scriptPhaseWeight && scriptPhaseWeight <= 1)) + { + throw new ArgumentOutOfRangeException("scriptPhaseWeight"); + } + + this.scriptPhaseWeight = scriptPhaseWeight; + } + + /// + /// Gets a number between 0 and 1 that indicates the overall installation progress. + /// + public double Progress { get; private set; } + + public void ProcessMessage(InstallMessage messageType, Record messageRecord) + { + // This MSI progress-handling code was mostly borrowed from burn and translated from C++ to C#. + + switch (messageType) + { + case InstallMessage.ActionStart: + if (this.enableActionData) + { + this.enableActionData = false; + } + break; + + case InstallMessage.ActionData: + if (this.enableActionData) + { + if (this.moveForward) + { + this.completed += this.step; + } + else + { + this.completed -= this.step; + } + + this.UpdateProgress(); + } + break; + + case InstallMessage.Progress: + this.ProcessProgressMessage(messageRecord); + break; + } + } + + private void ProcessProgressMessage(Record progressRecord) + { + // This MSI progress-handling code was mostly borrowed from burn and translated from C++ to C#. + + if (progressRecord == null || progressRecord.FieldCount == 0) + { + return; + } + + int fieldCount = progressRecord.FieldCount; + int progressType = progressRecord.GetInteger(1); + string progressTypeString = String.Empty; + switch (progressType) + { + case 0: // Master progress reset + if (fieldCount < 4) + { + return; + } + + this.progressPhase++; + + this.total = progressRecord.GetInteger(2); + if (this.progressPhase == 1) + { + // HACK!!! this is a hack courtesy of the Windows Installer team. It seems the script planning phase + // is always off by "about 50". So we'll toss an extra 50 ticks on so that the standard progress + // doesn't go over 100%. If there are any custom actions, they may blow the total so we'll call this + // "close" and deal with the rest. + this.total += 50; + } + + this.moveForward = (progressRecord.GetInteger(3) == 0); + this.completed = (this.moveForward ? 0 : this.total); // if forward start at 0, if backwards start at max + this.enableActionData = false; + + this.UpdateProgress(); + break; + + case 1: // Action info + if (fieldCount < 3) + { + return; + } + + if (progressRecord.GetInteger(3) == 0) + { + this.enableActionData = false; + } + else + { + this.enableActionData = true; + this.step = progressRecord.GetInteger(2); + } + break; + + case 2: // Progress report + if (fieldCount < 2 || this.total == 0 || this.progressPhase == 0) + { + return; + } + + if (this.moveForward) + { + this.completed += progressRecord.GetInteger(2); + } + else + { + this.completed -= progressRecord.GetInteger(2); + } + + this.UpdateProgress(); + break; + + case 3: // Progress total addition + this.total += progressRecord.GetInteger(2); + break; + } + } + + private void UpdateProgress() + { + if (this.progressPhase < 1 || this.total == 0) + { + this.Progress = 0; + } + else if (this.progressPhase == 1) + { + this.Progress = this.scriptPhaseWeight * Math.Min(this.completed, this.total) / this.total; + } + else if (this.progressPhase == 2) + { + this.Progress = this.scriptPhaseWeight + + (1 - this.scriptPhaseWeight) * Math.Min(this.completed, this.total) / this.total; + } + else + { + this.Progress = 1; + } + } + } +} diff --git a/src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/SampleEmbeddedUI.cs b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/SampleEmbeddedUI.cs new file mode 100644 index 00000000..ae86dc97 --- /dev/null +++ b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/SampleEmbeddedUI.cs @@ -0,0 +1,141 @@ +namespace WixToolset.Samples.EmbeddedUI +{ + using System; + using System.Collections.Generic; + using System.Configuration; + using System.Threading; + using System.Windows; + using System.Windows.Threading; + using WixToolset.Dtf.WindowsInstaller; + using Application = System.Windows.Application; + + public class SampleEmbeddedUI : IEmbeddedUI + { + private bool isMaintenance; + private Thread appThread; + private Application app; + private SetupWizard setupWizard; + private ManualResetEvent installStartEvent; + private ManualResetEvent installExitEvent; + + /// + /// Initializes the embedded UI. + /// + /// Handle to the installer which can be used to get and set properties. + /// The handle is only valid for the duration of this method call. + /// Path to the directory that contains all the files from the MsiEmbeddedUI table. + /// On entry, contains the current UI level for the installation. After this + /// method returns, the installer resets the UI level to the returned value of this parameter. + /// True if the embedded UI was successfully initialized; false if the installation + /// should continue without the embedded UI. + /// The installation was canceled by the user. + /// The embedded UI failed to initialize and + /// causes the installation to fail. + public bool Initialize(Session session, string resourcePath, ref InstallUIOptions internalUILevel) + { + if (session != null) + { + if ((internalUILevel & InstallUIOptions.Full) != InstallUIOptions.Full) + { + // Don't show custom UI when the UI level is set to basic. + return false; + + // An embedded UI could display an alternate dialog sequence for reduced or + // basic modes, but it's not implemented here. We'll just fall back to the + // built-in MSI basic UI. + } + + if (String.Equals(session["REMOVE"], "All", StringComparison.OrdinalIgnoreCase)) + { + // Don't show custom UI when uninstall was specified on the command line. + return false; + } + + this.isMaintenance = session.EvaluateCondition("Installed"); + } + + // Start the setup wizard on a separate thread. + this.installStartEvent = new ManualResetEvent(false); + this.installExitEvent = new ManualResetEvent(false); + this.appThread = new Thread(this.Run); + this.appThread.SetApartmentState(ApartmentState.STA); + this.appThread.Start(); + + // Wait for the setup wizard to either kickoff the install or prematurely exit. + int waitResult = WaitHandle.WaitAny(new WaitHandle[] { this.installStartEvent, this.installExitEvent }); + if (waitResult == 1) + { + // The setup wizard set the exit event instead of the start event. Cancel the installation. + throw new InstallCanceledException(); + } + else + { + switch (this.setupWizard.Operation) + { + case SetupOperationType.Repair: + session["REINSTALL"] = "ALL"; + break; + case SetupOperationType.Uninstall: + session["REMOVE"] = "ALL"; + break; + } + + // Start the installation with a silenced internal UI. + // This "embedded external UI" will handle message types except for source resolution. + internalUILevel = InstallUIOptions.NoChange | InstallUIOptions.SourceResolutionOnly; + return true; + } + } + + /// + /// Processes information and progress messages sent to the user interface. + /// + /// Message type. + /// Record that contains message data. + /// Message box buttons. + /// Message box icon. + /// Message box default button. + /// Result of processing the message. + public MessageResult ProcessMessage(InstallMessage messageType, Record messageRecord, + MessageButtons buttons, MessageIcon icon, MessageDefaultButton defaultButton) + { + // Synchronously send the message to the setup wizard window on its thread. + object result = this.setupWizard.Dispatcher.Invoke(DispatcherPriority.Send, + new Func(delegate() + { + return this.setupWizard.ProcessMessage(messageType, messageRecord, buttons, icon, defaultButton); + })); + return (MessageResult) result; + } + + /// + /// Shuts down the embedded UI at the end of the installation. + /// + /// + /// If the installation was canceled during initialization, this method will not be called. + /// If the installation was canceled or failed at any later point, this method will be called at the end. + /// + public void Shutdown() + { + // Wait for the user to exit the setup wizard. + this.setupWizard.Dispatcher.BeginInvoke(DispatcherPriority.Normal, + new Action(delegate() + { + this.setupWizard.EnableExit(); + })); + this.appThread.Join(); + } + + /// + /// Creates the setup wizard and runs the application thread. + /// + private void Run() + { + this.app = new Application(); + this.setupWizard = new SetupWizard(this.installStartEvent, this.isMaintenance); + this.setupWizard.InitializeComponent(); + this.app.Run(this.setupWizard); + this.installExitEvent.Set(); + } + } +} diff --git a/src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/SetupWizard.xaml b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/SetupWizard.xaml new file mode 100644 index 00000000..97e406c2 --- /dev/null +++ b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/SetupWizard.xaml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + diff --git a/src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/SetupWizard.xaml.cs b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/SetupWizard.xaml.cs new file mode 100644 index 00000000..a4345481 --- /dev/null +++ b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUI/SetupWizard.xaml.cs @@ -0,0 +1,154 @@ +namespace WixToolset.Samples.EmbeddedUI +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading; + using System.Windows; + using System.Windows.Controls; + using System.Windows.Data; + using System.Windows.Documents; + using System.Windows.Input; + using System.Windows.Media; + using System.Windows.Media.Imaging; + using System.Windows.Navigation; + using System.Windows.Shapes; + using WixToolset.Dtf.WindowsInstaller; + + public enum SetupOperationType + { + Install, + Repair, + Uninstall + } + + /// + /// Interaction logic for SetupWizard.xaml + /// + public partial class SetupWizard : Window + { + private bool isMaintenance; + private ManualResetEvent installStartEvent; + private InstallProgressCounter progressCounter; + private bool canceled; + + public SetupOperationType Operation { get; private set; } + + public SetupWizard(ManualResetEvent installStartEvent, bool isMaintenance) + { + this.installStartEvent = installStartEvent; + this.progressCounter = new InstallProgressCounter(0.5); + this.isMaintenance = isMaintenance; + + this.Loaded += this.SetupWizard_Loaded; + } + + private void SetupWizard_Loaded(object sender, RoutedEventArgs e) + { + this.Loaded -= this.SetupWizard_Loaded; + + if (this.isMaintenance) + { + this.installButton.Visibility = Visibility.Hidden; + this.repairButton.Visibility = Visibility.Visible; + this.uninstallButton.Visibility = Visibility.Visible; + } + } + + public MessageResult ProcessMessage(InstallMessage messageType, Record messageRecord, + MessageButtons buttons, MessageIcon icon, MessageDefaultButton defaultButton) + { + try + { + this.progressCounter.ProcessMessage(messageType, messageRecord); + this.progressBar.Value = this.progressBar.Minimum + + this.progressCounter.Progress * (this.progressBar.Maximum - this.progressBar.Minimum); + this.progressLabel.Content = "" + (int) Math.Round(100 * this.progressCounter.Progress) + "%"; + + switch (messageType) + { + case InstallMessage.Error: + case InstallMessage.Warning: + case InstallMessage.Info: + string message = String.Format("{0}: {1}", messageType, messageRecord); + this.LogMessage(message); + break; + } + + if (this.canceled) + { + this.canceled = false; + return MessageResult.Cancel; + } + } + catch (Exception ex) + { + this.LogMessage(ex.ToString()); + this.LogMessage(ex.StackTrace); + } + + return MessageResult.OK; + } + + private void LogMessage(string message) + { + this.messagesTextBox.Text += Environment.NewLine + message; + this.messagesTextBox.ScrollToEnd(); + } + + internal void EnableExit() + { + this.progressBar.Visibility = Visibility.Hidden; + this.progressLabel.Visibility = Visibility.Hidden; + this.cancelButton.Visibility = Visibility.Hidden; + this.exitButton.Visibility = Visibility.Visible; + } + + private void installButton_Click(object sender, RoutedEventArgs e) + { + this.Operation = SetupOperationType.Install; + this.StartInstall(); + } + + private void repairButton_Click(object sender, RoutedEventArgs e) + { + this.Operation = SetupOperationType.Repair; + this.StartInstall(); + } + + private void uninstallButton_Click(object sender, RoutedEventArgs e) + { + this.Operation = SetupOperationType.Uninstall; + this.StartInstall(); + } + + private void StartInstall() + { + this.installButton.Visibility = Visibility.Hidden; + this.repairButton.Visibility = Visibility.Hidden; + this.uninstallButton.Visibility = Visibility.Hidden; + this.progressBar.Visibility = Visibility.Visible; + this.progressLabel.Visibility = Visibility.Visible; + this.installStartEvent.Set(); + } + + private void exitButton_Click(object sender, RoutedEventArgs e) + { + this.Close(); + } + + private void cancelButton_Click(object sender, RoutedEventArgs e) + { + if (this.installButton.Visibility == Visibility.Visible) + { + this.Close(); + } + else + { + this.canceled = true; + this.cancelButton.IsEnabled = false; + } + } + } +} diff --git a/src/test/burn/TestData/WixIuiBaTests/EmbeddedUIBundle/Bundle.wxs b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUIBundle/Bundle.wxs new file mode 100644 index 00000000..195c159e --- /dev/null +++ b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUIBundle/Bundle.wxs @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/burn/TestData/WixIuiBaTests/EmbeddedUIBundle/EmbeddedUIBundle.wixproj b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUIBundle/EmbeddedUIBundle.wixproj new file mode 100644 index 00000000..7c856200 --- /dev/null +++ b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUIBundle/EmbeddedUIBundle.wixproj @@ -0,0 +1,15 @@ + + + + Bundle + iui + {5115D6AC-A1FE-40C6-BEB3-BEBB39E61579} + + + + + + + + + \ No newline at end of file diff --git a/src/test/burn/TestData/WixIuiBaTests/EmbeddedUIBundle/EmbeddedUIBundle.wxs b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUIBundle/EmbeddedUIBundle.wxs new file mode 100644 index 00000000..6adda9de --- /dev/null +++ b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUIBundle/EmbeddedUIBundle.wxs @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/test/burn/TestData/WixIuiBaTests/EmbeddedUIPackage/EmbeddedUIPackage.wixproj b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUIPackage/EmbeddedUIPackage.wixproj new file mode 100644 index 00000000..0a62f38b --- /dev/null +++ b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUIPackage/EmbeddedUIPackage.wixproj @@ -0,0 +1,16 @@ + + + + true + {A6826B6D-2F3F-456D-BABF-1B8CDCE3AE68} + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/burn/TestData/WixIuiBaTests/EmbeddedUIPackage/EmbeddedUIPackage.wxs b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUIPackage/EmbeddedUIPackage.wxs new file mode 100644 index 00000000..e5d18c80 --- /dev/null +++ b/src/test/burn/TestData/WixIuiBaTests/EmbeddedUIPackage/EmbeddedUIPackage.wxs @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/burn/TestData/WixIuiBaTests/InternalUIBundle/Bundle.wxs b/src/test/burn/TestData/WixIuiBaTests/InternalUIBundle/Bundle.wxs new file mode 100644 index 00000000..195c159e --- /dev/null +++ b/src/test/burn/TestData/WixIuiBaTests/InternalUIBundle/Bundle.wxs @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/burn/TestData/WixIuiBaTests/InternalUIBundle/InternalUIBundle.wixproj b/src/test/burn/TestData/WixIuiBaTests/InternalUIBundle/InternalUIBundle.wixproj new file mode 100644 index 00000000..193d0ca6 --- /dev/null +++ b/src/test/burn/TestData/WixIuiBaTests/InternalUIBundle/InternalUIBundle.wixproj @@ -0,0 +1,14 @@ + + + + Bundle + iui + {62CB2BAE-129D-41D1-928B-5353FC542130} + + + + + + + + \ No newline at end of file diff --git a/src/test/burn/TestData/WixIuiBaTests/InternalUIBundle/InternalUIBundle.wxs b/src/test/burn/TestData/WixIuiBaTests/InternalUIBundle/InternalUIBundle.wxs new file mode 100644 index 00000000..332f0b08 --- /dev/null +++ b/src/test/burn/TestData/WixIuiBaTests/InternalUIBundle/InternalUIBundle.wxs @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/test/burn/TestData/WixIuiBaTests/InternalUIPackage/InternalUIPackage.wixproj b/src/test/burn/TestData/WixIuiBaTests/InternalUIPackage/InternalUIPackage.wixproj new file mode 100644 index 00000000..2744d4b3 --- /dev/null +++ b/src/test/burn/TestData/WixIuiBaTests/InternalUIPackage/InternalUIPackage.wixproj @@ -0,0 +1,16 @@ + + + + true + {7A0FE267-9EAE-4780-B41E-60C9E7044F53} + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/burn/TestData/WixIuiBaTests/InternalUIPackage/InternalUIPackage.wxs b/src/test/burn/TestData/WixIuiBaTests/InternalUIPackage/InternalUIPackage.wxs new file mode 100644 index 00000000..d5f30729 --- /dev/null +++ b/src/test/burn/TestData/WixIuiBaTests/InternalUIPackage/InternalUIPackage.wxs @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/burn/TestData/WixIuiBaTests/InternalUIarm64Package/InternalUIarm64Package.wixproj b/src/test/burn/TestData/WixIuiBaTests/InternalUIarm64Package/InternalUIarm64Package.wixproj new file mode 100644 index 00000000..e7d5b484 --- /dev/null +++ b/src/test/burn/TestData/WixIuiBaTests/InternalUIarm64Package/InternalUIarm64Package.wixproj @@ -0,0 +1,14 @@ + + + + arm64 + true + {357EBF93-039C-4378-8BCB-8B53F9B9F69A} + + + + + + + + \ No newline at end of file diff --git a/src/test/burn/TestData/WixIuiBaTests/InternalUIarm64Package/InternalUIarm64Package.wxs b/src/test/burn/TestData/WixIuiBaTests/InternalUIarm64Package/InternalUIarm64Package.wxs new file mode 100644 index 00000000..c30cf607 --- /dev/null +++ b/src/test/burn/TestData/WixIuiBaTests/InternalUIarm64Package/InternalUIarm64Package.wxs @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/test/burn/TestData/WixIuiBaTests/InternalUIx64Package/InternalUIx64Package.wixproj b/src/test/burn/TestData/WixIuiBaTests/InternalUIx64Package/InternalUIx64Package.wixproj new file mode 100644 index 00000000..ff646914 --- /dev/null +++ b/src/test/burn/TestData/WixIuiBaTests/InternalUIx64Package/InternalUIx64Package.wixproj @@ -0,0 +1,14 @@ + + + + x64 + true + {2EBACF8F-BECD-401C-94F2-CFA2C9C3C07F} + + + + + + + + \ No newline at end of file diff --git a/src/test/burn/TestData/WixIuiBaTests/InternalUIx64Package/InternalUIx64Package.wxs b/src/test/burn/TestData/WixIuiBaTests/InternalUIx64Package/InternalUIx64Package.wxs new file mode 100644 index 00000000..c30cf607 --- /dev/null +++ b/src/test/burn/TestData/WixIuiBaTests/InternalUIx64Package/InternalUIx64Package.wxs @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/test/burn/TestData/WixIuiBaTests/InternalUIx86Package/InternalUIx86Package.wixproj b/src/test/burn/TestData/WixIuiBaTests/InternalUIx86Package/InternalUIx86Package.wixproj new file mode 100644 index 00000000..4107ab44 --- /dev/null +++ b/src/test/burn/TestData/WixIuiBaTests/InternalUIx86Package/InternalUIx86Package.wixproj @@ -0,0 +1,14 @@ + + + + x64 + true + {1FA46DA0-5F83-459A-8A30-3E5A54D20A4D} + + + + + + + + \ No newline at end of file diff --git a/src/test/burn/TestData/WixIuiBaTests/InternalUIx86Package/InternalUIx86Package.wxs b/src/test/burn/TestData/WixIuiBaTests/InternalUIx86Package/InternalUIx86Package.wxs new file mode 100644 index 00000000..c30cf607 --- /dev/null +++ b/src/test/burn/TestData/WixIuiBaTests/InternalUIx86Package/InternalUIx86Package.wxs @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/test/burn/TestData/WixIuiBaTests/ManagedCA/CustomAction.config b/src/test/burn/TestData/WixIuiBaTests/ManagedCA/CustomAction.config new file mode 100644 index 00000000..700aff6f --- /dev/null +++ b/src/test/burn/TestData/WixIuiBaTests/ManagedCA/CustomAction.config @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/test/burn/TestData/WixIuiBaTests/ManagedCA/ManagedCA.csproj b/src/test/burn/TestData/WixIuiBaTests/ManagedCA/ManagedCA.csproj new file mode 100644 index 00000000..866b7575 --- /dev/null +++ b/src/test/burn/TestData/WixIuiBaTests/ManagedCA/ManagedCA.csproj @@ -0,0 +1,16 @@ + + + net20 + Sample managed custom actions + + + + + + + + + + + + diff --git a/src/test/burn/TestData/WixIuiBaTests/ManagedCA/SampleCA.cs b/src/test/burn/TestData/WixIuiBaTests/ManagedCA/SampleCA.cs new file mode 100644 index 00000000..fc9f30fe --- /dev/null +++ b/src/test/burn/TestData/WixIuiBaTests/ManagedCA/SampleCA.cs @@ -0,0 +1,125 @@ +namespace WixToolset.Samples +{ + using System; + using System.Collections.Generic; + using System.IO; + using WixToolset.Dtf.WindowsInstaller; + + public class SampleCA + { + [CustomAction] + public static ActionResult SampleCA1(Session session) + { + using (Record msgRec = new Record(0)) + { + msgRec[0] = "Hello from SampleCA1!" + + "\r\nCLR version is v" + Environment.Version; + session.Message(InstallMessage.Info, msgRec); + session.Message(InstallMessage.User, msgRec); + } + + session.Log("Testing summary info..."); + SummaryInfo summInfo = session.Database.SummaryInfo; + session.Log("MSI PackageCode = {0}", summInfo.RevisionNumber); + session.Log("MSI ModifyDate = {0}", summInfo.LastSaveTime); + + string testProp = session["SampleCATest"]; + session.Log("Simple property test: [SampleCATest]={0}.", testProp); + + session.Log("Testing subdirectory extraction..."); + string testFilePath = "testsub\\SampleCAs.cs"; + if (!File.Exists(testFilePath)) + { + session.Log("Subdirectory extraction failed. File not found: " + testFilePath); + return ActionResult.Failure; + } + else + { + session.Log("Found file extracted in subdirectory."); + } + + session.Log("Testing record stream extraction..."); + string tempFile = null; + try + { + tempFile = Path.GetTempFileName(); + using (View binView = session.Database.OpenView( + "SELECT `Binary`.`Data` FROM `Binary`, `CustomAction` " + + "WHERE `CustomAction`.`Target` = 'SampleCA1' AND " + + "`CustomAction`.`Source` = `Binary`.`Name`")) + { + binView.Execute(); + using (Record binRec = binView.Fetch()) + { + binRec.GetStream(1, tempFile); + } + } + + session.Log("CA binary file size: {0}", new FileInfo(tempFile).Length); + string binFileVersion = Installer.GetFileVersion(tempFile); + session.Log("CA binary file version: {0}", binFileVersion); + } + finally + { + if (tempFile != null && File.Exists(tempFile)) + { + File.Delete(tempFile); + } + } + + session.Log("Testing record stream reading..."); + using (View binView2 = session.Database.OpenView("SELECT `Data` FROM `Binary` WHERE `Name` = 'TestData'")) + { + binView2.Execute(); + using (Record binRec2 = binView2.Fetch()) + { + Stream stream = binRec2.GetStream("Data"); + string testData = new StreamReader(stream, System.Text.Encoding.UTF8).ReadToEnd(); + session.Log("Test data: " + testData); + } + } + + session.Log("Listing components"); + using (View compView = session.Database.OpenView( + "SELECT `Component` FROM `Component`")) + { + compView.Execute(); + foreach (Record compRec in compView) + { + using (compRec) + { + session.Log("\t{0}", compRec["Component"]); + } + } + } + + session.Log("Testing the ability to access an external MSI database..."); + string tempDbFile = Path.GetTempFileName(); + using (Database tempDb = new Database(tempDbFile, DatabaseOpenMode.CreateDirect)) + { + // Just create an empty database. + } + using (Database tempDb2 = new Database(tempDbFile)) + { + // See if we can open and query the database. + IList tables = tempDb2.ExecuteStringQuery("SELECT `Name` FROM `_Tables`"); + session.Log("Found " + tables.Count + " tables in the newly created database."); + } + File.Delete(tempDbFile); + + return ActionResult.Success; + } + + [CustomAction("SampleCA2")] + public static ActionResult SampleCustomAction2(Session session) + { + using (Record msgRec = new Record(0)) + { + msgRec[0] = "Hello from SampleCA2!"; + session.Message(InstallMessage.Info, msgRec); + session.Message(InstallMessage.User, msgRec); + } + return ActionResult.UserExit; + } + } +} diff --git a/src/test/burn/TestData/WixIuiBaTests/ManagedCA/testsub/testfile.txt b/src/test/burn/TestData/WixIuiBaTests/ManagedCA/testsub/testfile.txt new file mode 100644 index 00000000..8056aefd --- /dev/null +++ b/src/test/burn/TestData/WixIuiBaTests/ManagedCA/testsub/testfile.txt @@ -0,0 +1 @@ +test file for testing subdirectory support and binary stream reading diff --git a/src/test/burn/WixTestTools/PackageVerifier.cs b/src/test/burn/WixTestTools/PackageVerifier.cs index fd8378e0..4545b9ec 100644 --- a/src/test/burn/WixTestTools/PackageVerifier.cs +++ b/src/test/burn/WixTestTools/PackageVerifier.cs @@ -63,17 +63,27 @@ namespace WixTestTools return row.Value; } - public void VerifyInstalled(bool installed) + public bool IsInstalled() { var productCode = this.GetProperty("ProductCode"); - Assert.Equal(installed, MsiUtilities.IsProductInstalled(productCode)); + return MsiUtilities.IsProductInstalled(productCode); } - public void VerifyInstalledWithVersion(bool installed) + public bool IsInstalledWithVersion() { var productCode = this.GetProperty("ProductCode"); Version prodVersion = new Version(this.GetProperty("ProductVersion")); - Assert.Equal(installed, MsiUtilities.IsProductInstalledWithVersion(productCode, prodVersion)); + return MsiUtilities.IsProductInstalledWithVersion(productCode, prodVersion); + } + + public void VerifyInstalled(bool installed) + { + Assert.Equal(installed, this.IsInstalled()); + } + + public void VerifyInstalledWithVersion(bool installed) + { + Assert.Equal(installed, this.IsInstalledWithVersion()); } public void DeleteTestRegistryValue(string name) diff --git a/src/test/burn/WixToolsetTest.BurnE2E/WixIuiBaTests.cs b/src/test/burn/WixToolsetTest.BurnE2E/WixIuiBaTests.cs new file mode 100644 index 00000000..18dd41db --- /dev/null +++ b/src/test/burn/WixToolsetTest.BurnE2E/WixIuiBaTests.cs @@ -0,0 +1,144 @@ +// 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.BurnE2E +{ + using WixTestTools; + using Xunit; + using Xunit.Abstractions; + + public class WixIuiBaTests : BurnE2ETests + { + public WixIuiBaTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { } + + [RuntimeFact] + public void CanInstallArchitectureSpecificPrimaryPackage() + { + var defaultPackage = this.CreatePackageInstaller("InternalUIPackage"); + var x86Package = this.CreatePackageInstaller("InternalUIx86Package"); + var x64Package = this.CreatePackageInstaller("InternalUIx64Package"); + var arm64Package = this.CreatePackageInstaller("InternalUIarm64Package"); + var bundle = this.CreateBundleInstaller("ArchSpecificBundle"); + + defaultPackage.VerifyInstalled(false); + x86Package.VerifyInstalled(false); + x64Package.VerifyInstalled(false); + arm64Package.VerifyInstalled(false); + bundle.VerifyUnregisteredAndRemovedFromPackageCache(); + + bundle.Install(); + bundle.VerifyRegisteredAndInPackageCache(); + defaultPackage.VerifyInstalled(false); + + var archSpecificInstalls = 0; + if (x86Package.IsInstalled()) + { + ++archSpecificInstalls; + } + + if (x64Package.IsInstalled()) + { + ++archSpecificInstalls; + } + + if (arm64Package.IsInstalled()) + { + ++archSpecificInstalls; + } + + Assert.Equal(1, archSpecificInstalls); + + bundle.Uninstall(); + bundle.VerifyUnregisteredAndRemovedFromPackageCache(); + defaultPackage.VerifyInstalled(false); + x86Package.VerifyInstalled(false); + x64Package.VerifyInstalled(false); + arm64Package.VerifyInstalled(false); + } + + [RuntimeFact] + public void CanSilentlyInstallAndUninstallEmbeddedUIBundle() + { + var prereqPackage = this.CreatePackageInstaller("InternalUIPackage"); + var package = this.CreatePackageInstaller("EmbeddedUIPackage"); + var bundle = this.CreateBundleInstaller("EmbeddedUIBundle"); + + prereqPackage.VerifyInstalled(false); + package.VerifyInstalled(false); + bundle.VerifyUnregisteredAndRemovedFromPackageCache(); + + bundle.Install(); + bundle.VerifyRegisteredAndInPackageCache(); + prereqPackage.VerifyInstalled(true); + package.VerifyInstalled(true); + + bundle.Uninstall(); + bundle.VerifyUnregisteredAndRemovedFromPackageCache(); + prereqPackage.VerifyInstalled(true); + package.VerifyInstalled(false); + } + + [RuntimeFact] + public void CanSilentlyInstallAndUninstallInternalUIBundle() + { + var package = this.CreatePackageInstaller("InternalUIPackage"); + var bundle = this.CreateBundleInstaller("InternalUIBundle"); + + package.VerifyInstalled(false); + bundle.VerifyUnregisteredAndRemovedFromPackageCache(); + + bundle.Install(); + bundle.VerifyRegisteredAndInPackageCache(); + package.VerifyInstalled(true); + + bundle.Uninstall(); + bundle.VerifyUnregisteredAndRemovedFromPackageCache(); + package.VerifyInstalled(false); + } + + // Manual test for EmbeddedUIBundle: + // 1. Double click EmbeddedUIBundle.exe. + // 2. Verify that the prereq BA came up and click the install button (allow elevation). + // 3. Verify that the prereq BA automatically closed after installing the prereq. + // 4. Verify that the MSI UI came up and click the install button. + // 5. After it's finished, click the exit button. + // 6. Verify that no other UI is shown and that everything was installed. + // 7. Double click EmbeddedUIBundle.exe (allow elevation). + // 8. Verify that the prereq BA did not come up. + // 9. Verify that the MSI UI came up and click the uninstall button. + // 10. After it's finished, click the exit button. + // 11. Verify that no other UI is shown and that everything was uninstalled except for the prereq which was permanent. + // 12. Uninstall InternalUIPackage to make sure the machine is clean for other tests. + + // Alternate EmbeddedUIBundle test - manually install InternalUIPackage first and verify that the prereq BA doesn't come up during install either. + + // Manual test for InternalUIBundle: + // 1. Double click InternalUIBundle.exe on a machine that will prompt for elevation. + // 2. Verify that the splash screen appeared but the prereq BA did not come up. + // 3. Verify that the elevation prompt came up immediately instead of flashing on the taskbar. (This is currently broken) + // 4. Allow elevation. + // 5. Verify that the MSI UI came up and the splash screen disappeared. + // 6. Accept the two CA messages and click the install button. + // 7. After it's finished, click the exit button. + // 8. Verify that no other UI is shown and that everything was installed. + // 9. Double click InternalUIBundle.exe (allow elevation). + // 10. Verify that the prereq BA did not come up. + // 11. Verify that the MSI UI came up and click the uninstall button. + // 12. After it's finished, click the exit button. + // 13. Verify that no other UI is shown and that everything was uninstalled to make sure the machine is clean for other tests. + + // Manual test for Help: + // 1. Run EmbeddedUIBundle.exe /help from the command line. + // 2. Verify that the prereq BA shows the help information without trying to install the prereqs. + + // Manual test for Layout: + // 1. Run EmbeddedUIBundle.exe /layout from an unelevated command line on a machine that will prompt for elevation. + // 2. Verify that the prereq BA performs the layout without requiring any input from the user. + // 3. Verify that it never prompted for elevation. + // 4. Click the exit button. + + // Manual test for Caching error: + // 1. Copy InternalUIBundle.exe to a separate folder so that it can't find InternalUIPackage.msi. + // 2. Attempt to install InternalUIBundle.exe (allow elevation). + // 3. Verify that the prereq BA comes up with the Failure page saying that a file couldn't be found. + } +} diff --git a/src/test/dtf/Directory.Build.props b/src/test/dtf/Directory.Build.props deleted file mode 100644 index 0035a9e6..00000000 --- a/src/test/dtf/Directory.Build.props +++ /dev/null @@ -1,11 +0,0 @@ - - - - - IntegrationDtf - false - - - - - diff --git a/src/test/dtf/Directory.Build.targets b/src/test/dtf/Directory.Build.targets deleted file mode 100644 index 4e97b6ca..00000000 --- a/src/test/dtf/Directory.Build.targets +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/src/test/dtf/DtfE2ETests.sln b/src/test/dtf/DtfE2ETests.sln deleted file mode 100644 index 39d8cf08..00000000 --- a/src/test/dtf/DtfE2ETests.sln +++ /dev/null @@ -1,28 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30114.105 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EmbeddedUI", "EmbeddedUI\EmbeddedUI.csproj", "{864B8C50-7895-4485-AC89-900D86FD8C0D}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleCA", "SampleCA\SampleCA.csproj", "{8F53B9CC-6FBE-493D-9C9A-09B2AD578CE7}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {864B8C50-7895-4485-AC89-900D86FD8C0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {864B8C50-7895-4485-AC89-900D86FD8C0D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {864B8C50-7895-4485-AC89-900D86FD8C0D}.Release|Any CPU.ActiveCfg = Debug|Any CPU - {864B8C50-7895-4485-AC89-900D86FD8C0D}.Release|Any CPU.Build.0 = Debug|Any CPU - {8F53B9CC-6FBE-493D-9C9A-09B2AD578CE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8F53B9CC-6FBE-493D-9C9A-09B2AD578CE7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8F53B9CC-6FBE-493D-9C9A-09B2AD578CE7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8F53B9CC-6FBE-493D-9C9A-09B2AD578CE7}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection -EndGlobal diff --git a/src/test/dtf/EmbeddedUI/EmbeddedUI.config b/src/test/dtf/EmbeddedUI/EmbeddedUI.config deleted file mode 100644 index 700aff6f..00000000 --- a/src/test/dtf/EmbeddedUI/EmbeddedUI.config +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/src/test/dtf/EmbeddedUI/EmbeddedUI.csproj b/src/test/dtf/EmbeddedUI/EmbeddedUI.csproj deleted file mode 100644 index a6339220..00000000 --- a/src/test/dtf/EmbeddedUI/EmbeddedUI.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - net35 - Sample managed embedded external UI - true - - - - - - - - - - - - - - - - - - - diff --git a/src/test/dtf/EmbeddedUI/InstallProgressCounter.cs b/src/test/dtf/EmbeddedUI/InstallProgressCounter.cs deleted file mode 100644 index 3d75081c..00000000 --- a/src/test/dtf/EmbeddedUI/InstallProgressCounter.cs +++ /dev/null @@ -1,174 +0,0 @@ -namespace WixToolset.Samples.EmbeddedUI -{ - using System; - using WixToolset.Dtf.WindowsInstaller; - - /// - /// Tracks MSI progress messages and converts them to usable progress. - /// - public class InstallProgressCounter - { - private int total; - private int completed; - private int step; - private bool moveForward; - private bool enableActionData; - private int progressPhase; - private double scriptPhaseWeight; - - public InstallProgressCounter() : this(0.3) - { - } - - public InstallProgressCounter(double scriptPhaseWeight) - { - if (!(0 <= scriptPhaseWeight && scriptPhaseWeight <= 1)) - { - throw new ArgumentOutOfRangeException("scriptPhaseWeight"); - } - - this.scriptPhaseWeight = scriptPhaseWeight; - } - - /// - /// Gets a number between 0 and 1 that indicates the overall installation progress. - /// - public double Progress { get; private set; } - - public void ProcessMessage(InstallMessage messageType, Record messageRecord) - { - // This MSI progress-handling code was mostly borrowed from burn and translated from C++ to C#. - - switch (messageType) - { - case InstallMessage.ActionStart: - if (this.enableActionData) - { - this.enableActionData = false; - } - break; - - case InstallMessage.ActionData: - if (this.enableActionData) - { - if (this.moveForward) - { - this.completed += this.step; - } - else - { - this.completed -= this.step; - } - - this.UpdateProgress(); - } - break; - - case InstallMessage.Progress: - this.ProcessProgressMessage(messageRecord); - break; - } - } - - private void ProcessProgressMessage(Record progressRecord) - { - // This MSI progress-handling code was mostly borrowed from burn and translated from C++ to C#. - - if (progressRecord == null || progressRecord.FieldCount == 0) - { - return; - } - - int fieldCount = progressRecord.FieldCount; - int progressType = progressRecord.GetInteger(1); - string progressTypeString = String.Empty; - switch (progressType) - { - case 0: // Master progress reset - if (fieldCount < 4) - { - return; - } - - this.progressPhase++; - - this.total = progressRecord.GetInteger(2); - if (this.progressPhase == 1) - { - // HACK!!! this is a hack courtesy of the Windows Installer team. It seems the script planning phase - // is always off by "about 50". So we'll toss an extra 50 ticks on so that the standard progress - // doesn't go over 100%. If there are any custom actions, they may blow the total so we'll call this - // "close" and deal with the rest. - this.total += 50; - } - - this.moveForward = (progressRecord.GetInteger(3) == 0); - this.completed = (this.moveForward ? 0 : this.total); // if forward start at 0, if backwards start at max - this.enableActionData = false; - - this.UpdateProgress(); - break; - - case 1: // Action info - if (fieldCount < 3) - { - return; - } - - if (progressRecord.GetInteger(3) == 0) - { - this.enableActionData = false; - } - else - { - this.enableActionData = true; - this.step = progressRecord.GetInteger(2); - } - break; - - case 2: // Progress report - if (fieldCount < 2 || this.total == 0 || this.progressPhase == 0) - { - return; - } - - if (this.moveForward) - { - this.completed += progressRecord.GetInteger(2); - } - else - { - this.completed -= progressRecord.GetInteger(2); - } - - this.UpdateProgress(); - break; - - case 3: // Progress total addition - this.total += progressRecord.GetInteger(2); - break; - } - } - - private void UpdateProgress() - { - if (this.progressPhase < 1 || this.total == 0) - { - this.Progress = 0; - } - else if (this.progressPhase == 1) - { - this.Progress = this.scriptPhaseWeight * Math.Min(this.completed, this.total) / this.total; - } - else if (this.progressPhase == 2) - { - this.Progress = this.scriptPhaseWeight + - (1 - this.scriptPhaseWeight) * Math.Min(this.completed, this.total) / this.total; - } - else - { - this.Progress = 1; - } - } - } -} diff --git a/src/test/dtf/EmbeddedUI/SampleEmbeddedUI.cs b/src/test/dtf/EmbeddedUI/SampleEmbeddedUI.cs deleted file mode 100644 index ae86dc97..00000000 --- a/src/test/dtf/EmbeddedUI/SampleEmbeddedUI.cs +++ /dev/null @@ -1,141 +0,0 @@ -namespace WixToolset.Samples.EmbeddedUI -{ - using System; - using System.Collections.Generic; - using System.Configuration; - using System.Threading; - using System.Windows; - using System.Windows.Threading; - using WixToolset.Dtf.WindowsInstaller; - using Application = System.Windows.Application; - - public class SampleEmbeddedUI : IEmbeddedUI - { - private bool isMaintenance; - private Thread appThread; - private Application app; - private SetupWizard setupWizard; - private ManualResetEvent installStartEvent; - private ManualResetEvent installExitEvent; - - /// - /// Initializes the embedded UI. - /// - /// Handle to the installer which can be used to get and set properties. - /// The handle is only valid for the duration of this method call. - /// Path to the directory that contains all the files from the MsiEmbeddedUI table. - /// On entry, contains the current UI level for the installation. After this - /// method returns, the installer resets the UI level to the returned value of this parameter. - /// True if the embedded UI was successfully initialized; false if the installation - /// should continue without the embedded UI. - /// The installation was canceled by the user. - /// The embedded UI failed to initialize and - /// causes the installation to fail. - public bool Initialize(Session session, string resourcePath, ref InstallUIOptions internalUILevel) - { - if (session != null) - { - if ((internalUILevel & InstallUIOptions.Full) != InstallUIOptions.Full) - { - // Don't show custom UI when the UI level is set to basic. - return false; - - // An embedded UI could display an alternate dialog sequence for reduced or - // basic modes, but it's not implemented here. We'll just fall back to the - // built-in MSI basic UI. - } - - if (String.Equals(session["REMOVE"], "All", StringComparison.OrdinalIgnoreCase)) - { - // Don't show custom UI when uninstall was specified on the command line. - return false; - } - - this.isMaintenance = session.EvaluateCondition("Installed"); - } - - // Start the setup wizard on a separate thread. - this.installStartEvent = new ManualResetEvent(false); - this.installExitEvent = new ManualResetEvent(false); - this.appThread = new Thread(this.Run); - this.appThread.SetApartmentState(ApartmentState.STA); - this.appThread.Start(); - - // Wait for the setup wizard to either kickoff the install or prematurely exit. - int waitResult = WaitHandle.WaitAny(new WaitHandle[] { this.installStartEvent, this.installExitEvent }); - if (waitResult == 1) - { - // The setup wizard set the exit event instead of the start event. Cancel the installation. - throw new InstallCanceledException(); - } - else - { - switch (this.setupWizard.Operation) - { - case SetupOperationType.Repair: - session["REINSTALL"] = "ALL"; - break; - case SetupOperationType.Uninstall: - session["REMOVE"] = "ALL"; - break; - } - - // Start the installation with a silenced internal UI. - // This "embedded external UI" will handle message types except for source resolution. - internalUILevel = InstallUIOptions.NoChange | InstallUIOptions.SourceResolutionOnly; - return true; - } - } - - /// - /// Processes information and progress messages sent to the user interface. - /// - /// Message type. - /// Record that contains message data. - /// Message box buttons. - /// Message box icon. - /// Message box default button. - /// Result of processing the message. - public MessageResult ProcessMessage(InstallMessage messageType, Record messageRecord, - MessageButtons buttons, MessageIcon icon, MessageDefaultButton defaultButton) - { - // Synchronously send the message to the setup wizard window on its thread. - object result = this.setupWizard.Dispatcher.Invoke(DispatcherPriority.Send, - new Func(delegate() - { - return this.setupWizard.ProcessMessage(messageType, messageRecord, buttons, icon, defaultButton); - })); - return (MessageResult) result; - } - - /// - /// Shuts down the embedded UI at the end of the installation. - /// - /// - /// If the installation was canceled during initialization, this method will not be called. - /// If the installation was canceled or failed at any later point, this method will be called at the end. - /// - public void Shutdown() - { - // Wait for the user to exit the setup wizard. - this.setupWizard.Dispatcher.BeginInvoke(DispatcherPriority.Normal, - new Action(delegate() - { - this.setupWizard.EnableExit(); - })); - this.appThread.Join(); - } - - /// - /// Creates the setup wizard and runs the application thread. - /// - private void Run() - { - this.app = new Application(); - this.setupWizard = new SetupWizard(this.installStartEvent, this.isMaintenance); - this.setupWizard.InitializeComponent(); - this.app.Run(this.setupWizard); - this.installExitEvent.Set(); - } - } -} diff --git a/src/test/dtf/EmbeddedUI/SetupWizard.xaml b/src/test/dtf/EmbeddedUI/SetupWizard.xaml deleted file mode 100644 index 97e406c2..00000000 --- a/src/test/dtf/EmbeddedUI/SetupWizard.xaml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/test/dtf/EmbeddedUI/SetupWizard.xaml.cs b/src/test/dtf/EmbeddedUI/SetupWizard.xaml.cs deleted file mode 100644 index a4345481..00000000 --- a/src/test/dtf/EmbeddedUI/SetupWizard.xaml.cs +++ /dev/null @@ -1,154 +0,0 @@ -namespace WixToolset.Samples.EmbeddedUI -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Threading; - using System.Windows; - using System.Windows.Controls; - using System.Windows.Data; - using System.Windows.Documents; - using System.Windows.Input; - using System.Windows.Media; - using System.Windows.Media.Imaging; - using System.Windows.Navigation; - using System.Windows.Shapes; - using WixToolset.Dtf.WindowsInstaller; - - public enum SetupOperationType - { - Install, - Repair, - Uninstall - } - - /// - /// Interaction logic for SetupWizard.xaml - /// - public partial class SetupWizard : Window - { - private bool isMaintenance; - private ManualResetEvent installStartEvent; - private InstallProgressCounter progressCounter; - private bool canceled; - - public SetupOperationType Operation { get; private set; } - - public SetupWizard(ManualResetEvent installStartEvent, bool isMaintenance) - { - this.installStartEvent = installStartEvent; - this.progressCounter = new InstallProgressCounter(0.5); - this.isMaintenance = isMaintenance; - - this.Loaded += this.SetupWizard_Loaded; - } - - private void SetupWizard_Loaded(object sender, RoutedEventArgs e) - { - this.Loaded -= this.SetupWizard_Loaded; - - if (this.isMaintenance) - { - this.installButton.Visibility = Visibility.Hidden; - this.repairButton.Visibility = Visibility.Visible; - this.uninstallButton.Visibility = Visibility.Visible; - } - } - - public MessageResult ProcessMessage(InstallMessage messageType, Record messageRecord, - MessageButtons buttons, MessageIcon icon, MessageDefaultButton defaultButton) - { - try - { - this.progressCounter.ProcessMessage(messageType, messageRecord); - this.progressBar.Value = this.progressBar.Minimum + - this.progressCounter.Progress * (this.progressBar.Maximum - this.progressBar.Minimum); - this.progressLabel.Content = "" + (int) Math.Round(100 * this.progressCounter.Progress) + "%"; - - switch (messageType) - { - case InstallMessage.Error: - case InstallMessage.Warning: - case InstallMessage.Info: - string message = String.Format("{0}: {1}", messageType, messageRecord); - this.LogMessage(message); - break; - } - - if (this.canceled) - { - this.canceled = false; - return MessageResult.Cancel; - } - } - catch (Exception ex) - { - this.LogMessage(ex.ToString()); - this.LogMessage(ex.StackTrace); - } - - return MessageResult.OK; - } - - private void LogMessage(string message) - { - this.messagesTextBox.Text += Environment.NewLine + message; - this.messagesTextBox.ScrollToEnd(); - } - - internal void EnableExit() - { - this.progressBar.Visibility = Visibility.Hidden; - this.progressLabel.Visibility = Visibility.Hidden; - this.cancelButton.Visibility = Visibility.Hidden; - this.exitButton.Visibility = Visibility.Visible; - } - - private void installButton_Click(object sender, RoutedEventArgs e) - { - this.Operation = SetupOperationType.Install; - this.StartInstall(); - } - - private void repairButton_Click(object sender, RoutedEventArgs e) - { - this.Operation = SetupOperationType.Repair; - this.StartInstall(); - } - - private void uninstallButton_Click(object sender, RoutedEventArgs e) - { - this.Operation = SetupOperationType.Uninstall; - this.StartInstall(); - } - - private void StartInstall() - { - this.installButton.Visibility = Visibility.Hidden; - this.repairButton.Visibility = Visibility.Hidden; - this.uninstallButton.Visibility = Visibility.Hidden; - this.progressBar.Visibility = Visibility.Visible; - this.progressLabel.Visibility = Visibility.Visible; - this.installStartEvent.Set(); - } - - private void exitButton_Click(object sender, RoutedEventArgs e) - { - this.Close(); - } - - private void cancelButton_Click(object sender, RoutedEventArgs e) - { - if (this.installButton.Visibility == Visibility.Visible) - { - this.Close(); - } - else - { - this.canceled = true; - this.cancelButton.IsEnabled = false; - } - } - } -} diff --git a/src/test/dtf/SampleCA/CustomAction.config b/src/test/dtf/SampleCA/CustomAction.config deleted file mode 100644 index 700aff6f..00000000 --- a/src/test/dtf/SampleCA/CustomAction.config +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/src/test/dtf/SampleCA/SampleCA.cs b/src/test/dtf/SampleCA/SampleCA.cs deleted file mode 100644 index fc9f30fe..00000000 --- a/src/test/dtf/SampleCA/SampleCA.cs +++ /dev/null @@ -1,125 +0,0 @@ -namespace WixToolset.Samples -{ - using System; - using System.Collections.Generic; - using System.IO; - using WixToolset.Dtf.WindowsInstaller; - - public class SampleCA - { - [CustomAction] - public static ActionResult SampleCA1(Session session) - { - using (Record msgRec = new Record(0)) - { - msgRec[0] = "Hello from SampleCA1!" + - "\r\nCLR version is v" + Environment.Version; - session.Message(InstallMessage.Info, msgRec); - session.Message(InstallMessage.User, msgRec); - } - - session.Log("Testing summary info..."); - SummaryInfo summInfo = session.Database.SummaryInfo; - session.Log("MSI PackageCode = {0}", summInfo.RevisionNumber); - session.Log("MSI ModifyDate = {0}", summInfo.LastSaveTime); - - string testProp = session["SampleCATest"]; - session.Log("Simple property test: [SampleCATest]={0}.", testProp); - - session.Log("Testing subdirectory extraction..."); - string testFilePath = "testsub\\SampleCAs.cs"; - if (!File.Exists(testFilePath)) - { - session.Log("Subdirectory extraction failed. File not found: " + testFilePath); - return ActionResult.Failure; - } - else - { - session.Log("Found file extracted in subdirectory."); - } - - session.Log("Testing record stream extraction..."); - string tempFile = null; - try - { - tempFile = Path.GetTempFileName(); - using (View binView = session.Database.OpenView( - "SELECT `Binary`.`Data` FROM `Binary`, `CustomAction` " + - "WHERE `CustomAction`.`Target` = 'SampleCA1' AND " + - "`CustomAction`.`Source` = `Binary`.`Name`")) - { - binView.Execute(); - using (Record binRec = binView.Fetch()) - { - binRec.GetStream(1, tempFile); - } - } - - session.Log("CA binary file size: {0}", new FileInfo(tempFile).Length); - string binFileVersion = Installer.GetFileVersion(tempFile); - session.Log("CA binary file version: {0}", binFileVersion); - } - finally - { - if (tempFile != null && File.Exists(tempFile)) - { - File.Delete(tempFile); - } - } - - session.Log("Testing record stream reading..."); - using (View binView2 = session.Database.OpenView("SELECT `Data` FROM `Binary` WHERE `Name` = 'TestData'")) - { - binView2.Execute(); - using (Record binRec2 = binView2.Fetch()) - { - Stream stream = binRec2.GetStream("Data"); - string testData = new StreamReader(stream, System.Text.Encoding.UTF8).ReadToEnd(); - session.Log("Test data: " + testData); - } - } - - session.Log("Listing components"); - using (View compView = session.Database.OpenView( - "SELECT `Component` FROM `Component`")) - { - compView.Execute(); - foreach (Record compRec in compView) - { - using (compRec) - { - session.Log("\t{0}", compRec["Component"]); - } - } - } - - session.Log("Testing the ability to access an external MSI database..."); - string tempDbFile = Path.GetTempFileName(); - using (Database tempDb = new Database(tempDbFile, DatabaseOpenMode.CreateDirect)) - { - // Just create an empty database. - } - using (Database tempDb2 = new Database(tempDbFile)) - { - // See if we can open and query the database. - IList tables = tempDb2.ExecuteStringQuery("SELECT `Name` FROM `_Tables`"); - session.Log("Found " + tables.Count + " tables in the newly created database."); - } - File.Delete(tempDbFile); - - return ActionResult.Success; - } - - [CustomAction("SampleCA2")] - public static ActionResult SampleCustomAction2(Session session) - { - using (Record msgRec = new Record(0)) - { - msgRec[0] = "Hello from SampleCA2!"; - session.Message(InstallMessage.Info, msgRec); - session.Message(InstallMessage.User, msgRec); - } - return ActionResult.UserExit; - } - } -} diff --git a/src/test/dtf/SampleCA/SampleCA.csproj b/src/test/dtf/SampleCA/SampleCA.csproj deleted file mode 100644 index 866b7575..00000000 --- a/src/test/dtf/SampleCA/SampleCA.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - net20 - Sample managed custom actions - - - - - - - - - - - - diff --git a/src/test/dtf/SampleCA/testsub/testfile.txt b/src/test/dtf/SampleCA/testsub/testfile.txt deleted file mode 100644 index 8056aefd..00000000 --- a/src/test/dtf/SampleCA/testsub/testfile.txt +++ /dev/null @@ -1 +0,0 @@ -test file for testing subdirectory support and binary stream reading diff --git a/src/test/test.cmd b/src/test/test.cmd index 1c2c154b..85deb61e 100644 --- a/src/test/test.cmd +++ b/src/test/test.cmd @@ -14,8 +14,6 @@ @call msi\test_msi.cmd %_C% %_T% || exit /b @call burn\test_burn.cmd %_C% %_T% || exit /b -msbuild -Restore dtf\DtfE2ETests.sln -p:Configuration=%_C% -nologo -m -warnaserror -bl:%_L%\dtfe2etests.binlog || exit /b - dotnet test wix -c %_C% --nologo -l "trx;LogFileName=%_L%\TestResults\WixToolsetTest.WixE2ETests.trx" || exit /b @popd -- cgit v1.2.3-55-g6feb