Skip to content

Commit

Permalink
Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
elinor-fung committed Apr 11, 2023
1 parent 25744de commit 684435a
Show file tree
Hide file tree
Showing 6 changed files with 295 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ static Component()
{
Assembly asm = Assembly.GetExecutingAssembly();
Console.WriteLine($"{asm.GetName().Name}: AssemblyLoadContext = {AssemblyLoadContext.GetLoadContext(asm)}");
Console.WriteLine($"{asm.GetName().Name}: Location = '{asm.Location}'");
}

private static void PrintComponentCallLog(string name, IntPtr arg, int size)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,10 @@ public static FluentAssertions.AndConstraint<CommandResultAssertions> ExecuteInD
{
return assertion.HaveStdOutContaining($"{assemblyName}: AssemblyLoadContext = \"Default\" System.Runtime.Loader.DefaultAssemblyLoadContext");
}

public static FluentAssertions.AndConstraint<CommandResultAssertions> ExecuteWithLocation(this CommandResultAssertions assertion, string assemblyName, string location)
{
return assertion.HaveStdOutContaining($"{assemblyName}: Location = '{location}'");
}
}
}
130 changes: 90 additions & 40 deletions src/installer/tests/HostActivation.Tests/NativeHosting/LoadAssembly.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,89 +11,128 @@

namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.NativeHosting
{
public partial class LoadAssembly : IClassFixture<LoadAssembly.SharedTestState>
public class LoadAssembly : IClassFixture<LoadAssembly.SharedTestState>
{
private const string AppLoadAssemblyArg = "app_load_assembly";
private const string ComponentLoadAssemblyArg = "component_load_assembly";

private const string AppLoadAssemblyBytesArg = "app_load_assembly_bytes";
private const string ComponentLoadAssemblyBytesArg = "component_load_assembly_bytes";

private readonly SharedTestState sharedState;

public LoadAssembly(SharedTestState sharedTestState)
{
sharedState = sharedTestState;
}

[Fact]
public void ApplicationContext()
private void ApplicationContext(bool loadAssemblyBytes, bool loadSymbolBytes)
{
var appProject = sharedState.Application;
var componentProject = sharedState.ComponentWithNoDependenciesFixture.TestProject;
string[] args =
var app = sharedState.Application;
var component = sharedState.Component;
IEnumerable<string> args = new[]
{
AppLoadAssemblyArg,
loadAssemblyBytes ? AppLoadAssemblyBytesArg : AppLoadAssemblyArg,
sharedState.HostFxrPath,
appProject.AppDll,
componentProject.AppDll,
sharedState.ComponentTypeName,
sharedState.ComponentEntryPoint1,
};
app.AppDll
}.Concat(sharedState.GetComponentLoadArgs(loadAssemblyBytes, loadSymbolBytes));

CommandResult result = sharedState.CreateNativeHostCommand(args, sharedState.DotNetRoot)
.Execute();

result.Should().Pass()
.And.InitializeContextForApp(appProject.AppDll)
.And.InitializeContextForApp(app.AppDll)
.And.ExecuteSelfContained(selfContained: false)
.And.ExecuteInDefaultContext(componentProject.AssemblyName)
.And.ExecuteInDefaultContext(component.AssemblyName)
.And.ExecuteWithLocation(component.AssemblyName, loadAssemblyBytes ? string.Empty : component.AppDll)
.And.ExecuteFunctionPointer(sharedState.ComponentEntryPoint1, 1, 1);
}

[Fact]
public void ComponentContext()
public void ApplicationContext_FilePath()
{
ApplicationContext(loadAssemblyBytes: false, loadSymbolBytes: false);
}

[Theory]
[InlineData(true)]
[InlineData(false)]
public void ApplicationContext_Bytes(bool loadSymbolBytes)
{
ApplicationContext(loadAssemblyBytes: true, loadSymbolBytes);
}

private void ComponentContext(bool loadAssemblyBytes, bool loadSymbolBytes)
{
var appProject = sharedState.Application;
var componentProject = sharedState.ComponentWithNoDependenciesFixture.TestProject;
string[] args =
var app = sharedState.Application;
var component = sharedState.Component;
IEnumerable<string> args = new[]
{
ComponentLoadAssemblyArg,
loadAssemblyBytes ? ComponentLoadAssemblyBytesArg : ComponentLoadAssemblyArg,
sharedState.HostFxrPath,
componentProject.RuntimeConfigJson,
componentProject.AppDll,
sharedState.ComponentTypeName,
sharedState.ComponentEntryPoint1,
};
component.RuntimeConfigJson
}.Concat(sharedState.GetComponentLoadArgs(loadAssemblyBytes, loadSymbolBytes));

CommandResult result = sharedState.CreateNativeHostCommand(args, sharedState.DotNetRoot)
.Execute();

result.Should().Pass()
.And.InitializeContextForConfig(componentProject.RuntimeConfigJson)
.And.ExecuteInDefaultContext(componentProject.AssemblyName)
.And.InitializeContextForConfig(component.RuntimeConfigJson)
.And.ExecuteInDefaultContext(component.AssemblyName)
.And.ExecuteWithLocation(component.AssemblyName, loadAssemblyBytes ? string.Empty : component.AppDll)
.And.ExecuteFunctionPointer(sharedState.ComponentEntryPoint1, 1, 1);
}

[Fact]
public void SelfContainedApplicationContext()
public void ComponentContext_FilePath()
{
ComponentContext(loadAssemblyBytes: false, loadSymbolBytes: false);
}

[Theory]
[InlineData(true)]
[InlineData(false)]
public void ComponentContext_Bytes(bool loadSymbolBytes)
{
ComponentContext(loadAssemblyBytes: true, loadSymbolBytes);
}

private void SelfContainedApplicationContext(bool loadAssemblyBytes, bool loadSymbolBytes)
{
var appProject = sharedState.SelfContainedApplication;
var componentProject = sharedState.ComponentWithNoDependenciesFixture.TestProject;
string[] args =
var app = sharedState.SelfContainedApplication;
var component = sharedState.Component;
IEnumerable<string> args = new[]
{
AppLoadAssemblyArg,
appProject.HostFxrDll,
appProject.AppDll,
componentProject.AppDll,
sharedState.ComponentTypeName,
sharedState.ComponentEntryPoint1
};
loadAssemblyBytes ? AppLoadAssemblyBytesArg : AppLoadAssemblyArg,
app.HostFxrDll,
app.AppDll
}.Concat(sharedState.GetComponentLoadArgs(loadAssemblyBytes, loadSymbolBytes));

CommandResult result = sharedState.CreateNativeHostCommand(args, sharedState.DotNetRoot)
.Execute();

result.Should().Pass()
.And.InitializeContextForApp(appProject.AppDll)
.And.InitializeContextForApp(app.AppDll)
.And.ExecuteSelfContained(selfContained: true)
.And.ExecuteInDefaultContext(componentProject.AssemblyName)
.And.ExecuteInDefaultContext(component.AssemblyName)
.And.ExecuteWithLocation(component.AssemblyName, loadAssemblyBytes ? string.Empty : component.AppDll)
.And.ExecuteFunctionPointer(sharedState.ComponentEntryPoint1, 1, 1);
}

[Fact]
public void SelfContainedApplicationContext_FilePath()
{
SelfContainedApplicationContext(loadAssemblyBytes: false, loadSymbolBytes: false);
}

[Theory]
[InlineData(true)]
[InlineData(false)]
public void SelfContainedApplicationContext_Bytes(bool loadSymbolBytes)
{
SelfContainedApplicationContext(loadAssemblyBytes: true, loadSymbolBytes);
}

public class SharedTestState : SharedTestStateBase
{
public string HostFxrPath { get; }
Expand All @@ -103,10 +142,10 @@ public class SharedTestState : SharedTestStateBase
public TestApp SelfContainedApplication { get; }

public TestProjectFixture ComponentWithNoDependenciesFixture { get; }
public TestApp Component => ComponentWithNoDependenciesFixture.TestProject.BuiltApp;

public string ComponentTypeName { get; }
public string ComponentEntryPoint1 => "ComponentEntryPoint1";
public string UnmanagedFunctionPointerEntryPoint1 => "UnmanagedFunctionPointerEntryPoint1";

public SharedTestState()
{
Expand All @@ -127,6 +166,17 @@ public SharedTestState()
ComponentTypeName = $"Component.Component, {ComponentWithNoDependenciesFixture.TestProject.AssemblyName}";
}

internal IEnumerable<string> GetComponentLoadArgs(bool loadAssemblyBytes, bool loadSymbolBytes)
{
List<string> args = new List<string>() { Component.AppDll };
if (loadAssemblyBytes)
args.Add(loadSymbolBytes ? $"{Path.GetFileNameWithoutExtension(Component.AppDll)}.pdb" : "nullptr");

args.Add(ComponentTypeName);
args.Add(ComponentEntryPoint1);
return args;
}

protected override void Dispose(bool disposing)
{
if (Application != null)
Expand Down
138 changes: 137 additions & 1 deletion src/native/corehost/test/nativehost/host_context_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,42 @@ namespace
return success;
}

bool call_load_assembly_bytes(
load_assembly_bytes_fn load_assembly_bytes,
const pal::char_t *assembly_path,
const pal::char_t *symbols_path,
const pal::char_t *log_prefix,
pal::stringstream_t &test_output)
{
std::ifstream assembly_file(assembly_path, std::ios::binary);
std::vector<char> assembly_bytes { std::istreambuf_iterator<char>(assembly_file), std::istreambuf_iterator<char>() };
assembly_file.close();

std::vector<char> symbols_bytes;
if (pal::strcmp(symbols_path, _X("nullptr")) != 0)
{
std::ifstream symbols_file(symbols_path, std::ios::binary);
symbols_bytes = std::vector<char>((std::istreambuf_iterator<char>(symbols_file)), (std::istreambuf_iterator<char>()));
symbols_file.close();
}

test_output << log_prefix << _X("calling load_assembly_bytes(")
<< std::hex << (size_t)(assembly_bytes.data()) << _X(", ") << assembly_bytes.size() << _X(", ")
<< std::hex << (size_t)(symbols_bytes.data()) << _X(", ") << symbols_bytes.size()
<< _X(")") << std::endl;

int rc = load_assembly_bytes(
(unsigned char *)assembly_bytes.data(),
assembly_bytes.size(),
symbols_bytes.empty() ? nullptr : (unsigned char *)symbols_bytes.data(),
symbols_bytes.size(),
nullptr /* load_context */,
nullptr /* reserved */);
bool success = rc == StatusCode::Success;
test_output << log_prefix << _X("load_assembly_bytes ") << (success ? _X("succeeded: ") : _X("failed: ")) << std::hex << std::showbase << rc << std::endl;
return success;
}

bool component_load_assembly_test(
const hostfxr_exports &hostfxr,
const pal::char_t *config_path,
Expand Down Expand Up @@ -436,7 +472,7 @@ namespace
{
const pal::char_t *assembly_path = argv[i];
success &= call_load_assembly(load_assembly, assembly_path, log_prefix, test_output);

const pal::char_t *type_name = argv[i + 1];
const pal::char_t *method_name = argv[i + 2];
success &= call_get_function_pointer_flavour(get_function_pointer, type_name, method_name, log_prefix, test_output);
Expand All @@ -448,6 +484,83 @@ namespace
return success && rcClose == StatusCode::Success;
}

bool component_load_assembly_bytes_test(
const hostfxr_exports &hostfxr,
const pal::char_t *config_path,
int argc,
const pal::char_t *argv[],
const pal::char_t *log_prefix,
pal::stringstream_t &test_output)
{
hostfxr_handle handle;
if (!init_for_config(hostfxr, config_path, &handle, log_prefix, test_output))
return false;

load_assembly_bytes_fn load_assembly_bytes = nullptr;
hostfxr_delegate_type hdt = hostfxr_delegate_type::hdt_load_assembly_bytes;
bool success = get_runtime_delegate(hostfxr, handle, hdt, (void **)&load_assembly_bytes, log_prefix, test_output);

get_function_pointer_fn get_function_pointer = nullptr;
hdt = hostfxr_delegate_type::hdt_get_function_pointer;
success &= get_runtime_delegate(hostfxr, handle, hdt, (void **)&get_function_pointer, log_prefix, test_output);
if (success)
{
for (int i = 0; i <= argc - 4; i += 4)
{
const pal::char_t *assembly_path = argv[i];
const pal::char_t *symbols_path = argv[i + 1];
success &= call_load_assembly_bytes(load_assembly_bytes, assembly_path, symbols_path, log_prefix, test_output);

const pal::char_t *type_name = argv[i + 2];
const pal::char_t *method_name = argv[i + 3];
success &= call_get_function_pointer_flavour(get_function_pointer, type_name, method_name, log_prefix, test_output);
}
}

int rcClose = hostfxr.close(handle);
if (rcClose != StatusCode::Success)
test_output << log_prefix << _X("hostfxr_close failed: ") << std::hex << std::showbase << rcClose << std::endl;

return success && rcClose == StatusCode::Success;
}

bool app_load_assembly_bytes_test(
const hostfxr_exports &hostfxr,
int argc,
const pal::char_t *argv[],
const pal::char_t *log_prefix,
pal::stringstream_t &test_output)
{
hostfxr_handle handle;
if (!init_for_command_line(hostfxr, argc, argv, &handle, log_prefix, test_output))
return false;

load_assembly_bytes_fn load_assembly_bytes = nullptr;
hostfxr_delegate_type hdt = hostfxr_delegate_type::hdt_load_assembly_bytes;
bool success = get_runtime_delegate(hostfxr, handle, hdt, (void **)&load_assembly_bytes, log_prefix, test_output);

get_function_pointer_fn get_function_pointer = nullptr;
hdt = hostfxr_delegate_type::hdt_get_function_pointer;
success &= get_runtime_delegate(hostfxr, handle, hdt, (void **)&get_function_pointer, log_prefix, test_output);
if (success)
{
for (int i = 1; i <= argc - 4; i += 4)
{
const pal::char_t *assembly_path = argv[i];
const pal::char_t *symbols_path = argv[i + 1];
success &= call_load_assembly_bytes(load_assembly_bytes, assembly_path, symbols_path, log_prefix, test_output);

const pal::char_t *type_name = argv[i + 2];
const pal::char_t *method_name = argv[i + 3];
success &= call_get_function_pointer_flavour(get_function_pointer, type_name, method_name, log_prefix, test_output);
}
}
int rcClose = hostfxr.close(handle);
if (rcClose != StatusCode::Success)
test_output << log_prefix << _X("hostfxr_close failed: ") << std::hex << std::showbase << rcClose << std::endl;
return success && rcClose == StatusCode::Success;
}

bool component_load_assembly_and_get_function_pointer_test(
const hostfxr_exports &hostfxr,
const pal::char_t *config_path,
Expand Down Expand Up @@ -819,6 +932,29 @@ bool host_context_test::app_load_assembly(
return app_load_assembly_test(hostfxr, argc, argv, config_log_prefix, test_output);
}

bool host_context_test::component_load_assembly_bytes(
const pal::string_t &hostfxr_path,
const pal::char_t *config_path,
int argc,
const pal::char_t *argv[],
pal::stringstream_t &test_output)
{
hostfxr_exports hostfxr{ hostfxr_path };

return component_load_assembly_bytes_test(hostfxr, config_path, argc, argv, config_log_prefix, test_output);
}

bool host_context_test::app_load_assembly_bytes(
const pal::string_t &hostfxr_path,
int argc,
const pal::char_t *argv[],
pal::stringstream_t &test_output)
{
hostfxr_exports hostfxr{ hostfxr_path };

return app_load_assembly_bytes_test(hostfxr, argc, argv, config_log_prefix, test_output);
}

bool host_context_test::component_load_assembly_and_get_function_pointer(
const pal::string_t &hostfxr_path,
const pal::char_t *config_path,
Expand Down
Loading

0 comments on commit 684435a

Please sign in to comment.