Port UtilExtension.UserTests from wix3.
Sean Hall committed
Apr 28, 2022 at 21:02 UTC
5b04bce6567855325810bc4e6bcd2f6e05b329c7
30 files changed
+1032
src/internal/SetBuildNumber/Directory.Packages.props.pp
+1
@@ -38,6 +38,7 @@
38
<ItemGroup>
39
<PackageVersion Include="System.Diagnostics.PerformanceCounter" Version="4.7.0" />
40
<PackageVersion Include="System.DirectoryServices" Version="4.7.0" />
41
+ <PackageVersion Include="System.DirectoryServices.AccountManagement" Version="4.7.0" />
42
<PackageVersion Include="System.IO.Compression" Version="4.3.0" />
43
<!-- Warning: The version for System.IO.FileSystem.AccessControl must be kept in sync with WixToolset.Core.Native.nuspec -->
44
<PackageVersion Include="System.IO.FileSystem.AccessControl" Version="4.6.0" />
src/test/burn/WixTestTools/UserVerifier.cs
new
+348
@@ -0,0 +1,348 @@
1
+// 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.
2
+
3
+namespace WixTestTools
4
+{
5
+ using System;
6
+ using System.Text;
7
+ using System.DirectoryServices;
8
+ using System.DirectoryServices.AccountManagement;
9
+ using System.Security.Principal;
10
+ using Xunit;
11
+
12
+ /// <summary>
13
+ /// Contains methods for User account verification
14
+ /// </summary>
15
+ public static class UserVerifier
16
+ {
17
+ public static class SIDStrings
18
+ {
19
+ // Built-In Local Groups
20
+ public static readonly string BUILTIN_ADMINISTRATORS = "S-1-5-32-544";
21
+ public static readonly string BUILTIN_USERS = "S-1-5-32-545";
22
+ public static readonly string BUILTIN_GUESTS = "S-1-5-32-546";
23
+ public static readonly string BUILTIN_ACCOUNT_OPERATORS = "S-1-5-32-548";
24
+ public static readonly string BUILTIN_SERVER_OPERATORS = "S-1-5-32-549";
25
+ public static readonly string BUILTIN_PRINT_OPERATORS = "S-1-5-32-550";
26
+ public static readonly string BUILTIN_BACKUP_OPERATORS = "S-1-5-32-551";
27
+ public static readonly string BUILTIN_REPLICATOR = "S-1-5-32-552";
28
+
29
+ // Special Groups
30
+ public static readonly string CREATOR_OWNER = "S-1-3-0";
31
+ public static readonly string EVERYONE = "S-1-1-0";
32
+ public static readonly string NT_AUTHORITY_NETWORK = "S-1-5-2";
33
+ public static readonly string NT_AUTHORITY_INTERACTIVE = "S-1-5-4";
34
+ public static readonly string NT_AUTHORITY_SYSTEM = "S-1-5-18";
35
+ public static readonly string NT_AUTHORITY_Authenticated_Users = "S-1-5-11";
36
+ public static readonly string NT_AUTHORITY_LOCAL_SERVICE = "S-1-5-19";
37
+ public static readonly string NT_AUTHORITY_NETWORK_SERVICE = "S-1-5-20";
38
+ }
39
+
40
+ /// <summary>
41
+ /// Create a local user on the machine
42
+ /// </summary>
43
+ /// <param name="userName"></param>
44
+ /// <param name="password"></param>
45
+ /// <remarks>Has to be run as an Admin</remarks>
46
+ public static void CreateLocalUser(string userName, string password)
47
+ {
48
+ DeleteLocalUser(userName);
49
+ UserPrincipal newUser = new UserPrincipal(new PrincipalContext(ContextType.Machine));
50
+ newUser.SetPassword(password);
51
+ newUser.Name = userName;
52
+ newUser.Description = "New test User";
53
+ newUser.UserCannotChangePassword = true;
54
+ newUser.PasswordNeverExpires = false;
55
+ newUser.Save();
56
+ }
57
+
58
+ /// <summary>
59
+ /// Deletes a local user from the machine
60
+ /// </summary>
61
+ /// <param name="userName">user name to delete</param>
62
+ /// <remarks>Has to be run as an Admin</remarks>
63
+ public static void DeleteLocalUser(string userName)
64
+ {
65
+ UserPrincipal newUser = GetUser(String.Empty, userName);
66
+ if (null != newUser)
67
+ {
68
+ newUser.Delete();
69
+ }
70
+ }
71
+
72
+ /// <summary>
73
+ /// Verifies that a user exisits or not
74
+ /// </summary>
75
+ /// <param name="domainName">domain name for the user, empty for local users</param>
76
+ /// <param name="userName">the user name</param>
77
+ public static bool UserExists(string domainName, string userName)
78
+ {
79
+ UserPrincipal user = GetUser(domainName, userName);
80
+
81
+ return null != user;
82
+ }
83
+
84
+ /// <summary>
85
+ /// Sets the user information for a given user
86
+ /// </summary>
87
+ /// <param name="domainName">domain name for the user, empty for local users</param>
88
+ /// <param name="userName">the user name</param>
89
+ /// <param name="passwordExpired">user is required to change the password on first login</param>
90
+ /// <param name="passwordNeverExpires">password never expires</param>
91
+ /// <param name="disabled">account is disabled</param>
92
+ public static void SetUserInformation(string domainName, string userName, bool passwordExpired, bool passwordNeverExpires, bool disabled)
93
+ {
94
+ UserPrincipal user = GetUser(domainName, userName);
95
+
96
+ Assert.False(null == user, String.Format("User '{0}' was not found under domain '{1}'.", userName, domainName));
97
+ user.PasswordNeverExpires = passwordNeverExpires;
98
+ user.Enabled = !disabled;
99
+ if (passwordExpired)
100
+ {
101
+ user.ExpirePasswordNow();
102
+ }
103
+ else
104
+ {
105
+ // extend the expiration date to a month
106
+ user.AccountExpirationDate = DateTime.Now.Add(new TimeSpan(30, 0, 0, 0, 0));
107
+ }
108
+ user.Save();
109
+ }
110
+
111
+ /// <summary>
112
+ /// Adds the specified user to the specified local group
113
+ /// </summary>
114
+ /// <param name="userName">User to add</param>
115
+ /// <param name="groupName">Group to add too</param>
116
+ public static void AddUserToGroup(string userName, string groupName)
117
+ {
118
+ DirectoryEntry localMachine;
119
+ DirectoryEntry localGroup;
120
+
121
+ localMachine = new DirectoryEntry("WinNT://" + Environment.MachineName.ToString());
122
+ localGroup = localMachine.Children.Find(groupName, "group");
123
+ Assert.False(null == localGroup, String.Format("Group '{0}' was not found.", groupName));
124
+ DirectoryEntry user = FindActiveDirectoryUser(userName);
125
+ localGroup.Invoke("Add", new object[] { user.Path.ToString() });
126
+ }
127
+
128
+ /// <summary>
129
+ /// Find the specified user in AD
130
+ /// </summary>
131
+ /// <param name="UserName">user name to lookup</param>
132
+ /// <returns>DirectoryEntry of the user</returns>
133
+ private static DirectoryEntry FindActiveDirectoryUser(string UserName)
134
+ {
135
+ var mLocalMachine = new DirectoryEntry("WinNT://" + Environment.MachineName.ToString());
136
+ var mLocalEntries = mLocalMachine.Children;
137
+
138
+ var theUser = mLocalEntries.Find(UserName);
139
+ return theUser;
140
+ }
141
+
142
+ /// <summary>
143
+ /// Verifies the user information for a given user
144
+ /// </summary>
145
+ /// <param name="domainName">domain name for the user, empty for local users</param>
146
+ /// <param name="userName">the user name</param>
147
+ /// <param name="passwordExpired">user is required to change the password on first login</param>
148
+ /// <param name="passwordNeverExpires">password never expires</param>
149
+ /// <param name="disabled">account is disabled</param>
150
+ public static void VerifyUserInformation(string domainName, string userName, bool passwordExpired, bool passwordNeverExpires, bool disabled)
151
+ {
152
+ UserPrincipal user = GetUser(domainName, userName);
153
+
154
+ Assert.False(null == user, String.Format("User '{0}' was not found under domain '{1}'.", userName, domainName));
155
+
156
+ Assert.True(passwordNeverExpires == user.PasswordNeverExpires, String.Format("Password Never Expires for user '{0}/{1}' is: '{2}', expected: '{3}'.", domainName, userName, user.PasswordNeverExpires, passwordNeverExpires));
157
+ Assert.True(disabled != user.Enabled, String.Format("Disappled for user '{0}/{1}' is: '{2}', expected: '{3}'.", domainName, userName, !user.Enabled, disabled));
158
+
159
+ DateTime expirationDate = user.AccountExpirationDate.GetValueOrDefault();
160
+ bool accountExpired = expirationDate.ToLocalTime().CompareTo(DateTime.Now) <= 0;
161
+ Assert.True(passwordExpired == accountExpired, String.Format("Password Expired for user '{0}/{1}' is: '{2}', expected: '{3}'.", domainName, userName, accountExpired, passwordExpired));
162
+ }
163
+
164
+ /// <summary>
165
+ /// Verify that a givin user is member of a local group
166
+ /// </summary>
167
+ /// <param name="domainName">domain name for the user, empty for local users</param>
168
+ /// <param name="userName">the user name</param>
169
+ /// <param name="groupNames">list of groups to check for membership</param>
170
+ public static void VerifyUserIsMemberOf(string domainName, string userName, params string[] groupNames)
171
+ {
172
+ IsUserMemberOf(domainName, userName, true, groupNames);
173
+ }
174
+
175
+ /// <summary>
176
+ /// Verify that a givin user is NOT member of a local group
177
+ /// </summary>
178
+ /// <param name="domainName">domain name for the user, empty for local users</param>
179
+ /// <param name="userName">the user name</param>
180
+ /// <param name="groupNames">list of groups to check for membership</param>
181
+ public static void VerifyUserIsNotMemberOf(string domainName, string userName, params string[] groupNames)
182
+ {
183
+ IsUserMemberOf(domainName, userName, false, groupNames);
184
+ }
185
+
186
+ /// <summary>
187
+ ///
188
+ /// </summary>
189
+ /// <param name="SID">SID to search for</param>
190
+ /// <returns>AccountName</returns>
191
+ public static string GetLocalUserNameFromSID(string sidString)
192
+ {
193
+ SecurityIdentifier sid = new SecurityIdentifier(sidString);
194
+ NTAccount account = (NTAccount)sid.Translate(typeof(NTAccount));
195
+ return account.Value;
196
+ }
197
+
198
+ /// <summary>
199
+ /// Get the SID string for a given user name
200
+ /// </summary>
201
+ /// <param name="Domain"></param>
202
+ /// <param name="UserName"></param>
203
+ /// <returns>SID string</returns>
204
+ public static string GetSIDFromUserName(string Domain, string UserName)
205
+ {
206
+ string retVal = null;
207
+ string domain = Domain;
208
+ string name = UserName;
209
+
210
+ if (String.IsNullOrEmpty(domain))
211
+ {
212
+ domain = System.Environment.MachineName;
213
+ }
214
+
215
+ try
216
+ {
217
+ DirectoryEntry de = new DirectoryEntry("WinNT://" + domain + "/" + name);
218
+
219
+ long iBigVal = 5;
220
+ byte[] bigArr = BitConverter.GetBytes(iBigVal);
221
+ System.DirectoryServices.PropertyCollection coll = de.Properties;
222
+ object obVal = coll["objectSid"].Value;
223
+ if (null != obVal)
224
+ {
225
+ retVal = ConvertByteToSidString((byte[])obVal);
226
+ }
227
+ }
228
+ catch (Exception ex)
229
+ {
230
+ retVal = String.Empty;
231
+ Console.Write(ex.Message);
232
+ }
233
+
234
+ return retVal;
235
+ }
236
+
237
+ /// <summary>
238
+ /// converts a byte array containing a SID into a string
239
+ /// </summary>
240
+ /// <param name="sidBytes"></param>
241
+ /// <returns>SID string</returns>
242
+ private static string ConvertByteToSidString(byte[] sidBytes)
243
+ {
244
+ short sSubAuthorityCount;
245
+ StringBuilder strSid = new StringBuilder();
246
+ strSid.Append("S-");
247
+ try
248
+ {
249
+ // Add SID revision.
250
+ strSid.Append(sidBytes[0].ToString());
251
+
252
+ sSubAuthorityCount = Convert.ToInt16(sidBytes[1]);
253
+
254
+ // Next six bytes are SID authority value.
255
+ if (sidBytes[2] != 0 || sidBytes[3] != 0)
256
+ {
257
+ string strAuth = String.Format("0x{0:2x}{1:2x}{2:2x}{3:2x}{4:2x}{5:2x}",
258
+ (short)sidBytes[2],
259
+ (short)sidBytes[3],
260
+ (short)sidBytes[4],
261
+ (short)sidBytes[5],
262
+ (short)sidBytes[6],
263
+ (short)sidBytes[7]);
264
+ strSid.Append("-");
265
+ strSid.Append(strAuth);
266
+ }
267
+ else
268
+ {
269
+ long iVal = (int)(sidBytes[7]) +
270
+ (int)(sidBytes[6] << 8) +
271
+ (int)(sidBytes[5] << 16) +
272
+ (int)(sidBytes[4] << 24);
273
+ strSid.Append("-");
274
+ strSid.Append(iVal.ToString());
275
+ }
276
+
277
+ // Get sub authority count...
278
+ int idxAuth = 0;
279
+ for (int i = 0; i < sSubAuthorityCount; i++)
280
+ {
281
+ idxAuth = 8 + i * 4;
282
+ uint iSubAuth = BitConverter.ToUInt32(sidBytes, idxAuth);
283
+ strSid.Append("-");
284
+ strSid.Append(iSubAuth.ToString());
285
+ }
286
+ }
287
+ catch (Exception ex)
288
+ {
289
+ Console.WriteLine(ex.Message);
290
+ return "";
291
+ }
292
+ return strSid.ToString();
293
+ }
294
+
295
+ /// <summary>
296
+ /// Verify that a given user is member of a local group
297
+ /// </summary>
298
+ /// <param name="domainName">domain name for the user, empty for local users</param>
299
+ /// <param name="userName">the user name</param>
300
+ /// <param name="shouldBeMember">whether the user is expected to be a member of the groups or not</param>
301
+ /// <param name="groupNames">list of groups to check for membership</param>
302
+ private static void IsUserMemberOf(string domainName, string userName, bool shouldBeMember, params string[] groupNames)
303
+ {
304
+ UserPrincipal user = GetUser(domainName, userName);
305
+ Assert.False(null == user, String.Format("User '{0}' was not found under domain '{1}'.", userName, domainName));
306
+
307
+ bool missedAGroup = false;
308
+ string message = String.Empty;
309
+ foreach (string groupName in groupNames)
310
+ {
311
+ try
312
+ {
313
+ bool found = user.IsMemberOf(new PrincipalContext(ContextType.Machine), IdentityType.Name, groupName);
314
+ if (found != shouldBeMember)
315
+ {
316
+ missedAGroup = true;
317
+ message += String.Format("User '{0}/{1}' is {2} a member of local group '{3}'. \r\n", domainName, userName, found ? String.Empty : "NOT", groupName);
318
+ }
319
+ }
320
+ catch (System.DirectoryServices.AccountManagement.PrincipalOperationException)
321
+ {
322
+ missedAGroup = true;
323
+ message += String.Format("Local group '{0}' was not found. \r\n", groupName);
324
+ }
325
+
326
+ }
327
+ Assert.False(missedAGroup, message);
328
+ }
329
+
330
+ /// <summary>
331
+ /// Returns the UserPrincipal object for a given user
332
+ /// </summary>
333
+ /// <param name="domainName">Domain name to look under, if Empty the LocalMachine is assumned as the domain</param>
334
+ /// <param name="userName"></param>
335
+ /// <returns>UserPrinicipal Object for the user if found, or null other wise</returns>
336
+ private static UserPrincipal GetUser(string domainName, string userName)
337
+ {
338
+ if (String.IsNullOrEmpty(domainName))
339
+ {
340
+ return UserPrincipal.FindByIdentity(new PrincipalContext(ContextType.Machine), IdentityType.Name, userName);
341
+ }
342
+ else
343
+ {
344
+ return UserPrincipal.Current;//.FindByIdentity(new PrincipalContext(ContextType.Domain,domainName), IdentityType.Name, userName);
345
+ }
346
+ }
347
+ }
348
+}
src/test/burn/WixTestTools/WixTestTools.csproj
+2
@@ -9,6 +9,8 @@
9
10
<ItemGroup>
11
<PackageReference Include="Microsoft.Win32.Registry" />
12
+ <PackageReference Include="System.DirectoryServices" />
13
+ <PackageReference Include="System.DirectoryServices.AccountManagement" />
14
<PackageReference Include="System.Security.Principal.Windows" />
15
<PackageReference Include="WixBuildTools.TestSupport" />
16
<PackageReference Include="WixToolset.Data" />
src/test/msi/Directory.Build.props
new
+11
@@ -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>IntegrationMsi</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>
src/test/msi/Directory.Build.targets
new
+6
@@ -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>
src/test/msi/Directory.wixproj.props
new
+11
@@ -0,0 +1,11 @@
1
+<!-- 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. -->
2
+<Project>
3
+ <PropertyGroup>
4
+ <TestGroupName Condition=" '$(TestGroupName)'=='' ">$([System.IO.Path]::GetFileName($([System.IO.Path]::GetDirectoryName($(MSBuildProjectDirectory)))))</TestGroupName>
5
+ <BaseIntermediateOutputPath>$(BaseOutputPath)obj\$(TestGroupName)\$(ProjectName)\</BaseIntermediateOutputPath>
6
+ <OutputPath>$(OutputPath)netcoreapp3.1\TestData\$(TestGroupName)\</OutputPath>
7
+ <DefaultCompressionLevel>None</DefaultCompressionLevel>
8
+ <CompilerAdditionalOptions>-wx</CompilerAdditionalOptions>
9
+ <SuppressValidation>true</SuppressValidation>
10
+ </PropertyGroup>
11
+</Project>
src/test/msi/Directory.wixproj.targets
new
+13
@@ -0,0 +1,13 @@
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
+ <PackageName Condition=" '$(PackageName)' == '' ">$(MSBuildProjectName)</PackageName>
6
+ <DefineConstants>TestGroupName=$(TestGroupName);PackageName=$(PackageName);$(DefineConstants)</DefineConstants>
7
+ <DefineConstants Condition=" '$(CabPrefix)' != '' ">CabPrefix=$(CabPrefix);$(DefineConstants)</DefineConstants>
8
+ <DefineConstants Condition=" '$(ProductCode)' != '' ">ProductCode=$(ProductCode);$(DefineConstants)</DefineConstants>
9
+ <DefineConstants Condition=" '$(ProductComponentsRef)' == 'true' ">ProductComponents=1;$(DefineConstants)</DefineConstants>
10
+ <DefineConstants Condition=" '$(UpgradeCode)' != '' ">UpgradeCode=$(UpgradeCode);$(DefineConstants)</DefineConstants>
11
+ <DefineConstants Condition=" '$(Version)' != '' ">Version=$(Version);$(DefineConstants)</DefineConstants>
12
+ </PropertyGroup>
13
+</Project>
src/test/msi/MsiE2ETests.sln
new
+51
@@ -0,0 +1,51 @@
1
+
2
+Microsoft Visual Studio Solution File, Format Version 12.00
3
+# Visual Studio Version 17
4
+VisualStudioVersion = 17.0.31919.166
5
+MinimumVisualStudioVersion = 10.0.40219.1
6
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixTestTools", "..\burn\WixTestTools\WixTestTools.csproj", "{3D3B02F3-79B6-4BD5-AD49-2889DA3849A7}"
7
+EndProject
8
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolsetTest.MsiE2E", "WixToolsetTest.MsiE2E\WixToolsetTest.MsiE2E.csproj", "{68E1A5F8-0F44-4B38-8876-C101A2A019F2}"
9
+EndProject
10
+Global
11
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
12
+ Debug|Any CPU = Debug|Any CPU
13
+ Debug|x64 = Debug|x64
14
+ Debug|x86 = Debug|x86
15
+ Release|Any CPU = Release|Any CPU
16
+ Release|x64 = Release|x64
17
+ Release|x86 = Release|x86
18
+ EndGlobalSection
19
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
20
+ {3D3B02F3-79B6-4BD5-AD49-2889DA3849A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21
+ {3D3B02F3-79B6-4BD5-AD49-2889DA3849A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
22
+ {3D3B02F3-79B6-4BD5-AD49-2889DA3849A7}.Debug|x64.ActiveCfg = Debug|Any CPU
23
+ {3D3B02F3-79B6-4BD5-AD49-2889DA3849A7}.Debug|x64.Build.0 = Debug|Any CPU
24
+ {3D3B02F3-79B6-4BD5-AD49-2889DA3849A7}.Debug|x86.ActiveCfg = Debug|Any CPU
25
+ {3D3B02F3-79B6-4BD5-AD49-2889DA3849A7}.Debug|x86.Build.0 = Debug|Any CPU
26
+ {3D3B02F3-79B6-4BD5-AD49-2889DA3849A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
27
+ {3D3B02F3-79B6-4BD5-AD49-2889DA3849A7}.Release|Any CPU.Build.0 = Release|Any CPU
28
+ {3D3B02F3-79B6-4BD5-AD49-2889DA3849A7}.Release|x64.ActiveCfg = Release|Any CPU
29
+ {3D3B02F3-79B6-4BD5-AD49-2889DA3849A7}.Release|x64.Build.0 = Release|Any CPU
30
+ {3D3B02F3-79B6-4BD5-AD49-2889DA3849A7}.Release|x86.ActiveCfg = Release|Any CPU
31
+ {3D3B02F3-79B6-4BD5-AD49-2889DA3849A7}.Release|x86.Build.0 = Release|Any CPU
32
+ {68E1A5F8-0F44-4B38-8876-C101A2A019F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33
+ {68E1A5F8-0F44-4B38-8876-C101A2A019F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
34
+ {68E1A5F8-0F44-4B38-8876-C101A2A019F2}.Debug|x64.ActiveCfg = Debug|Any CPU
35
+ {68E1A5F8-0F44-4B38-8876-C101A2A019F2}.Debug|x64.Build.0 = Debug|Any CPU
36
+ {68E1A5F8-0F44-4B38-8876-C101A2A019F2}.Debug|x86.ActiveCfg = Debug|Any CPU
37
+ {68E1A5F8-0F44-4B38-8876-C101A2A019F2}.Debug|x86.Build.0 = Debug|Any CPU
38
+ {68E1A5F8-0F44-4B38-8876-C101A2A019F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
39
+ {68E1A5F8-0F44-4B38-8876-C101A2A019F2}.Release|Any CPU.Build.0 = Release|Any CPU
40
+ {68E1A5F8-0F44-4B38-8876-C101A2A019F2}.Release|x64.ActiveCfg = Release|Any CPU
41
+ {68E1A5F8-0F44-4B38-8876-C101A2A019F2}.Release|x64.Build.0 = Release|Any CPU
42
+ {68E1A5F8-0F44-4B38-8876-C101A2A019F2}.Release|x86.ActiveCfg = Release|Any CPU
43
+ {68E1A5F8-0F44-4B38-8876-C101A2A019F2}.Release|x86.Build.0 = Release|Any CPU
44
+ EndGlobalSection
45
+ GlobalSection(SolutionProperties) = preSolution
46
+ HideSolutionNode = FALSE
47
+ EndGlobalSection
48
+ GlobalSection(ExtensibilityGlobals) = postSolution
49
+ SolutionGuid = {4C9978E3-8548-451E-8BF5-03AD1D0280C2}
50
+ EndGlobalSection
51
+EndGlobal
src/test/msi/README.md
new
+29
@@ -0,0 +1,29 @@
1
+# integration
2
+
3
+This layer is for building installers, and then executing xunit tests that run them and verify that they worked.
4
+
5
+## Running tests
6
+
7
+The main focus of these tests is to validate behavior in a real environment.
8
+Depending on who you talk to, these are integration or system-level or end-to-end (E2E) tests.
9
+They modify machine state so it's strongly recommended *not* to run these tests on your dev box.
10
+They should be run on a VM instead, where you can easily roll back.
11
+
12
+1. Run build.cmd to build everything (the tests will not automatically run).
13
+1. Copy the build\IntegrationMsi\Debug\netcoreapp3.1 folder to your VM.
14
+1. Open an elevated command prompt and navigate to the netcoreapp3.1 folder.
15
+1. Run the runtests.cmd file to run the tests.
16
+
17
+You can modify the runtests.cmd to run specific tests.
18
+For example, the following line runs only the specified test:
19
+
20
+> dotnet test --filter WixToolsetTest.BurnE2E.BasicFunctionalityTests.CanInstallAndUninstallSimpleBundle_x86_wixstdba WixToolsetTest.BurnE2E.dll
21
+
22
+The VM must have:
23
+1. x64 .NET Core SDK of 3.1 or later (for the test runner)
24
+
25
+## Building with local changes
26
+
27
+The current build process will poison your NuGet package cache, so you may have to run the following command to clear it:
28
+
29
+> nuget locals all -clear
\ No newline at end of file
src/test/msi/TestData/Templates/CustomActionFail.exe
Binary files /dev/null and b/src/test/msi/TestData/Templates/CustomActionFail.exe differ
src/test/msi/TestData/Templates/Product.wxs
new
+47
@@ -0,0 +1,47 @@
1
+<!-- 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. -->
2
+
3
+
4
+
5
+<?ifndef Version?>
6
+<?define Version = 1.0.0.0?>
7
+<?endif?>
8
+
9
+<?ifndef ProductCode?>
10
+<?define ProductCode = *?>
11
+<?endif?>
12
+
13
+<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
14
+ <Package Name="~$(var.TestGroupName) - $(var.PackageName)" Language="1033" Version="$(var.Version)" Manufacturer="WixToolset" UpgradeCode="$(var.UpgradeCode)" Compressed="yes" ProductCode="$(var.ProductCode)">
15
+ <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
16
+ <?ifndef CabPrefix?>
17
+ <MediaTemplate EmbedCab="yes" />
18
+ <?else?>
19
+ <MediaTemplate CabinetTemplate="{0}$(var.CabPrefix).cab" />
20
+ <?endif?>
21
+
22
+ <Feature Id="Complete" Level="1">
23
+ <ComponentRef Id="FileComponent" />
24
+ <?ifdef var.ProductComponents?>
25
+ <ComponentGroupRef Id="ProductComponents" />
26
+ <?endif?>
27
+ </Feature>
28
+ </Package>
29
+
30
+ <Fragment>
31
+ <StandardDirectory Id="ProgramFiles6432Folder">
32
+ <Directory Id="WixDir" Name="~Test WiX">
33
+ <Directory Id="TestDir" Name="$(var.TestGroupName)">
34
+ <Directory Id="INSTALLFOLDER" Name="$(var.PackageName)" />
35
+ </Directory>
36
+ </Directory>
37
+ </StandardDirectory>
38
+ </Fragment>
39
+
40
+ <Fragment>
41
+ <Component Id="FileComponent" Guid="12345678-9ABC-DEF0-1234-567890000000" Directory="INSTALLFOLDER">
42
+ <File Id="CAFile" Name="CustomActionFail.exe" Source="$(sys.SOURCEFILEDIR)\CustomActionFail.exe" />
43
+ </Component>
44
+
45
+ <CustomAction Id="CaFail" FileRef="CAFile" Execute="immediate" Return="check" ExeCommand="" />
46
+ </Fragment>
47
+</Wix>
src/test/msi/TestData/TestData.proj
new
+16
@@ -0,0 +1,16 @@
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
+
4
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
5
+ <ItemGroup>
6
+ <TestDataProject Include="**\*.wixproj" />
7
+ </ItemGroup>
8
+
9
+ <Target Name="Build">
10
+ <MSBuild Projects="%(TestDataProject.Identity)" />
11
+ </Target>
12
+
13
+ <Target Name="Restore">
14
+ <MSBuild Projects="%(TestDataProject.Identity)" Targets="Restore" />
15
+ </Target>
16
+</Project>
src/test/msi/TestData/UtilExtensionUserTests/ProductA/ProductA.wixproj
new
+13
@@ -0,0 +1,13 @@
1
+<!-- 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. -->
2
+<Project Sdk="WixToolset.Sdk">
3
+ <PropertyGroup>
4
+ <UpgradeCode>{1A1795A6-87C0-4A9A-ABD5-DF9BED697037}</UpgradeCode>
5
+ <ProductComponentsRef>true</ProductComponentsRef>
6
+ </PropertyGroup>
7
+ <ItemGroup>
8
+ <Compile Include="..\..\Templates\Product.wxs" Link="Product.wxs" />
9
+ </ItemGroup>
10
+ <ItemGroup>
11
+ <PackageReference Include="WixToolset.Util.wixext" />
12
+ </ItemGroup>
13
+</Project>
\ No newline at end of file
src/test/msi/TestData/UtilExtensionUserTests/ProductA/product.wxs
new
+35
@@ -0,0 +1,35 @@
1
+<!-- 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. -->
2
+
3
+
4
+<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
5
+ <Fragment>
6
+ <ComponentGroup Id="ProductComponents">
7
+ <ComponentRef Id="Component1" />
8
+ </ComponentGroup>
9
+
10
+ <Property Id="TEMPDOMAIN" Secure="yes" />
11
+ <Property Id="TEMPUSERNAME" Secure="yes" />
12
+ </Fragment>
13
+
14
+ <Fragment>
15
+ <util:Group Id="ADMIN" Name="Administrators" />
16
+ <util:Group Id="POWER_USER" Name="Power Users" />
17
+
18
+ <Component Id="Component1" Guid="00030829-0000-0000-C000-000000000046" Directory="INSTALLFOLDER">
19
+ <File Source="$(sys.SOURCEFILEPATH)" KeyPath="yes" />
20
+
21
+ <util:User Id="TEST_USER1" Name="testName1" Password="test123!@#" PasswordExpired="yes">
22
+ <util:GroupRef Id="ADMIN" />
23
+ <util:GroupRef Id="POWER_USER" />
24
+ </util:User>
25
+
26
+ <util:User Id="TEST_USER2" Name="testName2" Password="test123!@#" Disabled="yes" RemoveOnUninstall="no" PasswordNeverExpires="yes" UpdateIfExists="yes">
27
+ <util:GroupRef Id="POWER_USER" />
28
+ </util:User>
29
+
30
+ <util:User Id="TEST_USER3" Name="[TEMPUSERNAME]" Domain="[TEMPDOMAIN]" CreateUser="no">
31
+ <util:GroupRef Id="POWER_USER" />
32
+ </util:User>
33
+ </Component>
34
+ </Fragment>
35
+</Wix>
src/test/msi/TestData/UtilExtensionUserTests/ProductFail/ProductFail.wixproj
new
+13
@@ -0,0 +1,13 @@
1
+<!-- 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. -->
2
+<Project Sdk="WixToolset.Sdk">
3
+ <PropertyGroup>
4
+ <UpgradeCode>{91D27DAC-04C1-4160-914E-343676D36CAA}</UpgradeCode>
5
+ <ProductComponentsRef>true</ProductComponentsRef>
6
+ </PropertyGroup>
7
+ <ItemGroup>
8
+ <Compile Include="..\..\Templates\Product.wxs" Link="Product.wxs" />
9
+ </ItemGroup>
10
+ <ItemGroup>
11
+ <PackageReference Include="WixToolset.Util.wixext" />
12
+ </ItemGroup>
13
+</Project>
\ No newline at end of file
src/test/msi/TestData/UtilExtensionUserTests/ProductFail/product_fail.wxs
new
+39
@@ -0,0 +1,39 @@
1
+<!-- 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. -->
2
+
3
+
4
+<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
5
+ <Fragment>
6
+ <ComponentGroup Id="ProductComponents">
7
+ <ComponentRef Id="Component1" />
8
+ </ComponentGroup>
9
+
10
+ <Property Id="TEMPDOMAIN" Secure="yes" />
11
+ <Property Id="TEMPUSERNAME" Secure="yes" />
12
+
13
+ <InstallExecuteSequence>
14
+ <Custom Action="CaFail" After="Wix4ConfigureUsers_X86" />
15
+ </InstallExecuteSequence>
16
+ </Fragment>
17
+
18
+ <Fragment>
19
+ <util:Group Id="ADMIN" Name="Administrators" />
20
+ <util:Group Id="POWER_USER" Name="Power Users" />
21
+
22
+ <Component Id="Component1" Guid="00030829-0000-0000-C000-000000000046" Directory="INSTALLFOLDER">
23
+ <File Source="$(sys.SOURCEFILEPATH)" KeyPath="yes" />
24
+
25
+ <util:User Id="TEST_USER1" Name="testName1" Password="test123!@#" PasswordExpired="yes">
26
+ <util:GroupRef Id="ADMIN" />
27
+ <util:GroupRef Id="POWER_USER" />
28
+ </util:User>
29
+
30
+ <util:User Id="TEST_USER2" Name="testName2" Password="test123!@#" Disabled="yes" RemoveOnUninstall="no" PasswordNeverExpires="yes" UpdateIfExists="yes">
31
+ <util:GroupRef Id="POWER_USER" />
32
+ </util:User>
33
+
34
+ <util:User Id="TEST_USER3" Name="[TEMPUSERNAME]" Domain="[TEMPDOMAIN]" CreateUser="no">
35
+ <util:GroupRef Id="POWER_USER" />
36
+ </util:User>
37
+ </Component>
38
+ </Fragment>
39
+</Wix>
src/test/msi/TestData/UtilExtensionUserTests/ProductFailIfExists/FailIfExists.wxs
new
+24
@@ -0,0 +1,24 @@
1
+<!-- 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. -->
2
+
3
+
4
+<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
5
+ <Fragment>
6
+ <ComponentGroup Id="ProductComponents">
7
+ <ComponentRef Id="Component1" />
8
+ </ComponentGroup>
9
+ </Fragment>
10
+
11
+ <Fragment>
12
+ <util:Group Id="ADMIN" Name="Administrators" />
13
+ <util:Group Id="POWER_USER" Name="Power Users" />
14
+
15
+ <Component Id="Component1" Guid="00030829-0000-0000-C000-000000000046" Directory="INSTALLFOLDER">
16
+ <File Source="$(sys.SOURCEFILEPATH)" KeyPath="yes" />
17
+
18
+ <util:User Id="TEST_USER4" Name="existinguser" Password="test123!@#" LogonAsService="yes" FailIfExists="yes" RemoveOnUninstall="yes">
19
+ <util:GroupRef Id="ADMIN" />
20
+ <util:GroupRef Id="POWER_USER" />
21
+ </util:User>
22
+ </Component>
23
+ </Fragment>
24
+</Wix>
src/test/msi/TestData/UtilExtensionUserTests/ProductFailIfExists/ProductFailIfExists.wixproj
new
+13
@@ -0,0 +1,13 @@
1
+<!-- 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. -->
2
+<Project Sdk="WixToolset.Sdk">
3
+ <PropertyGroup>
4
+ <UpgradeCode>{BC803822-929E-47DA-AB3A-3A62EEEA2BFB}</UpgradeCode>
5
+ <ProductComponentsRef>true</ProductComponentsRef>
6
+ </PropertyGroup>
7
+ <ItemGroup>
8
+ <Compile Include="..\..\Templates\Product.wxs" Link="Product.wxs" />
9
+ </ItemGroup>
10
+ <ItemGroup>
11
+ <PackageReference Include="WixToolset.Util.wixext" />
12
+ </ItemGroup>
13
+</Project>
\ No newline at end of file
src/test/msi/TestData/UtilExtensionUserTests/ProductNonVitalUserGroup/NonVitalUserGroup.wxs
new
+22
@@ -0,0 +1,22 @@
1
+<!-- 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. -->
2
+
3
+
4
+<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
5
+ <Fragment>
6
+ <ComponentGroup Id="ProductComponents">
7
+ <ComponentRef Id="Component1" />
8
+ </ComponentGroup>
9
+ </Fragment>
10
+
11
+ <Fragment>
12
+ <util:Group Id="ShouldNotExist" Name="Should Not Exist" />
13
+
14
+ <Component Id="Component1" Guid="00030829-0000-0000-C000-000000000046" Directory="INSTALLFOLDER">
15
+ <File Source="$(sys.SOURCEFILEPATH)" KeyPath="yes" />
16
+
17
+ <util:User Id="CurrentUser" Name="[LogonUser]" Domain="[%USERDOMAIN]" RemoveOnUninstall="no" Vital="no">
18
+ <util:GroupRef Id="ShouldNotExist" />
19
+ </util:User>
20
+ </Component>
21
+ </Fragment>
22
+</Wix>
src/test/msi/TestData/UtilExtensionUserTests/ProductNonVitalUserGroup/ProductNonVitalUserGroup.wixproj
new
+13
@@ -0,0 +1,13 @@
1
+<!-- 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. -->
2
+<Project Sdk="WixToolset.Sdk">
3
+ <PropertyGroup>
4
+ <UpgradeCode>{455C8D4F-6D59-405C-AD51-0ACC7FB91A26}</UpgradeCode>
5
+ <ProductComponentsRef>true</ProductComponentsRef>
6
+ </PropertyGroup>
7
+ <ItemGroup>
8
+ <Compile Include="..\..\Templates\Product.wxs" Link="Product.wxs" />
9
+ </ItemGroup>
10
+ <ItemGroup>
11
+ <PackageReference Include="WixToolset.Util.wixext" />
12
+ </ItemGroup>
13
+</Project>
\ No newline at end of file
src/test/msi/TestData/UtilExtensionUserTests/ProductRestrictedDomain/ProductRestrictedDomain.wixproj
new
+13
@@ -0,0 +1,13 @@
1
+<!-- 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. -->
2
+<Project Sdk="WixToolset.Sdk">
3
+ <PropertyGroup>
4
+ <UpgradeCode>{50CF526C-A862-4327-9EA3-C96AAB6FABCE}</UpgradeCode>
5
+ <ProductComponentsRef>true</ProductComponentsRef>
6
+ </PropertyGroup>
7
+ <ItemGroup>
8
+ <Compile Include="..\..\Templates\Product.wxs" Link="Product.wxs" />
9
+ </ItemGroup>
10
+ <ItemGroup>
11
+ <PackageReference Include="WixToolset.Util.wixext" />
12
+ </ItemGroup>
13
+</Project>
\ No newline at end of file
src/test/msi/TestData/UtilExtensionUserTests/ProductRestrictedDomain/RestrictedDomain.wxs
new
+20
@@ -0,0 +1,20 @@
1
+<!-- 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. -->
2
+
3
+
4
+<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
5
+ <Fragment>
6
+ <ComponentGroup Id="ProductComponents">
7
+ <ComponentRef Id="Component1" />
8
+ </ComponentGroup>
9
+
10
+ <Property Id="TEMPDOMAIN" Secure="yes" />
11
+ </Fragment>
12
+
13
+ <Fragment>
14
+ <Component Id="Component1" Guid="00030829-0000-0000-C000-000000000046" Directory="INSTALLFOLDER">
15
+ <File Source="$(sys.SOURCEFILEPATH)" KeyPath="yes" />
16
+
17
+ <util:User Id="TEST_USER_test" Name="testName1" Domain="[TEMPDOMAIN]" Password="test123!@#" PasswordExpired="no" />
18
+ </Component>
19
+ </Fragment>
20
+</Wix>
src/test/msi/WixToolsetTest.MsiE2E/MsiE2EFixture.cs
new
+28
@@ -0,0 +1,28 @@
1
+// 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.
2
+
3
+namespace WixToolsetTest.MsiE2E
4
+{
5
+ using System;
6
+ using System.Security.Principal;
7
+
8
+ public class MsiE2EFixture
9
+ {
10
+ const string RequiredEnvironmentVariableName = "RuntimeTestsEnabled";
11
+
12
+ public MsiE2EFixture()
13
+ {
14
+ using var identity = WindowsIdentity.GetCurrent();
15
+ var principal = new WindowsPrincipal(identity);
16
+ if (!principal.IsInRole(WindowsBuiltInRole.Administrator))
17
+ {
18
+ throw new InvalidOperationException("These tests must run elevated.");
19
+ }
20
+
21
+ var testsEnabledString = Environment.GetEnvironmentVariable(RequiredEnvironmentVariableName);
22
+ if (!bool.TryParse(testsEnabledString, out var testsEnabled) || !testsEnabled)
23
+ {
24
+ throw new InvalidOperationException($"These tests affect machine state. Set the {RequiredEnvironmentVariableName} environment variable to true to accept the consequences.");
25
+ }
26
+ }
27
+ }
28
+}
src/test/msi/WixToolsetTest.MsiE2E/MsiE2ETests.cs
new
+44
@@ -0,0 +1,44 @@
1
+// 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.
2
+
3
+namespace WixToolsetTest.MsiE2E
4
+{
5
+ using System;
6
+ using System.Collections.Generic;
7
+ using WixTestTools;
8
+ using Xunit;
9
+ using Xunit.Abstractions;
10
+
11
+ [Collection("MsiE2E")]
12
+ public abstract class MsiE2ETests : WixTestBase, IDisposable
13
+ {
14
+ protected MsiE2ETests(ITestOutputHelper testOutputHelper) : base(testOutputHelper)
15
+ {
16
+ }
17
+
18
+ private Stack<IDisposable> Installers { get; } = new Stack<IDisposable>();
19
+
20
+ protected PackageInstaller CreatePackageInstaller(string filename)
21
+ {
22
+ var installer = new PackageInstaller(this.TestContext, filename);
23
+ this.Installers.Push(installer);
24
+ return installer;
25
+ }
26
+
27
+ public void Dispose()
28
+ {
29
+ while (this.Installers.TryPop(out var installer))
30
+ {
31
+ try
32
+ {
33
+ installer.Dispose();
34
+ }
35
+ catch { }
36
+ }
37
+ }
38
+ }
39
+
40
+ [CollectionDefinition("MsiE2E", DisableParallelization = true)]
41
+ public class MsiE2ECollectionDefinition : ICollectionFixture<MsiE2EFixture>
42
+ {
43
+ }
44
+}
src/test/msi/WixToolsetTest.MsiE2E/UtilExtensionUserTests.cs
new
+162
@@ -0,0 +1,162 @@
1
+// 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.
2
+
3
+namespace WixToolsetTest.MsiE2E
4
+{
5
+ using System;
6
+ using WixTestTools;
7
+ using Xunit;
8
+ using Xunit.Abstractions;
9
+
10
+ public class UtilExtensionUserTests : MsiE2ETests
11
+ {
12
+ public UtilExtensionUserTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { }
13
+
14
+ const string TempDomain = "USERDOMAIN";
15
+ const string TempUsername = "USERNAME";
16
+
17
+ // Verify that the users specified in the authoring are created as expected.
18
+ [Fact]
19
+ public void CanInstallAndUninstallUsers()
20
+ {
21
+ var arguments = new string[]
22
+ {
23
+ $"TEMPDOMAIN={Environment.GetEnvironmentVariable(TempDomain)}",
24
+ $"TEMPUSERNAME={Environment.GetEnvironmentVariable(TempUsername)}",
25
+ };
26
+ var productA = this.CreatePackageInstaller("ProductA");
27
+
28
+ productA.InstallProduct(MSIExec.MSIExecReturnCode.SUCCESS, arguments);
29
+
30
+ // Validate New User Information.
31
+ UserVerifier.VerifyUserInformation(String.Empty, "testName1", true, false, false);
32
+ UserVerifier.VerifyUserIsMemberOf(String.Empty, "testName1", "Administrators", "Power Users");
33
+
34
+ UserVerifier.VerifyUserInformation(String.Empty, "testName2", true, true, true);
35
+ UserVerifier.VerifyUserIsMemberOf(String.Empty, "testName2", "Power Users");
36
+
37
+ UserVerifier.VerifyUserIsMemberOf(Environment.GetEnvironmentVariable(TempDomain), Environment.GetEnvironmentVariable(TempUsername), "Power Users");
38
+
39
+ productA.UninstallProduct(MSIExec.MSIExecReturnCode.SUCCESS, arguments);
40
+
41
+ // Verify Users marked as RemoveOnUninstall were removed.
42
+ Assert.False(UserVerifier.UserExists(String.Empty, "testName1"), String.Format("User '{0}' was not removed on Uninstall", "testName1"));
43
+ Assert.True(UserVerifier.UserExists(String.Empty, "testName2"), String.Format("User '{0}' was removed on Uninstall", "testName2"));
44
+
45
+ // clean up
46
+ UserVerifier.DeleteLocalUser("testName2");
47
+
48
+ UserVerifier.VerifyUserIsNotMemberOf(Environment.GetEnvironmentVariable(TempDomain), Environment.GetEnvironmentVariable(TempUsername), "Power Users");
49
+ }
50
+
51
+ // Verify the rollback action reverts all Users changes.
52
+ [Fact]
53
+ public void CanRollbackUsers()
54
+ {
55
+ var arguments = new string[]
56
+ {
57
+ $"TEMPDOMAIN={Environment.GetEnvironmentVariable(TempDomain)}",
58
+ $"TEMPUSERNAME={Environment.GetEnvironmentVariable(TempUsername)}",
59
+ };
60
+ var productFail = this.CreatePackageInstaller("ProductFail");
61
+
62
+ // make sure the user accounts are deleted before we start
63
+ UserVerifier.DeleteLocalUser("testName1");
64
+ UserVerifier.DeleteLocalUser("testName2");
65
+ UserVerifier.VerifyUserIsNotMemberOf(Environment.GetEnvironmentVariable(TempDomain), Environment.GetEnvironmentVariable(TempUsername), "Power Users");
66
+
67
+ productFail.InstallProduct(MSIExec.MSIExecReturnCode.ERROR_INSTALL_FAILURE, arguments);
68
+
69
+ // Verify Users marked as RemoveOnUninstall were removed.
70
+ Assert.False(UserVerifier.UserExists(String.Empty, "testName1"), String.Format("User '{0}' was not removed on Rollback", "testName1"));
71
+ Assert.False(UserVerifier.UserExists(String.Empty, "testName2"), String.Format("User '{0}' was not removed on Rollback", "testName2"));
72
+
73
+ UserVerifier.VerifyUserIsNotMemberOf(Environment.GetEnvironmentVariable(TempDomain), Environment.GetEnvironmentVariable(TempUsername), "Power Users");
74
+ }
75
+
76
+ // Verify that the users specified in the authoring are created as expected on repair.
77
+ [Fact(Skip = "Test demonstrates failure")]
78
+ public void CanRepairUsers()
79
+ {
80
+ var arguments = new string[]
81
+ {
82
+ $"TEMPDOMAIN={Environment.GetEnvironmentVariable(TempDomain)}",
83
+ $"TEMPUSERNAME={Environment.GetEnvironmentVariable(TempUsername)}",
84
+ };
85
+ var productA = this.CreatePackageInstaller("ProductA");
86
+
87
+ productA.InstallProduct(MSIExec.MSIExecReturnCode.SUCCESS, arguments);
88
+
89
+ UserVerifier.DeleteLocalUser("testName1");
90
+ UserVerifier.SetUserInformation(String.Empty, "testName2", true, false, false);
91
+
92
+ productA.RepairProduct(MSIExec.MSIExecReturnCode.SUCCESS, arguments);
93
+
94
+ // Validate New User Information.
95
+ UserVerifier.VerifyUserInformation(String.Empty, "testName1", true, false, false);
96
+ UserVerifier.VerifyUserIsMemberOf(String.Empty, "testName1", "Administrators", "Power Users");
97
+
98
+ UserVerifier.VerifyUserInformation(String.Empty, "testName2", true, true, true);
99
+ UserVerifier.VerifyUserIsMemberOf(String.Empty, "testName2", "Power Users");
100
+
101
+ UserVerifier.VerifyUserIsMemberOf(Environment.GetEnvironmentVariable(TempDomain), Environment.GetEnvironmentVariable(TempUsername), "Power Users");
102
+
103
+ productA.UninstallProduct(MSIExec.MSIExecReturnCode.SUCCESS, arguments);
104
+
105
+ // Verify Users marked as RemoveOnUninstall were removed.
106
+ Assert.False(UserVerifier.UserExists(String.Empty, "testName1"), String.Format("User '{0}' was not removed on Uninstall", "testName1"));
107
+ Assert.True(UserVerifier.UserExists(String.Empty, "testName2"), String.Format("User '{0}' was removed on Uninstall", "testName2"));
108
+
109
+ // clean up
110
+ UserVerifier.DeleteLocalUser("testName2");
111
+
112
+ UserVerifier.VerifyUserIsNotMemberOf(Environment.GetEnvironmentVariable(TempDomain), Environment.GetEnvironmentVariable(TempUsername), "Power Users");
113
+ }
114
+
115
+ // Verify that Installation fails if FailIfExisits is set.
116
+ [Fact]
117
+ public void FailsIfUserExists()
118
+ {
119
+ var productFailIfExists = this.CreatePackageInstaller("ProductFailIfExists");
120
+
121
+ // Create 'existinguser'
122
+ UserVerifier.CreateLocalUser("existinguser", "test123!@#");
123
+
124
+ try
125
+ {
126
+ productFailIfExists.InstallProduct(MSIExec.MSIExecReturnCode.ERROR_INSTALL_FAILURE);
127
+
128
+ // Verify User still exists.
129
+ bool userExists = UserVerifier.UserExists(String.Empty, "existinguser");
130
+
131
+ Assert.True(userExists, String.Format("User '{0}' was removed on Rollback", "existinguser"));
132
+ }
133
+ finally
134
+ {
135
+ // clean up
136
+ UserVerifier.DeleteLocalUser("existinguser");
137
+ }
138
+
139
+ }
140
+
141
+ // Verify that a user cannot be created on a domain on which you dont have create user permission.
142
+ [Fact]
143
+ public void FailsIfRestrictedDomain()
144
+ {
145
+ var productRestrictedDomain = this.CreatePackageInstaller("ProductRestrictedDomain");
146
+
147
+ string logFile = productRestrictedDomain.InstallProduct(MSIExec.MSIExecReturnCode.ERROR_INSTALL_FAILURE, "TEMPDOMAIN=DOESNOTEXIST");
148
+
149
+ // Verify expected error message in the log file
150
+ Assert.True(LogVerifier.MessageInLogFile(logFile, "ConfigureUsers: Failed to check existence of domain: DOESNOTEXIST, user: testName1 (error code 0x800706ba) - continuing"));
151
+ }
152
+
153
+ // Verify that adding a user to a non-existent group does not fail the install when non-vital.
154
+ [Fact]
155
+ public void IgnoresMissingGroupWhenNonVital()
156
+ {
157
+ var productNonVitalGroup = this.CreatePackageInstaller("ProductNonVitalUserGroup");
158
+
159
+ productNonVitalGroup.InstallProduct();
160
+ }
161
+ }
162
+}
src/test/msi/WixToolsetTest.MsiE2E/WixToolsetTest.MsiE2E.csproj
new
+31
@@ -0,0 +1,31 @@
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
+
4
+<Project Sdk="Microsoft.NET.Sdk">
5
+ <PropertyGroup>
6
+ <TargetFramework>netcoreapp3.1</TargetFramework>
7
+ <PlatformTarget>x64</PlatformTarget>
8
+ <RollForward>Major</RollForward>
9
+ </PropertyGroup>
10
+
11
+ <ItemGroup>
12
+ <Content Include="runtests.cmd" CopyToOutputDirectory="PreserveNewest" />
13
+ </ItemGroup>
14
+
15
+ <ItemGroup>
16
+ <ProjectReference Include="..\..\burn\WixTestTools\WixTestTools.csproj" />
17
+ </ItemGroup>
18
+
19
+ <ItemGroup>
20
+ <PackageReference Include="Microsoft.Win32.Registry" />
21
+ <PackageReference Include="System.Security.Principal.Windows" />
22
+ <PackageReference Include="WixBuildTools.TestSupport" />
23
+ <PackageReference Include="WixToolset.Data" />
24
+ </ItemGroup>
25
+
26
+ <ItemGroup>
27
+ <PackageReference Include="Microsoft.NET.Test.Sdk" />
28
+ <PackageReference Include="xunit" />
29
+ <PackageReference Include="xunit.runner.visualstudio" PrivateAssets="All" />
30
+ </ItemGroup>
31
+</Project>
src/test/msi/WixToolsetTest.MsiE2E/runtests.cmd
new
+2
@@ -0,0 +1,2 @@
1
+SET RuntimeTestsEnabled=true
2
+dotnet test WixToolsetTest.MsiE2E.dll -v normal
\ No newline at end of file
src/test/msi/test_msi.cmd
new
+23
@@ -0,0 +1,23 @@
1
+@setlocal
2
+@pushd %~dp0
3
+
4
+@set _RESULT=0
5
+@set _C=Debug
6
+@set _L=%~dp0..\..\..\build\logs
7
+:parse_args
8
+@if /i "%1"=="release" set _C=Release
9
+@if /i "%1"=="test" set RuntimeTestsEnabled=true
10
+@if not "%1"=="" shift & goto parse_args
11
+
12
+@echo Msi integration tests %_C%
13
+
14
+msbuild -t:Build -Restore -p:Configuration=%_C% -warnaserror -bl:%_L%\test_msi_build.binlog || exit /b
15
+msbuild -t:Build -Restore TestData\TestData.proj -p:Configuration=%_C% -m -bl:%_L%\test_msi_data_build.binlog || exit /b
16
+
17
+@if not "%RuntimeTestsEnabled%"=="true" goto :LExit
18
+
19
+dotnet test -c %_C% --no-build WixToolsetTest.MsiE2E -l "trx;LogFileName=%_L%\TestResults\WixToolsetTest.MsiE2E.trx" || exit /b
20
+
21
+:LExit
22
+@popd
23
+@endlocal
src/test/test.cmd
+1
@@ -11,6 +11,7 @@
11
@if not "%RuntimeTestsEnabled%"=="true" echo Build integration tests %_C%
12
@if "%RuntimeTestsEnabled%"=="true" set _T=test&echo Run integration tests %_C%
13
14
+@call msi\test_msi.cmd %_C% %_T% || exit /b
15
@call burn\test_burn.cmd %_C% %_T% || exit /b
16
17
msbuild -t:Restore dtf\DtfE2ETests.sln -p:Configuration=%_C% -nologo -m -warnaserror -bl:%_L%\dtfe2etests.binlog || exit /b
src/testresultfilelist.txt
+1
@@ -17,5 +17,6 @@ build/logs/TestResults/WixToolsetTest.Dnc.HostGenerator.trx
17
build/logs/TestResults/WixToolsetTest.Heat.trx
18
build/logs/TestResults/WixToolsetTest.ManagedHost.trx
19
build/logs/TestResults/WixToolsetTest.Mba.Core.trx
20
+build/logs/TestResults/WixToolsetTest.MsiE2E.trx
21
build/logs/TestResults/WixToolsetTest.Sdk.trx
22
build/logs/TestResults/WixToolsetTest.WixE2ETests.trx
\ No newline at end of file