diff options
author | Sean Hall <r.sean.hall@gmail.com> | 2020-12-01 23:02:05 -0600 |
---|---|---|
committer | Sean Hall <r.sean.hall@gmail.com> | 2020-12-01 23:02:05 -0600 |
commit | 5d0434843c6f307a46fa95139c1e754221cc13af (patch) | |
tree | 559f1714b14c08fa09449b62770c9107fae968c3 /src/Utilities | |
parent | 86456524fe4640053616f6fc18311159e6fafea5 (diff) | |
download | wix-5d0434843c6f307a46fa95139c1e754221cc13af.tar.gz wix-5d0434843c6f307a46fa95139c1e754221cc13af.tar.bz2 wix-5d0434843c6f307a46fa95139c1e754221cc13af.zip |
Add MSI transaction tests.
Diffstat (limited to 'src/Utilities')
-rw-r--r-- | src/Utilities/TestBA/Hresult.cs | 22 | ||||
-rw-r--r-- | src/Utilities/TestBA/TestBA.BootstrapperCore.config | 18 | ||||
-rw-r--r-- | src/Utilities/TestBA/TestBA.cs | 469 | ||||
-rw-r--r-- | src/Utilities/TestBA/TestBA.csproj | 24 | ||||
-rw-r--r-- | src/Utilities/TestBA/TestBAFactory.cs | 22 |
5 files changed, 555 insertions, 0 deletions
diff --git a/src/Utilities/TestBA/Hresult.cs b/src/Utilities/TestBA/Hresult.cs new file mode 100644 index 00000000..bc1aa8c0 --- /dev/null +++ b/src/Utilities/TestBA/Hresult.cs | |||
@@ -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 | namespace WixToolset.Test.BA | ||
4 | { | ||
5 | using System; | ||
6 | |||
7 | /// <summary> | ||
8 | /// Utility class to work with HRESULTs | ||
9 | /// </summary> | ||
10 | internal class Hresult | ||
11 | { | ||
12 | /// <summary> | ||
13 | /// Determines if an HRESULT was a success code or not. | ||
14 | /// </summary> | ||
15 | /// <param name="status">HRESULT to verify.</param> | ||
16 | /// <returns>True if the status is a success code.</returns> | ||
17 | public static bool Succeeded(int status) | ||
18 | { | ||
19 | return status >= 0; | ||
20 | } | ||
21 | } | ||
22 | } | ||
diff --git a/src/Utilities/TestBA/TestBA.BootstrapperCore.config b/src/Utilities/TestBA/TestBA.BootstrapperCore.config new file mode 100644 index 00000000..55876a00 --- /dev/null +++ b/src/Utilities/TestBA/TestBA.BootstrapperCore.config | |||
@@ -0,0 +1,18 @@ | |||
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 | |||
5 | <configuration> | ||
6 | <configSections> | ||
7 | <sectionGroup name="wix.bootstrapper" type="WixToolset.Mba.Host.BootstrapperSectionGroup, WixToolset.Mba.Host"> | ||
8 | <section name="host" type="WixToolset.Mba.Host.HostSection, WixToolset.Mba.Host" /> | ||
9 | </sectionGroup> | ||
10 | </configSections> | ||
11 | <startup> | ||
12 | <supportedRuntime version="v4.0" /> | ||
13 | <supportedRuntime version="v2.0.50727" /> | ||
14 | </startup> | ||
15 | <wix.bootstrapper> | ||
16 | <host assemblyName="TestBA" /> | ||
17 | </wix.bootstrapper> | ||
18 | </configuration> | ||
diff --git a/src/Utilities/TestBA/TestBA.cs b/src/Utilities/TestBA/TestBA.cs new file mode 100644 index 00000000..e3305d33 --- /dev/null +++ b/src/Utilities/TestBA/TestBA.cs | |||
@@ -0,0 +1,469 @@ | |||
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 WixToolset.Test.BA | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using System.IO; | ||
8 | using System.Linq; | ||
9 | using System.Threading; | ||
10 | using System.Windows.Forms; | ||
11 | using Microsoft.Win32; | ||
12 | using WixToolset.Mba.Core; | ||
13 | |||
14 | /// <summary> | ||
15 | /// A minimal UX used for testing. | ||
16 | /// </summary> | ||
17 | public class TestBA : BootstrapperApplication | ||
18 | { | ||
19 | private const string BurnBundleVersionVariable = "WixBundleVersion"; | ||
20 | |||
21 | private ApplicationContext appContext; | ||
22 | private Form dummyWindow; | ||
23 | private LaunchAction action; | ||
24 | private int result; | ||
25 | |||
26 | private string updateBundlePath; | ||
27 | |||
28 | private int sleepDuringCache; | ||
29 | private int cancelCacheAtProgress; | ||
30 | private int sleepDuringExecute; | ||
31 | private int cancelExecuteAtProgress; | ||
32 | private int retryExecuteFilesInUse; | ||
33 | |||
34 | private IBootstrapperCommand Command { get; } | ||
35 | |||
36 | private IEngine Engine => this.engine; | ||
37 | |||
38 | /// <summary> | ||
39 | /// Initializes test user experience. | ||
40 | /// </summary> | ||
41 | public TestBA(IEngine engine, IBootstrapperCommand bootstrapperCommand) | ||
42 | : base(engine) | ||
43 | { | ||
44 | this.Command = bootstrapperCommand; | ||
45 | } | ||
46 | |||
47 | /// <summary> | ||
48 | /// Get the version of the install. | ||
49 | /// </summary> | ||
50 | public string Version { get; private set; } | ||
51 | |||
52 | /// <summary> | ||
53 | /// Indicates if DetectUpdate found a newer version to update. | ||
54 | /// </summary> | ||
55 | private bool UpdateAvailable { get; set; } | ||
56 | |||
57 | /// <summary> | ||
58 | /// UI Thread entry point for TestUX. | ||
59 | /// </summary> | ||
60 | protected override void Run() | ||
61 | { | ||
62 | this.action = this.Command.Action; | ||
63 | this.TestVariables(); | ||
64 | |||
65 | this.Version = this.engine.GetVariableVersion(BurnBundleVersionVariable); | ||
66 | this.Log("Version: {0}", this.Version); | ||
67 | |||
68 | List<string> verifyArguments = this.ReadVerifyArguments(); | ||
69 | |||
70 | foreach (string arg in this.Command.CommandLineArgs) | ||
71 | { | ||
72 | // If we're not in the update already, process the updatebundle. | ||
73 | if (this.Command.Relation != RelationType.Update && arg.StartsWith("-updatebundle:", StringComparison.OrdinalIgnoreCase)) | ||
74 | { | ||
75 | this.updateBundlePath = arg.Substring(14); | ||
76 | FileInfo info = new FileInfo(this.updateBundlePath); | ||
77 | this.Engine.SetUpdate(this.updateBundlePath, null, info.Length, UpdateHashType.None, null); | ||
78 | this.UpdateAvailable = true; | ||
79 | this.action = LaunchAction.UpdateReplaceEmbedded; | ||
80 | } | ||
81 | else if (this.Command.Relation != RelationType.Update && arg.StartsWith("-checkupdate", StringComparison.OrdinalIgnoreCase)) | ||
82 | { | ||
83 | this.action = LaunchAction.UpdateReplace; | ||
84 | } | ||
85 | |||
86 | verifyArguments.Remove(arg); | ||
87 | } | ||
88 | this.Log("Action: {0}", this.action); | ||
89 | |||
90 | // If there are any verification arguments left, error out. | ||
91 | if (0 < verifyArguments.Count) | ||
92 | { | ||
93 | foreach (string expectedArg in verifyArguments) | ||
94 | { | ||
95 | this.Log("Failure. Expected command-line to have argument: {0}", expectedArg); | ||
96 | } | ||
97 | |||
98 | this.Engine.Quit(-1); | ||
99 | return; | ||
100 | } | ||
101 | |||
102 | this.dummyWindow = new Form(); | ||
103 | this.dummyWindow.CreateControl(); | ||
104 | this.appContext = new ApplicationContext(); | ||
105 | |||
106 | int redetectCount = 0; | ||
107 | string redetect = this.ReadPackageAction(null, "RedetectCount"); | ||
108 | if (String.IsNullOrEmpty(redetect) || !Int32.TryParse(redetect, out redetectCount)) | ||
109 | { | ||
110 | redetectCount = 0; | ||
111 | } | ||
112 | |||
113 | do | ||
114 | { | ||
115 | this.Engine.Detect(); | ||
116 | this.Log("Completed detection phase: {0} re-runs remaining", redetectCount); | ||
117 | } while (0 < redetectCount--); | ||
118 | |||
119 | Application.Run(this.appContext); | ||
120 | this.Engine.Quit(this.result & 0xFFFF); // return plain old Win32 error, not HRESULT. | ||
121 | } | ||
122 | |||
123 | protected override void OnDetectUpdateBegin(DetectUpdateBeginEventArgs args) | ||
124 | { | ||
125 | this.Log("OnDetectUpdateBegin"); | ||
126 | if ((LaunchAction.UpdateReplaceEmbedded == this.action)|(LaunchAction.UpdateReplace == this.action)) | ||
127 | { | ||
128 | args.Skip = false; | ||
129 | } | ||
130 | } | ||
131 | |||
132 | protected override void OnDetectUpdate(DetectUpdateEventArgs e) | ||
133 | { | ||
134 | // The list of updates is sorted in descending version, so the first callback should be the largest update available. | ||
135 | // This update should be either larger than ours (so we are out of date), the same as ours (so we are current) | ||
136 | // or smaller than ours (we have a private build). If we really wanted to, we could leave the e.StopProcessingUpdates alone and | ||
137 | // enumerate all of the updates. | ||
138 | this.Log(String.Format("Potential update v{0} from '{1}'; current version: v{2}", e.Version, e.UpdateLocation, this.Version)); | ||
139 | if (this.Engine.CompareVersions(e.Version, this.Version) > 0) | ||
140 | { | ||
141 | this.Log(String.Format("Selected update v{0}", e.Version)); | ||
142 | this.Engine.SetUpdate(null, e.UpdateLocation, e.Size, UpdateHashType.None, null); | ||
143 | this.UpdateAvailable = true; | ||
144 | } | ||
145 | else | ||
146 | { | ||
147 | this.UpdateAvailable = false; | ||
148 | } | ||
149 | e.StopProcessingUpdates = true; | ||
150 | } | ||
151 | |||
152 | protected override void OnDetectUpdateComplete(DetectUpdateCompleteEventArgs e) | ||
153 | { | ||
154 | this.Log("OnDetectUpdateComplete"); | ||
155 | |||
156 | // Failed to process an update, allow the existing bundle to still install. | ||
157 | if (!Hresult.Succeeded(e.Status)) | ||
158 | { | ||
159 | this.Log(String.Format("Failed to locate an update, status of 0x{0:X8}, updates disabled.", e.Status)); | ||
160 | e.IgnoreError = true; // But continue on... | ||
161 | } | ||
162 | } | ||
163 | |||
164 | protected override void OnDetectComplete(DetectCompleteEventArgs args) | ||
165 | { | ||
166 | this.result = args.Status; | ||
167 | |||
168 | if (Hresult.Succeeded(this.result) && (this.UpdateAvailable | (!((LaunchAction.UpdateReplaceEmbedded == this.action) | (LaunchAction.UpdateReplace == this.action))))) | ||
169 | { | ||
170 | this.Engine.Plan(this.action); | ||
171 | } | ||
172 | else | ||
173 | { | ||
174 | this.appContext.ExitThread(); | ||
175 | } | ||
176 | } | ||
177 | |||
178 | protected override void OnPlanPackageBegin(PlanPackageBeginEventArgs args) | ||
179 | { | ||
180 | RequestState state; | ||
181 | string action = this.ReadPackageAction(args.PackageId, "Requested"); | ||
182 | if (TryParseEnum<RequestState>(action, out state)) | ||
183 | { | ||
184 | args.State = state; | ||
185 | } | ||
186 | } | ||
187 | |||
188 | protected override void OnPlanTargetMsiPackage(PlanTargetMsiPackageEventArgs args) | ||
189 | { | ||
190 | RequestState state; | ||
191 | string action = this.ReadPackageAction(args.PackageId, "Requested"); | ||
192 | if (TryParseEnum<RequestState>(action, out state)) | ||
193 | { | ||
194 | args.State = state; | ||
195 | } | ||
196 | } | ||
197 | |||
198 | protected override void OnPlanMsiFeature(PlanMsiFeatureEventArgs args) | ||
199 | { | ||
200 | FeatureState state; | ||
201 | string action = this.ReadFeatureAction(args.PackageId, args.FeatureId, "Requested"); | ||
202 | if (TryParseEnum<FeatureState>(action, out state)) | ||
203 | { | ||
204 | args.State = state; | ||
205 | } | ||
206 | } | ||
207 | |||
208 | protected override void OnPlanComplete(PlanCompleteEventArgs args) | ||
209 | { | ||
210 | this.result = args.Status; | ||
211 | if (Hresult.Succeeded(this.result)) | ||
212 | { | ||
213 | this.Engine.Apply(this.dummyWindow.Handle); | ||
214 | } | ||
215 | else | ||
216 | { | ||
217 | this.appContext.ExitThread(); | ||
218 | } | ||
219 | } | ||
220 | |||
221 | protected override void OnCachePackageBegin(CachePackageBeginEventArgs args) | ||
222 | { | ||
223 | this.Log("OnCachePackageBegin() - package: {0}, payloads to cache: {1}", args.PackageId, args.CachePayloads); | ||
224 | |||
225 | string slowProgress = this.ReadPackageAction(args.PackageId, "SlowCache"); | ||
226 | if (String.IsNullOrEmpty(slowProgress) || !Int32.TryParse(slowProgress, out this.sleepDuringCache)) | ||
227 | { | ||
228 | this.sleepDuringCache = 0; | ||
229 | } | ||
230 | |||
231 | string cancelCache = this.ReadPackageAction(args.PackageId, "CancelCacheAtProgress"); | ||
232 | if (String.IsNullOrEmpty(cancelCache) || !Int32.TryParse(cancelCache, out this.cancelCacheAtProgress)) | ||
233 | { | ||
234 | this.cancelCacheAtProgress = -1; | ||
235 | } | ||
236 | } | ||
237 | |||
238 | protected override void OnCacheAcquireProgress(CacheAcquireProgressEventArgs args) | ||
239 | { | ||
240 | this.Log("OnCacheAcquireProgress() - container/package: {0}, payload: {1}, progress: {2}, total: {3}, overall progress: {4}%", args.PackageOrContainerId, args.PayloadId, args.Progress, args.Total, args.OverallPercentage); | ||
241 | |||
242 | if (this.cancelCacheAtProgress > 0 && this.cancelCacheAtProgress <= args.Progress) | ||
243 | { | ||
244 | args.Cancel = true; | ||
245 | } | ||
246 | else if (this.sleepDuringCache > 0) | ||
247 | { | ||
248 | Thread.Sleep(this.sleepDuringCache); | ||
249 | } | ||
250 | } | ||
251 | |||
252 | protected override void OnExecutePackageBegin(ExecutePackageBeginEventArgs args) | ||
253 | { | ||
254 | this.Log("OnExecutePackageBegin() - package: {0}, rollback: {1}", args.PackageId, !args.ShouldExecute); | ||
255 | |||
256 | string slowProgress = this.ReadPackageAction(args.PackageId, "SlowExecute"); | ||
257 | if (String.IsNullOrEmpty(slowProgress) || !Int32.TryParse(slowProgress, out this.sleepDuringExecute)) | ||
258 | { | ||
259 | this.sleepDuringExecute = 0; | ||
260 | } | ||
261 | |||
262 | string cancelExecute = this.ReadPackageAction(args.PackageId, "CancelExecuteAtProgress"); | ||
263 | if (String.IsNullOrEmpty(cancelExecute) || !Int32.TryParse(cancelExecute, out this.cancelExecuteAtProgress)) | ||
264 | { | ||
265 | this.cancelExecuteAtProgress = -1; | ||
266 | } | ||
267 | |||
268 | string retryBeforeCancel = this.ReadPackageAction(args.PackageId, "RetryExecuteFilesInUse"); | ||
269 | if (String.IsNullOrEmpty(retryBeforeCancel) || !Int32.TryParse(retryBeforeCancel, out this.retryExecuteFilesInUse)) | ||
270 | { | ||
271 | this.retryExecuteFilesInUse = 0; | ||
272 | } | ||
273 | } | ||
274 | |||
275 | protected override void OnExecuteFilesInUse(ExecuteFilesInUseEventArgs args) | ||
276 | { | ||
277 | this.Log("OnExecuteFilesInUse() - package: {0}, retries remaining: {1}, data: {2}", args.PackageId, this.retryExecuteFilesInUse, String.Join(", ", args.Files.ToArray())); | ||
278 | |||
279 | if (this.retryExecuteFilesInUse > 0) | ||
280 | { | ||
281 | --this.retryExecuteFilesInUse; | ||
282 | args.Result = Result.Retry; | ||
283 | } | ||
284 | else | ||
285 | { | ||
286 | args.Result = Result.Abort; | ||
287 | } | ||
288 | } | ||
289 | |||
290 | protected override void OnExecuteProgress(ExecuteProgressEventArgs args) | ||
291 | { | ||
292 | this.Log("OnExecuteProgress() - package: {0}, progress: {1}%, overall progress: {2}%", args.PackageId, args.ProgressPercentage, args.OverallPercentage); | ||
293 | |||
294 | if (this.cancelExecuteAtProgress > 0 && this.cancelExecuteAtProgress <= args.ProgressPercentage) | ||
295 | { | ||
296 | args.Cancel = true; | ||
297 | } | ||
298 | else if (this.sleepDuringExecute > 0) | ||
299 | { | ||
300 | Thread.Sleep(this.sleepDuringExecute); | ||
301 | } | ||
302 | } | ||
303 | |||
304 | protected override void OnExecutePatchTarget(ExecutePatchTargetEventArgs args) | ||
305 | { | ||
306 | this.Log("OnExecutePatchTarget - Patch Package: {0}, Target Product Code: {1}", args.PackageId, args.TargetProductCode); | ||
307 | } | ||
308 | |||
309 | protected override void OnProgress(ProgressEventArgs args) | ||
310 | { | ||
311 | this.Log("OnProgress() - progress: {0}%, overall progress: {1}%", args.ProgressPercentage, args.OverallPercentage); | ||
312 | if (this.Command.Display == Display.Embedded) | ||
313 | { | ||
314 | this.Engine.SendEmbeddedProgress(args.ProgressPercentage, args.OverallPercentage); | ||
315 | } | ||
316 | } | ||
317 | |||
318 | protected override void OnResolveSource(ResolveSourceEventArgs args) | ||
319 | { | ||
320 | if (!String.IsNullOrEmpty(args.DownloadSource)) | ||
321 | { | ||
322 | args.Action = BOOTSTRAPPER_RESOLVESOURCE_ACTION.Download; | ||
323 | } | ||
324 | } | ||
325 | |||
326 | protected override void OnApplyComplete(ApplyCompleteEventArgs args) | ||
327 | { | ||
328 | // Output what the privileges are now. | ||
329 | this.Log("After elevation: WixBundleElevated = {0}", this.Engine.GetVariableNumeric("WixBundleElevated")); | ||
330 | |||
331 | this.result = args.Status; | ||
332 | this.appContext.ExitThread(); | ||
333 | } | ||
334 | |||
335 | protected override void OnSystemShutdown(SystemShutdownEventArgs args) | ||
336 | { | ||
337 | // Always prevent shutdown. | ||
338 | this.Log("Disallowed system request to shut down the bootstrapper application."); | ||
339 | args.Cancel = true; | ||
340 | |||
341 | this.appContext.ExitThread(); | ||
342 | } | ||
343 | |||
344 | private void TestVariables() | ||
345 | { | ||
346 | // First make sure we can check and get standard variables of each type. | ||
347 | { | ||
348 | string value = null; | ||
349 | if (this.Engine.ContainsVariable("WindowsFolder")) | ||
350 | { | ||
351 | value = this.Engine.GetVariableString("WindowsFolder"); | ||
352 | this.Engine.Log(LogLevel.Verbose, "TEST: Successfully retrieved a string variable: WindowsFolder"); | ||
353 | } | ||
354 | else | ||
355 | { | ||
356 | throw new Exception("Engine did not define a standard variable: WindowsFolder"); | ||
357 | } | ||
358 | } | ||
359 | |||
360 | { | ||
361 | long value = 0; | ||
362 | if (this.Engine.ContainsVariable("NTProductType")) | ||
363 | { | ||
364 | value = this.Engine.GetVariableNumeric("NTProductType"); | ||
365 | this.Engine.Log(LogLevel.Verbose, "TEST: Successfully retrieved a numeric variable: NTProductType"); | ||
366 | } | ||
367 | else | ||
368 | { | ||
369 | throw new Exception("Engine did not define a standard variable: NTProductType"); | ||
370 | } | ||
371 | } | ||
372 | |||
373 | { | ||
374 | string value = null; | ||
375 | if (this.Engine.ContainsVariable("VersionMsi")) | ||
376 | { | ||
377 | value = this.Engine.GetVariableVersion("VersionMsi"); | ||
378 | this.Engine.Log(LogLevel.Verbose, "TEST: Successfully retrieved a version variable: VersionMsi"); | ||
379 | } | ||
380 | else | ||
381 | { | ||
382 | throw new Exception("Engine did not define a standard variable: VersionMsi"); | ||
383 | } | ||
384 | } | ||
385 | |||
386 | // Now validate that Contians returns false for non-existant variables of each type. | ||
387 | if (this.Engine.ContainsVariable("TestStringVariableShouldNotExist")) | ||
388 | { | ||
389 | throw new Exception("Engine defined a variable that should not exist: TestStringVariableShouldNotExist"); | ||
390 | } | ||
391 | else | ||
392 | { | ||
393 | this.Engine.Log(LogLevel.Verbose, "TEST: Successfully checked for non-existent string variable: TestStringVariableShouldNotExist"); | ||
394 | } | ||
395 | |||
396 | if (this.Engine.ContainsVariable("TestNumericVariableShouldNotExist")) | ||
397 | { | ||
398 | throw new Exception("Engine defined a variable that should not exist: TestNumericVariableShouldNotExist"); | ||
399 | } | ||
400 | else | ||
401 | { | ||
402 | this.Engine.Log(LogLevel.Verbose, "TEST: Successfully checked for non-existent numeric variable: TestNumericVariableShouldNotExist"); | ||
403 | } | ||
404 | |||
405 | if (this.Engine.ContainsVariable("TestVersionVariableShouldNotExist")) | ||
406 | { | ||
407 | throw new Exception("Engine defined a variable that should not exist: TestVersionVariableShouldNotExist"); | ||
408 | } | ||
409 | else | ||
410 | { | ||
411 | this.Engine.Log(LogLevel.Verbose, "TEST: Successfully checked for non-existent version variable: TestVersionVariableShouldNotExist"); | ||
412 | } | ||
413 | |||
414 | // Output what the initially run privileges were. | ||
415 | this.Engine.Log(LogLevel.Verbose, String.Format("TEST: WixBundleElevated = {0}", this.Engine.GetVariableNumeric("WixBundleElevated"))); | ||
416 | } | ||
417 | |||
418 | private void Log(string format, params object[] args) | ||
419 | { | ||
420 | string relation = this.Command.Relation != RelationType.None ? String.Concat(" (", this.Command.Relation.ToString().ToLowerInvariant(), ")") : String.Empty; | ||
421 | string message = String.Format(format, args); | ||
422 | |||
423 | this.Engine.Log(LogLevel.Standard, String.Concat("TESTBA", relation, ": ", message)); | ||
424 | } | ||
425 | |||
426 | private List<string> ReadVerifyArguments() | ||
427 | { | ||
428 | string testName = this.Engine.GetVariableString("TestGroupName"); | ||
429 | using (RegistryKey testKey = Registry.LocalMachine.OpenSubKey(String.Format(@"Software\WiX\Tests\TestBAControl\{0}", testName))) | ||
430 | { | ||
431 | string verifyArguments = testKey == null ? null : testKey.GetValue("VerifyArguments") as string; | ||
432 | return verifyArguments == null ? new List<string>() : new List<string>(verifyArguments.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)); | ||
433 | } | ||
434 | } | ||
435 | |||
436 | private string ReadPackageAction(string packageId, string state) | ||
437 | { | ||
438 | string testName = this.Engine.GetVariableString("TestGroupName"); | ||
439 | using (RegistryKey testKey = Registry.LocalMachine.OpenSubKey(String.Format(@"Software\WiX\Tests\TestBAControl\{0}\{1}", testName, String.IsNullOrEmpty(packageId) ? String.Empty : packageId))) | ||
440 | { | ||
441 | return testKey == null ? null : testKey.GetValue(state) as string; | ||
442 | } | ||
443 | } | ||
444 | |||
445 | private string ReadFeatureAction(string packageId, string featureId, string state) | ||
446 | { | ||
447 | string testName = this.Engine.GetVariableString("TestGroupName"); | ||
448 | using (RegistryKey testKey = Registry.LocalMachine.OpenSubKey(String.Format(@"Software\WiX\Tests\TestBAControl\{0}\{1}", testName, packageId))) | ||
449 | { | ||
450 | string registryName = String.Concat(featureId, state); | ||
451 | return testKey == null ? null : testKey.GetValue(registryName) as string; | ||
452 | } | ||
453 | } | ||
454 | |||
455 | private static bool TryParseEnum<T>(string value, out T t) | ||
456 | { | ||
457 | try | ||
458 | { | ||
459 | t = (T)Enum.Parse(typeof(T), value, true); | ||
460 | return true; | ||
461 | } | ||
462 | catch (ArgumentException) { } | ||
463 | catch (OverflowException) { } | ||
464 | |||
465 | t = default(T); | ||
466 | return false; | ||
467 | } | ||
468 | } | ||
469 | } | ||
diff --git a/src/Utilities/TestBA/TestBA.csproj b/src/Utilities/TestBA/TestBA.csproj new file mode 100644 index 00000000..ad7a59b4 --- /dev/null +++ b/src/Utilities/TestBA/TestBA.csproj | |||
@@ -0,0 +1,24 @@ | |||
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>net35</TargetFramework> | ||
7 | <AssemblyName>TestBA</AssemblyName> | ||
8 | <RootNamespace>WixToolset.Test.BA</RootNamespace> | ||
9 | <DebugType>embedded</DebugType> | ||
10 | <RuntimeIdentifier>win-x86</RuntimeIdentifier> | ||
11 | </PropertyGroup> | ||
12 | |||
13 | <ItemGroup> | ||
14 | <Content Include="TestBA.BootstrapperCore.config" CopyToOutputDirectory="PreserveNewest" /> | ||
15 | </ItemGroup> | ||
16 | |||
17 | <ItemGroup> | ||
18 | <PackageReference Include="WixToolset.Mba.Core" Version="4.0.41" /> | ||
19 | </ItemGroup> | ||
20 | |||
21 | <ItemGroup> | ||
22 | <Reference Include="System.Windows.Forms" /> | ||
23 | </ItemGroup> | ||
24 | </Project> \ No newline at end of file | ||
diff --git a/src/Utilities/TestBA/TestBAFactory.cs b/src/Utilities/TestBA/TestBAFactory.cs new file mode 100644 index 00000000..ba1de367 --- /dev/null +++ b/src/Utilities/TestBA/TestBAFactory.cs | |||
@@ -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 | [assembly: WixToolset.Mba.Core.BootstrapperApplicationFactory(typeof(WixToolset.Test.BA.TestBAFactory))] | ||
4 | namespace WixToolset.Test.BA | ||
5 | { | ||
6 | using WixToolset.Mba.Core; | ||
7 | |||
8 | public class TestBAFactory : BaseBootstrapperApplicationFactory | ||
9 | { | ||
10 | private static int loadCount = 0; | ||
11 | |||
12 | protected override IBootstrapperApplication Create(IEngine engine, IBootstrapperCommand bootstrapperCommand) | ||
13 | { | ||
14 | if (loadCount > 0) | ||
15 | { | ||
16 | engine.Log(LogLevel.Standard, $"Reloaded {loadCount} time(s)"); | ||
17 | } | ||
18 | ++loadCount; | ||
19 | return new TestBA(engine, bootstrapperCommand); | ||
20 | } | ||
21 | } | ||
22 | } | ||