Detect VelopackApp.Run() in any method in main module

This commit is contained in:
Caelan Sayler
2024-03-27 10:23:01 +00:00
parent 97c9a05dbb
commit f1fd069ac7
10 changed files with 315 additions and 169 deletions

View File

@@ -85,4 +85,9 @@ public class MSBuildLogger(TaskLoggingHelper loggingHelper) : ILogger, IFancyCon
{ {
Log(LogLevel.Information, 0, null, null, (object? state, Exception? exception) => text); Log(LogLevel.Information, 0, null, null, (object? state, Exception? exception) => text);
} }
public string EscapeMarkup(string text)
{
return text;
}
} }

View File

@@ -33,11 +33,12 @@ public class WindowsPackCommandRunner : PackageBuilder<WindowsPackOptions>
if (Directory.EnumerateFiles(packDir, "*.application").Any(f => File.ReadAllText(f).Contains("clickonce"))) { if (Directory.EnumerateFiles(packDir, "*.application").Any(f => File.ReadAllText(f).Contains("clickonce"))) {
throw new ArgumentException( throw new ArgumentException(
"Velopack does not support building releases for ClickOnce applications. " + "Velopack does not support building releases for ClickOnce applications. " +
"Please publish your application to a folder without ClickOnce."); "Please remove all ClickOnce properties from your .csproj before continuing.");
} }
if (!Options.SkipVelopackAppCheck) { if (!Options.SkipVelopackAppCheck) {
DotnetUtil.VerifyVelopackApp(MainExePath, Log); var compat = new CompatUtil(Log, Console);
compat.Verify(MainExePath);
} else { } else {
Log.Info("Skipping VelopackApp.Build.Run() check."); Log.Info("Skipping VelopackApp.Build.Run() check.");
} }

View File

@@ -0,0 +1,241 @@
using AsmResolver.DotNet;
using AsmResolver.DotNet.Bundles;
using AsmResolver.DotNet.Serialized;
using AsmResolver.PE;
using AsmResolver.PE.DotNet.Cil;
using AsmResolver.PE.Win32Resources.Version;
using Microsoft.Extensions.Logging;
using NuGet.Versioning;
using Velopack.Packaging.Abstractions;
using Velopack.Packaging.Exceptions;
namespace Velopack.Packaging.Windows;
public class CompatUtil
{
private readonly ILogger _log;
private readonly IFancyConsole _console;
public CompatUtil(ILogger logger, IFancyConsole console)
{
_log = logger;
_console = console;
}
public NuGetVersion Verify(string exeFile)
{
VerifyVelopackApp(exeFile);
return VerifyVelopackVersion(exeFile);
}
public void VerifyVelopackApp(string exeFile)
{
try {
AssemblyDefinition mainAssy = LoadDotnetAssembly(exeFile);
if (mainAssy == null) {
return;
}
ModuleDefinition mainModule = mainAssy.Modules.Single();
var result = SearchAssemblyForVelopackApp(mainModule);
if (result == null) {
// if we've iterated the whole main assembly and not found the call, then the velopack builder is missing
throw new UserInfoException($"Unable to verify VelopackApp is called. " +
"Please ensure that 'VelopackApp.Build().Run()' is present in your Program.Main().");
}
result = _console.EscapeMarkup(result);
// could be regular Program.Main or async Main which has a slightly different signature.
if (result.Contains("Program") && result.Contains("Main")) {
_log.Info($"[green underline]Verified VelopackApp.Run()[/] in '{result}'.");
} else {
_log.Warn($"VelopackApp.Run() was found in method '{result}', which does not look like your application's entry point." +
$"It is [underline yellow]strongly recommended[/] that you move this to the very beginning of your Main() method. ");
}
} catch (Exception ex) when (ex is not UserInfoException) {
_log.Error("Unable to verify VelopackApp: " + ex.Message);
}
}
public NuGetVersion VerifyVelopackVersion(string exeFile)
{
var rawVersion = GetVelopackVersion(exeFile);
if (rawVersion == null) {
return null;
}
var dllVersion = new Version(rawVersion.ToString("V", VersionFormatter.Instance));
var myVersion = new Version(VelopackRuntimeInfo.VelopackNugetVersion.ToString("V", VersionFormatter.Instance));
if (dllVersion == myVersion) {
return new NuGetVersion(dllVersion);
}
if (dllVersion > myVersion) {
throw new UserInfoException($"Velopack library version is greater than vpk version ({dllVersion} > {myVersion}). This can cause compatibility issues, please update vpk first.");
}
_log.Warn($"Velopack library version is lower than vpk version ({dllVersion} < {myVersion}). This can occasionally cause compatibility issues.");
return new NuGetVersion(dllVersion);
}
private string SearchAssemblyForVelopackApp(ModuleDefinition mainModule)
{
MethodDefinition entryPoint = mainModule.ManagedEntryPointMethod;
string SearchMethod(MethodDefinition method)
{
foreach (var instr in method.CilMethodBody.Instructions) {
if (instr.OpCode.Code is CilCode.Call or CilCode.Callvirt or CilCode.Calli) {
var operand = instr.Operand as SerializedMemberReference;
if (operand != null) {
if (operand.Name == "Run" && operand.DeclaringType.FullName == "Velopack.VelopackApp") {
// success!
return method.FullName;
}
}
}
}
return null;
}
string SearchType(TypeDefinition type)
{
// search all methods in type
foreach (var method in type.Methods) {
if (method.HasMethodBody) {
var result = SearchMethod(method);
if (result != null) {
return result;
}
}
}
// then, search all nested types
foreach (var nestedType in type.NestedTypes) {
var result = SearchType(nestedType);
if (result != null) {
return result;
}
}
return null;
}
// search entry point first
string result;
if ((result = SearchMethod(entryPoint)) != null) {
return result;
}
// then, iterate all methods in the main module
foreach (var topType in mainModule.TopLevelTypes) {
if ((result = SearchType(topType)) != null) {
return result;
}
}
return null;
}
public NuGetVersion GetVelopackVersion(string exeFile)
{
try {
var velopackDll = FindVelopackDll(exeFile);
if (velopackDll == null) {
return null;
}
var versionInfo = VersionInfoResource.FromDirectory(velopackDll.Resources);
var actualInfo = versionInfo.GetChild<StringFileInfo>(StringFileInfo.StringFileInfoKey);
var versionTable = actualInfo.Tables[0];
var productVersion = versionTable.Where(v => v.Key == StringTable.ProductVersionKey).FirstOrDefault();
return NuGetVersion.Parse(productVersion.Value);
} catch (Exception ex) {
// don't really care
_log.Debug(ex, "Unable to read Velopack.dll version info.");
}
return null;
}
private IPEImage FindVelopackDll(string exeFile)
{
var versionFile = Path.Combine(Path.GetDirectoryName(exeFile), "Velopack.dll");
if (File.Exists(versionFile)) {
_log.Debug(exeFile + " has Velopack.dll in the same directory.");
return PEImage.FromFile(versionFile);
}
try {
var bundle = BundleManifest.FromFile(exeFile);
IList<BundleFile> embeddedFiles = bundle.Files;
var velopackEmbedded = embeddedFiles.SingleOrDefault(f => f.Type == BundleFileType.Assembly && f.RelativePath == "Velopack.dll");
if (velopackEmbedded != null && velopackEmbedded.TryGetReader(out var readerVel)) {
_log.Debug(exeFile + " has Velopack.dll embedded in a SingleFileHost.");
return PEImage.FromReader(readerVel);
}
} catch (BadImageFormatException) {
// not an AppHost / SingleFileHost binary
return null;
}
return null;
}
private AssemblyDefinition LoadDotnetAssembly(string exeFile)
{
try {
var assy = AssemblyDefinition.FromFile(exeFile);
return assy;
} catch (BadImageFormatException) {
// not a .Net Framework binary
}
try {
var bundle = BundleManifest.FromFile(exeFile);
IList<BundleFile> embeddedFiles = null;
try {
embeddedFiles = bundle.Files;
} catch {
// not a SingleFileHost binary, so we'll search on disk
var parentDir = Path.GetDirectoryName(exeFile);
var diskFile = Path.Combine(parentDir, Path.GetFileNameWithoutExtension(exeFile) + ".dll");
if (File.Exists(diskFile)) {
return AssemblyDefinition.FromFile(diskFile);
}
var runtimeConfigFile = Directory.EnumerateFiles(parentDir, "*.runtimeconfig.json").SingleOrDefault();
var possNameRuntime = Path.Combine(parentDir,
Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(runtimeConfigFile)) + ".dll");
if (File.Exists(possNameRuntime)) {
return AssemblyDefinition.FromFile(possNameRuntime);
}
return null;
}
var runtimeConfig = embeddedFiles.SingleOrDefault(f => f.Type == BundleFileType.RuntimeConfigJson);
if (runtimeConfig != null) {
var possName1 = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(runtimeConfig.RelativePath)) + ".dll";
var file = embeddedFiles.SingleOrDefault(f => f.Type == BundleFileType.Assembly && f.RelativePath == possName1);
if (file != null && file.TryGetReader(out var reader)) {
return AssemblyDefinition.FromReader(reader);
}
}
var possName2 = Path.GetFileNameWithoutExtension(exeFile) + ".dll";
var file2 = embeddedFiles.SingleOrDefault(f => f.Type == BundleFileType.Assembly && f.RelativePath == possName2);
if (file2 != null && file2.TryGetReader(out var reader2)) {
return AssemblyDefinition.FromReader(reader2);
}
return null;
} catch (BadImageFormatException) {
// not an AppHost / SingleFileHost binary
return null;
}
}
}

View File

@@ -1,151 +0,0 @@
using AsmResolver.DotNet;
using AsmResolver.DotNet.Bundles;
using AsmResolver.DotNet.Serialized;
using AsmResolver.PE;
using AsmResolver.PE.DotNet.Cil;
using AsmResolver.PE.Win32Resources.Version;
using Microsoft.Extensions.Logging;
using NuGet.Versioning;
using Velopack.Packaging.Exceptions;
namespace Velopack.Packaging.Windows;
public class DotnetUtil
{
public static NuGetVersion VerifyVelopackApp(string exeFile, ILogger log)
{
try {
NuGetVersion velopackVersion = null;
IPEImage velopackDll = null;
AssemblyDefinition mainAssy = null;
mainAssy ??= LoadFullFramework(exeFile, ref velopackDll);
mainAssy ??= LoadDncBundle(exeFile, ref velopackDll);
if (mainAssy == null) {
// not a dotnet binary
return null;
}
if (velopackDll != null) {
try {
var versionInfo = VersionInfoResource.FromDirectory(velopackDll.Resources);
var actualInfo = versionInfo.GetChild<StringFileInfo>(StringFileInfo.StringFileInfoKey);
var versionTable = actualInfo.Tables[0];
var productVersion = versionTable.Where(v => v.Key == StringTable.ProductVersionKey).FirstOrDefault();
velopackVersion = NuGetVersion.Parse(productVersion.Value);
} catch (Exception ex) {
// don't really care
log.Debug(ex, "Unable to read Velopack.dll version info.");
}
}
var mainModule = mainAssy.Modules.Single();
var entryPoint = mainModule.ManagedEntryPointMethod;
foreach (var instr in entryPoint.CilMethodBody.Instructions) {
if (instr.OpCode.Code is CilCode.Call or CilCode.Callvirt or CilCode.Calli) {
SerializedMemberReference operand = instr.Operand as SerializedMemberReference;
if (operand != null && operand.IsMethod) {
if (operand.Name == "Run" && operand.DeclaringType.FullName == "Velopack.VelopackApp") {
// success!
if (velopackVersion != null) {
log.Info($"Verified VelopackApp.Run() in '{entryPoint.FullName}', version {velopackVersion}.");
if (velopackVersion != VelopackRuntimeInfo.VelopackProductVersion) {
log.Warn(exeFile + " was built with a different version of Velopack than this tool. " +
$"This may cause compatibility issues. Expected {VelopackRuntimeInfo.VelopackProductVersion}, " +
$"but found {velopackVersion}.");
}
return velopackVersion;
} else {
log.Warn("VelopackApp verified at entry point, but ProductVersion could not be checked.");
return null;
}
}
}
}
}
// if we've iterated the whole main method and not found the call, then the velopack builder is missing
throw new UserInfoException($"Unable to verify VelopackApp, in application main method '{entryPoint.FullName}'. " +
"Please ensure that 'VelopackApp.Build().Run()' is present in your Program.Main().");
} catch (Exception ex) when (ex is not UserInfoException) {
log.Error("Unable to verify VelopackApp: " + ex.Message);
}
return null;
}
private static AssemblyDefinition LoadFullFramework(string exeFile, ref IPEImage velopackDll)
{
try {
var assy = AssemblyDefinition.FromFile(exeFile);
var versionFile = Path.Combine(Path.GetDirectoryName(exeFile), "Velopack.dll");
if (File.Exists(versionFile)) {
velopackDll = PEImage.FromFile(versionFile);
}
return assy;
} catch (BadImageFormatException) {
// not a .Net Framework binary
return null;
}
}
private static AssemblyDefinition LoadDncBundle(string exeFile, ref IPEImage velopackDll)
{
try {
var bundle = BundleManifest.FromFile(exeFile);
IList<BundleFile> embeddedFiles = null;
try {
embeddedFiles = bundle.Files;
} catch {
// not a SingleFileHost binary, so we'll search on disk
var parentDir = Path.GetDirectoryName(exeFile);
var versionFile = Path.Combine(parentDir, "Velopack.dll");
if (File.Exists(versionFile)) {
velopackDll = PEImage.FromFile(versionFile);
}
var diskFile = Path.Combine(parentDir, Path.GetFileNameWithoutExtension(exeFile) + ".dll");
if (File.Exists(diskFile)) {
return AssemblyDefinition.FromFile(diskFile);
}
var runtimeConfigFile = Directory.EnumerateFiles(parentDir, "*.runtimeconfig.json").SingleOrDefault();
var possNameRuntime = Path.Combine(parentDir,
Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(runtimeConfigFile)) + ".dll");
if (File.Exists(possNameRuntime)) {
return AssemblyDefinition.FromFile(possNameRuntime);
}
return null;
}
var velopackEmbedded = embeddedFiles.SingleOrDefault(f => f.Type == BundleFileType.Assembly && f.RelativePath == "Velopack.dll");
if (velopackEmbedded != null && velopackEmbedded.TryGetReader(out var readerVel)) {
velopackDll = PEImage.FromReader(readerVel);
}
var runtimeConfig = embeddedFiles.SingleOrDefault(f => f.Type == BundleFileType.RuntimeConfigJson);
if (runtimeConfig != null) {
var possName1 = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(runtimeConfig.RelativePath)) + ".dll";
var file = embeddedFiles.SingleOrDefault(f => f.Type == BundleFileType.Assembly && f.RelativePath == possName1);
if (file != null && file.TryGetReader(out var reader)) {
return AssemblyDefinition.FromReader(reader);
}
}
var possName2 = Path.GetFileNameWithoutExtension(exeFile) + ".dll";
var file2 = embeddedFiles.SingleOrDefault(f => f.Type == BundleFileType.Assembly && f.RelativePath == possName2);
if (file2 != null && file2.TryGetReader(out var reader2)) {
return AssemblyDefinition.FromReader(reader2);
}
return null;
} catch (BadImageFormatException) {
// not an AppHost / SingleFileHost binary
return null;
}
}
}

View File

@@ -7,4 +7,6 @@ public interface IFancyConsole : IConsole
void WriteTable(string tableName, IEnumerable<IEnumerable<string>> rows, bool hasHeaderRow = true); void WriteTable(string tableName, IEnumerable<IEnumerable<string>> rows, bool hasHeaderRow = true);
Task<bool> PromptYesNo(string prompt, bool? defaultValue = null, TimeSpan? timeout = null); Task<bool> PromptYesNo(string prompt, bool? defaultValue = null, TimeSpan? timeout = null);
string EscapeMarkup(string text);
} }

View File

@@ -13,6 +13,11 @@ public class BasicConsole : IFancyConsole
this.defaultFactory = defaultFactory; this.defaultFactory = defaultFactory;
} }
public string EscapeMarkup(string text)
{
return text;
}
public async Task ExecuteProgressAsync(Func<IFancyConsoleProgress, Task> action) public async Task ExecuteProgressAsync(Func<IFancyConsoleProgress, Task> action)
{ {
var start = DateTime.UtcNow; var start = DateTime.UtcNow;

View File

@@ -15,6 +15,11 @@ public class SpectreConsole : IFancyConsole
this.defaultFactory = defaultFactory; this.defaultFactory = defaultFactory;
} }
public string EscapeMarkup(string text)
{
return Markup.Escape(text);
}
public async Task ExecuteProgressAsync(Func<IFancyConsoleProgress, Task> action) public async Task ExecuteProgressAsync(Func<IFancyConsoleProgress, Task> action)
{ {
var start = DateTime.UtcNow; var start = DateTime.UtcNow;

View File

@@ -7,6 +7,10 @@ try {
bool shouldExit = false; bool shouldExit = false;
bool shouldAutoUpdate = args.Any(a => a.Equals("--autoupdate", StringComparison.OrdinalIgnoreCase)); bool shouldAutoUpdate = args.Any(a => a.Equals("--autoupdate", StringComparison.OrdinalIgnoreCase));
#if USE_ASYNC_MAIN
await Task.Delay(10).ConfigureAwait(false);
#endif
#if !NO_VELO_BUILDER #if !NO_VELO_BUILDER
VelopackApp.Build() VelopackApp.Build()
.SetAutoApplyOnStartup(shouldAutoUpdate) .SetAutoApplyOnStartup(shouldAutoUpdate)

View File

@@ -10,6 +10,10 @@
<DefineConstants>NO_VELO_BUILDER</DefineConstants> <DefineConstants>NO_VELO_BUILDER</DefineConstants>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" $(UseAsyncMain) != '' ">
<DefineConstants>USE_ASYNC_MAIN</DefineConstants>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\Velopack\Velopack.csproj" /> <ProjectReference Include="..\..\src\Velopack\Velopack.csproj" />
</ItemGroup> </ItemGroup>

View File

@@ -1,30 +1,39 @@
using Velopack.Packaging.Exceptions; using Divergic.Logging.Xunit;
using Velopack.Packaging.Exceptions;
using Velopack.Packaging.Windows; using Velopack.Packaging.Windows;
using Velopack.Vpk.Logging;
namespace Velopack.Packaging.Tests; namespace Velopack.Packaging.Tests;
public class DotnetUtilTests public class CompatUtilTests
{ {
private readonly ITestOutputHelper _output; private readonly ITestOutputHelper _output;
public DotnetUtilTests(ITestOutputHelper output) public CompatUtilTests(ITestOutputHelper output)
{ {
_output = output; _output = output;
} }
private ICacheLogger<CompatUtilTests> GetCompat(out CompatUtil compat)
{
var logger = _output.BuildLoggerFor<CompatUtilTests>();
compat = new CompatUtil(logger, new BasicConsole(logger, new DefaultPromptValueFactory(true)));
return logger;
}
[SkippableFact] [SkippableFact]
public void NonDotnetBinaryPasses() public void NonDotnetBinaryPasses()
{ {
Skip.IfNot(VelopackRuntimeInfo.IsWindows); Skip.IfNot(VelopackRuntimeInfo.IsWindows);
using var logger = _output.BuildLoggerFor<DotnetUtilTests>(); using var logger = GetCompat(out var compat);
Assert.Null(DotnetUtil.VerifyVelopackApp(PathHelper.GetRustAsset("testapp.exe"), logger)); Assert.Null(compat.Verify(PathHelper.GetRustAsset("testapp.exe")));
} }
[SkippableFact] [SkippableFact]
public void PublishSingleFilePasses() public void PublishSingleFilePasses()
{ {
Skip.IfNot(VelopackRuntimeInfo.IsWindows); Skip.IfNot(VelopackRuntimeInfo.IsWindows);
using var logger = _output.BuildLoggerFor<DotnetUtilTests>(); using var logger = GetCompat(out var compat);
using var _1 = Utility.GetTempDirectory(out var dir); using var _1 = Utility.GetTempDirectory(out var dir);
var sample = PathHelper.GetAvaloniaSample(); var sample = PathHelper.GetAvaloniaSample();
Exe.InvokeAndThrowIfNonZero( Exe.InvokeAndThrowIfNonZero(
@@ -34,18 +43,18 @@ public class DotnetUtilTests
sample); sample);
var path = Path.Combine(dir, "AvaloniaCrossPlat.exe"); var path = Path.Combine(dir, "AvaloniaCrossPlat.exe");
Assert.Equal(VelopackRuntimeInfo.VelopackProductVersion, DotnetUtil.VerifyVelopackApp(path, logger)); Assert.Equal(VelopackRuntimeInfo.VelopackProductVersion, compat.Verify(path));
var newPath = Path.Combine(dir, "AvaloniaCrossPlat-asd2.exe"); var newPath = Path.Combine(dir, "AvaloniaCrossPlat-asd2.exe");
File.Move(path, newPath); File.Move(path, newPath);
Assert.Equal(VelopackRuntimeInfo.VelopackProductVersion, DotnetUtil.VerifyVelopackApp(newPath, logger)); Assert.Equal(VelopackRuntimeInfo.VelopackProductVersion, compat.Verify(newPath));
} }
[SkippableFact] [SkippableFact]
public void PublishDotnet6Passes() public void PublishDotnet6Passes()
{ {
Skip.IfNot(VelopackRuntimeInfo.IsWindows); Skip.IfNot(VelopackRuntimeInfo.IsWindows);
using var logger = _output.BuildLoggerFor<DotnetUtilTests>(); using var logger = GetCompat(out var compat);
using var _1 = Utility.GetTempDirectory(out var dir); using var _1 = Utility.GetTempDirectory(out var dir);
var sample = PathHelper.GetAvaloniaSample(); var sample = PathHelper.GetAvaloniaSample();
Exe.InvokeAndThrowIfNonZero( Exe.InvokeAndThrowIfNonZero(
@@ -55,18 +64,18 @@ public class DotnetUtilTests
sample); sample);
var path = Path.Combine(dir, "AvaloniaCrossPlat.exe"); var path = Path.Combine(dir, "AvaloniaCrossPlat.exe");
Assert.Equal(VelopackRuntimeInfo.VelopackProductVersion, DotnetUtil.VerifyVelopackApp(path, logger)); Assert.Equal(VelopackRuntimeInfo.VelopackProductVersion, compat.Verify(path));
var newPath = Path.Combine(dir, "AvaloniaCrossPlat-asd2.exe"); var newPath = Path.Combine(dir, "AvaloniaCrossPlat-asd2.exe");
File.Move(path, newPath); File.Move(path, newPath);
Assert.Equal(VelopackRuntimeInfo.VelopackProductVersion, DotnetUtil.VerifyVelopackApp(newPath, logger)); Assert.Equal(VelopackRuntimeInfo.VelopackProductVersion, compat.Verify(newPath));
} }
[SkippableFact] [SkippableFact]
public void PublishNet48Passes() public void PublishNet48Passes()
{ {
Skip.IfNot(VelopackRuntimeInfo.IsWindows); Skip.IfNot(VelopackRuntimeInfo.IsWindows);
using var logger = _output.BuildLoggerFor<DotnetUtilTests>(); using var logger = GetCompat(out var compat);
using var _1 = Utility.GetTempDirectory(out var dir); using var _1 = Utility.GetTempDirectory(out var dir);
var sample = PathHelper.GetWpfSample(); var sample = PathHelper.GetWpfSample();
Exe.InvokeAndThrowIfNonZero( Exe.InvokeAndThrowIfNonZero(
@@ -75,18 +84,18 @@ public class DotnetUtilTests
sample); sample);
var path = Path.Combine(dir, "VeloWpfSample.exe"); var path = Path.Combine(dir, "VeloWpfSample.exe");
Assert.NotNull(DotnetUtil.VerifyVelopackApp(path, logger)); Assert.NotNull(compat.Verify(path));
var newPath = Path.Combine(dir, "VeloWpfSample-asd2.exe"); var newPath = Path.Combine(dir, "VeloWpfSample-asd2.exe");
File.Move(path, newPath); File.Move(path, newPath);
Assert.NotNull(DotnetUtil.VerifyVelopackApp(newPath, logger)); Assert.NotNull(compat.Verify(newPath));
} }
[SkippableFact] [SkippableFact]
public void UnawareDotnetAppFails() public void UnawareDotnetAppFails()
{ {
Skip.IfNot(VelopackRuntimeInfo.IsWindows); Skip.IfNot(VelopackRuntimeInfo.IsWindows);
using var logger = _output.BuildLoggerFor<DotnetUtilTests>(); using var logger = GetCompat(out var compat);
using var _1 = Utility.GetTempDirectory(out var dir); using var _1 = Utility.GetTempDirectory(out var dir);
var sample = PathHelper.GetTestRootPath("TestApp"); var sample = PathHelper.GetTestRootPath("TestApp");
Exe.InvokeAndThrowIfNonZero( Exe.InvokeAndThrowIfNonZero(
@@ -96,6 +105,27 @@ public class DotnetUtilTests
sample); sample);
var path = Path.Combine(dir, "TestApp.exe"); var path = Path.Combine(dir, "TestApp.exe");
Assert.Throws<UserInfoException>(() => DotnetUtil.VerifyVelopackApp(path, logger)); Assert.Throws<UserInfoException>(() => compat.Verify(path));
}
[SkippableFact]
public void PublishAsyncMainPasses()
{
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
using var logger = GetCompat(out var compat);
using var _1 = Utility.GetTempDirectory(out var dir);
var sample = PathHelper.GetTestRootPath("TestApp");
Exe.InvokeAndThrowIfNonZero(
"dotnet",
new string[] { "publish", "--no-self-contained", "-r", "win-x64", "-o", dir,
"-p:UseAsyncMain=true" },
sample);
var path = Path.Combine(dir, "TestApp.exe");
Assert.NotNull(compat.Verify(path));
var newPath = Path.Combine(dir, "VeloWpfSample-asd2.exe");
File.Move(path, newPath);
Assert.NotNull(compat.Verify(newPath));
} }
} }