diff --git a/src/installer/tests/Assets/TestProjects/BundleProbeTester/BundleProbeTester.csproj b/src/installer/tests/Assets/TestProjects/BundleProbeTester/BundleProbeTester.csproj deleted file mode 100644 index cb2391b1e4918..0000000000000 --- a/src/installer/tests/Assets/TestProjects/BundleProbeTester/BundleProbeTester.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - $(NetCoreAppCurrent) - Exe - $(TestTargetRid) - $(MNAVersion) - true - - - - true - - - diff --git a/src/installer/tests/Assets/TestProjects/BundleProbeTester/Program.cs b/src/installer/tests/Assets/TestProjects/BundleProbeTester/Program.cs deleted file mode 100644 index fdd1fdeb32f24..0000000000000 --- a/src/installer/tests/Assets/TestProjects/BundleProbeTester/Program.cs +++ /dev/null @@ -1,96 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; - -namespace BundleProbeTester -{ - public static class Program - { - // The return type on BundleProbeDelegate is byte instead of bool because - // using non-blitable bool type caused a failure (incorrect value) on linux-musl-x64. - // The bundle-probe callback is only called from native code in the product - // Therefore the type on this test is adjusted to circumvent the failure. - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate byte BundleProbeDelegate([MarshalAs(UnmanagedType.LPUTF8Str)] string path, IntPtr offset, IntPtr size, IntPtr compressedSize); - - unsafe static bool Probe(BundleProbeDelegate bundleProbe, string path, bool isExpected) - { - Int64 size, offset, compressedSize; - bool exists = bundleProbe(path, (IntPtr)(&offset), (IntPtr)(&size), (IntPtr)(&compressedSize)) != 0; - - switch (exists, isExpected) - { - case (true, true): - if (compressedSize < 0 || compressedSize > size) - { - Console.WriteLine($"Invalid compressedSize obtained for {path} within bundle."); - return false; - } - - if (size > 0 && offset > 0) - { - return true; - } - - Console.WriteLine($"Invalid location obtained for {path} within bundle."); - return false; - - case (true, false): - Console.WriteLine($"Unexpected file {path} found in bundle."); - return false; - - case (false, true): - Console.WriteLine($"Expected file {path} not found in bundle."); - return false; - - case (false, false): - return true; - } - } - - public static int Main(string[] args) - { - bool isSingleFile = args.Length > 0 && args[0].Equals("SingleFile"); - object probeObject = System.AppDomain.CurrentDomain.GetData("BUNDLE_PROBE"); - - if (!isSingleFile) - { - if (probeObject != null) - { - Console.WriteLine("BUNDLE_PROBE property passed in for a non-single-file app"); - return -1; - } - - Console.WriteLine("No BUNDLE_PROBE"); - return 0; - } - - if (probeObject == null) - { - Console.WriteLine("BUNDLE_PROBE property not passed in for a single-file app"); - return -2; - } - - string probeString = probeObject as string; - IntPtr probePtr = (IntPtr)Convert.ToUInt64(probeString, 16); - BundleProbeDelegate bundleProbeDelegate = Marshal.GetDelegateForFunctionPointer(probePtr); - bool success = - Probe(bundleProbeDelegate, "BundleProbeTester.dll", isExpected: true) && - Probe(bundleProbeDelegate, "BundleProbeTester.runtimeconfig.json", isExpected: true) && - Probe(bundleProbeDelegate, "System.Private.CoreLib.dll", isExpected: true) && - Probe(bundleProbeDelegate, "hostpolicy.dll", isExpected: false) && - Probe(bundleProbeDelegate, "--", isExpected: false) && - Probe(bundleProbeDelegate, "", isExpected: false); - - if (!success) - { - return -3; - } - - Console.WriteLine("BUNDLE_PROBE OK"); - return 0; - } - } -} diff --git a/src/installer/tests/Assets/TestProjects/HostApiInvokerApp/HostApiInvokerApp.csproj b/src/installer/tests/Assets/TestProjects/HostApiInvokerApp/HostApiInvokerApp.csproj index d040ee25bcfec..056d9ce8dfef6 100644 --- a/src/installer/tests/Assets/TestProjects/HostApiInvokerApp/HostApiInvokerApp.csproj +++ b/src/installer/tests/Assets/TestProjects/HostApiInvokerApp/HostApiInvokerApp.csproj @@ -3,6 +3,7 @@ $(NetCoreAppCurrent) Exe + $(TestTargetRid) $(MNAVersion) WINDOWS;$(DefineConstants) true diff --git a/src/installer/tests/Assets/TestProjects/HostApiInvokerApp/HostRuntimeContract.cs b/src/installer/tests/Assets/TestProjects/HostApiInvokerApp/HostRuntimeContract.cs index a2a4d6161acea..f9f1b15295eaa 100644 --- a/src/installer/tests/Assets/TestProjects/HostApiInvokerApp/HostRuntimeContract.cs +++ b/src/installer/tests/Assets/TestProjects/HostApiInvokerApp/HostRuntimeContract.cs @@ -15,7 +15,7 @@ internal struct host_runtime_contract public nint size; public void* context; public delegate* unmanaged[Stdcall] get_runtime_property; - public IntPtr bundle_probe; + public delegate* unmanaged[Stdcall] bundle_probe; public IntPtr pinvoke_override; } @@ -68,12 +68,52 @@ static string GetProperty(string name, host_runtime_contract contract) } } + public static void Test_bundle_probe(string[] args) + { + host_runtime_contract contract = GetContract(); + if (contract.bundle_probe == null) + { + Console.WriteLine("host_runtime_contract.bundle_probe is not set"); + return; + } + + bool success = true; + foreach (string path in args) + { + Probe(contract, path); + } + + unsafe static void Probe(host_runtime_contract contract, string path) + { + Span pathSpan = stackalloc byte[Encoding.UTF8.GetMaxByteCount(path.Length)]; + byte* pathPtr = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(pathSpan)); + int pathLen = Encoding.UTF8.GetBytes(path, pathSpan); + pathSpan[pathLen] = 0; + + Int64 size, offset, compressedSize; + bool exists = contract.bundle_probe(pathPtr, (IntPtr)(&offset), (IntPtr)(&size), (IntPtr)(&compressedSize)) != 0; + + Console.WriteLine($"{nameof(host_runtime_contract.get_runtime_property)}: {path} - found = {exists}"); + if (exists) + { + if (compressedSize < 0 || compressedSize > size) + throw new Exception($"Invalid compressedSize obtained for {path} within bundle."); + + if (size <= 0 || offset <= 0) + throw new Exception($"Invalid location obtained for {path} within bundle."); + } + } + } + public static bool RunTest(string apiToTest, string[] args) { switch (apiToTest) { case $"{nameof(host_runtime_contract)}.{nameof(host_runtime_contract.get_runtime_property)}": - Test_get_runtime_property(args); + Test_get_runtime_property(args[1..]); + break; + case $"{nameof(host_runtime_contract)}.{nameof(host_runtime_contract.bundle_probe)}": + Test_bundle_probe(args[1..]); break; default: return false; diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleProbe.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleProbe.cs index 8679f834a638a..1c61a709d3b50 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleProbe.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleProbe.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Linq; using BundleTests.Helpers; using Microsoft.DotNet.Cli.Build.Framework; using Microsoft.DotNet.CoreSetup.Test; @@ -19,35 +20,45 @@ public BundleProbe(SharedTestState fixture) } [Fact] - private void Bundle_Probe_Not_Passed_For_Non_Single_File_App() + private void NonSingleFileApp_NoProbe() { var fixture = sharedTestState.TestFixture.Copy(); string appExe = BundleHelper.GetHostPath(fixture); - Command.Create(appExe) + Command.Create(appExe, "host_runtime_contract.bundle_probe") .CaptureStdErr() .CaptureStdOut() .Execute() - .Should() - .Pass() - .And - .HaveStdOutContaining("No BUNDLE_PROBE"); + .Should().Pass() + .And.HaveStdOutContaining("host_runtime_contract.bundle_probe is not set"); } [Fact] - private void Bundle_Probe_Passed_For_Single_File_App() + private void SingleFileApp_ProbeFiles() { var fixture = sharedTestState.TestFixture.Copy(); string singleFile = BundleSelfContainedApp(fixture); - Command.Create(singleFile, "SingleFile") + (string Path, bool ShouldBeFound)[] itemsToProbe = new[] + { + ($"{fixture.TestProject.AssemblyName}.dll", true), + ($"{fixture.TestProject.AssemblyName}.runtimeconfig.json", true), + ("System.Private.CoreLib.dll", true), + ("hostpolicy.dll", false), + ("--", false), + (string.Empty, false), + }; + + var result = Command.Create(singleFile, $"host_runtime_contract.bundle_probe {string.Join(" ", itemsToProbe.Select(i => i.Path))}") .CaptureStdErr() .CaptureStdOut() - .Execute() - .Should() - .Pass() - .And - .HaveStdOutContaining("BUNDLE_PROBE OK"); + .Execute(); + + result.Should().Pass(); + foreach (var item in itemsToProbe) + { + result.Should().HaveStdOutContaining($"{item.Path} - found = {item.ShouldBeFound}"); + } } public class SharedTestState : SharedTestStateBase, IDisposable @@ -56,7 +67,7 @@ public class SharedTestState : SharedTestStateBase, IDisposable public SharedTestState() { - TestFixture = PreparePublishedSelfContainedTestProject("BundleProbeTester"); + TestFixture = PreparePublishedSelfContainedTestProject("HostApiInvokerApp"); } public void Dispose()