diff options
Diffstat (limited to 'src/internal/WixBuildTools.TestSupport')
17 files changed, 974 insertions, 0 deletions
diff --git a/src/internal/WixBuildTools.TestSupport/Builder.cs b/src/internal/WixBuildTools.TestSupport/Builder.cs new file mode 100644 index 00000000..ef0de8c9 --- /dev/null +++ b/src/internal/WixBuildTools.TestSupport/Builder.cs | |||
| @@ -0,0 +1,70 @@ | |||
| 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 WixBuildTools.TestSupport | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Collections.Generic; | ||
| 7 | using System.IO; | ||
| 8 | |||
| 9 | public class Builder | ||
| 10 | { | ||
| 11 | public Builder(string sourceFolder, Type extensionType = null, string[] bindPaths = null, string outputFile = null) | ||
| 12 | { | ||
| 13 | this.SourceFolder = sourceFolder; | ||
| 14 | this.ExtensionType = extensionType; | ||
| 15 | this.BindPaths = bindPaths; | ||
| 16 | this.OutputFile = outputFile ?? "test.msi"; | ||
| 17 | } | ||
| 18 | |||
| 19 | public string[] BindPaths { get; set; } | ||
| 20 | |||
| 21 | public Type ExtensionType { get; set; } | ||
| 22 | |||
| 23 | public string OutputFile { get; set; } | ||
| 24 | |||
| 25 | public string SourceFolder { get; } | ||
| 26 | |||
| 27 | public string[] BuildAndQuery(Action<string[]> buildFunc, params string[] tables) | ||
| 28 | { | ||
| 29 | var sourceFiles = Directory.GetFiles(this.SourceFolder, "*.wxs"); | ||
| 30 | var wxlFiles = Directory.GetFiles(this.SourceFolder, "*.wxl"); | ||
| 31 | |||
| 32 | using (var fs = new DisposableFileSystem()) | ||
| 33 | { | ||
| 34 | var intermediateFolder = fs.GetFolder(); | ||
| 35 | var outputPath = Path.Combine(intermediateFolder, "bin", this.OutputFile); | ||
| 36 | |||
| 37 | var args = new List<string> | ||
| 38 | { | ||
| 39 | "build", | ||
| 40 | "-o", outputPath, | ||
| 41 | "-intermediateFolder", intermediateFolder, | ||
| 42 | }; | ||
| 43 | |||
| 44 | if (this.ExtensionType != null) | ||
| 45 | { | ||
| 46 | args.Add("-ext"); | ||
| 47 | args.Add(Path.GetFullPath(new Uri(this.ExtensionType.Assembly.CodeBase).LocalPath)); | ||
| 48 | } | ||
| 49 | |||
| 50 | args.AddRange(sourceFiles); | ||
| 51 | |||
| 52 | foreach (var wxlFile in wxlFiles) | ||
| 53 | { | ||
| 54 | args.Add("-loc"); | ||
| 55 | args.Add(wxlFile); | ||
| 56 | } | ||
| 57 | |||
| 58 | foreach (var bindPath in this.BindPaths) | ||
| 59 | { | ||
| 60 | args.Add("-bindpath"); | ||
| 61 | args.Add(bindPath); | ||
| 62 | } | ||
| 63 | |||
| 64 | buildFunc(args.ToArray()); | ||
| 65 | |||
| 66 | return Query.QueryDatabase(outputPath, tables); | ||
| 67 | } | ||
| 68 | } | ||
| 69 | } | ||
| 70 | } | ||
diff --git a/src/internal/WixBuildTools.TestSupport/DisposableFileSystem.cs b/src/internal/WixBuildTools.TestSupport/DisposableFileSystem.cs new file mode 100644 index 00000000..f096db72 --- /dev/null +++ b/src/internal/WixBuildTools.TestSupport/DisposableFileSystem.cs | |||
| @@ -0,0 +1,93 @@ | |||
| 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 WixBuildTools.TestSupport | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Collections.Generic; | ||
| 7 | using System.IO; | ||
| 8 | |||
| 9 | public class DisposableFileSystem : IDisposable | ||
| 10 | { | ||
| 11 | protected bool Disposed { get; private set; } | ||
| 12 | |||
| 13 | private List<string> CleanupPaths { get; } = new List<string>(); | ||
| 14 | |||
| 15 | public bool Keep { get; } | ||
| 16 | |||
| 17 | public DisposableFileSystem(bool keep = false) | ||
| 18 | { | ||
| 19 | this.Keep = keep; | ||
| 20 | } | ||
| 21 | |||
| 22 | protected string GetFile(bool create = false) | ||
| 23 | { | ||
| 24 | var path = Path.GetTempFileName(); | ||
| 25 | |||
| 26 | if (!create) | ||
| 27 | { | ||
| 28 | File.Delete(path); | ||
| 29 | } | ||
| 30 | |||
| 31 | this.CleanupPaths.Add(path); | ||
| 32 | |||
| 33 | return path; | ||
| 34 | } | ||
| 35 | |||
| 36 | public string GetFolder(bool create = false) | ||
| 37 | { | ||
| 38 | var path = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); | ||
| 39 | |||
| 40 | if (create) | ||
| 41 | { | ||
| 42 | Directory.CreateDirectory(path); | ||
| 43 | } | ||
| 44 | |||
| 45 | this.CleanupPaths.Add(path); | ||
| 46 | |||
| 47 | return path; | ||
| 48 | } | ||
| 49 | |||
| 50 | |||
| 51 | #region // IDisposable | ||
| 52 | |||
| 53 | public void Dispose() | ||
| 54 | { | ||
| 55 | this.Dispose(true); | ||
| 56 | GC.SuppressFinalize(this); | ||
| 57 | } | ||
| 58 | |||
| 59 | protected virtual void Dispose(bool disposing) | ||
| 60 | { | ||
| 61 | if (this.Disposed) | ||
| 62 | { | ||
| 63 | return; | ||
| 64 | } | ||
| 65 | |||
| 66 | if (disposing && !this.Keep) | ||
| 67 | { | ||
| 68 | foreach (var path in this.CleanupPaths) | ||
| 69 | { | ||
| 70 | try | ||
| 71 | { | ||
| 72 | if (File.Exists(path)) | ||
| 73 | { | ||
| 74 | File.Delete(path); | ||
| 75 | } | ||
| 76 | else if (Directory.Exists(path)) | ||
| 77 | { | ||
| 78 | Directory.Delete(path, true); | ||
| 79 | } | ||
| 80 | } | ||
| 81 | catch | ||
| 82 | { | ||
| 83 | // Best effort delete, so ignore any failures. | ||
| 84 | } | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | this.Disposed = true; | ||
| 89 | } | ||
| 90 | |||
| 91 | #endregion | ||
| 92 | } | ||
| 93 | } | ||
diff --git a/src/internal/WixBuildTools.TestSupport/DotnetRunner.cs b/src/internal/WixBuildTools.TestSupport/DotnetRunner.cs new file mode 100644 index 00000000..82391178 --- /dev/null +++ b/src/internal/WixBuildTools.TestSupport/DotnetRunner.cs | |||
| @@ -0,0 +1,57 @@ | |||
| 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 WixBuildTools.TestSupport | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Collections.Generic; | ||
| 7 | using System.IO; | ||
| 8 | |||
| 9 | public class DotnetRunner : ExternalExecutable | ||
| 10 | { | ||
| 11 | private static readonly object InitLock = new object(); | ||
| 12 | private static bool Initialized; | ||
| 13 | private static DotnetRunner Instance; | ||
| 14 | |||
| 15 | public static ExternalExecutableResult Execute(string command, string[] arguments = null) => | ||
| 16 | InitAndExecute(command, arguments); | ||
| 17 | |||
| 18 | private static ExternalExecutableResult InitAndExecute(string command, string[] arguments) | ||
| 19 | { | ||
| 20 | lock (InitLock) | ||
| 21 | { | ||
| 22 | if (!Initialized) | ||
| 23 | { | ||
| 24 | Initialized = true; | ||
| 25 | var dotnetPath = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH"); | ||
| 26 | if (String.IsNullOrEmpty(dotnetPath) || !File.Exists(dotnetPath)) | ||
| 27 | { | ||
| 28 | dotnetPath = "dotnet"; | ||
| 29 | } | ||
| 30 | |||
| 31 | Instance = new DotnetRunner(dotnetPath); | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | return Instance.ExecuteCore(command, arguments); | ||
| 36 | } | ||
| 37 | |||
| 38 | private DotnetRunner(string exePath) : base(exePath) { } | ||
| 39 | |||
| 40 | private ExternalExecutableResult ExecuteCore(string command, string[] arguments) | ||
| 41 | { | ||
| 42 | var total = new List<string> | ||
| 43 | { | ||
| 44 | command, | ||
| 45 | }; | ||
| 46 | |||
| 47 | if (arguments != null) | ||
| 48 | { | ||
| 49 | total.AddRange(arguments); | ||
| 50 | } | ||
| 51 | |||
| 52 | var args = CombineArguments(total); | ||
| 53 | var mergeErrorIntoOutput = true; | ||
| 54 | return this.Run(args, mergeErrorIntoOutput); | ||
| 55 | } | ||
| 56 | } | ||
| 57 | } | ||
diff --git a/src/internal/WixBuildTools.TestSupport/ExternalExecutable.cs b/src/internal/WixBuildTools.TestSupport/ExternalExecutable.cs new file mode 100644 index 00000000..eb07aa13 --- /dev/null +++ b/src/internal/WixBuildTools.TestSupport/ExternalExecutable.cs | |||
| @@ -0,0 +1,88 @@ | |||
| 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 WixBuildTools.TestSupport | ||
| 4 | { | ||
| 5 | using System.Collections.Concurrent; | ||
| 6 | using System.Collections.Generic; | ||
| 7 | using System.Diagnostics; | ||
| 8 | using System.IO; | ||
| 9 | using System.Text; | ||
| 10 | |||
| 11 | public abstract class ExternalExecutable | ||
| 12 | { | ||
| 13 | private readonly string exePath; | ||
| 14 | |||
| 15 | protected ExternalExecutable(string exePath) | ||
| 16 | { | ||
| 17 | this.exePath = exePath; | ||
| 18 | } | ||
| 19 | |||
| 20 | protected ExternalExecutableResult Run(string args, bool mergeErrorIntoOutput = false, string workingDirectory = null) | ||
| 21 | { | ||
| 22 | var startInfo = new ProcessStartInfo(this.exePath, args) | ||
| 23 | { | ||
| 24 | CreateNoWindow = true, | ||
| 25 | RedirectStandardError = true, | ||
| 26 | RedirectStandardOutput = true, | ||
| 27 | UseShellExecute = false, | ||
| 28 | WorkingDirectory = workingDirectory ?? Path.GetDirectoryName(this.exePath), | ||
| 29 | }; | ||
| 30 | |||
| 31 | using (var process = Process.Start(startInfo)) | ||
| 32 | { | ||
| 33 | // This implementation of merging the streams does not guarantee that lines are retrieved in the same order that they were written. | ||
| 34 | // If the process is simultaneously writing to both streams, this is impossible to do anyway. | ||
| 35 | var standardOutput = new ConcurrentQueue<string>(); | ||
| 36 | var standardError = mergeErrorIntoOutput ? standardOutput : new ConcurrentQueue<string>(); | ||
| 37 | |||
| 38 | process.ErrorDataReceived += (s, e) => { if (e.Data != null) { standardError.Enqueue(e.Data); } }; | ||
| 39 | process.OutputDataReceived += (s, e) => { if (e.Data != null) { standardOutput.Enqueue(e.Data); } }; | ||
| 40 | |||
| 41 | process.BeginErrorReadLine(); | ||
| 42 | process.BeginOutputReadLine(); | ||
| 43 | |||
| 44 | process.WaitForExit(); | ||
| 45 | |||
| 46 | return new ExternalExecutableResult | ||
| 47 | { | ||
| 48 | ExitCode = process.ExitCode, | ||
| 49 | StandardError = mergeErrorIntoOutput ? null : standardError.ToArray(), | ||
| 50 | StandardOutput = standardOutput.ToArray(), | ||
| 51 | StartInfo = startInfo, | ||
| 52 | }; | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | // This is internal because it assumes backslashes aren't used as escape characters and there aren't any double quotes. | ||
| 57 | internal static string CombineArguments(IEnumerable<string> arguments) | ||
| 58 | { | ||
| 59 | if (arguments == null) | ||
| 60 | { | ||
| 61 | return null; | ||
| 62 | } | ||
| 63 | |||
| 64 | var sb = new StringBuilder(); | ||
| 65 | |||
| 66 | foreach (var arg in arguments) | ||
| 67 | { | ||
| 68 | if (sb.Length > 0) | ||
| 69 | { | ||
| 70 | sb.Append(' '); | ||
| 71 | } | ||
| 72 | |||
| 73 | if (arg.IndexOf(' ') > -1) | ||
| 74 | { | ||
| 75 | sb.Append("\""); | ||
| 76 | sb.Append(arg); | ||
| 77 | sb.Append("\""); | ||
| 78 | } | ||
| 79 | else | ||
| 80 | { | ||
| 81 | sb.Append(arg); | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | return sb.ToString(); | ||
| 86 | } | ||
| 87 | } | ||
| 88 | } | ||
diff --git a/src/internal/WixBuildTools.TestSupport/ExternalExecutableResult.cs b/src/internal/WixBuildTools.TestSupport/ExternalExecutableResult.cs new file mode 100644 index 00000000..19b5183b --- /dev/null +++ b/src/internal/WixBuildTools.TestSupport/ExternalExecutableResult.cs | |||
| @@ -0,0 +1,17 @@ | |||
| 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 WixBuildTools.TestSupport | ||
| 4 | { | ||
| 5 | using System.Diagnostics; | ||
| 6 | |||
| 7 | public class ExternalExecutableResult | ||
| 8 | { | ||
| 9 | public int ExitCode { get; set; } | ||
| 10 | |||
| 11 | public string[] StandardError { get; set; } | ||
| 12 | |||
| 13 | public string[] StandardOutput { get; set; } | ||
| 14 | |||
| 15 | public ProcessStartInfo StartInfo { get; set; } | ||
| 16 | } | ||
| 17 | } | ||
diff --git a/src/internal/WixBuildTools.TestSupport/FakeBuildEngine.cs b/src/internal/WixBuildTools.TestSupport/FakeBuildEngine.cs new file mode 100644 index 00000000..20545970 --- /dev/null +++ b/src/internal/WixBuildTools.TestSupport/FakeBuildEngine.cs | |||
| @@ -0,0 +1,33 @@ | |||
| 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 WixBuildTools.TestSupport | ||
| 4 | { | ||
| 5 | using System.Collections; | ||
| 6 | using System.Text; | ||
| 7 | using Microsoft.Build.Framework; | ||
| 8 | |||
| 9 | public class FakeBuildEngine : IBuildEngine | ||
| 10 | { | ||
| 11 | private readonly StringBuilder output = new StringBuilder(); | ||
| 12 | |||
| 13 | public int ColumnNumberOfTaskNode => 0; | ||
| 14 | |||
| 15 | public bool ContinueOnError => false; | ||
| 16 | |||
| 17 | public int LineNumberOfTaskNode => 0; | ||
| 18 | |||
| 19 | public string ProjectFileOfTaskNode => "fake_wix.targets"; | ||
| 20 | |||
| 21 | public string Output => this.output.ToString(); | ||
| 22 | |||
| 23 | public bool BuildProjectFile(string projectFileName, string[] targetNames, IDictionary globalProperties, IDictionary targetOutputs) => throw new System.NotImplementedException(); | ||
| 24 | |||
| 25 | public void LogCustomEvent(CustomBuildEventArgs e) => this.output.AppendLine(e.Message); | ||
| 26 | |||
| 27 | public void LogErrorEvent(BuildErrorEventArgs e) => this.output.AppendLine(e.Message); | ||
| 28 | |||
| 29 | public void LogMessageEvent(BuildMessageEventArgs e) => this.output.AppendLine(e.Message); | ||
| 30 | |||
| 31 | public void LogWarningEvent(BuildWarningEventArgs e) => this.output.AppendLine(e.Message); | ||
| 32 | } | ||
| 33 | } | ||
diff --git a/src/internal/WixBuildTools.TestSupport/MsbuildRunner.cs b/src/internal/WixBuildTools.TestSupport/MsbuildRunner.cs new file mode 100644 index 00000000..35e53de6 --- /dev/null +++ b/src/internal/WixBuildTools.TestSupport/MsbuildRunner.cs | |||
| @@ -0,0 +1,168 @@ | |||
| 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 WixBuildTools.TestSupport | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Collections.Generic; | ||
| 7 | using System.IO; | ||
| 8 | |||
| 9 | public class MsbuildRunner : ExternalExecutable | ||
| 10 | { | ||
| 11 | private static readonly string VswhereFindArguments = "-property installationPath"; | ||
| 12 | private static readonly string Msbuild15RelativePath = @"MSBuild\15.0\Bin\MSBuild.exe"; | ||
| 13 | private static readonly string Msbuild15RelativePath64 = @"MSBuild\15.0\Bin\amd64\MSBuild.exe"; | ||
| 14 | private static readonly string MsbuildCurrentRelativePath = @"MSBuild\Current\Bin\MSBuild.exe"; | ||
| 15 | private static readonly string MsbuildCurrentRelativePath64 = @"MSBuild\Current\Bin\amd64\MSBuild.exe"; | ||
| 16 | |||
| 17 | private static readonly object InitLock = new object(); | ||
| 18 | |||
| 19 | private static bool Initialized; | ||
| 20 | private static MsbuildRunner Msbuild15Runner; | ||
| 21 | private static MsbuildRunner Msbuild15Runner64; | ||
| 22 | private static MsbuildRunner MsbuildCurrentRunner; | ||
| 23 | private static MsbuildRunner MsbuildCurrentRunner64; | ||
| 24 | |||
| 25 | public static MsbuildRunnerResult Execute(string projectPath, string[] arguments = null, bool x64 = false) => | ||
| 26 | InitAndExecute(String.Empty, projectPath, arguments, x64); | ||
| 27 | |||
| 28 | public static MsbuildRunnerResult ExecuteWithMsbuild15(string projectPath, string[] arguments = null, bool x64 = false) => | ||
| 29 | InitAndExecute("15", projectPath, arguments, x64); | ||
| 30 | |||
| 31 | public static MsbuildRunnerResult ExecuteWithMsbuildCurrent(string projectPath, string[] arguments = null, bool x64 = false) => | ||
| 32 | InitAndExecute("Current", projectPath, arguments, x64); | ||
| 33 | |||
| 34 | private static MsbuildRunnerResult InitAndExecute(string msbuildVersion, string projectPath, string[] arguments, bool x64) | ||
| 35 | { | ||
| 36 | lock (InitLock) | ||
| 37 | { | ||
| 38 | if (!Initialized) | ||
| 39 | { | ||
| 40 | Initialized = true; | ||
| 41 | var vswhereResult = VswhereRunner.Execute(VswhereFindArguments, true); | ||
| 42 | if (vswhereResult.ExitCode != 0) | ||
| 43 | { | ||
| 44 | throw new InvalidOperationException($"Failed to execute vswhere.exe, exit code: {vswhereResult.ExitCode}. Output:\r\n{String.Join("\r\n", vswhereResult.StandardOutput)}"); | ||
| 45 | } | ||
| 46 | |||
| 47 | string msbuild15Path = null; | ||
| 48 | string msbuild15Path64 = null; | ||
| 49 | string msbuildCurrentPath = null; | ||
| 50 | string msbuildCurrentPath64 = null; | ||
| 51 | |||
| 52 | foreach (var installPath in vswhereResult.StandardOutput) | ||
| 53 | { | ||
| 54 | if (msbuildCurrentPath == null) | ||
| 55 | { | ||
| 56 | var path = Path.Combine(installPath, MsbuildCurrentRelativePath); | ||
| 57 | if (File.Exists(path)) | ||
| 58 | { | ||
| 59 | msbuildCurrentPath = path; | ||
| 60 | } | ||
| 61 | } | ||
| 62 | |||
| 63 | if (msbuildCurrentPath64 == null) | ||
| 64 | { | ||
| 65 | var path = Path.Combine(installPath, MsbuildCurrentRelativePath64); | ||
| 66 | if (File.Exists(path)) | ||
| 67 | { | ||
| 68 | msbuildCurrentPath64 = path; | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | if (msbuild15Path == null) | ||
| 73 | { | ||
| 74 | var path = Path.Combine(installPath, Msbuild15RelativePath); | ||
| 75 | if (File.Exists(path)) | ||
| 76 | { | ||
| 77 | msbuild15Path = path; | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | if (msbuild15Path64 == null) | ||
| 82 | { | ||
| 83 | var path = Path.Combine(installPath, Msbuild15RelativePath64); | ||
| 84 | if (File.Exists(path)) | ||
| 85 | { | ||
| 86 | msbuild15Path64 = path; | ||
| 87 | } | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | if (msbuildCurrentPath != null) | ||
| 92 | { | ||
| 93 | MsbuildCurrentRunner = new MsbuildRunner(msbuildCurrentPath); | ||
| 94 | } | ||
| 95 | |||
| 96 | if (msbuildCurrentPath64 != null) | ||
| 97 | { | ||
| 98 | MsbuildCurrentRunner64 = new MsbuildRunner(msbuildCurrentPath64); | ||
| 99 | } | ||
| 100 | |||
| 101 | if (msbuild15Path != null) | ||
| 102 | { | ||
| 103 | Msbuild15Runner = new MsbuildRunner(msbuild15Path); | ||
| 104 | } | ||
| 105 | |||
| 106 | if (msbuild15Path64 != null) | ||
| 107 | { | ||
| 108 | Msbuild15Runner64 = new MsbuildRunner(msbuild15Path64); | ||
| 109 | } | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | MsbuildRunner runner; | ||
| 114 | switch (msbuildVersion) | ||
| 115 | { | ||
| 116 | case "15": | ||
| 117 | { | ||
| 118 | runner = x64 ? Msbuild15Runner64 : Msbuild15Runner; | ||
| 119 | break; | ||
| 120 | } | ||
| 121 | case "Current": | ||
| 122 | { | ||
| 123 | runner = x64 ? MsbuildCurrentRunner64 : MsbuildCurrentRunner; | ||
| 124 | break; | ||
| 125 | } | ||
| 126 | default: | ||
| 127 | { | ||
| 128 | runner = x64 ? MsbuildCurrentRunner64 ?? Msbuild15Runner64 | ||
| 129 | : MsbuildCurrentRunner ?? Msbuild15Runner; | ||
| 130 | break; | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | if (runner == null) | ||
| 135 | { | ||
| 136 | throw new InvalidOperationException($"Failed to find an installed{(x64 ? " 64-bit" : String.Empty)} MSBuild{msbuildVersion}"); | ||
| 137 | } | ||
| 138 | |||
| 139 | return runner.ExecuteCore(projectPath, arguments); | ||
| 140 | } | ||
| 141 | |||
| 142 | private MsbuildRunner(string exePath) : base(exePath) { } | ||
| 143 | |||
| 144 | private MsbuildRunnerResult ExecuteCore(string projectPath, string[] arguments) | ||
| 145 | { | ||
| 146 | var total = new List<string> | ||
| 147 | { | ||
| 148 | projectPath, | ||
| 149 | }; | ||
| 150 | |||
| 151 | if (arguments != null) | ||
| 152 | { | ||
| 153 | total.AddRange(arguments); | ||
| 154 | } | ||
| 155 | |||
| 156 | var args = CombineArguments(total); | ||
| 157 | var mergeErrorIntoOutput = true; | ||
| 158 | var workingFolder = Path.GetDirectoryName(projectPath); | ||
| 159 | var result = this.Run(args, mergeErrorIntoOutput, workingFolder); | ||
| 160 | |||
| 161 | return new MsbuildRunnerResult | ||
| 162 | { | ||
| 163 | ExitCode = result.ExitCode, | ||
| 164 | Output = result.StandardOutput, | ||
| 165 | }; | ||
| 166 | } | ||
| 167 | } | ||
| 168 | } | ||
diff --git a/src/internal/WixBuildTools.TestSupport/MsbuildRunnerResult.cs b/src/internal/WixBuildTools.TestSupport/MsbuildRunnerResult.cs new file mode 100644 index 00000000..5610987e --- /dev/null +++ b/src/internal/WixBuildTools.TestSupport/MsbuildRunnerResult.cs | |||
| @@ -0,0 +1,19 @@ | |||
| 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 WixBuildTools.TestSupport | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using Xunit; | ||
| 7 | |||
| 8 | public class MsbuildRunnerResult | ||
| 9 | { | ||
| 10 | public int ExitCode { get; set; } | ||
| 11 | |||
| 12 | public string[] Output { get; set; } | ||
| 13 | |||
| 14 | public void AssertSuccess() | ||
| 15 | { | ||
| 16 | Assert.True(0 == this.ExitCode, $"MSBuild failed unexpectedly. Output:\r\n{String.Join("\r\n", this.Output)}"); | ||
| 17 | } | ||
| 18 | } | ||
| 19 | } | ||
diff --git a/src/internal/WixBuildTools.TestSupport/Pushd.cs b/src/internal/WixBuildTools.TestSupport/Pushd.cs new file mode 100644 index 00000000..d0545215 --- /dev/null +++ b/src/internal/WixBuildTools.TestSupport/Pushd.cs | |||
| @@ -0,0 +1,46 @@ | |||
| 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 WixBuildTools.TestSupport | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.IO; | ||
| 7 | |||
| 8 | public class Pushd : IDisposable | ||
| 9 | { | ||
| 10 | protected bool Disposed { get; private set; } | ||
| 11 | |||
| 12 | public Pushd(string path) | ||
| 13 | { | ||
| 14 | this.PreviousDirectory = Directory.GetCurrentDirectory(); | ||
| 15 | |||
| 16 | Directory.SetCurrentDirectory(path); | ||
| 17 | } | ||
| 18 | |||
| 19 | public string PreviousDirectory { get; } | ||
| 20 | |||
| 21 | #region // IDisposable | ||
| 22 | |||
| 23 | public void Dispose() | ||
| 24 | { | ||
| 25 | this.Dispose(true); | ||
| 26 | GC.SuppressFinalize(this); | ||
| 27 | } | ||
| 28 | |||
| 29 | protected virtual void Dispose(bool disposing) | ||
| 30 | { | ||
| 31 | if (this.Disposed) | ||
| 32 | { | ||
| 33 | return; | ||
| 34 | } | ||
| 35 | |||
| 36 | if (disposing) | ||
| 37 | { | ||
| 38 | Directory.SetCurrentDirectory(this.PreviousDirectory); | ||
| 39 | } | ||
| 40 | |||
| 41 | this.Disposed = true; | ||
| 42 | } | ||
| 43 | |||
| 44 | #endregion | ||
| 45 | } | ||
| 46 | } | ||
diff --git a/src/internal/WixBuildTools.TestSupport/Query.cs b/src/internal/WixBuildTools.TestSupport/Query.cs new file mode 100644 index 00000000..101a8890 --- /dev/null +++ b/src/internal/WixBuildTools.TestSupport/Query.cs | |||
| @@ -0,0 +1,172 @@ | |||
| 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 WixBuildTools.TestSupport | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Collections.Generic; | ||
| 7 | using System.IO; | ||
| 8 | using System.Linq; | ||
| 9 | using System.Text; | ||
| 10 | using WixToolset.Dtf.Compression.Cab; | ||
| 11 | using WixToolset.Dtf.WindowsInstaller; | ||
| 12 | |||
| 13 | public class Query | ||
| 14 | { | ||
| 15 | public static string[] QueryDatabase(string path, string[] tables) | ||
| 16 | { | ||
| 17 | var results = new List<string>(); | ||
| 18 | var resultsByTable = QueryDatabaseByTable(path, tables); | ||
| 19 | var sortedTables = tables.ToList(); | ||
| 20 | sortedTables.Sort(); | ||
| 21 | foreach (var tableName in sortedTables) | ||
| 22 | { | ||
| 23 | var rows = resultsByTable[tableName]; | ||
| 24 | rows?.ForEach(r => results.Add($"{tableName}:{r}")); | ||
| 25 | } | ||
| 26 | return results.ToArray(); | ||
| 27 | } | ||
| 28 | |||
| 29 | /// <summary> | ||
| 30 | /// Returns rows from requested tables formatted to facilitate testing. | ||
| 31 | /// If the table did not exist in the database, its list will be null. | ||
| 32 | /// </summary> | ||
| 33 | /// <param name="path"></param> | ||
| 34 | /// <param name="tables"></param> | ||
| 35 | /// <returns></returns> | ||
| 36 | public static Dictionary<string, List<string>> QueryDatabaseByTable(string path, string[] tables) | ||
| 37 | { | ||
| 38 | var results = new Dictionary<string, List<string>>(); | ||
| 39 | |||
| 40 | if (tables?.Length > 0) | ||
| 41 | { | ||
| 42 | var sb = new StringBuilder(); | ||
| 43 | using (var db = new Database(path)) | ||
| 44 | { | ||
| 45 | foreach (var table in tables) | ||
| 46 | { | ||
| 47 | if (table == "_SummaryInformation") | ||
| 48 | { | ||
| 49 | var entries = new List<string>(); | ||
| 50 | results.Add(table, entries); | ||
| 51 | |||
| 52 | entries.Add($"Title\t{db.SummaryInfo.Title}"); | ||
| 53 | entries.Add($"Subject\t{db.SummaryInfo.Subject}"); | ||
| 54 | entries.Add($"Author\t{db.SummaryInfo.Author}"); | ||
| 55 | entries.Add($"Keywords\t{db.SummaryInfo.Keywords}"); | ||
| 56 | entries.Add($"Comments\t{db.SummaryInfo.Comments}"); | ||
| 57 | entries.Add($"Template\t{db.SummaryInfo.Template}"); | ||
| 58 | entries.Add($"CodePage\t{db.SummaryInfo.CodePage}"); | ||
| 59 | entries.Add($"PageCount\t{db.SummaryInfo.PageCount}"); | ||
| 60 | entries.Add($"WordCount\t{db.SummaryInfo.WordCount}"); | ||
| 61 | entries.Add($"CharacterCount\t{db.SummaryInfo.CharacterCount}"); | ||
| 62 | entries.Add($"Security\t{db.SummaryInfo.Security}"); | ||
| 63 | |||
| 64 | continue; | ||
| 65 | } | ||
| 66 | |||
| 67 | if (!db.IsTablePersistent(table)) | ||
| 68 | { | ||
| 69 | results.Add(table, null); | ||
| 70 | continue; | ||
| 71 | } | ||
| 72 | |||
| 73 | var rows = new List<string>(); | ||
| 74 | results.Add(table, rows); | ||
| 75 | |||
| 76 | using (var view = db.OpenView("SELECT * FROM `{0}`", table)) | ||
| 77 | { | ||
| 78 | view.Execute(); | ||
| 79 | |||
| 80 | Record record; | ||
| 81 | while ((record = view.Fetch()) != null) | ||
| 82 | { | ||
| 83 | sb.Clear(); | ||
| 84 | |||
| 85 | using (record) | ||
| 86 | { | ||
| 87 | for (var i = 0; i < record.FieldCount; ++i) | ||
| 88 | { | ||
| 89 | if (i > 0) | ||
| 90 | { | ||
| 91 | sb.Append("\t"); | ||
| 92 | } | ||
| 93 | |||
| 94 | sb.Append(record[i + 1]?.ToString()); | ||
| 95 | } | ||
| 96 | } | ||
| 97 | |||
| 98 | rows.Add(sb.ToString()); | ||
| 99 | } | ||
| 100 | } | ||
| 101 | rows.Sort(); | ||
| 102 | } | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | return results; | ||
| 107 | } | ||
| 108 | |||
| 109 | public static CabFileInfo[] GetCabinetFiles(string path) | ||
| 110 | { | ||
| 111 | var cab = new CabInfo(path); | ||
| 112 | |||
| 113 | var result = cab.GetFiles(); | ||
| 114 | |||
| 115 | return result.Select(c => c).ToArray(); | ||
| 116 | } | ||
| 117 | |||
| 118 | public static void ExtractStream(string path, string streamName, string outputPath) | ||
| 119 | { | ||
| 120 | Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); | ||
| 121 | |||
| 122 | using (var db = new Database(path)) | ||
| 123 | using (var view = db.OpenView("SELECT `Data` FROM `_Streams` WHERE `Name` = '{0}'", streamName)) | ||
| 124 | { | ||
| 125 | view.Execute(); | ||
| 126 | |||
| 127 | using (var record = view.Fetch()) | ||
| 128 | { | ||
| 129 | record.GetStream(1, outputPath); | ||
| 130 | } | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | public static void ExtractSubStorage(string path, string subStorageName, string outputPath) | ||
| 135 | { | ||
| 136 | Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); | ||
| 137 | |||
| 138 | using (var db = new Database(path)) | ||
| 139 | using (var view = db.OpenView("SELECT `Name`, `Data` FROM `_Storages` WHERE `Name` = '{0}'", subStorageName)) | ||
| 140 | { | ||
| 141 | view.Execute(); | ||
| 142 | |||
| 143 | using (var record = view.Fetch()) | ||
| 144 | { | ||
| 145 | var name = record.GetString(1); | ||
| 146 | record.GetStream(2, outputPath); | ||
| 147 | } | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | public static string[] GetSubStorageNames(string path) | ||
| 152 | { | ||
| 153 | var result = new List<string>(); | ||
| 154 | |||
| 155 | using (var db = new Database(path)) | ||
| 156 | using (var view = db.OpenView("SELECT `Name` FROM `_Storages`")) | ||
| 157 | { | ||
| 158 | view.Execute(); | ||
| 159 | |||
| 160 | Record record; | ||
| 161 | while ((record = view.Fetch()) != null) | ||
| 162 | { | ||
| 163 | var name = record.GetString(1); | ||
| 164 | result.Add(name); | ||
| 165 | } | ||
| 166 | } | ||
| 167 | |||
| 168 | result.Sort(); | ||
| 169 | return result.ToArray(); | ||
| 170 | } | ||
| 171 | } | ||
| 172 | } | ||
diff --git a/src/internal/WixBuildTools.TestSupport/RobocopyRunner.cs b/src/internal/WixBuildTools.TestSupport/RobocopyRunner.cs new file mode 100644 index 00000000..49d53351 --- /dev/null +++ b/src/internal/WixBuildTools.TestSupport/RobocopyRunner.cs | |||
| @@ -0,0 +1,16 @@ | |||
| 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 WixBuildTools.TestSupport | ||
| 4 | { | ||
| 5 | public class RobocopyRunner : ExternalExecutable | ||
| 6 | { | ||
| 7 | private static readonly RobocopyRunner Instance = new RobocopyRunner(); | ||
| 8 | |||
| 9 | private RobocopyRunner() : base("robocopy") { } | ||
| 10 | |||
| 11 | public static ExternalExecutableResult Execute(string args) | ||
| 12 | { | ||
| 13 | return Instance.Run(args); | ||
| 14 | } | ||
| 15 | } | ||
| 16 | } | ||
diff --git a/src/internal/WixBuildTools.TestSupport/SucceededException.cs b/src/internal/WixBuildTools.TestSupport/SucceededException.cs new file mode 100644 index 00000000..00b31d68 --- /dev/null +++ b/src/internal/WixBuildTools.TestSupport/SucceededException.cs | |||
| @@ -0,0 +1,18 @@ | |||
| 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 WixBuildTools.TestSupport | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using Xunit.Sdk; | ||
| 7 | |||
| 8 | public class SucceededException : XunitException | ||
| 9 | { | ||
| 10 | public SucceededException(int hr, string userMessage) | ||
| 11 | : base(String.Format("WixAssert.Succeeded() Failure\r\n" + | ||
| 12 | "HRESULT: 0x{0:X8}\r\n" + | ||
| 13 | "Message: {1}", | ||
| 14 | hr, userMessage)) | ||
| 15 | { | ||
| 16 | } | ||
| 17 | } | ||
| 18 | } | ||
diff --git a/src/internal/WixBuildTools.TestSupport/TestData.cs b/src/internal/WixBuildTools.TestSupport/TestData.cs new file mode 100644 index 00000000..8587330d --- /dev/null +++ b/src/internal/WixBuildTools.TestSupport/TestData.cs | |||
| @@ -0,0 +1,16 @@ | |||
| 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 WixBuildTools.TestSupport | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.IO; | ||
| 7 | |||
| 8 | public class TestData | ||
| 9 | { | ||
| 10 | public static string Get(params string[] paths) | ||
| 11 | { | ||
| 12 | var localPath = Path.GetDirectoryName(new Uri(System.Reflection.Assembly.GetCallingAssembly().CodeBase).LocalPath); | ||
| 13 | return Path.Combine(localPath, Path.Combine(paths)); | ||
| 14 | } | ||
| 15 | } | ||
| 16 | } | ||
diff --git a/src/internal/WixBuildTools.TestSupport/TestDataFolderFileSystem.cs b/src/internal/WixBuildTools.TestSupport/TestDataFolderFileSystem.cs new file mode 100644 index 00000000..8d670bf0 --- /dev/null +++ b/src/internal/WixBuildTools.TestSupport/TestDataFolderFileSystem.cs | |||
| @@ -0,0 +1,42 @@ | |||
| 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 WixBuildTools.TestSupport | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | |||
| 7 | /// <summary> | ||
| 8 | /// This class builds on top of DisposableFileSystem | ||
| 9 | /// to make it easy to write a test that needs a whole folder of test data copied to a temp location | ||
| 10 | /// that will automatically be cleaned up at the end of the test. | ||
| 11 | /// </summary> | ||
| 12 | public class TestDataFolderFileSystem : IDisposable | ||
| 13 | { | ||
| 14 | private DisposableFileSystem fileSystem; | ||
| 15 | |||
| 16 | public string BaseFolder { get; private set; } | ||
| 17 | |||
| 18 | public void Dispose() | ||
| 19 | { | ||
| 20 | this.fileSystem?.Dispose(); | ||
| 21 | } | ||
| 22 | |||
| 23 | public void Initialize(string sourceDirectoryPath) | ||
| 24 | { | ||
| 25 | if (this.fileSystem != null) | ||
| 26 | { | ||
| 27 | throw new InvalidOperationException(); | ||
| 28 | } | ||
| 29 | this.fileSystem = new DisposableFileSystem(); | ||
| 30 | |||
| 31 | this.BaseFolder = this.fileSystem.GetFolder(); | ||
| 32 | |||
| 33 | RobocopyFolder(sourceDirectoryPath, this.BaseFolder); | ||
| 34 | } | ||
| 35 | |||
| 36 | private static ExternalExecutableResult RobocopyFolder(string sourceFolderPath, string destinationFolderPath) | ||
| 37 | { | ||
| 38 | var args = $"\"{sourceFolderPath}\" \"{destinationFolderPath}\" /E /R:1 /W:1"; | ||
| 39 | return RobocopyRunner.Execute(args); | ||
| 40 | } | ||
| 41 | } | ||
| 42 | } | ||
diff --git a/src/internal/WixBuildTools.TestSupport/VswhereRunner.cs b/src/internal/WixBuildTools.TestSupport/VswhereRunner.cs new file mode 100644 index 00000000..0197e125 --- /dev/null +++ b/src/internal/WixBuildTools.TestSupport/VswhereRunner.cs | |||
| @@ -0,0 +1,41 @@ | |||
| 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 WixBuildTools.TestSupport | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.IO; | ||
| 7 | |||
| 8 | public class VswhereRunner : ExternalExecutable | ||
| 9 | { | ||
| 10 | private static readonly string VswhereRelativePath = @"Microsoft Visual Studio\Installer\vswhere.exe"; | ||
| 11 | |||
| 12 | private static readonly object InitLock = new object(); | ||
| 13 | private static bool Initialized; | ||
| 14 | private static VswhereRunner Instance; | ||
| 15 | |||
| 16 | public static ExternalExecutableResult Execute(string args, bool mergeErrorIntoOutput = false) => | ||
| 17 | InitAndExecute(args, mergeErrorIntoOutput); | ||
| 18 | |||
| 19 | private static ExternalExecutableResult InitAndExecute(string args, bool mergeErrorIntoOutput) | ||
| 20 | { | ||
| 21 | lock (InitLock) | ||
| 22 | { | ||
| 23 | if (!Initialized) | ||
| 24 | { | ||
| 25 | Initialized = true; | ||
| 26 | var vswherePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), VswhereRelativePath); | ||
| 27 | if (!File.Exists(vswherePath)) | ||
| 28 | { | ||
| 29 | throw new InvalidOperationException($"Failed to find vswhere at: {vswherePath}"); | ||
| 30 | } | ||
| 31 | |||
| 32 | Instance = new VswhereRunner(vswherePath); | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | return Instance.Run(args, mergeErrorIntoOutput); | ||
| 37 | } | ||
| 38 | |||
| 39 | private VswhereRunner(string exePath) : base(exePath) { } | ||
| 40 | } | ||
| 41 | } | ||
diff --git a/src/internal/WixBuildTools.TestSupport/WixAssert.cs b/src/internal/WixBuildTools.TestSupport/WixAssert.cs new file mode 100644 index 00000000..5638a787 --- /dev/null +++ b/src/internal/WixBuildTools.TestSupport/WixAssert.cs | |||
| @@ -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 | namespace WixBuildTools.TestSupport | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Linq; | ||
| 7 | using System.Xml.Linq; | ||
| 8 | using Xunit; | ||
| 9 | |||
| 10 | public class WixAssert : Assert | ||
| 11 | { | ||
| 12 | public static void CompareLineByLine(string[] expectedLines, string[] actualLines) | ||
| 13 | { | ||
| 14 | for (var i = 0; i < expectedLines.Length; ++i) | ||
| 15 | { | ||
| 16 | Assert.True(actualLines.Length > i, $"{i}: expectedLines longer than actualLines"); | ||
| 17 | Assert.Equal($"{i}: {expectedLines[i]}", $"{i}: {actualLines[i]}"); | ||
| 18 | } | ||
| 19 | |||
| 20 | Assert.True(expectedLines.Length == actualLines.Length, "actualLines longer than expectedLines"); | ||
| 21 | } | ||
| 22 | |||
| 23 | public static void CompareXml(XContainer xExpected, XContainer xActual) | ||
| 24 | { | ||
| 25 | var expecteds = xExpected.Descendants().Select(x => $"{x.Name.LocalName}:{String.Join(",", x.Attributes().OrderBy(a => a.Name.LocalName).Select(a => $"{a.Name.LocalName}={a.Value}"))}"); | ||
| 26 | var actuals = xActual.Descendants().Select(x => $"{x.Name.LocalName}:{String.Join(",", x.Attributes().OrderBy(a => a.Name.LocalName).Select(a => $"{a.Name.LocalName}={a.Value}"))}"); | ||
| 27 | |||
| 28 | CompareLineByLine(expecteds.OrderBy(s => s).ToArray(), actuals.OrderBy(s => s).ToArray()); | ||
| 29 | } | ||
| 30 | |||
| 31 | public static void CompareXml(string expectedPath, string actualPath) | ||
| 32 | { | ||
| 33 | var expectedDoc = XDocument.Load(expectedPath, LoadOptions.PreserveWhitespace | LoadOptions.SetBaseUri | LoadOptions.SetLineInfo); | ||
| 34 | var actualDoc = XDocument.Load(actualPath, LoadOptions.PreserveWhitespace | LoadOptions.SetBaseUri | LoadOptions.SetLineInfo); | ||
| 35 | |||
| 36 | CompareXml(expectedDoc, actualDoc); | ||
| 37 | } | ||
| 38 | |||
| 39 | public static void Succeeded(int hr, string format, params object[] formatArgs) | ||
| 40 | { | ||
| 41 | if (0 > hr) | ||
| 42 | { | ||
| 43 | throw new SucceededException(hr, String.Format(format, formatArgs)); | ||
| 44 | } | ||
| 45 | } | ||
| 46 | } | ||
| 47 | } | ||
diff --git a/src/internal/WixBuildTools.TestSupport/WixBuildTools.TestSupport.csproj b/src/internal/WixBuildTools.TestSupport/WixBuildTools.TestSupport.csproj new file mode 100644 index 00000000..f59e5eca --- /dev/null +++ b/src/internal/WixBuildTools.TestSupport/WixBuildTools.TestSupport.csproj | |||
| @@ -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 | |||
| 6 | <PropertyGroup> | ||
| 7 | <TargetFrameworks>netstandard2.0;net461;net472</TargetFrameworks> | ||
| 8 | <IsPackable>true</IsPackable> | ||
| 9 | <DebugType>embedded</DebugType> | ||
| 10 | <PublishRepositoryUrl>true</PublishRepositoryUrl> | ||
| 11 | <CreateDocumentationFile>true</CreateDocumentationFile> | ||
| 12 | <NoWarn>CS1591</NoWarn> | ||
| 13 | </PropertyGroup> | ||
| 14 | |||
| 15 | <ItemGroup> | ||
| 16 | <PackageReference Include="Microsoft.Build.Tasks.Core" Version="14.3" /> | ||
| 17 | <PackageReference Include="WixToolset.Dtf.WindowsInstaller" Version="4.0.*" /> | ||
| 18 | <PackageReference Include="WixToolset.Dtf.Compression" Version="4.0.*" /> | ||
| 19 | <PackageReference Include="WixToolset.Dtf.Compression.Cab" Version="4.0.*" /> | ||
| 20 | </ItemGroup> | ||
| 21 | |||
| 22 | <ItemGroup> | ||
| 23 | <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" /> | ||
| 24 | <PackageReference Include="Nerdbank.GitVersioning" Version="3.3.37" PrivateAssets="All" /> | ||
| 25 | </ItemGroup> | ||
| 26 | |||
| 27 | <ItemGroup> | ||
| 28 | <PackageReference Include="xunit.assert" Version="2.4.1" /> | ||
| 29 | </ItemGroup> | ||
| 30 | |||
| 31 | </Project> | ||
