aboutsummaryrefslogtreecommitdiff
path: root/src/test
diff options
context:
space:
mode:
Diffstat (limited to 'src/test')
-rw-r--r--src/test/dtf/Directory.Build.props11
-rw-r--r--src/test/dtf/Directory.Build.targets6
-rw-r--r--src/test/dtf/DtfE2ETests.sln28
-rw-r--r--src/test/dtf/EmbeddedUI/AssemblyInfo.cs3
-rw-r--r--src/test/dtf/EmbeddedUI/EmbeddedUI.csproj49
-rw-r--r--src/test/dtf/EmbeddedUI/InstallProgressCounter.cs174
-rw-r--r--src/test/dtf/EmbeddedUI/SampleEmbeddedUI.cs130
-rw-r--r--src/test/dtf/EmbeddedUI/SetupWizard.xaml17
-rw-r--r--src/test/dtf/EmbeddedUI/SetupWizard.xaml.cs109
-rw-r--r--src/test/dtf/SampleCA/SampleCA.cs125
-rw-r--r--src/test/dtf/SampleCA/SampleCA.csproj10
-rw-r--r--src/test/test.cmd2
12 files changed, 664 insertions, 0 deletions
diff --git a/src/test/dtf/Directory.Build.props b/src/test/dtf/Directory.Build.props
new file mode 100644
index 00000000..0035a9e6
--- /dev/null
+++ b/src/test/dtf/Directory.Build.props
@@ -0,0 +1,11 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- 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. -->
3<Project>
4 <PropertyGroup>
5 <SegmentName>IntegrationDtf</SegmentName>
6 <SignOutput>false</SignOutput>
7 </PropertyGroup>
8
9 <Import Project="..\..\Directory.Build.props" />
10 <Import Project="Directory$(MSBuildProjectExtension).props" Condition=" Exists('Directory$(MSBuildProjectExtension).props') " />
11</Project>
diff --git a/src/test/dtf/Directory.Build.targets b/src/test/dtf/Directory.Build.targets
new file mode 100644
index 00000000..4e97b6ca
--- /dev/null
+++ b/src/test/dtf/Directory.Build.targets
@@ -0,0 +1,6 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- 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. -->
3<Project>
4 <Import Project="..\..\Directory.Build.targets" />
5 <Import Project="Directory$(MSBuildProjectExtension).targets" Condition=" Exists('Directory$(MSBuildProjectExtension).targets') " />
6</Project>
diff --git a/src/test/dtf/DtfE2ETests.sln b/src/test/dtf/DtfE2ETests.sln
new file mode 100644
index 00000000..39d8cf08
--- /dev/null
+++ b/src/test/dtf/DtfE2ETests.sln
@@ -0,0 +1,28 @@
1
2Microsoft Visual Studio Solution File, Format Version 12.00
3# Visual Studio Version 16
4VisualStudioVersion = 16.0.30114.105
5MinimumVisualStudioVersion = 10.0.40219.1
6Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EmbeddedUI", "EmbeddedUI\EmbeddedUI.csproj", "{864B8C50-7895-4485-AC89-900D86FD8C0D}"
7EndProject
8Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleCA", "SampleCA\SampleCA.csproj", "{8F53B9CC-6FBE-493D-9C9A-09B2AD578CE7}"
9EndProject
10Global
11 GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 Debug|Any CPU = Debug|Any CPU
13 Release|Any CPU = Release|Any CPU
14 EndGlobalSection
15 GlobalSection(SolutionProperties) = preSolution
16 HideSolutionNode = FALSE
17 EndGlobalSection
18 GlobalSection(ProjectConfigurationPlatforms) = postSolution
19 {864B8C50-7895-4485-AC89-900D86FD8C0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
20 {864B8C50-7895-4485-AC89-900D86FD8C0D}.Debug|Any CPU.Build.0 = Debug|Any CPU
21 {864B8C50-7895-4485-AC89-900D86FD8C0D}.Release|Any CPU.ActiveCfg = Debug|Any CPU
22 {864B8C50-7895-4485-AC89-900D86FD8C0D}.Release|Any CPU.Build.0 = Debug|Any CPU
23 {8F53B9CC-6FBE-493D-9C9A-09B2AD578CE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
24 {8F53B9CC-6FBE-493D-9C9A-09B2AD578CE7}.Debug|Any CPU.Build.0 = Debug|Any CPU
25 {8F53B9CC-6FBE-493D-9C9A-09B2AD578CE7}.Release|Any CPU.ActiveCfg = Release|Any CPU
26 {8F53B9CC-6FBE-493D-9C9A-09B2AD578CE7}.Release|Any CPU.Build.0 = Release|Any CPU
27 EndGlobalSection
28EndGlobal
diff --git a/src/test/dtf/EmbeddedUI/AssemblyInfo.cs b/src/test/dtf/EmbeddedUI/AssemblyInfo.cs
new file mode 100644
index 00000000..27aeb535
--- /dev/null
+++ b/src/test/dtf/EmbeddedUI/AssemblyInfo.cs
@@ -0,0 +1,3 @@
1using System.Reflection;
2
3[assembly: AssemblyDescription("Sample managed embedded external UI")]
diff --git a/src/test/dtf/EmbeddedUI/EmbeddedUI.csproj b/src/test/dtf/EmbeddedUI/EmbeddedUI.csproj
new file mode 100644
index 00000000..9f745a19
--- /dev/null
+++ b/src/test/dtf/EmbeddedUI/EmbeddedUI.csproj
@@ -0,0 +1,49 @@
1<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
2 <PropertyGroup>
3 <ProjectGuid>{864B8C50-7895-4485-AC89-900D86FD8C0D}</ProjectGuid>
4 <OutputType>Library</OutputType>
5 <RootNamespace>WixToolset.Samples.EmbeddedUI</RootNamespace>
6 <AssemblyName>WixToolset.Samples.EmbeddedUI</AssemblyName>
7 <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
8 <FileAlignment>512</FileAlignment>
9 </PropertyGroup>
10
11 <ItemGroup>
12 <Compile Include="AssemblyInfo.cs" />
13 <Compile Include="InstallProgressCounter.cs" />
14 <Compile Include="SampleEmbeddedUI.cs" />
15 <Compile Include="SetupWizard.xaml.cs">
16 <DependentUpon>SetupWizard.xaml</DependentUpon>
17 </Compile>
18 </ItemGroup>
19
20 <ItemGroup>
21 <Page Include="SetupWizard.xaml">
22 <Generator>MSBuild:Compile</Generator>
23 <SubType>Designer</SubType>
24 </Page>
25 </ItemGroup>
26
27 <ItemGroup>
28 <Reference Include="PresentationCore">
29 <RequiredTargetFramework>3.0</RequiredTargetFramework>
30 </Reference>
31 <Reference Include="PresentationFramework">
32 <RequiredTargetFramework>3.0</RequiredTargetFramework>
33 </Reference>
34 <Reference Include="System" />
35 <Reference Include="System.Core">
36 <RequiredTargetFramework>3.5</RequiredTargetFramework>
37 </Reference>
38 <Reference Include="System.Xml" />
39 <Reference Include="WindowsBase">
40 <RequiredTargetFramework>3.0</RequiredTargetFramework>
41 </Reference>
42 </ItemGroup>
43
44 <ItemGroup>
45 <PackageReference Include="WixToolset.Dtf.CustomAction" />
46 </ItemGroup>
47
48 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
49</Project>
diff --git a/src/test/dtf/EmbeddedUI/InstallProgressCounter.cs b/src/test/dtf/EmbeddedUI/InstallProgressCounter.cs
new file mode 100644
index 00000000..3d75081c
--- /dev/null
+++ b/src/test/dtf/EmbeddedUI/InstallProgressCounter.cs
@@ -0,0 +1,174 @@
1namespace WixToolset.Samples.EmbeddedUI
2{
3 using System;
4 using WixToolset.Dtf.WindowsInstaller;
5
6 /// <summary>
7 /// Tracks MSI progress messages and converts them to usable progress.
8 /// </summary>
9 public class InstallProgressCounter
10 {
11 private int total;
12 private int completed;
13 private int step;
14 private bool moveForward;
15 private bool enableActionData;
16 private int progressPhase;
17 private double scriptPhaseWeight;
18
19 public InstallProgressCounter() : this(0.3)
20 {
21 }
22
23 public InstallProgressCounter(double scriptPhaseWeight)
24 {
25 if (!(0 <= scriptPhaseWeight && scriptPhaseWeight <= 1))
26 {
27 throw new ArgumentOutOfRangeException("scriptPhaseWeight");
28 }
29
30 this.scriptPhaseWeight = scriptPhaseWeight;
31 }
32
33 /// <summary>
34 /// Gets a number between 0 and 1 that indicates the overall installation progress.
35 /// </summary>
36 public double Progress { get; private set; }
37
38 public void ProcessMessage(InstallMessage messageType, Record messageRecord)
39 {
40 // This MSI progress-handling code was mostly borrowed from burn and translated from C++ to C#.
41
42 switch (messageType)
43 {
44 case InstallMessage.ActionStart:
45 if (this.enableActionData)
46 {
47 this.enableActionData = false;
48 }
49 break;
50
51 case InstallMessage.ActionData:
52 if (this.enableActionData)
53 {
54 if (this.moveForward)
55 {
56 this.completed += this.step;
57 }
58 else
59 {
60 this.completed -= this.step;
61 }
62
63 this.UpdateProgress();
64 }
65 break;
66
67 case InstallMessage.Progress:
68 this.ProcessProgressMessage(messageRecord);
69 break;
70 }
71 }
72
73 private void ProcessProgressMessage(Record progressRecord)
74 {
75 // This MSI progress-handling code was mostly borrowed from burn and translated from C++ to C#.
76
77 if (progressRecord == null || progressRecord.FieldCount == 0)
78 {
79 return;
80 }
81
82 int fieldCount = progressRecord.FieldCount;
83 int progressType = progressRecord.GetInteger(1);
84 string progressTypeString = String.Empty;
85 switch (progressType)
86 {
87 case 0: // Master progress reset
88 if (fieldCount < 4)
89 {
90 return;
91 }
92
93 this.progressPhase++;
94
95 this.total = progressRecord.GetInteger(2);
96 if (this.progressPhase == 1)
97 {
98 // HACK!!! this is a hack courtesy of the Windows Installer team. It seems the script planning phase
99 // is always off by "about 50". So we'll toss an extra 50 ticks on so that the standard progress
100 // doesn't go over 100%. If there are any custom actions, they may blow the total so we'll call this
101 // "close" and deal with the rest.
102 this.total += 50;
103 }
104
105 this.moveForward = (progressRecord.GetInteger(3) == 0);
106 this.completed = (this.moveForward ? 0 : this.total); // if forward start at 0, if backwards start at max
107 this.enableActionData = false;
108
109 this.UpdateProgress();
110 break;
111
112 case 1: // Action info
113 if (fieldCount < 3)
114 {
115 return;
116 }
117
118 if (progressRecord.GetInteger(3) == 0)
119 {
120 this.enableActionData = false;
121 }
122 else
123 {
124 this.enableActionData = true;
125 this.step = progressRecord.GetInteger(2);
126 }
127 break;
128
129 case 2: // Progress report
130 if (fieldCount < 2 || this.total == 0 || this.progressPhase == 0)
131 {
132 return;
133 }
134
135 if (this.moveForward)
136 {
137 this.completed += progressRecord.GetInteger(2);
138 }
139 else
140 {
141 this.completed -= progressRecord.GetInteger(2);
142 }
143
144 this.UpdateProgress();
145 break;
146
147 case 3: // Progress total addition
148 this.total += progressRecord.GetInteger(2);
149 break;
150 }
151 }
152
153 private void UpdateProgress()
154 {
155 if (this.progressPhase < 1 || this.total == 0)
156 {
157 this.Progress = 0;
158 }
159 else if (this.progressPhase == 1)
160 {
161 this.Progress = this.scriptPhaseWeight * Math.Min(this.completed, this.total) / this.total;
162 }
163 else if (this.progressPhase == 2)
164 {
165 this.Progress = this.scriptPhaseWeight +
166 (1 - this.scriptPhaseWeight) * Math.Min(this.completed, this.total) / this.total;
167 }
168 else
169 {
170 this.Progress = 1;
171 }
172 }
173 }
174}
diff --git a/src/test/dtf/EmbeddedUI/SampleEmbeddedUI.cs b/src/test/dtf/EmbeddedUI/SampleEmbeddedUI.cs
new file mode 100644
index 00000000..b9cd213a
--- /dev/null
+++ b/src/test/dtf/EmbeddedUI/SampleEmbeddedUI.cs
@@ -0,0 +1,130 @@
1namespace WixToolset.Samples.EmbeddedUI
2{
3 using System;
4 using System.Collections.Generic;
5 using System.Configuration;
6 using System.Threading;
7 using System.Windows;
8 using System.Windows.Threading;
9 using WixToolset.Dtf.WindowsInstaller;
10 using Application = System.Windows.Application;
11
12 public class SampleEmbeddedUI : IEmbeddedUI
13 {
14 private Thread appThread;
15 private Application app;
16 private SetupWizard setupWizard;
17 private ManualResetEvent installStartEvent;
18 private ManualResetEvent installExitEvent;
19
20 /// <summary>
21 /// Initializes the embedded UI.
22 /// </summary>
23 /// <param name="session">Handle to the installer which can be used to get and set properties.
24 /// The handle is only valid for the duration of this method call.</param>
25 /// <param name="resourcePath">Path to the directory that contains all the files from the MsiEmbeddedUI table.</param>
26 /// <param name="internalUILevel">On entry, contains the current UI level for the installation. After this
27 /// method returns, the installer resets the UI level to the returned value of this parameter.</param>
28 /// <returns>True if the embedded UI was successfully initialized; false if the installation
29 /// should continue without the embedded UI.</returns>
30 /// <exception cref="InstallCanceledException">The installation was canceled by the user.</exception>
31 /// <exception cref="InstallerException">The embedded UI failed to initialize and
32 /// causes the installation to fail.</exception>
33 public bool Initialize(Session session, string resourcePath, ref InstallUIOptions internalUILevel)
34 {
35 if (session != null)
36 {
37 if ((internalUILevel & InstallUIOptions.Full) != InstallUIOptions.Full)
38 {
39 // Don't show custom UI when the UI level is set to basic.
40 return false;
41
42 // An embedded UI could display an alternate dialog sequence for reduced or
43 // basic modes, but it's not implemented here. We'll just fall back to the
44 // built-in MSI basic UI.
45 }
46
47 if (String.Equals(session["REMOVE"], "All", StringComparison.OrdinalIgnoreCase))
48 {
49 // Don't show custom UI when uninstalling.
50 return false;
51
52 // An embedded UI could display an uninstall wizard, it's just not imlemented here.
53 }
54 }
55
56 // Start the setup wizard on a separate thread.
57 this.installStartEvent = new ManualResetEvent(false);
58 this.installExitEvent = new ManualResetEvent(false);
59 this.appThread = new Thread(this.Run);
60 this.appThread.SetApartmentState(ApartmentState.STA);
61 this.appThread.Start();
62
63 // Wait for the setup wizard to either kickoff the install or prematurely exit.
64 int waitResult = WaitHandle.WaitAny(new WaitHandle[] { this.installStartEvent, this.installExitEvent });
65 if (waitResult == 1)
66 {
67 // The setup wizard set the exit event instead of the start event. Cancel the installation.
68 throw new InstallCanceledException();
69 }
70 else
71 {
72 // Start the installation with a silenced internal UI.
73 // This "embedded external UI" will handle message types except for source resolution.
74 internalUILevel = InstallUIOptions.NoChange | InstallUIOptions.SourceResolutionOnly;
75 return true;
76 }
77 }
78
79 /// <summary>
80 /// Processes information and progress messages sent to the user interface.
81 /// </summary>
82 /// <param name="messageType">Message type.</param>
83 /// <param name="messageRecord">Record that contains message data.</param>
84 /// <param name="buttons">Message box buttons.</param>
85 /// <param name="icon">Message box icon.</param>
86 /// <param name="defaultButton">Message box default button.</param>
87 /// <returns>Result of processing the message.</returns>
88 public MessageResult ProcessMessage(InstallMessage messageType, Record messageRecord,
89 MessageButtons buttons, MessageIcon icon, MessageDefaultButton defaultButton)
90 {
91 // Synchronously send the message to the setup wizard window on its thread.
92 object result = this.setupWizard.Dispatcher.Invoke(DispatcherPriority.Send,
93 new Func<MessageResult>(delegate()
94 {
95 return this.setupWizard.ProcessMessage(messageType, messageRecord, buttons, icon, defaultButton);
96 }));
97 return (MessageResult) result;
98 }
99
100 /// <summary>
101 /// Shuts down the embedded UI at the end of the installation.
102 /// </summary>
103 /// <remarks>
104 /// If the installation was canceled during initialization, this method will not be called.
105 /// If the installation was canceled or failed at any later point, this method will be called at the end.
106 /// </remarks>
107 public void Shutdown()
108 {
109 // Wait for the user to exit the setup wizard.
110 this.setupWizard.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
111 new Action(delegate()
112 {
113 this.setupWizard.EnableExit();
114 }));
115 this.appThread.Join();
116 }
117
118 /// <summary>
119 /// Creates the setup wizard and runs the application thread.
120 /// </summary>
121 private void Run()
122 {
123 this.app = new Application();
124 this.setupWizard = new SetupWizard(this.installStartEvent);
125 this.setupWizard.InitializeComponent();
126 this.app.Run(this.setupWizard);
127 this.installExitEvent.Set();
128 }
129 }
130}
diff --git a/src/test/dtf/EmbeddedUI/SetupWizard.xaml b/src/test/dtf/EmbeddedUI/SetupWizard.xaml
new file mode 100644
index 00000000..9fd493a7
--- /dev/null
+++ b/src/test/dtf/EmbeddedUI/SetupWizard.xaml
@@ -0,0 +1,17 @@
1
2<!-- 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. -->
3
4
5<Window x:Class="WixToolset.Samples.EmbeddedUI.SetupWizard"
6 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
7 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
8 Title="Sample Embedded UI" Height="400" Width="540" Visibility="Visible">
9 <Grid>
10 <TextBox Margin="8,8,8,63" Name="messagesTextBox" IsReadOnly="True" VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Auto" FontFamily="Lucida Console" FontSize="10" />
11 <Button Height="23" HorizontalAlignment="Right" Name="installButton" VerticalAlignment="Bottom" Width="75" Click="installButton_Click" Margin="0,0,91,8">Install</Button>
12 <Button Height="23" HorizontalAlignment="Right" Name="exitButton" VerticalAlignment="Bottom" Width="75" Visibility="Hidden" Click="exitButton_Click" Margin="0,0,8,8">Exit</Button>
13 <Button Height="23" Margin="0,0,8,8" Name="cancelButton" VerticalAlignment="Bottom" Width="75" HorizontalAlignment="Right" Click="cancelButton_Click">Cancel</Button>
14 <ProgressBar Height="16" Margin="8,0,8,39" Name="progressBar" VerticalAlignment="Bottom" Visibility="Hidden" IsIndeterminate="False" />
15 <Label Height="28" HorizontalAlignment="Left" Margin="8,0,0,4.48" Name="progressLabel" VerticalAlignment="Bottom" Width="120" Visibility="Hidden">0%</Label>
16 </Grid>
17</Window>
diff --git a/src/test/dtf/EmbeddedUI/SetupWizard.xaml.cs b/src/test/dtf/EmbeddedUI/SetupWizard.xaml.cs
new file mode 100644
index 00000000..b846d61f
--- /dev/null
+++ b/src/test/dtf/EmbeddedUI/SetupWizard.xaml.cs
@@ -0,0 +1,109 @@
1namespace WixToolset.Samples.EmbeddedUI
2{
3 using System;
4 using System.Collections.Generic;
5 using System.Linq;
6 using System.Text;
7 using System.Threading;
8 using System.Windows;
9 using System.Windows.Controls;
10 using System.Windows.Data;
11 using System.Windows.Documents;
12 using System.Windows.Input;
13 using System.Windows.Media;
14 using System.Windows.Media.Imaging;
15 using System.Windows.Navigation;
16 using System.Windows.Shapes;
17 using WixToolset.Dtf.WindowsInstaller;
18
19 /// <summary>
20 /// Interaction logic for SetupWizard.xaml
21 /// </summary>
22 public partial class SetupWizard : Window
23 {
24 private ManualResetEvent installStartEvent;
25 private InstallProgressCounter progressCounter;
26 private bool canceled;
27
28 public SetupWizard(ManualResetEvent installStartEvent)
29 {
30 this.installStartEvent = installStartEvent;
31 this.progressCounter = new InstallProgressCounter(0.5);
32 }
33
34 public MessageResult ProcessMessage(InstallMessage messageType, Record messageRecord,
35 MessageButtons buttons, MessageIcon icon, MessageDefaultButton defaultButton)
36 {
37 try
38 {
39 this.progressCounter.ProcessMessage(messageType, messageRecord);
40 this.progressBar.Value = this.progressBar.Minimum +
41 this.progressCounter.Progress * (this.progressBar.Maximum - this.progressBar.Minimum);
42 this.progressLabel.Content = "" + (int) Math.Round(100 * this.progressCounter.Progress) + "%";
43
44 switch (messageType)
45 {
46 case InstallMessage.Error:
47 case InstallMessage.Warning:
48 case InstallMessage.Info:
49 string message = String.Format("{0}: {1}", messageType, messageRecord);
50 this.LogMessage(message);
51 break;
52 }
53
54 if (this.canceled)
55 {
56 this.canceled = false;
57 return MessageResult.Cancel;
58 }
59 }
60 catch (Exception ex)
61 {
62 this.LogMessage(ex.ToString());
63 this.LogMessage(ex.StackTrace);
64 }
65
66 return MessageResult.OK;
67 }
68
69 private void LogMessage(string message)
70 {
71 this.messagesTextBox.Text += Environment.NewLine + message;
72 this.messagesTextBox.ScrollToEnd();
73 }
74
75 internal void EnableExit()
76 {
77 this.progressBar.Visibility = Visibility.Hidden;
78 this.progressLabel.Visibility = Visibility.Hidden;
79 this.cancelButton.Visibility = Visibility.Hidden;
80 this.exitButton.Visibility = Visibility.Visible;
81 }
82
83 private void installButton_Click(object sender, RoutedEventArgs e)
84 {
85 this.installButton.Visibility = Visibility.Hidden;
86 this.progressBar.Visibility = Visibility.Visible;
87 this.progressLabel.Visibility = Visibility.Visible;
88 this.installStartEvent.Set();
89 }
90
91 private void exitButton_Click(object sender, RoutedEventArgs e)
92 {
93 this.Close();
94 }
95
96 private void cancelButton_Click(object sender, RoutedEventArgs e)
97 {
98 if (this.installButton.Visibility == Visibility.Visible)
99 {
100 this.Close();
101 }
102 else
103 {
104 this.canceled = true;
105 this.cancelButton.IsEnabled = false;
106 }
107 }
108 }
109}
diff --git a/src/test/dtf/SampleCA/SampleCA.cs b/src/test/dtf/SampleCA/SampleCA.cs
new file mode 100644
index 00000000..fc9f30fe
--- /dev/null
+++ b/src/test/dtf/SampleCA/SampleCA.cs
@@ -0,0 +1,125 @@
1namespace WixToolset.Samples
2{
3 using System;
4 using System.Collections.Generic;
5 using System.IO;
6 using WixToolset.Dtf.WindowsInstaller;
7
8 public class SampleCA
9 {
10 [CustomAction]
11 public static ActionResult SampleCA1(Session session)
12 {
13 using (Record msgRec = new Record(0))
14 {
15 msgRec[0] = "Hello from SampleCA1!" +
16 "\r\nCLR version is v" + Environment.Version;
17 session.Message(InstallMessage.Info, msgRec);
18 session.Message(InstallMessage.User, msgRec);
19 }
20
21 session.Log("Testing summary info...");
22 SummaryInfo summInfo = session.Database.SummaryInfo;
23 session.Log("MSI PackageCode = {0}", summInfo.RevisionNumber);
24 session.Log("MSI ModifyDate = {0}", summInfo.LastSaveTime);
25
26 string testProp = session["SampleCATest"];
27 session.Log("Simple property test: [SampleCATest]={0}.", testProp);
28
29 session.Log("Testing subdirectory extraction...");
30 string testFilePath = "testsub\\SampleCAs.cs";
31 if (!File.Exists(testFilePath))
32 {
33 session.Log("Subdirectory extraction failed. File not found: " + testFilePath);
34 return ActionResult.Failure;
35 }
36 else
37 {
38 session.Log("Found file extracted in subdirectory.");
39 }
40
41 session.Log("Testing record stream extraction...");
42 string tempFile = null;
43 try
44 {
45 tempFile = Path.GetTempFileName();
46 using (View binView = session.Database.OpenView(
47 "SELECT `Binary`.`Data` FROM `Binary`, `CustomAction` " +
48 "WHERE `CustomAction`.`Target` = 'SampleCA1' AND " +
49 "`CustomAction`.`Source` = `Binary`.`Name`"))
50 {
51 binView.Execute();
52 using (Record binRec = binView.Fetch())
53 {
54 binRec.GetStream(1, tempFile);
55 }
56 }
57
58 session.Log("CA binary file size: {0}", new FileInfo(tempFile).Length);
59 string binFileVersion = Installer.GetFileVersion(tempFile);
60 session.Log("CA binary file version: {0}", binFileVersion);
61 }
62 finally
63 {
64 if (tempFile != null && File.Exists(tempFile))
65 {
66 File.Delete(tempFile);
67 }
68 }
69
70 session.Log("Testing record stream reading...");
71 using (View binView2 = session.Database.OpenView("SELECT `Data` FROM `Binary` WHERE `Name` = 'TestData'"))
72 {
73 binView2.Execute();
74 using (Record binRec2 = binView2.Fetch())
75 {
76 Stream stream = binRec2.GetStream("Data");
77 string testData = new StreamReader(stream, System.Text.Encoding.UTF8).ReadToEnd();
78 session.Log("Test data: " + testData);
79 }
80 }
81
82 session.Log("Listing components");
83 using (View compView = session.Database.OpenView(
84 "SELECT `Component` FROM `Component`"))
85 {
86 compView.Execute();
87 foreach (Record compRec in compView)
88 {
89 using (compRec)
90 {
91 session.Log("\t{0}", compRec["Component"]);
92 }
93 }
94 }
95
96 session.Log("Testing the ability to access an external MSI database...");
97 string tempDbFile = Path.GetTempFileName();
98 using (Database tempDb = new Database(tempDbFile, DatabaseOpenMode.CreateDirect))
99 {
100 // Just create an empty database.
101 }
102 using (Database tempDb2 = new Database(tempDbFile))
103 {
104 // See if we can open and query the database.
105 IList<string> tables = tempDb2.ExecuteStringQuery("SELECT `Name` FROM `_Tables`");
106 session.Log("Found " + tables.Count + " tables in the newly created database.");
107 }
108 File.Delete(tempDbFile);
109
110 return ActionResult.Success;
111 }
112
113 [CustomAction("SampleCA2")]
114 public static ActionResult SampleCustomAction2(Session session)
115 {
116 using (Record msgRec = new Record(0))
117 {
118 msgRec[0] = "Hello from SampleCA2!";
119 session.Message(InstallMessage.Info, msgRec);
120 session.Message(InstallMessage.User, msgRec);
121 }
122 return ActionResult.UserExit;
123 }
124 }
125}
diff --git a/src/test/dtf/SampleCA/SampleCA.csproj b/src/test/dtf/SampleCA/SampleCA.csproj
new file mode 100644
index 00000000..fb6d8dca
--- /dev/null
+++ b/src/test/dtf/SampleCA/SampleCA.csproj
@@ -0,0 +1,10 @@
1<Project Sdk="Microsoft.NET.Sdk">
2 <PropertyGroup>
3 <TargetFramework>net472</TargetFramework>
4 <Description>Sample managed custom actions</Description>
5 </PropertyGroup>
6
7 <ItemGroup>
8 <PackageReference Include="WixToolset.Dtf.CustomAction" />
9 </ItemGroup>
10</Project>
diff --git a/src/test/test.cmd b/src/test/test.cmd
index 3158b2c2..4c80ba7d 100644
--- a/src/test/test.cmd
+++ b/src/test/test.cmd
@@ -13,6 +13,8 @@
13 13
14@call burn\test_burn.cmd %_C% %_T% || exit /b 14@call burn\test_burn.cmd %_C% %_T% || exit /b
15 15
16msbuild -t:Restore dtf\DtfE2ETests.sln -p:Configuration=%_C% -nologo -m -warnaserror -bl:%_L%\dtfe2etests.binlog || exit /b
17
16dotnet test wix -c %_C% --nologo -l "trx;LogFileName=%_L%\TestResults\WixToolsetTest.WixE2ETests.trx" || exit /b 18dotnet test wix -c %_C% --nologo -l "trx;LogFileName=%_L%\TestResults\WixToolsetTest.WixE2ETests.trx" || exit /b
17 19
18@popd 20@popd