From 999f247298c0ebc5e17223a6f2c08caab9983767 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 13 Dec 2023 14:42:08 -0800 Subject: [PATCH 01/59] Move the delta builder into a separate tool --- test/DNMD.Tests.sln | 6 + test/Regression.DeltaBuilder/Program.cs | 140 ++++++++++++++++++ .../Regression.DeltaBuilder.csproj | 16 ++ 3 files changed, 162 insertions(+) create mode 100644 test/Regression.DeltaBuilder/Program.cs create mode 100644 test/Regression.DeltaBuilder/Regression.DeltaBuilder.csproj diff --git a/test/DNMD.Tests.sln b/test/DNMD.Tests.sln index 66062f70..44b8f333 100644 --- a/test/DNMD.Tests.sln +++ b/test/DNMD.Tests.sln @@ -9,6 +9,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Regression.Performance", "R EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Regression.TargetAssembly", "Regression.TargetAssembly\Regression.TargetAssembly.ilproj", "{D3BEEF3D-C137-49AC-96DD-E5B93E2F4C21}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Regression.DeltaBuilder", "Regression.DeltaBuilder\Regression.DeltaBuilder.csproj", "{6E62512A-7FB4-41DD-9E93-014D0280C77D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -27,6 +29,10 @@ Global {D3BEEF3D-C137-49AC-96DD-E5B93E2F4C21}.Debug|Any CPU.Build.0 = Debug|Any CPU {D3BEEF3D-C137-49AC-96DD-E5B93E2F4C21}.Release|Any CPU.ActiveCfg = Release|Any CPU {D3BEEF3D-C137-49AC-96DD-E5B93E2F4C21}.Release|Any CPU.Build.0 = Release|Any CPU + {6E62512A-7FB4-41DD-9E93-014D0280C77D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6E62512A-7FB4-41DD-9E93-014D0280C77D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6E62512A-7FB4-41DD-9E93-014D0280C77D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6E62512A-7FB4-41DD-9E93-014D0280C77D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/test/Regression.DeltaBuilder/Program.cs b/test/Regression.DeltaBuilder/Program.cs new file mode 100644 index 00000000..c770312c --- /dev/null +++ b/test/Regression.DeltaBuilder/Program.cs @@ -0,0 +1,140 @@ +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Emit; + +/// +/// Generates images with metadata deltas for use in the regression tests. +/// +public static class Program +{ + /// + /// Generate images with metadata deltas into the output directory. + /// + /// The directory to output the base and delta images into. + public static void Main(DirectoryInfo output) + { + // Create the directory if it does not exist. + output.Create(); + + ImmutableArray imageScenarios = [ DeltaAssembly1() ]; + + foreach (DeltaAssembly scenario in imageScenarios) + { + File.WriteAllBytes(Path.Combine(output.FullName, $"{scenario.Name}.dll"), scenario.BaseImage); + Console.WriteLine($"Wrote {scenario.Name}.dll base image"); + for (int i = 0; i < scenario.MetadataDeltas.Length; i++) + { + File.WriteAllBytes(Path.Combine(output.FullName, $"{scenario.Name}.{i + 1}.mddelta"), scenario.MetadataDeltas[i]); + Console.WriteLine($"Wrote {scenario.Name}.dll metadata delta {i + 1}"); + } + } + } + + static DeltaAssembly DeltaAssembly1() + { + Compilation baselineCompilation = CSharpCompilation.Create("DeltaAssembly1") + .WithReferences(Basic.Reference.Assemblies.NetStandard20.ReferenceInfos.netstandard.Reference) + .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); + SyntaxTree sourceBase = CSharpSyntaxTree.ParseText(""" + using System; + public class Class1 + { + private int field; + public void Method(int x) + { + } + + public int Property { get; set; } + + public event EventHandler? Event; + } + """); + baselineCompilation = baselineCompilation.AddSyntaxTrees( + sourceBase, + CSharpSyntaxTree.ParseText(""" + using System; + public class Class2 + { + private int field; + public void Method(int x) + { + } + + public int Property { get; set; } + + public event EventHandler? Event; + } + """)); + + Compilation diffCompilation = baselineCompilation.ReplaceSyntaxTree( + sourceBase, + CSharpSyntaxTree.ParseText(""" + using System; + public class Class1 + { + private class Attr : Attribute { } + + private short field2; + private int field; + + [return:Attr] + public void Method(int x) + { + } + + public int Property { get; set; } + + public short Property2 { get; set; } + + public event EventHandler? Event; + + public event EventHandler? Event2; + } + """)); + + var diagnostics = baselineCompilation.GetDiagnostics(); + MemoryStream baselineImage = new(); + baselineCompilation.Emit(baselineImage, options: new EmitOptions(debugInformationFormat: DebugInformationFormat.PortablePdb)); + baselineImage.Seek(0, SeekOrigin.Begin); + + ModuleMetadata metadata = ModuleMetadata.CreateFromStream(baselineImage); + EmitBaseline baseline = EmitBaseline.CreateInitialBaseline(metadata, _ => default, _ => default, true); + + MemoryStream mddiffStream = new(); + + diffCompilation.EmitDifference( + baseline, + new[] + { + CreateSemanticEdit(SemanticEditKind.Update, baselineCompilation, diffCompilation, c => c.GetTypeByMetadataName("Class1")), + CreateSemanticEdit(SemanticEditKind.Insert, baselineCompilation, diffCompilation, c => c.GetTypeByMetadataName("Class1")!.GetMembers("field2").FirstOrDefault()), + CreateSemanticEdit(SemanticEditKind.Insert, baselineCompilation, diffCompilation, c => c.GetTypeByMetadataName("Class1")!.GetMembers("Property2").FirstOrDefault()), + CreateSemanticEdit(SemanticEditKind.Insert, baselineCompilation, diffCompilation, c => c.GetTypeByMetadataName("Class1")!.GetMembers("Event2").FirstOrDefault()), + CreateSemanticEdit(SemanticEditKind.Update, baselineCompilation, diffCompilation, c => c.GetTypeByMetadataName("Class1")!.GetMembers("Method").FirstOrDefault()), + CreateSemanticEdit(SemanticEditKind.Insert, baselineCompilation, diffCompilation, c => c.GetTypeByMetadataName("Class1")!.GetTypeMembers("Attr").FirstOrDefault()), + }, + s => + { + return false; + }, + mddiffStream, + new MemoryStream(), // il stream + new MemoryStream() // pdb diff stream + ); + + baselineImage.Seek(0, SeekOrigin.Begin); + return new DeltaAssembly( + nameof(DeltaAssembly1), + baselineImage.ToArray(), + [ mddiffStream.ToArray() ] + ); + } + + static SemanticEdit CreateSemanticEdit(SemanticEditKind editKind, Compilation baseline, Compilation diff, Func findSymbol) + { + return new SemanticEdit(editKind, findSymbol(baseline), findSymbol(diff)); + } + + record struct DeltaAssembly(string Name, byte[] BaseImage, ImmutableArray MetadataDeltas); +} \ No newline at end of file diff --git a/test/Regression.DeltaBuilder/Regression.DeltaBuilder.csproj b/test/Regression.DeltaBuilder/Regression.DeltaBuilder.csproj new file mode 100644 index 00000000..f1bd79b1 --- /dev/null +++ b/test/Regression.DeltaBuilder/Regression.DeltaBuilder.csproj @@ -0,0 +1,16 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + From 246ab6e740374eebc992d5f471c0e1b8617576a9 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 13 Dec 2023 15:28:27 -0800 Subject: [PATCH 02/59] Add tool to locate CoreCLR --- test/DNMD.Tests.sln | 6 ++++++ test/Regression.CoreClrLocator/Program.cs | 2 ++ .../Regression.CoreClrLocator.csproj | 10 ++++++++++ 3 files changed, 18 insertions(+) create mode 100644 test/Regression.CoreClrLocator/Program.cs create mode 100644 test/Regression.CoreClrLocator/Regression.CoreClrLocator.csproj diff --git a/test/DNMD.Tests.sln b/test/DNMD.Tests.sln index 44b8f333..65ef9ee0 100644 --- a/test/DNMD.Tests.sln +++ b/test/DNMD.Tests.sln @@ -11,6 +11,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Regression.TargetAssembly", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Regression.DeltaBuilder", "Regression.DeltaBuilder\Regression.DeltaBuilder.csproj", "{6E62512A-7FB4-41DD-9E93-014D0280C77D}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Regression.CoreClrLocator", "Regression.CoreClrLocator\Regression.CoreClrLocator.csproj", "{F472BDA7-5315-48CC-83DE-0F7F8E4C5D42}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -33,6 +35,10 @@ Global {6E62512A-7FB4-41DD-9E93-014D0280C77D}.Debug|Any CPU.Build.0 = Debug|Any CPU {6E62512A-7FB4-41DD-9E93-014D0280C77D}.Release|Any CPU.ActiveCfg = Release|Any CPU {6E62512A-7FB4-41DD-9E93-014D0280C77D}.Release|Any CPU.Build.0 = Release|Any CPU + {F472BDA7-5315-48CC-83DE-0F7F8E4C5D42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F472BDA7-5315-48CC-83DE-0F7F8E4C5D42}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F472BDA7-5315-48CC-83DE-0F7F8E4C5D42}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F472BDA7-5315-48CC-83DE-0F7F8E4C5D42}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/test/Regression.CoreClrLocator/Program.cs b/test/Regression.CoreClrLocator/Program.cs new file mode 100644 index 00000000..a29d30f6 --- /dev/null +++ b/test/Regression.CoreClrLocator/Program.cs @@ -0,0 +1,2 @@ +// Output the location of CoreCLR so we can locate it in the tests. +Console.WriteLine(Path.Combine(Path.GetDirectoryName(typeof(object).Assembly.Location!)!, OperatingSystem.IsWindows() ? "coreclr.dll" : "libcoreclr.so")); diff --git a/test/Regression.CoreClrLocator/Regression.CoreClrLocator.csproj b/test/Regression.CoreClrLocator/Regression.CoreClrLocator.csproj new file mode 100644 index 00000000..2150e379 --- /dev/null +++ b/test/Regression.CoreClrLocator/Regression.CoreClrLocator.csproj @@ -0,0 +1,10 @@ + + + + Exe + net8.0 + enable + enable + + + From 30c64f9950fa540e5635ecb27e1949693b44d546 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 13 Dec 2023 15:28:38 -0800 Subject: [PATCH 03/59] Add basic infra for GoogleTest --- test/CMakeLists.txt | 24 +++++++++++++++++++++++- test/regtest/CMakeLists.txt | 0 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 test/regtest/CMakeLists.txt diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1c4112a8..9a933bfc 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,4 +1,26 @@ # Configure the compiler include(../configure.cmake) -add_subdirectory(regnative/) \ No newline at end of file +add_subdirectory(regnative/) + +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +if (POLICY CMP0135) + cmake_policy(SET CMP0135 NEW) # Set timestamps in downloaded archives to the time of download. +endif() + +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip +) +# For Windows: Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) + +enable_testing() + +include(GoogleTest) + +add_subdirectory(regtest) \ No newline at end of file diff --git a/test/regtest/CMakeLists.txt b/test/regtest/CMakeLists.txt new file mode 100644 index 00000000..e69de29b From 2979f9cff4ff24c24a9e8090d6d3219fe752eb94 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 19 Dec 2023 14:54:25 -0800 Subject: [PATCH 04/59] Build out some basic structure for locating CoreCLR and discovering files to test --- test/regtest/CMakeLists.txt | 17 ++++++++ test/regtest/baseline.h | 14 +++++++ test/regtest/discovery.cpp | 55 ++++++++++++++++++++++++++ test/regtest/fixtures.h | 35 +++++++++++++++++ test/regtest/main.cpp | 78 +++++++++++++++++++++++++++++++++++++ 5 files changed, 199 insertions(+) create mode 100644 test/regtest/baseline.h create mode 100644 test/regtest/discovery.cpp create mode 100644 test/regtest/fixtures.h create mode 100644 test/regtest/main.cpp diff --git a/test/regtest/CMakeLists.txt b/test/regtest/CMakeLists.txt index e69de29b..61bf3558 100644 --- a/test/regtest/CMakeLists.txt +++ b/test/regtest/CMakeLists.txt @@ -0,0 +1,17 @@ +set(HEADERS + ./baseline.h + ./fixtures.h +) +set(SOURCES + ./main.cpp + ./discovery.cpp +) + + +add_executable(regtest ${SOURCES} ${HEADERS}) +target_link_libraries(regtest PRIVATE dnmd::interfaces gtest dncp::dncp) +set_target_properties(regtest PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON) # Require C++17 for the tests so we can use std::filesystem. + +if (NOT WIN32) + target_link_libraries(regtest PRIVATE dncp::winhdrs) +endif() \ No newline at end of file diff --git a/test/regtest/baseline.h b/test/regtest/baseline.h new file mode 100644 index 00000000..ccb567d9 --- /dev/null +++ b/test/regtest/baseline.h @@ -0,0 +1,14 @@ +#ifndef _TEST_REGTEST_BASELINE_H_ +#define _TEST_REGTEST_BASELINE_H_ + +#include +#include +#include + +namespace TestBaseline +{ + extern dncp::com_ptr Metadata; + extern dncp::com_ptr Symbol; +} + +#endif // !_TEST_REGTEST_BASELINE_H_ \ No newline at end of file diff --git a/test/regtest/discovery.cpp b/test/regtest/discovery.cpp new file mode 100644 index 00000000..a8b6da96 --- /dev/null +++ b/test/regtest/discovery.cpp @@ -0,0 +1,55 @@ +#include "fixtures.h" +#include "baseline.h" +#include +#include +#include "dnmd_interfaces.hpp" + +#define THROW_IF_FAILED(hr) if (FAILED(hr)) throw std::runtime_error("HRESULT: " #hr) + +namespace +{ + std::vector MetadataInDirectory(std::string directory, CorOpenFlags flags) + { + std::vector scenarios; + for (auto& entry : std::filesystem::directory_iterator(directory)) + { + if (entry.is_regular_file()) + { + auto path = entry.path(); + auto ext = path.extension(); + if (ext == ".dll") + { + MetadataRegresssion scenario; + scenario.modulePath = path.string(); + scenario.baseline = nullptr; + scenario.test = nullptr; + + std::basic_string u16path; // use basic_string instead of wstring to get the right type on all platforms + u16path.resize(scenario.modulePath.size()); + // This is a hack to convert from char to WCHAR + // TODO: Refactor to use the PAL from dnmd::interfaces + std::transform(scenario.modulePath.begin(), scenario.modulePath.end(), u16path.begin(), [](char c) { return (WCHAR)c; }); + + THROW_IF_FAILED(TestBaseline::Metadata->OpenScope(u16path.c_str(), flags, IID_IMetaDataImport2, (IUnknown**)&scenario.baseline)); + + dncp::com_ptr dnmdDispenser; + THROW_IF_FAILED(GetDispenser(IID_IMetaDataDispenser, (void**)&dnmdDispenser)); + + THROW_IF_FAILED(dnmdDispenser->OpenScope(u16path.c_str(), flags, IID_IMetaDataImport2, (IUnknown**)&scenario.test)); + + scenarios.push_back(scenario); + } + } + } + } +} + +std::vector ReadOnlyMetadataInDirectory(std::string directory) +{ + return MetadataInDirectory(directory, ofReadOnly); +} + +std::vector ReadWriteMetadataInDirectory(std::string directory) +{ + return MetadataInDirectory(directory, ofWrite); +} diff --git a/test/regtest/fixtures.h b/test/regtest/fixtures.h new file mode 100644 index 00000000..be4d1ab7 --- /dev/null +++ b/test/regtest/fixtures.h @@ -0,0 +1,35 @@ +#ifndef _TEST_REGTEST_FIXTURES_H_ +#define _TEST_REGTEST_FIXTURES_H_ + +#include +#include "baseline.h" +#include + +#include "cor.h" +#include "corsym.h" +#include + +template +struct Regression +{ + std::string modulePath; + dncp::com_ptr baseline; + dncp::com_ptr test; +}; + +using MetadataRegresssion = Regression; + +using SymbolRegression = Regression; + +std::vector ReadOnlyMetadataInDirectory(std::string directory); +std::vector ReadWriteMetadataInDirectory(std::string directory); + +class MetaDataRegressionTest : public ::testing::TestWithParam +{ +}; + +class SymbolRegressionTest : public ::testing::TestWithParam +{ +}; + +#endif // !_TEST_REGTEST_FIXTURES_H_ \ No newline at end of file diff --git a/test/regtest/main.cpp b/test/regtest/main.cpp new file mode 100644 index 00000000..83c2ffcf --- /dev/null +++ b/test/regtest/main.cpp @@ -0,0 +1,78 @@ +#include "baseline.h" +#include +#include + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#else +#include +#endif + +namespace TestBaseline +{ + dncp::com_ptr Metadata = nullptr; + dncp::com_ptr Symbol = nullptr; +} + +namespace +{ + void* LoadModule(char const* path) + { +#ifdef _WIN32 + return LoadLibraryA(path); +#else + return dlopen(path, RTLD_LAZY); +#endif + } + + void* GetSymbol(void* module, char const* name) + { +#ifdef _WIN32 + return GetProcAddress((HMODULE)module, name); +#else + return dlsym(module, name); +#endif + } + + using MetaDataGetDispenser = HRESULT(__stdcall*)(REFCLSID, REFIID, LPVOID*); +} + +#define RETURN_IF_FAILED(x) { auto hr = x; if (FAILED(hr)) return hr; } + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + + for (int i = 0; i < argc; ++i) { + if (std::string(argv[i]) == "--md-baseline" && i + 1 < argc) + { + auto mod = LoadModule(argv[++i]); + if (TestBaseline::Metadata == nullptr) + { + printf("Failed to load metadata baseline module: %s\n", argv[i]); + return -1; + } + + auto getDispenser = (MetaDataGetDispenser)GetSymbol(mod, "MetaDataGetDispenser"); + if (getDispenser == nullptr) + { + printf("Failed to find MetaDataGetDispenser in module: %s\n", argv[i]); + return -1; + } + + RETURN_IF_FAILED(getDispenser(CLSID_CorMetaDataDispenser, IID_IMetaDataDispenser, (void**)&TestBaseline::Metadata)); + } + else if (std::string(argv[i]) == "--sym-baseline" && i + 1 < argc) + { + auto mod = LoadModule(argv[i + 1]); + if (TestBaseline::Metadata == nullptr) + { + printf("Failed to load symbol baseline module: %s\n", argv[i]); + return -1; + } + } + } + + return RUN_ALL_TESTS(); +} From 54ecd2f3507eecded3f123b88ce43dc4c8e1a703 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 19 Dec 2023 16:15:07 -0800 Subject: [PATCH 05/59] Do a first pass of moving a test over and building out the infrastructure in C++ to be able to run the tests we want to run. --- test/regtest/CMakeLists.txt | 6 +- test/regtest/asserts.h | 9 + test/regtest/baseline.h | 5 + test/regtest/discovery.cpp | 19 +- test/regtest/fixtures.h | 21 +- test/regtest/main.cpp | 53 +- test/regtest/metadata.cpp | 1734 +++++++++++++++++++++++++++++++++++ test/regtest/pal.cpp | 26 + test/regtest/pal.hpp | 7 + 9 files changed, 1838 insertions(+), 42 deletions(-) create mode 100644 test/regtest/asserts.h create mode 100644 test/regtest/metadata.cpp create mode 100644 test/regtest/pal.cpp create mode 100644 test/regtest/pal.hpp diff --git a/test/regtest/CMakeLists.txt b/test/regtest/CMakeLists.txt index 61bf3558..b413ce83 100644 --- a/test/regtest/CMakeLists.txt +++ b/test/regtest/CMakeLists.txt @@ -1,15 +1,19 @@ set(HEADERS ./baseline.h ./fixtures.h + ./asserts.h + ./pal.hpp ) set(SOURCES ./main.cpp ./discovery.cpp + ./pal.cpp + ./metadata.cpp ) add_executable(regtest ${SOURCES} ${HEADERS}) -target_link_libraries(regtest PRIVATE dnmd::interfaces gtest dncp::dncp) +target_link_libraries(regtest PRIVATE dnmd::interfaces gtest gmock dncp::dncp) # Reference gmock for better collection assertions set_target_properties(regtest PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON) # Require C++17 for the tests so we can use std::filesystem. if (NOT WIN32) diff --git a/test/regtest/asserts.h b/test/regtest/asserts.h new file mode 100644 index 00000000..f5e1cd16 --- /dev/null +++ b/test/regtest/asserts.h @@ -0,0 +1,9 @@ +#ifndef _TEST_REGTEST_ASSERTS_H_ +#define _TEST_REGTEST_ASSERTS_H_ + +#include + +#define ASSERT_THAT_AND_RETURN(a, match) ([&](){ auto&& _actual = (a); ASSERT_THAT(_actual, match); return _actual; }()) +#define EXPECT_THAT_AND_RETURN(a, match) ([&](){ auto&& _actual = (a); EXPECT_THAT(_actual, match); return _actual; }()) + +#endif // !_TEST_REGTEST_ASSERTS_H_ diff --git a/test/regtest/baseline.h b/test/regtest/baseline.h index ccb567d9..e357da0a 100644 --- a/test/regtest/baseline.h +++ b/test/regtest/baseline.h @@ -1,7 +1,12 @@ #ifndef _TEST_REGTEST_BASELINE_H_ #define _TEST_REGTEST_BASELINE_H_ +#if defined(_MSC_VER) +#define NOMINMAX +#include +#endif #include + #include #include diff --git a/test/regtest/discovery.cpp b/test/regtest/discovery.cpp index a8b6da96..9a64b2c9 100644 --- a/test/regtest/discovery.cpp +++ b/test/regtest/discovery.cpp @@ -37,10 +37,12 @@ namespace THROW_IF_FAILED(dnmdDispenser->OpenScope(u16path.c_str(), flags, IID_IMetaDataImport2, (IUnknown**)&scenario.test)); - scenarios.push_back(scenario); + scenarios.push_back(std::move(scenario)); } } } + + return scenarios; } } @@ -53,3 +55,18 @@ std::vector ReadWriteMetadataInDirectory(std::string direct { return MetadataInDirectory(directory, ofWrite); } + +namespace +{ + std::string baselinePath; +} + +std::string GetBaselineDirectory() +{ + return std::filesystem::path(baselinePath).parent_path().string(); +} + +void SetBaselineModulePath(std::string path) +{ + baselinePath = path; +} \ No newline at end of file diff --git a/test/regtest/fixtures.h b/test/regtest/fixtures.h index be4d1ab7..6126d025 100644 --- a/test/regtest/fixtures.h +++ b/test/regtest/fixtures.h @@ -2,16 +2,25 @@ #define _TEST_REGTEST_FIXTURES_H_ #include -#include "baseline.h" + +#if defined(_MSC_VER) +#define NOMINMAX +#include +#endif #include -#include "cor.h" -#include "corsym.h" +#include +#include + #include + +// TODO: Can't pre-create here as dncp::com_ptr is non-copyable, need to create in the test I guess. Change discovery to be a function that returns a vector of spans of the metadata in PE. template struct Regression { + using ParamType = Regression; + std::string modulePath; dncp::com_ptr baseline; dncp::com_ptr test; @@ -24,6 +33,12 @@ using SymbolRegression = Regression; std::vector ReadOnlyMetadataInDirectory(std::string directory); std::vector ReadWriteMetadataInDirectory(std::string directory); +std::string FindFrameworkInstall(std::string version); + +std::string GetBaselineDirectory(); + +void SetBaselineModulePath(std::string path); + class MetaDataRegressionTest : public ::testing::TestWithParam { }; diff --git a/test/regtest/main.cpp b/test/regtest/main.cpp index 83c2ffcf..3f515400 100644 --- a/test/regtest/main.cpp +++ b/test/regtest/main.cpp @@ -1,14 +1,8 @@ -#include "baseline.h" #include #include - -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#define NOMINMAX -#include -#else -#include -#endif +#include "baseline.h" +#include "fixtures.h" +#include "pal.hpp" namespace TestBaseline { @@ -18,36 +12,28 @@ namespace TestBaseline namespace { - void* LoadModule(char const* path) - { -#ifdef _WIN32 - return LoadLibraryA(path); -#else - return dlopen(path, RTLD_LAZY); -#endif - } - - void* GetSymbol(void* module, char const* name) - { -#ifdef _WIN32 - return GetProcAddress((HMODULE)module, name); -#else - return dlsym(module, name); -#endif - } - using MetaDataGetDispenser = HRESULT(__stdcall*)(REFCLSID, REFIID, LPVOID*); } #define RETURN_IF_FAILED(x) { auto hr = x; if (FAILED(hr)) return hr; } +class ThrowListener final : public testing::EmptyTestEventListener { + void OnTestPartResult(const testing::TestPartResult& result) override { + if (result.failed()) { + throw testing::AssertionException(result); + } + } +}; + int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); for (int i = 0; i < argc; ++i) { if (std::string(argv[i]) == "--md-baseline" && i + 1 < argc) { - auto mod = LoadModule(argv[++i]); + std::string path { argv[++i] }; + SetBaselineModulePath(path); + auto mod = LoadModule(path.c_str()); if (TestBaseline::Metadata == nullptr) { printf("Failed to load metadata baseline module: %s\n", argv[i]); @@ -63,16 +49,9 @@ int main(int argc, char** argv) { RETURN_IF_FAILED(getDispenser(CLSID_CorMetaDataDispenser, IID_IMetaDataDispenser, (void**)&TestBaseline::Metadata)); } - else if (std::string(argv[i]) == "--sym-baseline" && i + 1 < argc) - { - auto mod = LoadModule(argv[i + 1]); - if (TestBaseline::Metadata == nullptr) - { - printf("Failed to load symbol baseline module: %s\n", argv[i]); - return -1; - } - } } + testing::UnitTest::GetInstance()->listeners().Append(new ThrowListener); + return RUN_ALL_TESTS(); } diff --git a/test/regtest/metadata.cpp b/test/regtest/metadata.cpp new file mode 100644 index 00000000..d271802f --- /dev/null +++ b/test/regtest/metadata.cpp @@ -0,0 +1,1734 @@ +#include "asserts.h" +#include "fixtures.h" + +#include +#include + +#include +#include + +namespace +{ + template + using static_enum_buffer = std::array; + + template + using static_char_buffer = std::array; + + // default values recommended by http://isthe.com/chongo/tech/comp/fnv/ + uint32_t const Prime = 0x01000193; // 16777619 + uint32_t const Seed = 0x811C9DC5; // 2166136261 + // hash a single byte + uint32_t fnv1a(uint8_t oneByte, uint32_t hash = Seed) + { + return (oneByte ^ hash) * Prime; + } + + // Based on https://create.stephan-brumme.com/fnv-hash/ + uint32_t HashCharArray(static_char_buffer const& arr, uint32_t written) + { + uint32_t hash = Seed; + auto curr = std::begin(arr); + auto end = curr + written; + for (; curr < end; ++curr) + { + WCHAR c = *curr; + std::array r; + memcpy(r.data(), &c, r.size()); + for (uint8_t b : r) + hash = fnv1a(b, hash); + } + return hash; + } + + // Based on https://create.stephan-brumme.com/fnv-hash/ + uint32_t HashByteArray(void const* arr, size_t byteLength) + { + uint32_t hash = Seed; + auto curr = (uint8_t const*)arr; + auto end = curr + byteLength; + for (; curr < end; ++curr) + { + hash = fnv1a(*curr, hash); + } + return hash; + } + + std::vector GetCustomAttributeByName(IMetaDataImport2* import, LPCWSTR customAttr, mdToken tkObj) + { + std::vector values; + + void const* ppData; + ULONG pcbData; + HRESULT hr = import->GetCustomAttributeByName(tkObj, + customAttr, + &ppData, + &pcbData); + + values.push_back(hr); + if (hr == S_OK) + { + values.push_back(HashByteArray(ppData, pcbData)); + values.push_back(pcbData); + } + return values; + } + + std::vector GetCustomAttribute_Nullable(IMetaDataImport2* import, mdToken tkObj) + { + auto NullableAttrName = W("System.Runtime.CompilerServices.NullableAttribute"); + return GetCustomAttributeByName(import, NullableAttrName, tkObj); + } + + std::vector GetCustomAttribute_CompilerGenerated(IMetaDataImport2* import, mdToken tkObj) + { + auto CompilerGeneratedAttrName = W("System.Runtime.CompilerServices.CompilerGeneratedAttribute"); + return GetCustomAttributeByName(import, CompilerGeneratedAttrName, tkObj); + } + + void ValidateAndCloseEnum(IMetaDataImport2* import, HCORENUM hcorenum, ULONG expectedCount) + { + ULONG count; + ASSERT_HRESULT_SUCCEEDED(import->CountEnum(hcorenum, &count)); + ASSERT_EQ(count, expectedCount); + import->CloseEnum(hcorenum); + } + + std::vector EnumTypeDefs(IMetaDataImport2* import) + { + std::vector tokens; + static_enum_buffer tokensBuffer{}; + HCORENUM hcorenum{}; + ULONG returned; + while (0 == import->EnumTypeDefs(&hcorenum, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) + && returned != 0) + { + for (ULONG i = 0; i < returned; ++i) + { + tokens.push_back(tokensBuffer[i]); + } + } + ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); + return tokens; + } + + std::vector EnumTypeRefs(IMetaDataImport2* import) + { + std::vector tokens; + static_enum_buffer tokensBuffer{}; + HCORENUM hcorenum{}; + ULONG returned; + while (0 == import->EnumTypeRefs(&hcorenum, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) + && returned != 0) + { + for (ULONG i = 0; i < returned; ++i) + tokens.push_back(tokensBuffer[i]); + } + ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); + return tokens; + } + + std::vector EnumTypeSpecs(IMetaDataImport2* import) + { + std::vector tokens; + static_enum_buffer tokensBuffer{}; + HCORENUM hcorenum{}; + ULONG returned; + while (0 == import->EnumTypeSpecs(&hcorenum, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) + && returned != 0) + { + for (ULONG i = 0; i < returned; ++i) + tokens.push_back(tokensBuffer[i]); + } + ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); + return tokens; + } + + std::vector EnumModuleRefs(IMetaDataImport2* import) + { + std::vector tokens; + static_enum_buffer tokensBuffer{}; + HCORENUM hcorenum{}; + ULONG returned; + while (0 == import->EnumModuleRefs(&hcorenum, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) + && returned != 0) + { + for (ULONG i = 0; i < returned; ++i) + tokens.push_back(tokensBuffer[i]); + } + ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); + return tokens; + } + + std::vector EnumInterfaceImpls(IMetaDataImport2* import, mdTypeDef typdef) + { + std::vector tokens; + static_enum_buffer tokensBuffer{}; + HCORENUM hcorenum{}; + ULONG returned; + while (0 == import->EnumInterfaceImpls(&hcorenum, typdef, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) + && returned != 0) + { + for (ULONG i = 0; i < returned; ++i) + tokens.push_back(tokensBuffer[i]); + } + ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); + return tokens; + } + + std::vector EnumMembers(IMetaDataImport2* import, mdTypeDef typdef) + { + std::vector tokens; + static_enum_buffer tokensBuffer{}; + HCORENUM hcorenum{}; + ULONG returned; + while (0 == import->EnumMembers(&hcorenum, typdef, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) + && returned != 0) + { + for (ULONG i = 0; i < returned; ++i) + tokens.push_back(tokensBuffer[i]); + } + ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); + return tokens; + } + + std::vector EnumMembersWithName(IMetaDataImport2* import, mdTypeDef typdef, LPCWSTR memberName = W(".ctor")) + { + std::vector tokens; + static_enum_buffer tokensBuffer{}; + HCORENUM hcorenum{}; + ULONG returned; + while (0 == import->EnumMembersWithName(&hcorenum, typdef, memberName, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) + && returned != 0) + { + for (ULONG i = 0; i < returned; ++i) + tokens.push_back(tokensBuffer[i]); + } + ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); + return tokens; + } + + std::vector EnumMemberRefs(IMetaDataImport2* import, mdToken tkParent) + { + std::vector tokens; + static_enum_buffer tokensBuffer{}; + HCORENUM hcorenum{}; + ULONG returned; + while (0 == import->EnumMemberRefs(&hcorenum, tkParent, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) + && returned != 0) + { + for (ULONG i = 0; i < returned; ++i) + tokens.push_back(tokensBuffer[i]); + } + ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); + return tokens; + } + + std::vector EnumMethods(IMetaDataImport2* import, mdTypeDef typdef) + { + std::vector tokens; + static_enum_buffer tokensBuffer{}; + HCORENUM hcorenum{}; + ULONG returned; + while (0 == import->EnumMethods(&hcorenum, typdef, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) + && returned != 0) + { + for (ULONG i = 0; i < returned; ++i) + tokens.push_back(tokensBuffer[i]); + } + ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); + return tokens; + } + + std::vector EnumMethodsWithName(IMetaDataImport2* import, mdToken typdef, LPCWSTR methodName = W(".ctor")) + { + std::vector tokens; + static_enum_buffer tokensBuffer{}; + HCORENUM hcorenum{}; + ULONG returned; + while (0 == import->EnumMethodsWithName(&hcorenum, typdef, methodName, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) + && returned != 0) + { + for (ULONG i = 0; i < returned; ++i) + tokens.push_back(tokensBuffer[i]); + } + ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); + return tokens; + } + + std::vector EnumMethodImpls(IMetaDataImport2* import, mdTypeDef typdef) + { + std::vector tokens; + static_enum_buffer tokensBuffer1{}; + static_enum_buffer tokensBuffer2{}; + HCORENUM hcorenum{}; + ULONG returned; + while (0 == import->EnumMethodImpls(&hcorenum, typdef, tokensBuffer1.data(), tokensBuffer2.data(), (ULONG)tokensBuffer1.size(), &returned) + && returned != 0) + { + for (ULONG i = 0; i < returned; ++i) + { + tokens.push_back(tokensBuffer1[i]); + tokens.push_back(tokensBuffer2[i]); + } + } + ValidateAndCloseEnum(import, hcorenum, (ULONG)(tokens.size() / 2)); + return tokens; + } + + std::vector EnumMethodSemantics(IMetaDataImport2* import, mdMethodDef mb) + { + std::vector tokens; + static_enum_buffer tokensBuffer{}; + HCORENUM hcorenum{}; + ULONG returned; + while (0 == import->EnumMethodSemantics(&hcorenum, mb, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) + && returned != 0) + { + for (ULONG i = 0; i < returned; ++i) + tokens.push_back(tokensBuffer[i]); + } + ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); + return tokens; + } + + std::vector EnumParams(IMetaDataImport2* import, mdMethodDef methoddef) + { + std::vector tokens; + static_enum_buffer tokensBuffer{}; + HCORENUM hcorenum{}; + ULONG returned; + while (0 == import->EnumParams(&hcorenum, methoddef, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) + && returned != 0) + { + for (ULONG i = 0; i < returned; ++i) + tokens.push_back(tokensBuffer[i]); + } + ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); + return tokens; + } + + std::vector EnumMethodSpecs(IMetaDataImport2* import, mdToken tk) + { + std::vector tokens; + static_enum_buffer tokensBuffer{}; + HCORENUM hcorenum{}; + ULONG returned; + while (0 == import->EnumMethodSpecs(&hcorenum, tk, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) + && returned != 0) + { + for (ULONG i = 0; i < returned; ++i) + tokens.push_back(tokensBuffer[i]); + } + ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); + return tokens; + } + + std::vector EnumEvents(IMetaDataImport2* import, mdTypeDef tk) + { + std::vector tokens; + static_enum_buffer tokensBuffer{}; + HCORENUM hcorenum{}; + ULONG returned; + while (0 == import->EnumEvents(&hcorenum, tk, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) + && returned != 0) + { + for (ULONG i = 0; i < returned; ++i) + tokens.push_back(tokensBuffer[i]); + } + ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); + return tokens; + } + + std::vector EnumProperties(IMetaDataImport2* import, mdTypeDef tk) + { + std::vector tokens; + static_enum_buffer tokensBuffer{}; + HCORENUM hcorenum{}; + ULONG returned; + while (0 == import->EnumProperties(&hcorenum, tk, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) + && returned != 0) + { + for (ULONG i = 0; i < returned; ++i) + tokens.push_back(tokensBuffer[i]); + } + ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); + return tokens; + } + + std::vector EnumFields(IMetaDataImport2* import, mdTypeDef tk) + { + std::vector tokens; + static_enum_buffer tokensBuffer{}; + HCORENUM hcorenum{}; + ULONG returned; + while (0 == import->EnumFields(&hcorenum, tk, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) + && returned != 0) + { + for (ULONG i = 0; i < returned; ++i) + tokens.push_back(tokensBuffer[i]); + } + ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); + return tokens; + } + + std::vector EnumFieldsWithName(IMetaDataImport2* import, mdTypeDef tk, LPCWSTR name = W("_name")) + { + std::vector tokens; + static_enum_buffer tokensBuffer{}; + HCORENUM hcorenum{}; + ULONG returned; + while (0 == import->EnumFieldsWithName(&hcorenum, tk, name, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) + && returned != 0) + { + for (ULONG i = 0; i < returned; ++i) + tokens.push_back(tokensBuffer[i]); + } + ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); + return tokens; + } + + std::vector EnumSignatures(IMetaDataImport2* import) + { + std::vector tokens; + static_enum_buffer tokensBuffer{}; + HCORENUM hcorenum{}; + ULONG returned; + while (0 == import->EnumSignatures(&hcorenum, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) + && returned != 0) + { + for (ULONG i = 0; i < returned; ++i) + tokens.push_back(tokensBuffer[i]); + } + ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); + return tokens; + } + + std::vector EnumUserStrings(IMetaDataImport2* import) + { + std::vector tokens; + static_enum_buffer tokensBuffer{}; + HCORENUM hcorenum{}; + ULONG returned; + while (0 == import->EnumUserStrings(&hcorenum, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) + && returned != 0) + { + for (ULONG i = 0; i < returned; ++i) + tokens.push_back(tokensBuffer[i]); + } + ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); + return tokens; + } + + std::vector EnumCustomAttributes(IMetaDataImport2* import, mdToken tk = mdTokenNil, mdToken tkType = mdTokenNil) + { + std::vector tokens; + static_enum_buffer tokensBuffer{}; + HCORENUM hcorenum{}; + ULONG returned; + while (0 == import->EnumCustomAttributes(&hcorenum, tk, tkType, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) + && returned != 0) + { + for (ULONG i = 0; i < returned; ++i) + tokens.push_back(tokensBuffer[i]); + } + ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); + return tokens; + } + + std::vector EnumGenericParams(IMetaDataImport2* import, mdToken tk) + { + std::vector tokens; + static_enum_buffer tokensBuffer{}; + HCORENUM hcorenum{}; + ULONG returned; + while (0 == import->EnumGenericParams(&hcorenum, tk, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) + && returned != 0) + { + for (ULONG i = 0; i < returned; ++i) + tokens.push_back(tokensBuffer[i]); + } + ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); + return tokens; + } + + std::vector EnumGenericParamConstraints(IMetaDataImport2* import, mdGenericParam tk) + { + std::vector tokens; + static_enum_buffer tokensBuffer{}; + HCORENUM hcorenum{}; + ULONG returned; + while (0 == import->EnumGenericParamConstraints(&hcorenum, tk, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) + && returned != 0) + { + for (ULONG i = 0; i < returned; ++i) + tokens.push_back(tokensBuffer[i]); + } + ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); + return tokens; + } + + std::vector EnumPermissionSetsAndGetProps(IMetaDataImport2* import, mdToken permTk) + { + std::vector values; + static_enum_buffer tokensBuffer{}; + + // See CorDeclSecurity for actions definitions + for (int32_t action = (int32_t)dclActionNil; action <= dclMaximumValue; ++action) + { + std::vector tokens; + HCORENUM hcorenum{}; + { + ULONG returned; + while (0 == import->EnumPermissionSets(&hcorenum, permTk, action, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) + && returned != 0) + { + for (ULONG j = 0; j < returned; ++j) + { + tokens.push_back(tokensBuffer[j]); + } + } + ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); + } + + for (uint32_t pk : tokens) + { + DWORD a; + void const* ppvPermission; + ULONG pcbPermission; + HRESULT hr = import->GetPermissionSetProps(pk, &a, &ppvPermission, &pcbPermission); + values.push_back(hr); + if (hr == S_OK) + { + values.push_back(a); + values.push_back(HashByteArray(ppvPermission, pcbPermission)); + values.push_back(pcbPermission); + } + } + } + return values; + } + + std::vector EnumAssemblyRefs(IMetaDataAssemblyImport* import) + { + std::vector tokens; + static_enum_buffer tokensBuffer{}; + HCORENUM hcorenum{}; + ULONG returned; + while (0 == import->EnumAssemblyRefs(&hcorenum, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) + && returned != 0) + { + for (ULONG i = 0; i < returned; ++i) + tokens.push_back(tokensBuffer[i]); + } + dncp::com_ptr mdImport; + HRESULT hr = import->QueryInterface(IID_IMetaDataImport2, (void**)&mdImport); + EXPECT_HRESULT_SUCCEEDED(hr); + ValidateAndCloseEnum(mdImport, hcorenum, (ULONG)tokens.size()); + return tokens; + } + + std::vector EnumFiles(IMetaDataAssemblyImport* import) + { + std::vector tokens; + static_enum_buffer tokensBuffer{}; + HCORENUM hcorenum{}; + ULONG returned; + while (0 == import->EnumFiles(&hcorenum, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) + && returned != 0) + { + for (ULONG i = 0; i < returned; ++i) + tokens.push_back(tokensBuffer[i]); + } + dncp::com_ptr mdImport; + HRESULT hr = import->QueryInterface(IID_IMetaDataImport2, (void**)&mdImport); + EXPECT_HRESULT_SUCCEEDED(hr); + ValidateAndCloseEnum(mdImport, hcorenum, (ULONG)tokens.size()); + return tokens; + } + + std::vector EnumExportedTypes(IMetaDataAssemblyImport* import) + { + std::vector tokens; + static_enum_buffer tokensBuffer{}; + HCORENUM hcorenum{}; + ULONG returned; + while (0 == import->EnumExportedTypes(&hcorenum, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) + && returned != 0) + { + for (ULONG i = 0; i < returned; ++i) + tokens.push_back(tokensBuffer[i]); + } + dncp::com_ptr mdImport; + HRESULT hr = import->QueryInterface(IID_IMetaDataImport2, (void**)&mdImport); + EXPECT_HRESULT_SUCCEEDED(hr); + ValidateAndCloseEnum(mdImport, hcorenum, (ULONG)tokens.size()); + return tokens; + } + + std::vector EnumManifestResources(IMetaDataAssemblyImport* import) + { + std::vector tokens; + static_enum_buffer tokensBuffer{}; + HCORENUM hcorenum{}; + ULONG returned; + while (0 == import->EnumManifestResources(&hcorenum, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) + && returned != 0) + { + for (ULONG i = 0; i < returned; ++i) + tokens.push_back(tokensBuffer[i]); + } + dncp::com_ptr mdImport; + HRESULT hr = import->QueryInterface(IID_IMetaDataImport2, (void**)&mdImport); + EXPECT_HRESULT_SUCCEEDED(hr); + ValidateAndCloseEnum(mdImport, hcorenum, (ULONG)tokens.size()); + return tokens; + } + + std::vector FindTypeRef(IMetaDataImport2* import) + { + std::vector values; + HRESULT hr; + mdToken tk; + + // The first assembly ref token typically contains System.Object and Enumerator. + mdToken const assemblyRefToken = 0x23000001; + hr = import->FindTypeRef(assemblyRefToken, W("System.Object"), &tk); + values.push_back(hr); + if (hr == S_OK) + values.push_back(tk); + + // Look for a type that won't ever exist + hr = import->FindTypeRef(assemblyRefToken, W("DoesntExist"), &tk); + values.push_back(hr); + if (hr == S_OK) + values.push_back(tk); + return values; + } + + std::vector FindTypeDefByName(IMetaDataImport2* import, LPCWSTR name, mdToken scope) + { + std::vector values; + + mdTypeDef ptd; + HRESULT hr = import->FindTypeDefByName(name, scope, &ptd); + + values.push_back(hr); + if (hr == S_OK) + values.push_back(ptd); + return values; + } + + std::vector FindExportedTypeByName(IMetaDataAssemblyImport* import, LPCWSTR name, mdToken tkImplementation) + { + std::vector values; + + mdExportedType exported; + HRESULT hr = import->FindExportedTypeByName(name, tkImplementation, &exported); + + values.push_back(hr); + if (hr == S_OK) + values.push_back(exported); + return values; + } + + std::vector FindManifestResourceByName(IMetaDataAssemblyImport* import, LPCWSTR name) + { + std::vector values; + + mdManifestResource resource; + HRESULT hr = import->FindManifestResourceByName(name, &resource); + + values.push_back(hr); + if (hr == S_OK) + values.push_back(resource); + return values; + } + + std::vector GetTypeDefProps(IMetaDataImport2* import, mdTypeDef typdef) + { + std::vector values; + + static_char_buffer name{}; + ULONG pchTypeDef; + DWORD pdwTypeDefFlags; + mdToken ptkExtends; + HRESULT hr = import->GetTypeDefProps(typdef, + name.data(), + (ULONG)name.size(), + &pchTypeDef, + &pdwTypeDefFlags, + &ptkExtends); + + values.push_back(hr); + if (hr == S_OK) + { + uint32_t hash = HashCharArray(name, pchTypeDef); + values.push_back(hash); + values.push_back(pchTypeDef); + values.push_back(pdwTypeDefFlags); + values.push_back(ptkExtends); + } + return values; + } + + std::vector GetTypeRefProps(IMetaDataImport2* import, mdTypeRef typeref) + { + std::vector values; + + static_char_buffer name{}; + mdToken tkResolutionScope; + ULONG pchTypeRef; + HRESULT hr = import->GetTypeRefProps(typeref, + &tkResolutionScope, + name.data(), + (ULONG)name.size(), + &pchTypeRef); + + values.push_back(hr); + if (hr == S_OK) + { + values.push_back(tkResolutionScope); + uint32_t hash = HashCharArray(name, pchTypeRef); + values.push_back(hash); + values.push_back(pchTypeRef); + } + return values; + } + + std::vector GetScopeProps(IMetaDataImport2* import) + { + std::vector values; + + static_char_buffer name{}; + ULONG pchName; + GUID mvid; + HRESULT hr = import->GetScopeProps( + name.data(), + (ULONG)name.size(), + &pchName, + &mvid); + + values.push_back(hr); + if (hr == S_OK) + { + uint32_t hash = HashCharArray(name, pchName); + values.push_back(hash); + values.push_back(pchName); + + std::array buffer{}; + memcpy(buffer.data(), &mvid, buffer.size()); + for (auto b : buffer) + values.push_back(b); + } + return values; + } + + std::vector GetModuleRefProps(IMetaDataImport2* import, mdModuleRef moduleref) + { + std::vector values; + + static_char_buffer name{}; + ULONG pchModuleRef; + HRESULT hr = import->GetModuleRefProps(moduleref, + name.data(), + (ULONG)name.size(), + &pchModuleRef); + + values.push_back(hr); + if (hr == S_OK) + { + uint32_t hash = HashCharArray(name, pchModuleRef); + values.push_back(hash); + values.push_back(pchModuleRef); + } + return values; + } + + std::vector GetMethodProps(IMetaDataImport2* import, mdToken tk, void const** sig = nullptr, ULONG* sigLen = nullptr) + { + std::vector values; + + mdTypeDef pClass; + static_char_buffer name{}; + ULONG pchMethod; + DWORD pdwAttr; + PCCOR_SIGNATURE ppvSigBlob; + ULONG pcbSigBlob; + ULONG pulCodeRVA; + DWORD pdwImplFlags; + HRESULT hr = import->GetMethodProps(tk, + &pClass, + name.data(), + (ULONG)name.size(), + &pchMethod, + &pdwAttr, + &ppvSigBlob, + &pcbSigBlob, + &pulCodeRVA, + &pdwImplFlags); + values.push_back(hr); + if (hr == S_OK) + { + values.push_back(pClass); + uint32_t hash = HashCharArray(name, pchMethod); + values.push_back(hash); + values.push_back(pchMethod); + values.push_back(pdwAttr); + values.push_back(HashByteArray(ppvSigBlob, pcbSigBlob)); + values.push_back(pcbSigBlob); + values.push_back(pulCodeRVA); + values.push_back(pdwImplFlags); + + if (sig != nullptr) + *sig = ppvSigBlob; + if (sigLen != nullptr) + *sigLen = pcbSigBlob; + } + return values; + } + + std::vector GetParamProps(IMetaDataImport2* import, mdToken tk) + { + std::vector values; + + mdMethodDef pmd; + ULONG pulSequence; + static_char_buffer name{}; + ULONG pchName; + DWORD pdwAttr; + DWORD pdwCPlusTypeFlag; + UVCP_CONSTANT ppValue; + ULONG pcchValue; + HRESULT hr = import->GetParamProps(tk, + &pmd, + &pulSequence, + name.data(), + (ULONG)name.size(), + &pchName, + &pdwAttr, + &pdwCPlusTypeFlag, + &ppValue, + &pcchValue); + + values.push_back(hr); + if (hr == S_OK) + { + values.push_back(pmd); + values.push_back(pulSequence); + uint32_t hash = HashCharArray(name, pchName); + values.push_back(hash); + values.push_back(pchName); + values.push_back(pdwAttr); + values.push_back(pdwCPlusTypeFlag); + values.push_back(HashByteArray(ppValue, pcchValue)); + values.push_back(pcchValue); + } + return values; + } + + std::vector GetMethodSpecProps(IMetaDataImport2* import, mdMethodSpec methodSpec) + { + std::vector values; + + mdToken parent; + PCCOR_SIGNATURE sig; + ULONG sigLen; + HRESULT hr = import->GetMethodSpecProps(methodSpec, + &parent, + &sig, + &sigLen); + + values.push_back(hr); + if (hr == S_OK) + { + values.push_back(parent); + values.push_back(HashByteArray(sig, sigLen)); + values.push_back(sigLen); + } + return values; + } + + std::vector GetMemberRefProps(IMetaDataImport2* import, mdMemberRef mr, PCCOR_SIGNATURE* sig = nullptr, ULONG* sigLen = nullptr) + { + std::vector values; + + mdToken ptk; + static_char_buffer name{}; + ULONG pchMember; + PCCOR_SIGNATURE ppvSigBlob; + ULONG pcbSigBlob; + HRESULT hr = import->GetMemberRefProps(mr, + &ptk, + name.data(), + (ULONG)name.size(), + &pchMember, + &ppvSigBlob, + &pcbSigBlob); + + values.push_back(hr); + if (hr == S_OK) + { + values.push_back(ptk); + uint32_t hash = HashCharArray(name, pchMember); + values.push_back(hash); + values.push_back(pchMember); + values.push_back(HashByteArray(ppvSigBlob, pcbSigBlob)); + values.push_back(pcbSigBlob); + + if (sig != nullptr) + *sig = ppvSigBlob; + if (sigLen != nullptr) + *sigLen = pcbSigBlob; + } + return values; + } + + std::vector GetEventProps(IMetaDataImport2* import, mdEvent tk, std::vector* methoddefs = nullptr) + { + std::vector values; + + mdTypeDef pClass; + static_char_buffer name{}; + ULONG pchEvent; + DWORD pdwEventFlags; + mdToken ptkEventType; + mdMethodDef pmdAddOn; + mdMethodDef pmdRemoveOn; + mdMethodDef pmdFire; + static_enum_buffer rmdOtherMethod; + ULONG pcOtherMethod; + HRESULT hr = import->GetEventProps(tk, + &pClass, + name.data(), + (ULONG)name.size(), + &pchEvent, + &pdwEventFlags, + &ptkEventType, + &pmdAddOn, + &pmdRemoveOn, + &pmdFire, + rmdOtherMethod.data(), + (ULONG)rmdOtherMethod.size(), + &pcOtherMethod); + values.push_back(hr); + if (hr == S_OK) + { + values.push_back(pClass); + uint32_t hash = HashCharArray(name, pchEvent); + values.push_back(hash); + values.push_back(pchEvent); + values.push_back(pdwEventFlags); + values.push_back(ptkEventType); + values.push_back(pmdAddOn); + values.push_back(pmdRemoveOn); + values.push_back(pmdFire); + + std::vector retMaybe; + for (ULONG i = 0; i < std::min(pcOtherMethod, (ULONG)rmdOtherMethod.size()); ++i) + { + values.push_back(rmdOtherMethod[i]); + retMaybe.push_back(rmdOtherMethod[i]); + } + + retMaybe.push_back(pmdAddOn); + retMaybe.push_back(pmdRemoveOn); + retMaybe.push_back(pmdFire); + + if (methoddefs != nullptr) + *methoddefs = std::move(retMaybe); + } + return values; + } + + std::vector GetPropertyProps(IMetaDataImport2* import, mdProperty tk, std::vector* methoddefs = nullptr) + { + std::vector values; + + mdTypeDef pClass; + static_char_buffer name{}; + ULONG pchProperty; + DWORD pdwPropFlags; + PCCOR_SIGNATURE sig; + ULONG sigLen; + DWORD pdwCPlusTypeFlag; + UVCP_CONSTANT ppDefaultValue; + ULONG pcchDefaultValue; + mdMethodDef pmdSetter; + mdMethodDef pmdGetter; + static_enum_buffer rmdOtherMethod{}; + ULONG pcOtherMethod; + HRESULT hr = import->GetPropertyProps(tk, + &pClass, + name.data(), + (ULONG)name.size(), + &pchProperty, + &pdwPropFlags, + &sig, + &sigLen, + &pdwCPlusTypeFlag, + &ppDefaultValue, + &pcchDefaultValue, + &pmdSetter, + &pmdGetter, + rmdOtherMethod.data(), + (ULONG)rmdOtherMethod.size(), + &pcOtherMethod); + values.push_back(hr); + if (hr == S_OK) + { + values.push_back(pClass); + uint32_t hash = HashCharArray(name, pchProperty); + values.push_back(hash); + values.push_back(pchProperty); + values.push_back(pdwPropFlags); + values.push_back(HashByteArray(sig, sigLen)); + values.push_back(sigLen); + values.push_back(pdwCPlusTypeFlag); + values.push_back(HashByteArray(ppDefaultValue, pcchDefaultValue)); + values.push_back(pcchDefaultValue); + values.push_back(pmdSetter); + values.push_back(pmdGetter); + + std::vector retMaybe; + for (ULONG i = 0; i < std::min(pcOtherMethod, (ULONG)rmdOtherMethod.size()); ++i) + { + values.push_back(rmdOtherMethod[i]); + retMaybe.push_back(rmdOtherMethod[i]); + } + + retMaybe.push_back(pmdSetter); + retMaybe.push_back(pmdGetter); + + if (methoddefs != nullptr) + *methoddefs = std::move(retMaybe); + } + return values; + } + + std::vector GetFieldProps(IMetaDataImport2* import, mdFieldDef tk, void const** sig = nullptr, ULONG* sigLen = nullptr) + { + std::vector values; + + mdTypeDef pClass; + static_char_buffer name{}; + ULONG pchField; + DWORD pdwAttr; + PCCOR_SIGNATURE ppvSigBlob; + ULONG pcbSigBlob; + DWORD pdwCPlusTypeFlag; + UVCP_CONSTANT ppValue = nullptr; + ULONG pcchValue = 0; + HRESULT hr = import->GetFieldProps(tk, + &pClass, + name.data(), + (ULONG)name.size(), + &pchField, + &pdwAttr, + &ppvSigBlob, + &pcbSigBlob, + &pdwCPlusTypeFlag, + &ppValue, + &pcchValue); + + values.push_back(hr); + if (hr == S_OK) + { + values.push_back(pClass); + uint32_t hash = HashCharArray(name, pchField); + values.push_back(hash); + values.push_back(pchField); + values.push_back(pdwAttr); + values.push_back(HashByteArray(ppvSigBlob, pcbSigBlob)); + values.push_back(pcbSigBlob); + values.push_back(pdwCPlusTypeFlag); + values.push_back(HashByteArray(ppValue, pcchValue)); + values.push_back(pcchValue); + + if (sig != nullptr) + *sig = ppvSigBlob; + if (sigLen != nullptr) + *sigLen = pcbSigBlob; + } + return values; + } + + std::vector GetCustomAttributeProps(IMetaDataImport2* import, mdCustomAttribute cv) + { + std::vector values; + + mdToken ptkObj; + mdToken ptkType; + void const* sig; + ULONG sigLen; + HRESULT hr = import->GetCustomAttributeProps(cv, + &ptkObj, + &ptkType, + &sig, + &sigLen); + + values.push_back(hr); + if (hr == S_OK) + { + values.push_back(ptkObj); + values.push_back(ptkType); + values.push_back(HashByteArray(sig, sigLen)); + values.push_back(sigLen); + } + return values; + } + + std::vector GetGenericParamProps(IMetaDataImport2* import, mdGenericParam gp) + { + std::vector values; + + ULONG pulParamSeq; + DWORD pdwParamFlags; + mdToken ptOwner; + DWORD reserved; + static_char_buffer name{}; + ULONG pchName; + HRESULT hr = import->GetGenericParamProps(gp, + &pulParamSeq, + &pdwParamFlags, + &ptOwner, + &reserved, + name.data(), + (ULONG)name.size(), + &pchName); + + values.push_back(hr); + if (hr == S_OK) + { + values.push_back(pulParamSeq); + values.push_back(pdwParamFlags); + values.push_back(ptOwner); + values.push_back(reserved); + uint32_t hash = HashCharArray(name, pchName); + values.push_back(hash); + values.push_back(pchName); + } + return values; + } + + std::vector GetGenericParamConstraintProps(IMetaDataImport2* import, mdGenericParamConstraint tk) + { + std::vector values; + + mdGenericParam ptGenericParam; + mdToken ptkConstraintType; + HRESULT hr = import->GetGenericParamConstraintProps(tk, + &ptGenericParam, + &ptkConstraintType); + + values.push_back(hr); + if (hr == S_OK) + { + values.push_back(ptGenericParam); + values.push_back(ptkConstraintType); + } + return values; + } + + std::vector GetPinvokeMap(IMetaDataImport2* import, mdToken tk) + { + std::vector values; + + DWORD pdwMappingFlags; + static_char_buffer name{}; + ULONG pchImportName; + mdModuleRef pmrImportDLL; + HRESULT hr = import->GetPinvokeMap(tk, + &pdwMappingFlags, + name.data(), + (ULONG)name.size(), + &pchImportName, + &pmrImportDLL); + + values.push_back(hr); + if (hr == S_OK) + { + values.push_back(pdwMappingFlags); + uint32_t hash = HashCharArray(name, pchImportName); + values.push_back(hash); + values.push_back(pchImportName); + values.push_back(pmrImportDLL); + } + return values; + } + + std::vector GetNativeCallConvFromSig(IMetaDataImport2* import, void const* sig, ULONG sigLen) + { + std::vector values; + + // .NET 2,4 and CoreCLR metadata imports do not handle null signatures. + if (sigLen != 0) + { + ULONG pCallConv; + HRESULT hr = import->GetNativeCallConvFromSig(sig, sigLen, &pCallConv); + + values.push_back(hr); + if (hr == S_OK) + values.push_back(pCallConv); + } + + return values; + } + + std::vector GetTypeSpecFromToken(IMetaDataImport2* import, mdTypeSpec typespec) + { + std::vector values; + + PCCOR_SIGNATURE sig; + ULONG sigLen; + HRESULT hr = import->GetTypeSpecFromToken(typespec, &sig, &sigLen); + values.push_back(hr); + if (hr == S_OK) + { + values.push_back(HashByteArray(sig, sigLen)); + values.push_back(sigLen); + } + return values; + } + + std::vector GetSigFromToken(IMetaDataImport2* import, mdSignature tkSig) + { + std::vector values; + + PCCOR_SIGNATURE sig; + ULONG sigLen; + HRESULT hr = import->GetSigFromToken(tkSig, &sig, &sigLen); + values.push_back(hr); + if (hr == S_OK) + { + values.push_back(HashByteArray(sig, sigLen)); + values.push_back(sigLen); + } + return values; + } + + std::vector GetMethodSemantics(IMetaDataImport2* import, mdToken tkEventProp, mdMethodDef methodDef) + { + std::vector values; + + DWORD pdwSemanticsFlags; + HRESULT hr = import->GetMethodSemantics(methodDef, tkEventProp, &pdwSemanticsFlags); + + values.push_back(hr); + if (hr == S_OK) + values.push_back(pdwSemanticsFlags); + + return values; + } + + std::vector GetUserString(IMetaDataImport2* import, mdString tkStr) + { + std::vector values; + + static_char_buffer name{}; + ULONG pchString; + HRESULT hr = import->GetUserString(tkStr, name.data(), (ULONG)name.size(), &pchString); + values.push_back(hr); + if (hr == S_OK) + { + uint32_t hash = HashCharArray(name, pchString); + values.push_back(hash); + values.push_back(pchString); + } + return values; + } + + std::vector GetNameFromToken(IMetaDataImport2* import, mdToken tkObj) + { + std::vector values; + + MDUTF8CSTR pszUtf8NamePtr; + HRESULT hr = import->GetNameFromToken(tkObj, &pszUtf8NamePtr); + values.push_back(hr); + if (hr == S_OK) + { + values.push_back(HashByteArray(pszUtf8NamePtr, ::strlen(pszUtf8NamePtr) + 1)); + } + return values; + } + + std::vector GetFieldMarshal(IMetaDataImport2* import, mdToken tk) + { + std::vector values; + + PCCOR_SIGNATURE sig; + ULONG sigLen; + HRESULT hr = import->GetFieldMarshal(tk, &sig, &sigLen); + values.push_back(hr); + if (hr == S_OK) + { + values.push_back(HashByteArray(sig, sigLen)); + values.push_back(sigLen); + } + return values; + } + + std::vector GetNestedClassProps(IMetaDataImport2* import, mdTypeDef tk) + { + std::vector values; + + mdTypeDef ptdEnclosingClass; + HRESULT hr = import->GetNestedClassProps(tk, &ptdEnclosingClass); + values.push_back(hr); + if (hr == S_OK) + values.push_back(ptdEnclosingClass); + + return values; + } + + std::vector GetClassLayout(IMetaDataImport2* import, mdTypeDef tk) + { + std::vector values; + + DWORD pdwPackSize; + std::vector offsets(24); + ULONG pcFieldOffset; + ULONG pulClassSize; + HRESULT hr = import->GetClassLayout(tk, + &pdwPackSize, + offsets.data(), + (ULONG)offsets.size(), + &pcFieldOffset, + &pulClassSize); + values.push_back(hr); + if (hr == S_OK) + { + values.push_back(pdwPackSize); + for (ULONG i = 0; i < std::min(pcFieldOffset, (ULONG)offsets.size()); ++i) + { + COR_FIELD_OFFSET const& o = offsets[i]; + values.push_back(o.ridOfField); + values.push_back(o.ulOffset); + } + values.push_back(pcFieldOffset); + values.push_back(pulClassSize); + } + + return values; + } + + std::vector GetRVA(IMetaDataImport2* import, mdToken tk) + { + std::vector values; + + ULONG pulCodeRVA; + DWORD pdwImplFlags; + HRESULT hr = import->GetRVA(tk, &pulCodeRVA, &pdwImplFlags); + values.push_back(hr); + if (hr == S_OK) + { + values.push_back(pulCodeRVA); + values.push_back(pdwImplFlags); + } + return values; + } + + std::vector GetParamForMethodIndex(IMetaDataImport2* import, mdToken tk) + { + std::vector values; + + mdParamDef def; + for (uint32_t i = 0; i < std::numeric_limits::max(); ++i) + { + HRESULT hr = import->GetParamForMethodIndex(tk, i, &def); + values.push_back(hr); + if (hr != S_OK) + break; + values.push_back(def); + } + return values; + } + + int32_t IsGlobal(IMetaDataImport2* import, mdToken tk) + { + int32_t pbGlobal; + HRESULT hr = import->IsGlobal(tk, &pbGlobal); + if (hr != S_OK) + return hr; + return pbGlobal; + } + + std::vector GetVersionString(IMetaDataImport2* import) + { + std::vector values; + + static_char_buffer name{}; + ULONG pccBufSize; + HRESULT hr = import->GetVersionString(name.data(), (DWORD)name.size(), &pccBufSize); + values.push_back(hr); + if (hr == S_OK) + { + uint32_t hash = HashCharArray(name, pccBufSize); + values.push_back(hash); + values.push_back(pccBufSize); + } + + return values; + } + + std::vector GetAssemblyFromScope(IMetaDataAssemblyImport* import) + { + std::vector values; + + mdAssembly mdAsm; + HRESULT hr = import->GetAssemblyFromScope(&mdAsm); + if (hr == S_OK) + values.push_back(mdAsm); + return values; + } + + std::vector GetAssemblyProps(IMetaDataAssemblyImport* import, mdAssembly mda) + { + std::vector values; + static_char_buffer name{}; + static_char_buffer locale{}; + std::vector processor(1); + std::vector osInfo(1); + + ASSEMBLYMETADATA metadata; + metadata.szLocale = locale.data(); + metadata.cbLocale = (ULONG)locale.size(); + metadata.rProcessor = processor.data(); + metadata.ulProcessor = (ULONG)processor.size(); + metadata.rOS = osInfo.data(); + metadata.ulOS = (ULONG)osInfo.size(); + + void const* publicKey; + ULONG publicKeyLength; + ULONG hashAlgId; + ULONG nameLength; + ULONG flags; + HRESULT hr = import->GetAssemblyProps(mda, &publicKey, &publicKeyLength, &hashAlgId, name.data(), (ULONG)name.size(), &nameLength, &metadata, &flags); + values.push_back(hr); + + if (hr == S_OK) + { + values.push_back(HashByteArray(publicKey, publicKeyLength)); + values.push_back(publicKeyLength); + values.push_back(hashAlgId); + values.push_back(HashCharArray(name, nameLength)); + values.push_back((size_t)nameLength); + values.push_back(metadata.usMajorVersion); + values.push_back(metadata.usMinorVersion); + values.push_back(metadata.usBuildNumber); + values.push_back(metadata.usRevisionNumber); + values.push_back(HashCharArray(locale, metadata.cbLocale)); + values.push_back(metadata.cbLocale); + values.push_back(metadata.ulProcessor); + values.push_back(metadata.ulOS); + values.push_back(flags); + } + return values; + } + + std::vector GetAssemblyRefProps(IMetaDataAssemblyImport* import, mdAssemblyRef mdar) + { + std::vector values; + static_char_buffer name{}; + static_char_buffer locale{}; + std::vector processor(1); + std::vector osInfo(1); + + ASSEMBLYMETADATA metadata; + metadata.szLocale = locale.data(); + metadata.cbLocale = (ULONG)locale.size(); + metadata.rProcessor = processor.data(); + metadata.ulProcessor = (ULONG)processor.size(); + metadata.rOS = osInfo.data(); + metadata.ulOS = (ULONG)osInfo.size(); + + void const* publicKeyOrToken; + ULONG publicKeyOrTokenLength; + ULONG nameLength; + void const* hash; + ULONG hashLength; + DWORD flags; + HRESULT hr = import->GetAssemblyRefProps(mdar, &publicKeyOrToken, &publicKeyOrTokenLength, name.data(), (ULONG)name.size(), &nameLength, &metadata, &hash, &hashLength, &flags); + values.push_back(hr); + + if (hr == S_OK) + { + values.push_back(HashByteArray(publicKeyOrToken, publicKeyOrTokenLength)); + values.push_back(publicKeyOrTokenLength); + values.push_back(HashCharArray(name, nameLength)); + values.push_back((size_t)nameLength); + values.push_back(metadata.usMajorVersion); + values.push_back(metadata.usMinorVersion); + values.push_back(metadata.usBuildNumber); + values.push_back(metadata.usRevisionNumber); + values.push_back(HashCharArray(locale, metadata.cbLocale)); + values.push_back(metadata.cbLocale); + values.push_back(metadata.ulProcessor); + values.push_back(metadata.ulOS); + values.push_back(HashByteArray(hash, hashLength)); + values.push_back(hashLength); + values.push_back(flags); + } + return values; + } + + std::vector GetFileProps(IMetaDataAssemblyImport* import, mdFile mdf) + { + std::vector values; + static_char_buffer name{}; + + ULONG nameLength; + void const* hash; + ULONG hashLength; + DWORD flags; + HRESULT hr = import->GetFileProps(mdf, name.data(), (ULONG)name.size(), &nameLength, &hash, &hashLength, &flags); + values.push_back(hr); + + if (hr == S_OK) + { + values.push_back(HashCharArray(name, nameLength)); + values.push_back((size_t)nameLength); + values.push_back(hashLength != 0 ? (size_t)hash : 0); + values.push_back(hashLength); + values.push_back(flags); + } + return values; + } + + std::vector GetExportedTypeProps(IMetaDataAssemblyImport* import, mdFile mdf, std::vector* nameBuffer = nullptr, uint32_t* implementationToken = nullptr) + { + std::vector values; + static_char_buffer name{}; + + ULONG nameLength; + mdToken implementation; + mdTypeDef typeDef; + DWORD flags; + HRESULT hr = import->GetExportedTypeProps(mdf, name.data(), (ULONG)name.size(), &nameLength, &implementation, &typeDef, &flags); + values.push_back(hr); + + if (hr == S_OK) + { + values.push_back(HashCharArray(name, nameLength)); + values.push_back(nameLength); + values.push_back(implementation); + values.push_back(typeDef); + values.push_back(flags); + + if (nameBuffer != nullptr) + *nameBuffer = { std::begin(name), std::begin(name) + nameLength }; + if (implementationToken != nullptr) + *implementationToken = implementation; + } + return values; + } + + std::vector GetManifestResourceProps(IMetaDataAssemblyImport* import, mdManifestResource mmr, std::vector* nameBuffer = nullptr) + { + std::vector values; + static_char_buffer name{}; + + ULONG nameLength; + ULONG offset; + mdToken implementation; + DWORD flags; + HRESULT hr = import->GetManifestResourceProps(mmr, name.data(), (ULONG)name.size(), &nameLength, &implementation, &offset, &flags); + values.push_back(hr); + + if (hr == S_OK) + { + values.push_back(HashCharArray(name, nameLength)); + values.push_back(nameLength); + values.push_back(implementation); + values.push_back(flags); + + if (nameBuffer != nullptr) + *nameBuffer = { std::begin(name), std::begin(name) + nameLength }; + } + return values; + } + + std::vector ResetEnum(IMetaDataImport2* import) + { + // We are going to test the ResetEnum() API using the + // EnumMembers() API because it enumerates more than one table. + std::vector tokens; + auto typedefs = EnumTypeDefs(import); + if (typedefs.size() == 0) + return tokens; + + auto tk = typedefs[0]; + HCORENUM hcorenum{}; + try + { + static auto ReadInMembers = [](IMetaDataImport2* import, HCORENUM& hcorenum, mdToken tk, std::vector& tokens) + { + static_enum_buffer tokensBuffer{}; + ULONG returned; + if (0 == import->EnumMembers(&hcorenum, tk, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) + && returned != 0) + { + for (ULONG i = 0; i < returned; ++i) + tokens.push_back(tokensBuffer[i]); + } + }; + + ReadInMembers(import, hcorenum, tk, tokens); + + // Determine how many we have and move to right before end + ULONG count; + EXPECT_HRESULT_SUCCEEDED(import->CountEnum(hcorenum, &count)); + if (count != 0) + { + EXPECT_HRESULT_SUCCEEDED(import->ResetEnum(hcorenum, count - 1)); + ReadInMembers(import, hcorenum, tk, tokens); + + // Fully reset the enum + EXPECT_HRESULT_SUCCEEDED(import->ResetEnum(hcorenum, 0)); + ReadInMembers(import, hcorenum, tk, tokens); + } + } + catch (...) + { + import->CloseEnum(hcorenum); + throw; + } + return tokens; + } +} + +TEST_P(MetaDataRegressionTest, FindAPIs) +{ + auto& param = GetParam(); + // Load metadata + dncp::com_ptr baselineImport { param.baseline.p }; + dncp::com_ptr currentImport { param.test.p }; + + static auto FindTokenByName = [](IMetaDataImport2* import, LPCWSTR name, mdToken enclosing = mdTokenNil) -> mdToken + { + mdTypeDef ptd; + EXPECT_HRESULT_SUCCEEDED(import->FindTypeDefByName(name, enclosing, &ptd)); + return ptd; + }; + + static auto GetTypeDefBaseToken = [](IMetaDataImport2* import, mdTypeDef tk) -> mdToken + { + static_char_buffer name{}; + ULONG pchTypeDef; + DWORD pdwTypeDefFlags; + mdToken ptkExtends; + EXPECT_HRESULT_SUCCEEDED(import->GetTypeDefProps(tk, + name.data(), + (ULONG)name.size(), + &pchTypeDef, + &pdwTypeDefFlags, + &ptkExtends)); + return ptkExtends; + }; + + static auto FindMethodDef = [](IMetaDataImport2* import, mdTypeDef type, LPCWSTR methodName) -> mdToken + { + std::vector methoddefs = EnumMembersWithName(import, type, methodName); + EXPECT_TRUE(!methoddefs.empty()); + return methoddefs[0]; + }; + + static auto FindMemberRef = [](IMetaDataImport2* import, mdTypeDef type, LPCWSTR methodName) -> mdToken + { + auto methodDef = FindMethodDef(import, type, methodName); + mdMemberRef pmr; + EXPECT_HRESULT_SUCCEEDED(import->FindMemberRef(methodDef, methodName, nullptr, 0, &pmr)); + return pmr; + }; + + static auto FindMethod = [](IMetaDataImport2* import, mdTypeDef td, LPCWSTR name, void const* pvSigBlob, ULONG cbSigBlob) -> uint32_t + { + mdMethodDef tkMethod; + mdToken tkMember; + EXPECT_HRESULT_SUCCEEDED(import->FindMethod(td, name, (PCCOR_SIGNATURE)pvSigBlob, cbSigBlob, &tkMethod)); + EXPECT_HRESULT_SUCCEEDED(import->FindMember(td, name, (PCCOR_SIGNATURE)pvSigBlob, cbSigBlob, &tkMember)); + EXPECT_EQ(tkMethod, tkMember); + return tkMethod; + }; + + static auto FindField = [](IMetaDataImport2* import, mdTypeDef td, LPCWSTR name, void const* pvSigBlob, ULONG cbSigBlob) -> uint32_t + { + mdFieldDef tkField; + mdToken tkMember; + EXPECT_HRESULT_SUCCEEDED(import->FindField(td, name, (PCCOR_SIGNATURE)pvSigBlob, cbSigBlob, &tkField)); + EXPECT_HRESULT_SUCCEEDED(import->FindMember(td, name, (PCCOR_SIGNATURE)pvSigBlob, cbSigBlob, &tkMember)); + EXPECT_EQ(tkField, tkMember); + return tkField; + }; + + auto tgt = W("C"); + + auto baseTypeDef = W("B1"); + auto tkB1 = EXPECT_THAT_AND_RETURN(FindTokenByName(baselineImport, baseTypeDef), testing::Eq(FindTokenByName(currentImport, baseTypeDef))); + auto tkB1Base = EXPECT_THAT_AND_RETURN(GetTypeDefBaseToken(baselineImport, tkB1), testing::Eq(GetTypeDefBaseToken(currentImport, tkB1))); + EXPECT_EQ(FindTypeDefByName(baselineImport, tgt, tkB1Base), FindTypeDefByName(currentImport, tgt, tkB1Base)); + + auto baseTypeRef = W("B2"); + auto tkB2 = EXPECT_THAT_AND_RETURN(FindTokenByName(baselineImport, baseTypeRef), testing::Eq(FindTokenByName(currentImport, baseTypeRef))); + auto tkB2Base = EXPECT_THAT_AND_RETURN(GetTypeDefBaseToken(baselineImport, tkB2), testing::Eq(GetTypeDefBaseToken(currentImport, tkB2))); + EXPECT_THAT(FindTypeDefByName(baselineImport, tgt, tkB2Base), testing::Eq(FindTypeDefByName(currentImport, tgt, tkB2Base))); + + auto methodDefName = W("MethodDef"); + auto tkMethodDef = EXPECT_THAT_AND_RETURN(FindMethodDef(baselineImport, tkB1Base, methodDefName), testing::Eq(FindMethodDef(currentImport, tkB1Base, methodDefName))); + + void const* defSigBlob; + ULONG defSigBlobLength; + EXPECT_EQ( + GetMethodProps(baselineImport, tkMethodDef), + GetMethodProps(currentImport, tkMethodDef, &defSigBlob, &defSigBlobLength)); + EXPECT_EQ( + FindMethod(baselineImport, tkB1Base, methodDefName, defSigBlob, defSigBlobLength), + FindMethod(currentImport, tkB1Base, methodDefName, defSigBlob, defSigBlobLength)); + + auto methodRef1Name = W("MethodRef1"); + auto tkMemberRefNoVarArgsBase = EXPECT_THAT_AND_RETURN( + FindMemberRef(baselineImport, tkB1Base, methodRef1Name), + testing::Eq(FindMemberRef(currentImport, tkB1Base, methodRef1Name))); + + PCCOR_SIGNATURE ref1Blob; + ULONG ref1BlobLength; + EXPECT_EQ( + GetMemberRefProps(baselineImport, tkMemberRefNoVarArgsBase), + GetMemberRefProps(currentImport, tkMemberRefNoVarArgsBase, &ref1Blob, &ref1BlobLength)); + EXPECT_EQ( + FindMethod(baselineImport, tkB1Base, methodRef1Name, ref1Blob, ref1BlobLength), + FindMethod(currentImport, tkB1Base, methodRef1Name, ref1Blob, ref1BlobLength)); + + auto methodRef2Name = W("MethodRef2"); + auto tkMemberRefVarArgsBase = EXPECT_THAT_AND_RETURN( + FindMemberRef(baselineImport, tkB1Base, methodRef2Name), + testing::Eq(FindMemberRef(currentImport, tkB1Base, methodRef2Name))); + + PCCOR_SIGNATURE ref2Blob; + ULONG ref2BlobLength; + EXPECT_EQ( + GetMemberRefProps(baselineImport, tkMemberRefVarArgsBase), + GetMemberRefProps(currentImport, tkMemberRefVarArgsBase, &ref2Blob, &ref2BlobLength)); + EXPECT_EQ( + FindMethod(baselineImport, tkB1Base, methodRef2Name, ref2Blob, ref2BlobLength), + FindMethod(currentImport, tkB1Base, methodRef2Name, ref2Blob, ref2BlobLength)); + + auto fieldName = W("Field1"); + auto tkFields = EXPECT_THAT_AND_RETURN( + EnumFieldsWithName(baselineImport, tkB2, fieldName), + testing::ElementsAreArray(EnumFieldsWithName(currentImport, tkB2, fieldName))); + EXPECT_TRUE(!tkFields.empty()); + mdToken tkField = tkFields[0]; + + void const* sigBlob; + ULONG sigBlobLength; + EXPECT_EQ( + GetFieldProps(baselineImport, tkField), + GetFieldProps(currentImport, tkField, &sigBlob, &sigBlobLength)); + EXPECT_EQ( + FindField(baselineImport, tkB2, fieldName, sigBlob, sigBlobLength), + FindField(currentImport, tkB2, fieldName, sigBlob, sigBlobLength)); + +} + +INSTANTIATE_TEST_SUITE_P(MetaDataRegressionTestNetCore, MetaDataRegressionTest, testing::ValuesIn(std::move(ReadOnlyMetadataInDirectory(GetBaselineDirectory())))); diff --git a/test/regtest/pal.cpp b/test/regtest/pal.cpp new file mode 100644 index 00000000..9b4e7545 --- /dev/null +++ b/test/regtest/pal.cpp @@ -0,0 +1,26 @@ +#include "pal.hpp" +#ifdef _WIN32 +#define NOMINMAX +#include +#else +#include +#include +#endif + +void* LoadModule(char const* path) +{ +#ifdef _WIN32 + return LoadLibraryA(path); +#else + return dlopen(path, RTLD_LAZY); +#endif +} + +void* GetSymbol(void* module, char const* name) +{ +#ifdef _WIN32 + return GetProcAddress((HMODULE)module, name); +#else + return dlsym(module, name); +#endif +} \ No newline at end of file diff --git a/test/regtest/pal.hpp b/test/regtest/pal.hpp new file mode 100644 index 00000000..346e3777 --- /dev/null +++ b/test/regtest/pal.hpp @@ -0,0 +1,7 @@ +#ifndef _TEST_REGTEST_PAL_H_ +#define _TEST_REGTEST_PAL_H_ + +void* LoadModule(char const* path); +void* GetSymbol(void* module, char const* name); + +#endif // !_TEST_REGTEST_PAL_H_ \ No newline at end of file From 1b29d4a36a36d02e6ad5d0f00f6d68701a4fa3f0 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 19 Dec 2023 16:53:49 -0800 Subject: [PATCH 06/59] Pass around the raw blobs with file names instead of COM objects. --- test/regtest/asserts.h | 1 - test/regtest/discovery.cpp | 62 ++++++++++++++---------------------- test/regtest/fixtures.h | 28 +++++++--------- test/regtest/metadata.cpp | 65 +++++++++++++++++++++++++++++--------- 4 files changed, 85 insertions(+), 71 deletions(-) diff --git a/test/regtest/asserts.h b/test/regtest/asserts.h index f5e1cd16..3c7f89ba 100644 --- a/test/regtest/asserts.h +++ b/test/regtest/asserts.h @@ -3,7 +3,6 @@ #include -#define ASSERT_THAT_AND_RETURN(a, match) ([&](){ auto&& _actual = (a); ASSERT_THAT(_actual, match); return _actual; }()) #define EXPECT_THAT_AND_RETURN(a, match) ([&](){ auto&& _actual = (a); EXPECT_THAT(_actual, match); return _actual; }()) #endif // !_TEST_REGTEST_ASSERTS_H_ diff --git a/test/regtest/discovery.cpp b/test/regtest/discovery.cpp index 9a64b2c9..2fa66c24 100644 --- a/test/regtest/discovery.cpp +++ b/test/regtest/discovery.cpp @@ -2,58 +2,44 @@ #include "baseline.h" #include #include -#include "dnmd_interfaces.hpp" + +#include #define THROW_IF_FAILED(hr) if (FAILED(hr)) throw std::runtime_error("HRESULT: " #hr) -namespace +std::vector MetadataInDirectory(std::string directory) { - std::vector MetadataInDirectory(std::string directory, CorOpenFlags flags) + std::vector scenarios; + for (auto& entry : std::filesystem::directory_iterator(directory)) { - std::vector scenarios; - for (auto& entry : std::filesystem::directory_iterator(directory)) + if (entry.is_regular_file()) { - if (entry.is_regular_file()) + auto path = entry.path(); + auto ext = path.extension(); + if (ext == ".dll") { - auto path = entry.path(); - auto ext = path.extension(); - if (ext == ".dll") - { - MetadataRegresssion scenario; - scenario.modulePath = path.string(); - scenario.baseline = nullptr; - scenario.test = nullptr; - - std::basic_string u16path; // use basic_string instead of wstring to get the right type on all platforms - u16path.resize(scenario.modulePath.size()); - // This is a hack to convert from char to WCHAR - // TODO: Refactor to use the PAL from dnmd::interfaces - std::transform(scenario.modulePath.begin(), scenario.modulePath.end(), u16path.begin(), [](char c) { return (WCHAR)c; }); + FileBlob scenario; + scenario.path = path.string(); - THROW_IF_FAILED(TestBaseline::Metadata->OpenScope(u16path.c_str(), flags, IID_IMetaDataImport2, (IUnknown**)&scenario.baseline)); - - dncp::com_ptr dnmdDispenser; - THROW_IF_FAILED(GetDispenser(IID_IMetaDataDispenser, (void**)&dnmdDispenser)); - - THROW_IF_FAILED(dnmdDispenser->OpenScope(u16path.c_str(), flags, IID_IMetaDataImport2, (IUnknown**)&scenario.test)); + malloc_span b; + if (!read_in_file(scenario.path.c_str(), b)) + { + std::cerr << "Failed to read in '" << scenario.path << "'\n"; + continue; + } - scenarios.push_back(std::move(scenario)); + if (!get_metadata_from_pe(b)) + { + std::cerr << "Failed to '" << scenario.path << "' << as PE\n"; + continue; } + + scenario.blob = std::vector{ (uint8_t*)b, b + b.size() }; } } - - return scenarios; } -} -std::vector ReadOnlyMetadataInDirectory(std::string directory) -{ - return MetadataInDirectory(directory, ofReadOnly); -} - -std::vector ReadWriteMetadataInDirectory(std::string directory) -{ - return MetadataInDirectory(directory, ofWrite); + return scenarios; } namespace diff --git a/test/regtest/fixtures.h b/test/regtest/fixtures.h index 6126d025..a906885b 100644 --- a/test/regtest/fixtures.h +++ b/test/regtest/fixtures.h @@ -14,24 +14,22 @@ #include +#include + // TODO: Can't pre-create here as dncp::com_ptr is non-copyable, need to create in the test I guess. Change discovery to be a function that returns a vector of spans of the metadata in PE. -template -struct Regression +struct FileBlob { - using ParamType = Regression; - - std::string modulePath; - dncp::com_ptr baseline; - dncp::com_ptr test; + std::string path; + std::vector blob; }; -using MetadataRegresssion = Regression; - -using SymbolRegression = Regression; +inline std::string PrintFileBlob(testing::TestParamInfo info) +{ + return info.param.path; +} -std::vector ReadOnlyMetadataInDirectory(std::string directory); -std::vector ReadWriteMetadataInDirectory(std::string directory); +std::vector MetadataInDirectory(std::string directory); std::string FindFrameworkInstall(std::string version); @@ -39,11 +37,7 @@ std::string GetBaselineDirectory(); void SetBaselineModulePath(std::string path); -class MetaDataRegressionTest : public ::testing::TestWithParam -{ -}; - -class SymbolRegressionTest : public ::testing::TestWithParam +class RegressionTest : public ::testing::TestWithParam { }; diff --git a/test/regtest/metadata.cpp b/test/regtest/metadata.cpp index d271802f..06f54081 100644 --- a/test/regtest/metadata.cpp +++ b/test/regtest/metadata.cpp @@ -1,14 +1,42 @@ #include "asserts.h" #include "fixtures.h" +#include "baseline.h" + +#include #include #include #include #include - namespace { + IMetaDataDispenser* g_baselineDisp; + IMetaDataDispenser* g_deltaImageBuilder; + IMetaDataDispenser* g_currentDisp; + + HRESULT CreateImport(IMetaDataDispenser* disp, void const* data, uint32_t dataLen, IMetaDataImport2** import) + { + assert(disp != nullptr && data != nullptr && dataLen > 0 && import != nullptr); + return disp->OpenScopeOnMemory( + data, + dataLen, + CorOpenFlags::ofReadOnly, + IID_IMetaDataImport2, + reinterpret_cast(import)); + } + + HRESULT CreateEmit(IMetaDataDispenser* disp, void const* data, uint32_t dataLen, IMetaDataEmit2** emit) + { + assert(disp != nullptr && data != nullptr && dataLen > 0 && emit != nullptr); + return disp->OpenScopeOnMemory( + data, + dataLen, + CorOpenFlags::ofWrite, + IID_IMetaDataEmit2, + reinterpret_cast(emit)); + } + template using static_enum_buffer = std::array; @@ -1597,12 +1625,19 @@ namespace } } -TEST_P(MetaDataRegressionTest, FindAPIs) +TEST_P(RegressionTest, FindAPIs) { auto& param = GetParam(); + + dncp::com_ptr baselineImport; + ASSERT_HRESULT_SUCCEEDED(CreateImport(TestBaseline::Metadata, param.blob.data(), (uint32_t)param.blob.size(), &baselineImport)); // Load metadata - dncp::com_ptr baselineImport { param.baseline.p }; - dncp::com_ptr currentImport { param.test.p }; + dncp::com_ptr currentImport; + + dncp::com_ptr dispenser; + ASSERT_HRESULT_SUCCEEDED(GetDispenser(IID_IMetaDataDispenser, (void**)&dispenser)); + + ASSERT_HRESULT_SUCCEEDED(CreateImport(dispenser, param.blob.data(), (uint32_t)param.blob.size(), &baselineImport)); static auto FindTokenByName = [](IMetaDataImport2* import, LPCWSTR name, mdToken enclosing = mdTokenNil) -> mdToken { @@ -1666,22 +1701,22 @@ TEST_P(MetaDataRegressionTest, FindAPIs) auto baseTypeDef = W("B1"); auto tkB1 = EXPECT_THAT_AND_RETURN(FindTokenByName(baselineImport, baseTypeDef), testing::Eq(FindTokenByName(currentImport, baseTypeDef))); auto tkB1Base = EXPECT_THAT_AND_RETURN(GetTypeDefBaseToken(baselineImport, tkB1), testing::Eq(GetTypeDefBaseToken(currentImport, tkB1))); - EXPECT_EQ(FindTypeDefByName(baselineImport, tgt, tkB1Base), FindTypeDefByName(currentImport, tgt, tkB1Base)); + ASSERT_EQ(FindTypeDefByName(baselineImport, tgt, tkB1Base), FindTypeDefByName(currentImport, tgt, tkB1Base)); auto baseTypeRef = W("B2"); auto tkB2 = EXPECT_THAT_AND_RETURN(FindTokenByName(baselineImport, baseTypeRef), testing::Eq(FindTokenByName(currentImport, baseTypeRef))); auto tkB2Base = EXPECT_THAT_AND_RETURN(GetTypeDefBaseToken(baselineImport, tkB2), testing::Eq(GetTypeDefBaseToken(currentImport, tkB2))); - EXPECT_THAT(FindTypeDefByName(baselineImport, tgt, tkB2Base), testing::Eq(FindTypeDefByName(currentImport, tgt, tkB2Base))); + ASSERT_THAT(FindTypeDefByName(baselineImport, tgt, tkB2Base), testing::Eq(FindTypeDefByName(currentImport, tgt, tkB2Base))); auto methodDefName = W("MethodDef"); auto tkMethodDef = EXPECT_THAT_AND_RETURN(FindMethodDef(baselineImport, tkB1Base, methodDefName), testing::Eq(FindMethodDef(currentImport, tkB1Base, methodDefName))); void const* defSigBlob; ULONG defSigBlobLength; - EXPECT_EQ( + ASSERT_EQ( GetMethodProps(baselineImport, tkMethodDef), GetMethodProps(currentImport, tkMethodDef, &defSigBlob, &defSigBlobLength)); - EXPECT_EQ( + ASSERT_EQ( FindMethod(baselineImport, tkB1Base, methodDefName, defSigBlob, defSigBlobLength), FindMethod(currentImport, tkB1Base, methodDefName, defSigBlob, defSigBlobLength)); @@ -1692,10 +1727,10 @@ TEST_P(MetaDataRegressionTest, FindAPIs) PCCOR_SIGNATURE ref1Blob; ULONG ref1BlobLength; - EXPECT_EQ( + ASSERT_EQ( GetMemberRefProps(baselineImport, tkMemberRefNoVarArgsBase), GetMemberRefProps(currentImport, tkMemberRefNoVarArgsBase, &ref1Blob, &ref1BlobLength)); - EXPECT_EQ( + ASSERT_EQ( FindMethod(baselineImport, tkB1Base, methodRef1Name, ref1Blob, ref1BlobLength), FindMethod(currentImport, tkB1Base, methodRef1Name, ref1Blob, ref1BlobLength)); @@ -1706,10 +1741,10 @@ TEST_P(MetaDataRegressionTest, FindAPIs) PCCOR_SIGNATURE ref2Blob; ULONG ref2BlobLength; - EXPECT_EQ( + ASSERT_EQ( GetMemberRefProps(baselineImport, tkMemberRefVarArgsBase), GetMemberRefProps(currentImport, tkMemberRefVarArgsBase, &ref2Blob, &ref2BlobLength)); - EXPECT_EQ( + ASSERT_EQ( FindMethod(baselineImport, tkB1Base, methodRef2Name, ref2Blob, ref2BlobLength), FindMethod(currentImport, tkB1Base, methodRef2Name, ref2Blob, ref2BlobLength)); @@ -1722,13 +1757,13 @@ TEST_P(MetaDataRegressionTest, FindAPIs) void const* sigBlob; ULONG sigBlobLength; - EXPECT_EQ( + ASSERT_EQ( GetFieldProps(baselineImport, tkField), GetFieldProps(currentImport, tkField, &sigBlob, &sigBlobLength)); - EXPECT_EQ( + ASSERT_EQ( FindField(baselineImport, tkB2, fieldName, sigBlob, sigBlobLength), FindField(currentImport, tkB2, fieldName, sigBlob, sigBlobLength)); } -INSTANTIATE_TEST_SUITE_P(MetaDataRegressionTestNetCore, MetaDataRegressionTest, testing::ValuesIn(std::move(ReadOnlyMetadataInDirectory(GetBaselineDirectory())))); +INSTANTIATE_TEST_SUITE_P(MetaDataRegressionTestNetCore, RegressionTest, testing::ValuesIn(MetadataInDirectory(GetBaselineDirectory())), PrintFileBlob); From 5eabd676c6ac1ea475c663aca01cf9e2bfb77e0d Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 20 Dec 2023 11:45:21 -0800 Subject: [PATCH 07/59] Get FindAPIs test passing --- test/regtest/CMakeLists.txt | 2 +- test/regtest/asserts.h | 16 +++++++++++++ test/regtest/discovery.cpp | 48 ++++++++++++++++++++++++++----------- test/regtest/fixtures.h | 4 ++++ test/regtest/main.cpp | 24 +++++++++++++++---- test/regtest/metadata.cpp | 16 ++++++------- 6 files changed, 82 insertions(+), 28 deletions(-) diff --git a/test/regtest/CMakeLists.txt b/test/regtest/CMakeLists.txt index b413ce83..c488cebd 100644 --- a/test/regtest/CMakeLists.txt +++ b/test/regtest/CMakeLists.txt @@ -13,7 +13,7 @@ set(SOURCES add_executable(regtest ${SOURCES} ${HEADERS}) -target_link_libraries(regtest PRIVATE dnmd::interfaces gtest gmock dncp::dncp) # Reference gmock for better collection assertions +target_link_libraries(regtest PRIVATE dnmd::interfaces_static gtest gmock dncp::dncp) # Reference gmock for better collection assertions set_target_properties(regtest PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON) # Require C++17 for the tests so we can use std::filesystem. if (NOT WIN32) diff --git a/test/regtest/asserts.h b/test/regtest/asserts.h index 3c7f89ba..b4d1b061 100644 --- a/test/regtest/asserts.h +++ b/test/regtest/asserts.h @@ -5,4 +5,20 @@ #define EXPECT_THAT_AND_RETURN(a, match) ([&](){ auto&& _actual = (a); EXPECT_THAT(_actual, match); return _actual; }()) +template +void AssertEqualAndSet(T& result, T&& expected, T&& actual) +{ + ASSERT_EQ(actual, expected); + result = std::move(actual); +} + +template +void AssertEqualAndSet(std::vector& result, std::vector&& expected, std::vector&& actual) +{ + ASSERT_THAT(actual, ::testing::ElementsAreArray(expected)); + result = std::move(actual); +} + +#define ASSERT_EQUAL_AND_SET(a, b, result) ASSERT_NO_FATAL_FAILURE(AssertEqualAndSet(a, b, result)) + #endif // !_TEST_REGTEST_ASSERTS_H_ diff --git a/test/regtest/discovery.cpp b/test/regtest/discovery.cpp index 2fa66c24..a24e62d8 100644 --- a/test/regtest/discovery.cpp +++ b/test/regtest/discovery.cpp @@ -5,7 +5,27 @@ #include -#define THROW_IF_FAILED(hr) if (FAILED(hr)) throw std::runtime_error("HRESULT: " #hr) +namespace +{ + malloc_span ReadMetadataFromFile(std::filesystem::path path) + { + malloc_span b; + if (!read_in_file(path.generic_string().c_str(), b)) + { + std::cerr << "Failed to read in '" << path << "'\n"; + return {}; + } + + if (!get_metadata_from_pe(b)) + { + std::cerr << "Failed to '" << path << "' << as PE\n"; + return {}; + } + + return b; + } +} + std::vector MetadataInDirectory(std::string directory) { @@ -19,20 +39,9 @@ std::vector MetadataInDirectory(std::string directory) if (ext == ".dll") { FileBlob scenario; - scenario.path = path.string(); - - malloc_span b; - if (!read_in_file(scenario.path.c_str(), b)) - { - std::cerr << "Failed to read in '" << scenario.path << "'\n"; - continue; - } + scenario.path = path.filename().generic_string(); - if (!get_metadata_from_pe(b)) - { - std::cerr << "Failed to '" << scenario.path << "' << as PE\n"; - continue; - } + malloc_span b = ReadMetadataFromFile(path); scenario.blob = std::vector{ (uint8_t*)b, b + b.size() }; } @@ -45,6 +54,7 @@ std::vector MetadataInDirectory(std::string directory) namespace { std::string baselinePath; + std::string regressionAssemblyPath; } std::string GetBaselineDirectory() @@ -55,4 +65,14 @@ std::string GetBaselineDirectory() void SetBaselineModulePath(std::string path) { baselinePath = path; +} + +void SetRegressionAssemblyPath(std::string path) +{ + regressionAssemblyPath = path; +} + +malloc_span GetRegressionAssemblyMetadata() +{ + return ReadMetadataFromFile(regressionAssemblyPath); } \ No newline at end of file diff --git a/test/regtest/fixtures.h b/test/regtest/fixtures.h index a906885b..64bdcc81 100644 --- a/test/regtest/fixtures.h +++ b/test/regtest/fixtures.h @@ -31,12 +31,16 @@ inline std::string PrintFileBlob(testing::TestParamInfo info) std::vector MetadataInDirectory(std::string directory); +malloc_span GetRegressionAssemblyMetadata(); + std::string FindFrameworkInstall(std::string version); std::string GetBaselineDirectory(); void SetBaselineModulePath(std::string path); +void SetRegressionAssemblyPath(std::string path); + class RegressionTest : public ::testing::TestWithParam { }; diff --git a/test/regtest/main.cpp b/test/regtest/main.cpp index 3f515400..ac71072b 100644 --- a/test/regtest/main.cpp +++ b/test/regtest/main.cpp @@ -19,7 +19,7 @@ namespace class ThrowListener final : public testing::EmptyTestEventListener { void OnTestPartResult(const testing::TestPartResult& result) override { - if (result.failed()) { + if (result.fatally_failed()) { throw testing::AssertionException(result); } } @@ -34,21 +34,37 @@ int main(int argc, char** argv) { std::string path { argv[++i] }; SetBaselineModulePath(path); auto mod = LoadModule(path.c_str()); - if (TestBaseline::Metadata == nullptr) + if (mod == nullptr) { - printf("Failed to load metadata baseline module: %s\n", argv[i]); + std::cout << "Failed to load metadata baseline module: " << argv[i] << std::endl; return -1; } auto getDispenser = (MetaDataGetDispenser)GetSymbol(mod, "MetaDataGetDispenser"); if (getDispenser == nullptr) { - printf("Failed to find MetaDataGetDispenser in module: %s\n", argv[i]); + std::cout << "Failed to find MetaDataGetDispenser in module: " << argv[i] << std::endl; return -1; } RETURN_IF_FAILED(getDispenser(CLSID_CorMetaDataDispenser, IID_IMetaDataDispenser, (void**)&TestBaseline::Metadata)); } + else if (std::string(argv[i]) == "--regression-asm" && i + 1 < argc) + { + SetRegressionAssemblyPath(argv[++i]); + } + } + + if (TestBaseline::Metadata == nullptr) + { + std::cout << "No metadata baseline module specified" << std::endl; + return -1; + } + + if (GetRegressionAssemblyMetadata().size() == 0) + { + std::cout << "Invalid regression assembly metadata specified" << std::endl; + return -1; } testing::UnitTest::GetInstance()->listeners().Append(new ThrowListener); diff --git a/test/regtest/metadata.cpp b/test/regtest/metadata.cpp index 06f54081..a264d095 100644 --- a/test/regtest/metadata.cpp +++ b/test/regtest/metadata.cpp @@ -1625,19 +1625,19 @@ namespace } } -TEST_P(RegressionTest, FindAPIs) +TEST(FindTest, FindAPIs) { - auto& param = GetParam(); + malloc_span metadata = GetRegressionAssemblyMetadata(); dncp::com_ptr baselineImport; - ASSERT_HRESULT_SUCCEEDED(CreateImport(TestBaseline::Metadata, param.blob.data(), (uint32_t)param.blob.size(), &baselineImport)); + ASSERT_HRESULT_SUCCEEDED(CreateImport(TestBaseline::Metadata, metadata, (uint32_t)metadata.size(), &baselineImport)); // Load metadata dncp::com_ptr currentImport; dncp::com_ptr dispenser; ASSERT_HRESULT_SUCCEEDED(GetDispenser(IID_IMetaDataDispenser, (void**)&dispenser)); - ASSERT_HRESULT_SUCCEEDED(CreateImport(dispenser, param.blob.data(), (uint32_t)param.blob.size(), &baselineImport)); + ASSERT_HRESULT_SUCCEEDED(CreateImport(dispenser, metadata, (uint32_t)metadata.size(), ¤tImport)); static auto FindTokenByName = [](IMetaDataImport2* import, LPCWSTR name, mdToken enclosing = mdTokenNil) -> mdToken { @@ -1744,7 +1744,7 @@ TEST_P(RegressionTest, FindAPIs) ASSERT_EQ( GetMemberRefProps(baselineImport, tkMemberRefVarArgsBase), GetMemberRefProps(currentImport, tkMemberRefVarArgsBase, &ref2Blob, &ref2BlobLength)); - ASSERT_EQ( + EXPECT_EQ( FindMethod(baselineImport, tkB1Base, methodRef2Name, ref2Blob, ref2BlobLength), FindMethod(currentImport, tkB1Base, methodRef2Name, ref2Blob, ref2BlobLength)); @@ -1752,7 +1752,7 @@ TEST_P(RegressionTest, FindAPIs) auto tkFields = EXPECT_THAT_AND_RETURN( EnumFieldsWithName(baselineImport, tkB2, fieldName), testing::ElementsAreArray(EnumFieldsWithName(currentImport, tkB2, fieldName))); - EXPECT_TRUE(!tkFields.empty()); + ASSERT_FALSE(tkFields.empty()); mdToken tkField = tkFields[0]; void const* sigBlob; @@ -1760,10 +1760,8 @@ TEST_P(RegressionTest, FindAPIs) ASSERT_EQ( GetFieldProps(baselineImport, tkField), GetFieldProps(currentImport, tkField, &sigBlob, &sigBlobLength)); - ASSERT_EQ( + EXPECT_EQ( FindField(baselineImport, tkB2, fieldName, sigBlob, sigBlobLength), FindField(currentImport, tkB2, fieldName, sigBlob, sigBlobLength)); } - -INSTANTIATE_TEST_SUITE_P(MetaDataRegressionTestNetCore, RegressionTest, testing::ValuesIn(MetadataInDirectory(GetBaselineDirectory())), PrintFileBlob); From 66b292e5de49979e903eee1f6845bd4aa90d622d Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 20 Dec 2023 12:29:33 -0800 Subject: [PATCH 08/59] Use new artifacts output support in the SDK to have a simpler layout on disk. --- Directory.Build.props | 17 ++++------------- .../Regression.Performance.csproj | 1 - .../Regression.TargetAssembly.ilproj | 1 - .../Regression.UnitTests.csproj | 1 - 4 files changed, 4 insertions(+), 16 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index c1a9124c..07ba7464 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,22 +1,13 @@ - Debug - - - - $(MSBuildThisFileDirectory) - $(RepoDir)artifacts - $(BaseArtifactsPath)/$(Configuration)/ - - $(ArtifactsDir)obj/$(MSBuildProjectName) - $(ArtifactsDir)bin/$(MSBuildProjectName) + true + $(MSBuildThisFileDirectory)/artifacts/managed - $(BaseIntermediateOutputPath) - $(BaseOutputPath) $(BaseArtifactsPath)/bin $(BaseArtifactsPath)/lib - net8.0 + net8.0 + $(Configuration) \ No newline at end of file diff --git a/test/Regression.Performance/Regression.Performance.csproj b/test/Regression.Performance/Regression.Performance.csproj index 8e616900..f2ef8624 100644 --- a/test/Regression.Performance/Regression.Performance.csproj +++ b/test/Regression.Performance/Regression.Performance.csproj @@ -1,7 +1,6 @@ - $(TargetFrameworkLatest) Exe enable enable diff --git a/test/Regression.TargetAssembly/Regression.TargetAssembly.ilproj b/test/Regression.TargetAssembly/Regression.TargetAssembly.ilproj index ed558a53..5f92f5ae 100644 --- a/test/Regression.TargetAssembly/Regression.TargetAssembly.ilproj +++ b/test/Regression.TargetAssembly/Regression.TargetAssembly.ilproj @@ -1,6 +1,5 @@ - $(TargetFrameworkLatest) Library false diff --git a/test/Regression.UnitTests/Regression.UnitTests.csproj b/test/Regression.UnitTests/Regression.UnitTests.csproj index 249921d9..c3ee93cb 100644 --- a/test/Regression.UnitTests/Regression.UnitTests.csproj +++ b/test/Regression.UnitTests/Regression.UnitTests.csproj @@ -1,7 +1,6 @@  - $(TargetFrameworkLatest) enable enable true From 914d4cfcd40bbbb4b086b72bdd79ec091c62e602 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 20 Dec 2023 14:31:48 -0800 Subject: [PATCH 09/59] Use DNNE to make a native entrypoint for tests to use for locating managed assets --- CMakeLists.txt | 4 ++ Directory.Build.props | 1 - test/CMakeLists.txt | 2 +- test/DNMD.Tests.sln | 10 +-- test/ImportManagedComponents.cmake | 28 +++++++++ test/Regression.CoreClrLocator/Program.cs | 2 - .../Regression.CoreClrLocator.csproj | 10 --- test/Regression.Locator/LocatorHelpers.cs | 19 ++++++ .../Regression.Locator.csproj | 19 ++++++ test/regtest/CMakeLists.txt | 15 ++++- test/regtest/main.cpp | 63 ++++++++++--------- 11 files changed, 121 insertions(+), 52 deletions(-) create mode 100644 test/ImportManagedComponents.cmake delete mode 100644 test/Regression.CoreClrLocator/Program.cs delete mode 100644 test/Regression.CoreClrLocator/Regression.CoreClrLocator.csproj create mode 100644 test/Regression.Locator/LocatorHelpers.cs create mode 100644 test/Regression.Locator/Regression.Locator.csproj diff --git a/CMakeLists.txt b/CMakeLists.txt index ea98b419..41ba6750 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.10) project(dnmd LANGUAGES C CXX) +set(CMAKE_CONFIGURATION_TYPES Debug;Release) option(INCLUDE_VENDORED_LIBS "Include vendored libraries (submodules) instead of discovering dependencies through packages." ON) set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}) @@ -16,5 +17,8 @@ include_directories(src/inc) include_directories(src/inc/external) # Hiding the "external" subdirectory due to uses of <...> in cor.h. add_subdirectory(src/) + +enable_testing() + add_subdirectory(test/) diff --git a/Directory.Build.props b/Directory.Build.props index 07ba7464..c8149211 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -8,6 +8,5 @@ $(BaseArtifactsPath)/lib net8.0 - $(Configuration) \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9a933bfc..1f683f33 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -19,7 +19,7 @@ FetchContent_Declare( set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) FetchContent_MakeAvailable(googletest) -enable_testing() +include(ImportManagedComponents.cmake) include(GoogleTest) diff --git a/test/DNMD.Tests.sln b/test/DNMD.Tests.sln index 65ef9ee0..3e9f05d3 100644 --- a/test/DNMD.Tests.sln +++ b/test/DNMD.Tests.sln @@ -11,7 +11,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Regression.TargetAssembly", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Regression.DeltaBuilder", "Regression.DeltaBuilder\Regression.DeltaBuilder.csproj", "{6E62512A-7FB4-41DD-9E93-014D0280C77D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Regression.CoreClrLocator", "Regression.CoreClrLocator\Regression.CoreClrLocator.csproj", "{F472BDA7-5315-48CC-83DE-0F7F8E4C5D42}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Regression.Locator", "Regression.Locator\Regression.Locator.csproj", "{CD7DEF6B-7254-4541-951D-026CDB8928FD}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -35,10 +35,10 @@ Global {6E62512A-7FB4-41DD-9E93-014D0280C77D}.Debug|Any CPU.Build.0 = Debug|Any CPU {6E62512A-7FB4-41DD-9E93-014D0280C77D}.Release|Any CPU.ActiveCfg = Release|Any CPU {6E62512A-7FB4-41DD-9E93-014D0280C77D}.Release|Any CPU.Build.0 = Release|Any CPU - {F472BDA7-5315-48CC-83DE-0F7F8E4C5D42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F472BDA7-5315-48CC-83DE-0F7F8E4C5D42}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F472BDA7-5315-48CC-83DE-0F7F8E4C5D42}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F472BDA7-5315-48CC-83DE-0F7F8E4C5D42}.Release|Any CPU.Build.0 = Release|Any CPU + {CD7DEF6B-7254-4541-951D-026CDB8928FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CD7DEF6B-7254-4541-951D-026CDB8928FD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CD7DEF6B-7254-4541-951D-026CDB8928FD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CD7DEF6B-7254-4541-951D-026CDB8928FD}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/test/ImportManagedComponents.cmake b/test/ImportManagedComponents.cmake new file mode 100644 index 00000000..3991384a --- /dev/null +++ b/test/ImportManagedComponents.cmake @@ -0,0 +1,28 @@ +execute_process( + COMMAND dotnet build -c Debug + COMMAND dotnet build -c Release + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} +) + +add_library(Regression.Locator IMPORTED SHARED) + +if (WIN32) + set_target_properties(Regression.Locator PROPERTIES + IMPORTED_LOCATION_DEBUG ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/debug/Regression.LocatorNE.dll + IMPORTED_LOCATION_RELEASE ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/release/Regression.LocatorNE.dll + IMPORTED_IMPLIB_DEBUG ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/debug/Regression.LocatorNE.lib + IMPORTED_IMPLIB_RELEASE ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/release/Regression.LocatorNE.lib) +elseif (APPLE) + set_target_properties(Regression.Locator PROPERTIES + IMPORTED_LOCATION_DEBUG ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/debug/libRegression.LocatorNE.dylib + IMPORTED_LOCATION_RELEASE ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/release/libRegression.LocatorNE.dylib) +else() + set_target_properties(Regression.Locator PROPERTIES + IMPORTED_LOCATION_DEBUG ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/debug/libRegression.LocatorNE.so + IMPORTED_LOCATION_RELEASE ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/release/libRegression.LocatorNE.so) +endif() + +set_target_properties(Regression.Locator PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/$>/) + +set(RegressionLocatorDirectory ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/$/) diff --git a/test/Regression.CoreClrLocator/Program.cs b/test/Regression.CoreClrLocator/Program.cs deleted file mode 100644 index a29d30f6..00000000 --- a/test/Regression.CoreClrLocator/Program.cs +++ /dev/null @@ -1,2 +0,0 @@ -// Output the location of CoreCLR so we can locate it in the tests. -Console.WriteLine(Path.Combine(Path.GetDirectoryName(typeof(object).Assembly.Location!)!, OperatingSystem.IsWindows() ? "coreclr.dll" : "libcoreclr.so")); diff --git a/test/Regression.CoreClrLocator/Regression.CoreClrLocator.csproj b/test/Regression.CoreClrLocator/Regression.CoreClrLocator.csproj deleted file mode 100644 index 2150e379..00000000 --- a/test/Regression.CoreClrLocator/Regression.CoreClrLocator.csproj +++ /dev/null @@ -1,10 +0,0 @@ - - - - Exe - net8.0 - enable - enable - - - diff --git a/test/Regression.Locator/LocatorHelpers.cs b/test/Regression.Locator/LocatorHelpers.cs new file mode 100644 index 00000000..5b85280b --- /dev/null +++ b/test/Regression.Locator/LocatorHelpers.cs @@ -0,0 +1,19 @@ +using System.Runtime.InteropServices; +namespace Regression.Locator; + +public static unsafe class LocatorHelpers +{ + [UnmanagedCallersOnly(EntryPoint = "GetCoreClrPath")] + public static byte* GetCoreClrPath() + { + string path = Path.Combine(Path.GetDirectoryName(typeof(object).Assembly.Location!)!, OperatingSystem.IsWindows() ? "coreclr.dll" : "libcoreclr.so"); + return (byte*)Marshal.StringToCoTaskMemUTF8(path); + } + + [UnmanagedCallersOnly(EntryPoint = "GetRegressionTargetAssemblyPath")] + public static byte* GetCoreLibPath() + { + string path = Path.Combine(Path.GetDirectoryName(typeof(LocatorHelpers).Assembly.Location!)!, "Regression.TargetAssembly.dll"); + return (byte*)Marshal.StringToCoTaskMemUTF8(path); + } +} diff --git a/test/Regression.Locator/Regression.Locator.csproj b/test/Regression.Locator/Regression.Locator.csproj new file mode 100644 index 00000000..f86384c1 --- /dev/null +++ b/test/Regression.Locator/Regression.Locator.csproj @@ -0,0 +1,19 @@ + + + + net8.0 + enable + enable + true + true + + + + + + + + + + + diff --git a/test/regtest/CMakeLists.txt b/test/regtest/CMakeLists.txt index c488cebd..038037b9 100644 --- a/test/regtest/CMakeLists.txt +++ b/test/regtest/CMakeLists.txt @@ -11,11 +11,20 @@ set(SOURCES ./metadata.cpp ) - add_executable(regtest ${SOURCES} ${HEADERS}) -target_link_libraries(regtest PRIVATE dnmd::interfaces_static gtest gmock dncp::dncp) # Reference gmock for better collection assertions +target_link_libraries(regtest PRIVATE dnmd::interfaces gtest gmock dncp::dncp) # Reference gmock for better collection assertions set_target_properties(regtest PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON) # Require C++17 for the tests so we can use std::filesystem. if (NOT WIN32) target_link_libraries(regtest PRIVATE dncp::winhdrs) -endif() \ No newline at end of file +endif() + +target_link_libraries(regtest PRIVATE Regression.Locator) + +add_custom_command(TARGET regtest POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy $ $) + +add_custom_command(TARGET regtest POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory ${RegressionLocatorDirectory} $) + +gtest_discover_tests(regtest DISCOVERY_MODE PRE_TEST) \ No newline at end of file diff --git a/test/regtest/main.cpp b/test/regtest/main.cpp index ac71072b..38ac815a 100644 --- a/test/regtest/main.cpp +++ b/test/regtest/main.cpp @@ -4,6 +4,11 @@ #include "fixtures.h" #include "pal.hpp" +#ifdef _WIN32 +#define DNNE_API_OVERRIDE __declspec(dllimport) +#endif +#include + namespace TestBaseline { dncp::com_ptr Metadata = nullptr; @@ -25,48 +30,46 @@ class ThrowListener final : public testing::EmptyTestEventListener { } }; -int main(int argc, char** argv) { +int main(int argc, char** argv) +{ ::testing::InitGoogleTest(&argc, argv); - for (int i = 0; i < argc; ++i) { - if (std::string(argv[i]) == "--md-baseline" && i + 1 < argc) - { - std::string path { argv[++i] }; - SetBaselineModulePath(path); - auto mod = LoadModule(path.c_str()); - if (mod == nullptr) - { - std::cout << "Failed to load metadata baseline module: " << argv[i] << std::endl; - return -1; - } - - auto getDispenser = (MetaDataGetDispenser)GetSymbol(mod, "MetaDataGetDispenser"); - if (getDispenser == nullptr) - { - std::cout << "Failed to find MetaDataGetDispenser in module: " << argv[i] << std::endl; - return -1; - } - - RETURN_IF_FAILED(getDispenser(CLSID_CorMetaDataDispenser, IID_IMetaDataDispenser, (void**)&TestBaseline::Metadata)); - } - else if (std::string(argv[i]) == "--regression-asm" && i + 1 < argc) - { - SetRegressionAssemblyPath(argv[++i]); - } + dncp::cotaskmem_ptr coreClrPath{ (char*)GetCoreClrPath() }; + if (coreClrPath == nullptr) + { + std::cout << "Failed to get coreclr path" << std::endl; + return -1; + } + + auto mod = LoadModule(coreClrPath.get()); + if (mod == nullptr) + { + std::cout << "Failed to load metadata baseline module: " << coreClrPath.get() << std::endl; + return -1; } - if (TestBaseline::Metadata == nullptr) + auto getDispenser = (MetaDataGetDispenser)GetSymbol(mod, "MetaDataGetDispenser"); + if (getDispenser == nullptr) { - std::cout << "No metadata baseline module specified" << std::endl; + std::cout << "Failed to find MetaDataGetDispenser in module: " << coreClrPath.get() << std::endl; return -1; } - if (GetRegressionAssemblyMetadata().size() == 0) + RETURN_IF_FAILED(getDispenser(CLSID_CorMetaDataDispenser, IID_IMetaDataDispenser, (void**)&TestBaseline::Metadata)); + + std::cout << "Loaded metadata baseline module: " << coreClrPath.get() << std::endl; + + dncp::cotaskmem_ptr regressionAssemblyPath{ (char*)GetRegressionTargetAssemblyPath() }; + if (regressionAssemblyPath == nullptr) { - std::cout << "Invalid regression assembly metadata specified" << std::endl; + std::cout << "Failed to get regression assembly path" << std::endl; return -1; } + SetRegressionAssemblyPath(regressionAssemblyPath.get()); + + std::cout << "Regression assembly path: " << regressionAssemblyPath.get() << std::endl; + testing::UnitTest::GetInstance()->listeners().Append(new ThrowListener); return RUN_ALL_TESTS(); From feaa32f76fac34f3dde1f15f44e1f3bae087829d Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 20 Dec 2023 14:46:48 -0800 Subject: [PATCH 10/59] Update CI to run the new tests --- .github/workflows/main.yml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1a4d9231..82a07ac9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -46,13 +46,8 @@ jobs: cmake -S . -B artifacts -DCMAKE_BUILD_TYPE=${{ matrix.flavor }} -DINCLUDE_VENDORED_LIBS=${{ matrix.use-vendored-libs }} cmake --build artifacts --config ${{ matrix.flavor }} --target install - - name: Build Managed Test Components - run: dotnet build --configuration ${{ matrix.flavor }} - working-directory: ./test - - - name: Run Unit Tests - run: dotnet test --configuration ${{ matrix.flavor }} --logger trx --results-directory "TestResults-${{ matrix.os }}-${{ matrix.flavor }}" - working-directory: ./test/Regression.UnitTests + - name: Run Tests + run: ctest --test-dir artifacts --output-on-failure -C ${{ matrix.flavor }} # - name: Upload Test Results # if: always() From 575603acdaf049322fc8ae1a6860192bff64eb9a Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 20 Dec 2023 14:47:15 -0800 Subject: [PATCH 11/59] Remove FindAPIs as we have moved this API to the new test harness --- test/Regression.UnitTests/ImportTests.cs | 13 --- test/regnative/unit_cor.cpp | 139 ----------------------- 2 files changed, 152 deletions(-) diff --git a/test/Regression.UnitTests/ImportTests.cs b/test/Regression.UnitTests/ImportTests.cs index 6eb6a8f1..0142b55e 100644 --- a/test/Regression.UnitTests/ImportTests.cs +++ b/test/Regression.UnitTests/ImportTests.cs @@ -20,7 +20,6 @@ public unsafe class ImportTests { private delegate* unmanaged _importAPIs; private delegate* unmanaged _longRunningAPIs; - private delegate* unmanaged _findAPIs; private delegate* unmanaged _importAPIsIndirectionTables; public ImportTests(ITestOutputHelper outputHelper) @@ -37,7 +36,6 @@ public ImportTests(ITestOutputHelper outputHelper) _importAPIs = (delegate* unmanaged)NativeLibrary.GetExport(mod, "UnitImportAPIs"); _longRunningAPIs = (delegate* unmanaged)NativeLibrary.GetExport(mod, "UnitLongRunningAPIs"); - _findAPIs = (delegate* unmanaged)NativeLibrary.GetExport(mod, "UnitFindAPIs"); _importAPIsIndirectionTables = (delegate* unmanaged)NativeLibrary.GetExport(mod, "UnitImportAPIsIndirectionTables"); } @@ -309,16 +307,5 @@ public void LongRunningAPIs(string filename, PEReader managedLibrary) _longRunningAPIs(block.Pointer, block.Length).Check(); } - - [Fact] - public void FindAPIs() - { - var dir = Path.GetDirectoryName(typeof(ImportTests).Assembly.Location)!; - var tgtAssembly = Path.Combine(dir, "Regression.TargetAssembly.dll"); - using PEReader managedLibrary = new(File.OpenRead(tgtAssembly)); - PEMemoryBlock block = managedLibrary.GetMetadata(); - - _findAPIs(block.Pointer, block.Length).Check(); - } } } \ No newline at end of file diff --git a/test/regnative/unit_cor.cpp b/test/regnative/unit_cor.cpp index 222943a0..5cf85a56 100644 --- a/test/regnative/unit_cor.cpp +++ b/test/regnative/unit_cor.cpp @@ -1949,145 +1949,6 @@ TestResult UnitLongRunningAPIs(void const* data, uint32_t dataLen) END_TEST(); } -EXPORT -TestResult UnitFindAPIs(void const* data, uint32_t dataLen) -{ - BEGIN_TEST(); - - // Load metadata - dncp::com_ptr baselineImport; - ASSERT_EQUAL(S_OK, CreateImport(g_baselineDisp, data, dataLen, &baselineImport)); - dncp::com_ptr currentImport; - ASSERT_EQUAL(S_OK, CreateImport(g_currentDisp, data, dataLen, ¤tImport)); - - static auto FindTokenByName = [](IMetaDataImport2* import, LPCWSTR name, mdToken enclosing = mdTokenNil) -> mdToken - { - mdTypeDef ptd; - ASSERT_EQUAL(S_OK, import->FindTypeDefByName(name, enclosing, &ptd)); - return ptd; - }; - - static auto GetTypeDefBaseToken = [](IMetaDataImport2* import, mdTypeDef tk) -> mdToken - { - static_char_buffer name{}; - ULONG pchTypeDef; - DWORD pdwTypeDefFlags; - mdToken ptkExtends; - ASSERT_EQUAL(S_OK, import->GetTypeDefProps(tk, - name.data(), - (ULONG)name.size(), - &pchTypeDef, - &pdwTypeDefFlags, - &ptkExtends)); - return ptkExtends; - }; - - static auto FindMethodDef = [](IMetaDataImport2* import, mdTypeDef type, LPCWSTR methodName) -> mdToken - { - std::vector methoddefs = EnumMembersWithName(import, type, methodName); - ASSERT_TRUE(!methoddefs.empty()); - return methoddefs[0]; - }; - - static auto FindMemberRef = [](IMetaDataImport2* import, mdTypeDef type, LPCWSTR methodName) -> mdToken - { - auto methodDef = FindMethodDef(import, type, methodName); - mdMemberRef pmr; - ASSERT_EQUAL(S_OK, import->FindMemberRef(methodDef, methodName, nullptr, 0, &pmr)); - return pmr; - }; - - static auto FindMethod = [](IMetaDataImport2* import, mdTypeDef td, LPCWSTR name, void const* pvSigBlob, ULONG cbSigBlob) -> uint32_t - { - mdMethodDef tkMethod; - mdToken tkMember; - ASSERT_EQUAL(S_OK, import->FindMethod(td, name, (PCCOR_SIGNATURE)pvSigBlob, cbSigBlob, &tkMethod)); - ASSERT_EQUAL(S_OK, import->FindMember(td, name, (PCCOR_SIGNATURE)pvSigBlob, cbSigBlob, &tkMember)); - ASSERT_EQUAL(tkMethod, tkMember); - return tkMethod; - }; - - static auto FindField = [](IMetaDataImport2* import, mdTypeDef td, LPCWSTR name, void const* pvSigBlob, ULONG cbSigBlob) -> uint32_t - { - mdFieldDef tkField; - mdToken tkMember; - ASSERT_EQUAL(S_OK, import->FindField(td, name, (PCCOR_SIGNATURE)pvSigBlob, cbSigBlob, &tkField)); - ASSERT_EQUAL(S_OK, import->FindMember(td, name, (PCCOR_SIGNATURE)pvSigBlob, cbSigBlob, &tkMember)); - ASSERT_EQUAL(tkField, tkMember); - return tkField; - }; - - auto tgt = W("C"); - - auto baseTypeDef = W("B1"); - auto tkB1 = ASSERT_AND_RETURN(FindTokenByName(baselineImport, baseTypeDef), FindTokenByName(currentImport, baseTypeDef)); - auto tkB1Base = ASSERT_AND_RETURN(GetTypeDefBaseToken(baselineImport, tkB1), GetTypeDefBaseToken(currentImport, tkB1)); - ASSERT_EQUAL(FindTypeDefByName(baselineImport, tgt, tkB1Base), FindTypeDefByName(currentImport, tgt, tkB1Base)); - - auto baseTypeRef = W("B2"); - auto tkB2 = ASSERT_AND_RETURN(FindTokenByName(baselineImport, baseTypeRef), FindTokenByName(currentImport, baseTypeRef)); - auto tkB2Base = ASSERT_AND_RETURN(GetTypeDefBaseToken(baselineImport, tkB2), GetTypeDefBaseToken(currentImport, tkB2)); - ASSERT_EQUAL(FindTypeDefByName(baselineImport, tgt, tkB2Base), FindTypeDefByName(currentImport, tgt, tkB2Base)); - - auto methodDefName = W("MethodDef"); - auto tkMethodDef = ASSERT_AND_RETURN(FindMethodDef(baselineImport, tkB1Base, methodDefName), FindMethodDef(currentImport, tkB1Base, methodDefName)); - - void const* defSigBlob; - ULONG defSigBlobLength; - ASSERT_EQUAL( - GetMethodProps(baselineImport, tkMethodDef), - GetMethodProps(currentImport, tkMethodDef, &defSigBlob, &defSigBlobLength)); - ASSERT_EQUAL( - FindMethod(baselineImport, tkB1Base, methodDefName, defSigBlob, defSigBlobLength), - FindMethod(currentImport, tkB1Base, methodDefName, defSigBlob, defSigBlobLength)); - - auto methodRef1Name = W("MethodRef1"); - auto tkMemberRefNoVarArgsBase = ASSERT_AND_RETURN( - FindMemberRef(baselineImport, tkB1Base, methodRef1Name), - FindMemberRef(currentImport, tkB1Base, methodRef1Name)); - - PCCOR_SIGNATURE ref1Blob; - ULONG ref1BlobLength; - ASSERT_EQUAL( - GetMemberRefProps(baselineImport, tkMemberRefNoVarArgsBase), - GetMemberRefProps(currentImport, tkMemberRefNoVarArgsBase, &ref1Blob, &ref1BlobLength)); - ASSERT_EQUAL( - FindMethod(baselineImport, tkB1Base, methodRef1Name, ref1Blob, ref1BlobLength), - FindMethod(currentImport, tkB1Base, methodRef1Name, ref1Blob, ref1BlobLength)); - - auto methodRef2Name = W("MethodRef2"); - auto tkMemberRefVarArgsBase = ASSERT_AND_RETURN( - FindMemberRef(baselineImport, tkB1Base, methodRef2Name), - FindMemberRef(currentImport, tkB1Base, methodRef2Name)); - - PCCOR_SIGNATURE ref2Blob; - ULONG ref2BlobLength; - ASSERT_EQUAL( - GetMemberRefProps(baselineImport, tkMemberRefVarArgsBase), - GetMemberRefProps(currentImport, tkMemberRefVarArgsBase, &ref2Blob, &ref2BlobLength)); - ASSERT_EQUAL( - FindMethod(baselineImport, tkB1Base, methodRef2Name, ref2Blob, ref2BlobLength), - FindMethod(currentImport, tkB1Base, methodRef2Name, ref2Blob, ref2BlobLength)); - - auto fieldName = W("Field1"); - auto tkFields = ASSERT_AND_RETURN( - EnumFieldsWithName(baselineImport, tkB2, fieldName), - EnumFieldsWithName(currentImport, tkB2, fieldName)); - ASSERT_TRUE(!tkFields.empty()); - mdToken tkField = tkFields[0]; - - void const* sigBlob; - ULONG sigBlobLength; - ASSERT_EQUAL( - GetFieldProps(baselineImport, tkField), - GetFieldProps(currentImport, tkField, &sigBlob, &sigBlobLength)); - ASSERT_EQUAL( - FindField(baselineImport, tkB2, fieldName, sigBlob, sigBlobLength), - FindField(currentImport, tkB2, fieldName, sigBlob, sigBlobLength)); - - END_TEST(); -} - EXPORT TestResult UnitImportAPIsIndirectionTables(void const* data, uint32_t dataLen, void const** deltaImages, uint32_t* deltaImageLengths, uint32_t numDeltaImages) { From 1dd1f29af6631a8ce87f89c1fce8a471048b749e Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 20 Dec 2023 16:07:19 -0800 Subject: [PATCH 12/59] Move ImportAPIs test to the new GTest harness. --- test/Regression.Locator/LocatorHelpers.cs | 16 ++ test/Regression.UnitTests/ImportTests.cs | 28 --- test/regnative/unit_cor.cpp | 1 - test/regtest/asserts.h | 3 +- test/regtest/discovery.cpp | 37 +++- test/regtest/fixtures.h | 5 +- test/regtest/main.cpp | 5 +- test/regtest/metadata.cpp | 235 ++++++++++++++++++++++ 8 files changed, 295 insertions(+), 35 deletions(-) diff --git a/test/Regression.Locator/LocatorHelpers.cs b/test/Regression.Locator/LocatorHelpers.cs index 5b85280b..82c30091 100644 --- a/test/Regression.Locator/LocatorHelpers.cs +++ b/test/Regression.Locator/LocatorHelpers.cs @@ -1,4 +1,6 @@ using System.Runtime.InteropServices; +using System.Runtime.Versioning; +using Microsoft.Win32; namespace Regression.Locator; public static unsafe class LocatorHelpers @@ -16,4 +18,18 @@ public static unsafe class LocatorHelpers string path = Path.Combine(Path.GetDirectoryName(typeof(LocatorHelpers).Assembly.Location!)!, "Regression.TargetAssembly.dll"); return (byte*)Marshal.StringToCoTaskMemUTF8(path); } + + [SupportedOSPlatform("windows")] + [UnmanagedCallersOnly(EntryPoint = "GetFrameworkPath")] + public static byte* GetFrameworkPath([DNNE.C99Type("char const*")]sbyte* version) + { + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return null; + } + + using var key = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\.NETFramework")!; + string path = Path.Combine((string)key.GetValue("InstallRoot")!, new string(version)); + return (byte*)Marshal.StringToCoTaskMemUTF8(path); + } } diff --git a/test/Regression.UnitTests/ImportTests.cs b/test/Regression.UnitTests/ImportTests.cs index 0142b55e..0f06cdcd 100644 --- a/test/Regression.UnitTests/ImportTests.cs +++ b/test/Regression.UnitTests/ImportTests.cs @@ -18,7 +18,6 @@ namespace Regression.UnitTests { public unsafe class ImportTests { - private delegate* unmanaged _importAPIs; private delegate* unmanaged _longRunningAPIs; private delegate* unmanaged _importAPIsIndirectionTables; @@ -34,7 +33,6 @@ public ImportTests(ITestOutputHelper outputHelper) throw new Exception($"Initialization failed: 0x{hr:x}"); } - _importAPIs = (delegate* unmanaged)NativeLibrary.GetExport(mod, "UnitImportAPIs"); _longRunningAPIs = (delegate* unmanaged)NativeLibrary.GetExport(mod, "UnitLongRunningAPIs"); _importAPIsIndirectionTables = (delegate* unmanaged)NativeLibrary.GetExport(mod, "UnitImportAPIsIndirectionTables"); } @@ -226,18 +224,6 @@ static SemanticEdit CreateSemanticEdit(SemanticEditKind editKind, Compilation ba } } - [Theory] - [MemberData(nameof(CoreFrameworkLibraries))] - public void ImportAPIs_Core(string filename, PEReader managedLibrary) => ImportAPIs(filename, managedLibrary); - - [WindowsOnlyTheory] - [MemberData(nameof(Net20FrameworkLibraries))] - public void ImportAPIs_Net20(string filename, PEReader managedLibrary) => ImportAPIs(filename, managedLibrary); - - [WindowsOnlyTheory] - [MemberData(nameof(Net40FrameworkLibraries))] - public void ImportAPIs_Net40(string filename, PEReader managedLibrary) => ImportAPIs(filename, managedLibrary); - [Theory] [MemberData(nameof(AssembliesWithDelta))] public unsafe void ImportAPIs_AssembliesWithAppliedDeltas(string filename, PEReader deltaBaseline, IList> diffs) @@ -279,20 +265,6 @@ public unsafe void ImportAPIs_AssembliesWithAppliedDeltas(string filename, PERea } } - private void ImportAPIs(string filename, PEReader managedLibrary) - { - Debug.WriteLine($"{nameof(ImportAPIs)} - {filename}"); - using var _ = managedLibrary; - PEMemoryBlock block = managedLibrary.GetMetadata(); - _importAPIs(block.Pointer, block.Length).Check(); - } - - private void ImportAPIs(string filename, MetadataReader managedLibrary) - { - Debug.WriteLine($"{nameof(ImportAPIs)} - {filename}"); - _importAPIs(managedLibrary.MetadataPointer, managedLibrary.MetadataLength).Check(); - } - /// /// These APIs are very expensive to run on all managed libraries. This library only runs /// them on the system corelibs and only on a reduced selection of the tokens. diff --git a/test/regnative/unit_cor.cpp b/test/regnative/unit_cor.cpp index 5cf85a56..06629fc8 100644 --- a/test/regnative/unit_cor.cpp +++ b/test/regnative/unit_cor.cpp @@ -1658,7 +1658,6 @@ namespace } } -EXPORT TestResult UnitImportAPIs(void const* data, uint32_t dataLen) { BEGIN_TEST(); diff --git a/test/regtest/asserts.h b/test/regtest/asserts.h index b4d1b061..0cc2c4b1 100644 --- a/test/regtest/asserts.h +++ b/test/regtest/asserts.h @@ -2,6 +2,7 @@ #define _TEST_REGTEST_ASSERTS_H_ #include +#include #define EXPECT_THAT_AND_RETURN(a, match) ([&](){ auto&& _actual = (a); EXPECT_THAT(_actual, match); return _actual; }()) @@ -19,6 +20,6 @@ void AssertEqualAndSet(std::vector& result, std::vector&& expected, std::v result = std::move(actual); } -#define ASSERT_EQUAL_AND_SET(a, b, result) ASSERT_NO_FATAL_FAILURE(AssertEqualAndSet(a, b, result)) +#define ASSERT_EQUAL_AND_SET(result, expected, actual) ASSERT_NO_FATAL_FAILURE(AssertEqualAndSet(result, expected, actual)) #endif // !_TEST_REGTEST_ASSERTS_H_ diff --git a/test/regtest/discovery.cpp b/test/regtest/discovery.cpp index a24e62d8..4c0afdc0 100644 --- a/test/regtest/discovery.cpp +++ b/test/regtest/discovery.cpp @@ -5,6 +5,11 @@ #include +#ifdef _WIN32 +#define DNNE_API_OVERRIDE __declspec(dllimport) +#endif +#include + namespace { malloc_span ReadMetadataFromFile(std::filesystem::path path) @@ -18,7 +23,7 @@ namespace if (!get_metadata_from_pe(b)) { - std::cerr << "Failed to '" << path << "' << as PE\n"; + std::cerr << "Failed to read '" << path << "' as PE\n"; return {}; } @@ -26,10 +31,16 @@ namespace } } - std::vector MetadataInDirectory(std::string directory) { std::vector scenarios; + + if (!std::filesystem::exists(directory)) + { + std::cerr << "Directory '" << directory << "' does not exist\n"; + return scenarios; + } + for (auto& entry : std::filesystem::directory_iterator(directory)) { if (entry.is_regular_file()) @@ -43,7 +54,14 @@ std::vector MetadataInDirectory(std::string directory) malloc_span b = ReadMetadataFromFile(path); + if (b.size() == 0) + { + // Some DLLs don't have metadata, so skip them. + continue; + } + scenario.blob = std::vector{ (uint8_t*)b, b + b.size() }; + scenarios.push_back(std::move(scenario)); } } } @@ -75,4 +93,19 @@ void SetRegressionAssemblyPath(std::string path) malloc_span GetRegressionAssemblyMetadata() { return ReadMetadataFromFile(regressionAssemblyPath); +} + +std::string FindFrameworkInstall(std::string version) +{ +#ifdef _WIN32 + dncp::cotaskmem_ptr installPath { (char*)GetFrameworkPath(version.c_str()) }; + if (installPath == nullptr) + { + std::cerr << "Failed to find framework install for version: " << version << "\n"; + return {}; + } + return installPath.get(); +#else + return {}; +#endif } \ No newline at end of file diff --git a/test/regtest/fixtures.h b/test/regtest/fixtures.h index 64bdcc81..c5cafd14 100644 --- a/test/regtest/fixtures.h +++ b/test/regtest/fixtures.h @@ -13,6 +13,7 @@ #include #include +#include #include @@ -26,7 +27,9 @@ struct FileBlob inline std::string PrintFileBlob(testing::TestParamInfo info) { - return info.param.path; + std::string name = info.param.path; + std::replace(name.begin(), name.end(), '.', '_'); + return name; } std::vector MetadataInDirectory(std::string directory); diff --git a/test/regtest/main.cpp b/test/regtest/main.cpp index 38ac815a..37d46f8b 100644 --- a/test/regtest/main.cpp +++ b/test/regtest/main.cpp @@ -32,8 +32,6 @@ class ThrowListener final : public testing::EmptyTestEventListener { int main(int argc, char** argv) { - ::testing::InitGoogleTest(&argc, argv); - dncp::cotaskmem_ptr coreClrPath{ (char*)GetCoreClrPath() }; if (coreClrPath == nullptr) { @@ -57,6 +55,8 @@ int main(int argc, char** argv) RETURN_IF_FAILED(getDispenser(CLSID_CorMetaDataDispenser, IID_IMetaDataDispenser, (void**)&TestBaseline::Metadata)); + SetBaselineModulePath(coreClrPath.get()); + std::cout << "Loaded metadata baseline module: " << coreClrPath.get() << std::endl; dncp::cotaskmem_ptr regressionAssemblyPath{ (char*)GetRegressionTargetAssemblyPath() }; @@ -70,6 +70,7 @@ int main(int argc, char** argv) std::cout << "Regression assembly path: " << regressionAssemblyPath.get() << std::endl; + ::testing::InitGoogleTest(&argc, argv); testing::UnitTest::GetInstance()->listeners().Append(new ThrowListener); return RUN_ALL_TESTS(); diff --git a/test/regtest/metadata.cpp b/test/regtest/metadata.cpp index a264d095..0ac246f0 100644 --- a/test/regtest/metadata.cpp +++ b/test/regtest/metadata.cpp @@ -1763,5 +1763,240 @@ TEST(FindTest, FindAPIs) EXPECT_EQ( FindField(baselineImport, tkB2, fieldName, sigBlob, sigBlobLength), FindField(currentImport, tkB2, fieldName, sigBlob, sigBlobLength)); +} + +class MetadataImportTest : public RegressionTest +{ +protected: + using TokenList = std::vector; +}; + +TEST_P(MetadataImportTest, ImportAPIs) +{ + auto param = GetParam(); + void const* data = param.blob.data(); + uint32_t dataLen = (uint32_t)param.blob.size(); + + // Load metadata + dncp::com_ptr baselineImport; + ASSERT_HRESULT_SUCCEEDED(CreateImport(TestBaseline::Metadata, data, dataLen, &baselineImport)); + + dncp::com_ptr dispenser; + ASSERT_HRESULT_SUCCEEDED(GetDispenser(IID_IMetaDataDispenser, (void**)&dispenser)); + dncp::com_ptr currentImport; + ASSERT_HRESULT_SUCCEEDED(CreateImport(dispenser, data, dataLen, ¤tImport)); + + // Verify APIs + ASSERT_THAT(ResetEnum(currentImport), testing::ElementsAreArray(ResetEnum(baselineImport))); + ASSERT_THAT(GetScopeProps(currentImport), testing::ElementsAreArray(GetScopeProps(baselineImport))); + ASSERT_THAT(GetVersionString(currentImport), testing::ElementsAreArray(GetVersionString(baselineImport))); + + TokenList sigs; + ASSERT_EQUAL_AND_SET(sigs, EnumSignatures(baselineImport), EnumSignatures(currentImport)); + for (auto sig : sigs) + { + ASSERT_THAT(GetSigFromToken(currentImport, sig), testing::ElementsAreArray(GetSigFromToken(baselineImport, sig))); + } + + TokenList userStrings; + ASSERT_EQUAL_AND_SET(userStrings, EnumUserStrings(baselineImport), EnumUserStrings(currentImport)); + for (auto us : userStrings) + { + ASSERT_THAT(GetUserString(currentImport, us), testing::ElementsAreArray(GetUserString(baselineImport, us))); + } + + TokenList custAttrs; + ASSERT_EQUAL_AND_SET(custAttrs, EnumCustomAttributes(baselineImport), EnumCustomAttributes(currentImport)); + for (auto ca : custAttrs) + { + ASSERT_THAT(GetCustomAttributeProps(currentImport, ca), testing::ElementsAreArray(GetCustomAttributeProps(baselineImport, ca))); + } + + TokenList modulerefs; + ASSERT_EQUAL_AND_SET(modulerefs, EnumModuleRefs(baselineImport), EnumModuleRefs(currentImport)); + for (auto moduleref : modulerefs) + { + ASSERT_THAT(GetModuleRefProps(currentImport, moduleref), testing::ElementsAreArray(GetModuleRefProps(baselineImport, moduleref))); + ASSERT_THAT(GetNameFromToken(currentImport, moduleref), testing::ElementsAreArray(GetNameFromToken(baselineImport, moduleref))); + } + + ASSERT_THAT(FindTypeRef(currentImport), testing::ElementsAreArray(FindTypeRef(baselineImport))); + TokenList typerefs; + ASSERT_EQUAL_AND_SET(typerefs, EnumTypeRefs(baselineImport), EnumTypeRefs(currentImport)); + for (auto typeref : typerefs) + { + ASSERT_THAT(GetTypeRefProps(currentImport, typeref), testing::ElementsAreArray(GetTypeRefProps(baselineImport, typeref))); + ASSERT_THAT(GetCustomAttribute_CompilerGenerated(currentImport, typeref), testing::ElementsAreArray(GetCustomAttribute_CompilerGenerated(baselineImport, typeref))); + ASSERT_THAT(GetNameFromToken(currentImport, typeref), testing::ElementsAreArray(GetNameFromToken(baselineImport, typeref))); + } + + TokenList typespecs; + ASSERT_EQUAL_AND_SET(typespecs, EnumTypeSpecs(baselineImport), EnumTypeSpecs(currentImport)); + for (auto typespec : typespecs) + { + ASSERT_THAT(GetTypeSpecFromToken(currentImport, typespec), testing::ElementsAreArray(GetTypeSpecFromToken(baselineImport, typespec))); + ASSERT_THAT(GetCustomAttribute_CompilerGenerated(currentImport, typespec), testing::ElementsAreArray(GetCustomAttribute_CompilerGenerated(baselineImport, typespec))); + } + TokenList typedefs; + ASSERT_EQUAL_AND_SET(typedefs, EnumTypeDefs(baselineImport), EnumTypeDefs(currentImport)); + for (auto typdef : typedefs) + { + ASSERT_THAT(GetTypeDefProps(currentImport, typdef), testing::ElementsAreArray(GetTypeDefProps(baselineImport, typdef))); + ASSERT_THAT(GetNameFromToken(currentImport, typdef), testing::ElementsAreArray(GetNameFromToken(baselineImport, typdef))); + ASSERT_THAT(IsGlobal(currentImport, typdef), testing::Eq(IsGlobal(baselineImport, typdef))); + ASSERT_THAT(EnumInterfaceImpls(currentImport, typdef), testing::ElementsAreArray(EnumInterfaceImpls(baselineImport, typdef))); + ASSERT_THAT(EnumPermissionSetsAndGetProps(currentImport, typdef), testing::ElementsAreArray(EnumPermissionSetsAndGetProps(baselineImport, typdef))); + ASSERT_THAT(EnumMembers(currentImport, typdef), testing::ElementsAreArray(EnumMembers(baselineImport, typdef))); + ASSERT_THAT(EnumMembersWithName(currentImport, typdef), testing::ElementsAreArray(EnumMembersWithName(baselineImport, typdef))); + ASSERT_THAT(EnumMethodsWithName(currentImport, typdef), testing::ElementsAreArray(EnumMethodsWithName(baselineImport, typdef))); + ASSERT_THAT(EnumMethodImpls(currentImport, typdef), testing::ElementsAreArray(EnumMethodImpls(baselineImport, typdef))); + ASSERT_THAT(GetNestedClassProps(currentImport, typdef), testing::ElementsAreArray(GetNestedClassProps(baselineImport, typdef))); + ASSERT_THAT(GetClassLayout(currentImport, typdef), testing::ElementsAreArray(GetClassLayout(baselineImport, typdef))); + ASSERT_THAT(GetCustomAttribute_CompilerGenerated(currentImport, typdef), testing::ElementsAreArray(GetCustomAttribute_CompilerGenerated(baselineImport, typdef))); + + TokenList methoddefs; + ASSERT_EQUAL_AND_SET(methoddefs, EnumMethods(baselineImport, typdef), EnumMethods(currentImport, typdef)); + for (auto methoddef : methoddefs) + { + void const* sig = nullptr; + ULONG sigLen = 0; + ASSERT_THAT(GetMethodProps(currentImport, methoddef, &sig, &sigLen), testing::ElementsAreArray(GetMethodProps(baselineImport, methoddef))); + ASSERT_THAT(GetNativeCallConvFromSig(currentImport, sig, sigLen), testing::ElementsAreArray(GetNativeCallConvFromSig(baselineImport, sig, sigLen))); + ASSERT_THAT(GetNameFromToken(currentImport, methoddef), testing::ElementsAreArray(GetNameFromToken(baselineImport, methoddef))); + ASSERT_THAT(IsGlobal(currentImport, methoddef), testing::Eq(IsGlobal(baselineImport, methoddef))); + ASSERT_THAT(GetCustomAttribute_CompilerGenerated(currentImport, methoddef), testing::ElementsAreArray(GetCustomAttribute_CompilerGenerated(baselineImport, methoddef))); + + TokenList paramdefs; + ASSERT_EQUAL_AND_SET(paramdefs, EnumParams(baselineImport, methoddef), EnumParams(currentImport, methoddef)); + for (auto paramdef : paramdefs) + { + ASSERT_THAT(GetParamProps(currentImport, paramdef), testing::ElementsAreArray(GetParamProps(baselineImport, paramdef))); + ASSERT_THAT(GetFieldMarshal(currentImport, paramdef), testing::ElementsAreArray(GetFieldMarshal(baselineImport, paramdef))); + ASSERT_THAT(GetCustomAttribute_Nullable(currentImport, paramdef), testing::ElementsAreArray(GetCustomAttribute_Nullable(baselineImport, paramdef))); + ASSERT_THAT(GetNameFromToken(currentImport, paramdef), testing::ElementsAreArray(GetNameFromToken(baselineImport, paramdef))); + } + + ASSERT_THAT(GetParamForMethodIndex(currentImport, methoddef), testing::ElementsAreArray(GetParamForMethodIndex(baselineImport, methoddef))); + ASSERT_THAT(EnumPermissionSetsAndGetProps(currentImport, methoddef), testing::ElementsAreArray(EnumPermissionSetsAndGetProps(baselineImport, methoddef))); + ASSERT_THAT(GetPinvokeMap(currentImport, methoddef), testing::ElementsAreArray(GetPinvokeMap(baselineImport, methoddef))); + ASSERT_THAT(GetRVA(currentImport, methoddef), testing::ElementsAreArray(GetRVA(baselineImport, methoddef))); + + TokenList methodspecs; + ASSERT_EQUAL_AND_SET(methodspecs, EnumMethodSpecs(baselineImport, methoddef), EnumMethodSpecs(currentImport, methoddef)); + for (auto methodspec : methodspecs) + { + ASSERT_THAT(GetMethodSpecProps(currentImport, methodspec), testing::ElementsAreArray(GetMethodSpecProps(baselineImport, methodspec))); + } + } + + TokenList eventdefs; + ASSERT_EQUAL_AND_SET(eventdefs, EnumEvents(baselineImport, typdef), EnumEvents(currentImport, typdef)); + for (auto eventdef : eventdefs) + { + std::vector mds; + ASSERT_THAT(GetEventProps(currentImport, eventdef, &mds), testing::ElementsAreArray(GetEventProps(baselineImport, eventdef))); + for (auto md : mds) + { + ASSERT_THAT(GetMethodSemantics(currentImport, eventdef, md), testing::ElementsAreArray(GetMethodSemantics(baselineImport, eventdef, md))); + } + + ASSERT_THAT(GetNameFromToken(currentImport, eventdef), testing::ElementsAreArray(GetNameFromToken(baselineImport, eventdef))); + ASSERT_THAT(IsGlobal(currentImport, eventdef), testing::Eq(IsGlobal(baselineImport, eventdef))); + } + + TokenList properties; + ASSERT_EQUAL_AND_SET(properties, EnumProperties(baselineImport, typdef), EnumProperties(currentImport, typdef)); + for (auto props : properties) + { + std::vector mds; + ASSERT_THAT(GetPropertyProps(currentImport, props, &mds), testing::ElementsAreArray(GetPropertyProps(baselineImport, props))); + for (auto md : mds) + { + ASSERT_THAT(GetMethodSemantics(currentImport, props, md), testing::ElementsAreArray(GetMethodSemantics(baselineImport, props, md))); + } + + ASSERT_THAT(GetNameFromToken(currentImport, props), testing::ElementsAreArray(GetNameFromToken(baselineImport, props))); + ASSERT_THAT(IsGlobal(currentImport, props), testing::Eq(IsGlobal(baselineImport, props))); + } + + ASSERT_THAT(EnumFieldsWithName(baselineImport, typdef), EnumFieldsWithName(currentImport, typdef)); + TokenList fielddefs; + ASSERT_EQUAL_AND_SET(fielddefs, EnumFields(baselineImport, typdef), EnumFields(currentImport, typdef)); + for (auto fielddef : fielddefs) + { + ASSERT_THAT(GetFieldProps(currentImport, fielddef), testing::ElementsAreArray(GetFieldProps(baselineImport, fielddef))); + ASSERT_THAT(GetNameFromToken(currentImport, fielddef), testing::ElementsAreArray(GetNameFromToken(baselineImport, fielddef))); + ASSERT_THAT(IsGlobal(currentImport, fielddef), testing::Eq(IsGlobal(baselineImport, fielddef))); + ASSERT_THAT(GetPinvokeMap(currentImport, fielddef), testing::ElementsAreArray(GetPinvokeMap(baselineImport, fielddef))); + ASSERT_THAT(GetRVA(currentImport, fielddef), testing::ElementsAreArray(GetRVA(baselineImport, fielddef))); + ASSERT_THAT(GetFieldMarshal(currentImport, fielddef), testing::ElementsAreArray(GetFieldMarshal(baselineImport, fielddef))); + ASSERT_THAT(GetCustomAttribute_Nullable(currentImport, fielddef), testing::ElementsAreArray(GetCustomAttribute_Nullable(baselineImport, fielddef))); + } + + TokenList genparams; + ASSERT_EQUAL_AND_SET(genparams, EnumGenericParams(baselineImport, typdef), EnumGenericParams(currentImport, typdef)); + for (auto genparam : genparams) + { + ASSERT_THAT(GetGenericParamProps(currentImport, genparam), testing::ElementsAreArray(GetGenericParamProps(baselineImport, genparam))); + TokenList genparamconsts; + ASSERT_EQUAL_AND_SET(genparamconsts, EnumGenericParamConstraints(baselineImport, genparam), EnumGenericParamConstraints(currentImport, genparam)); + for (auto genparamconst : genparamconsts) + { + ASSERT_THAT(GetGenericParamConstraintProps(currentImport, genparamconst), testing::ElementsAreArray(GetGenericParamConstraintProps(baselineImport, genparamconst))); + } + } + } + + dncp::com_ptr baselineAssembly; + ASSERT_THAT(S_OK, baselineImport->QueryInterface(IID_IMetaDataAssemblyImport, (void**)&baselineAssembly)); + dncp::com_ptr currentAssembly; + ASSERT_THAT(S_OK, currentImport->QueryInterface(IID_IMetaDataAssemblyImport, (void**)¤tAssembly)); + + TokenList assemblyTokens; + ASSERT_EQUAL_AND_SET(assemblyTokens, GetAssemblyFromScope(baselineAssembly), GetAssemblyFromScope(currentAssembly)); + for (auto assembly : assemblyTokens) + { + ASSERT_THAT(GetAssemblyProps(currentAssembly, assembly), testing::ElementsAreArray(GetAssemblyProps(baselineAssembly, assembly))); + } + + TokenList assemblyRefs; + ASSERT_EQUAL_AND_SET(assemblyRefs, EnumAssemblyRefs(baselineAssembly), EnumAssemblyRefs(currentAssembly)); + for (auto assemblyRef : assemblyRefs) + { + ASSERT_THAT(GetAssemblyRefProps(currentAssembly, assemblyRef), testing::ElementsAreArray(GetAssemblyRefProps(baselineAssembly, assemblyRef))); + } + + TokenList files; + ASSERT_EQUAL_AND_SET(files, EnumFiles(baselineAssembly), EnumFiles(currentAssembly)); + for (auto file : files) + { + ASSERT_THAT(GetFileProps(currentAssembly, file), testing::ElementsAreArray(GetFileProps(baselineAssembly, file))); + } + + TokenList exports; + ASSERT_EQUAL_AND_SET(exports, EnumExportedTypes(baselineAssembly), EnumExportedTypes(currentAssembly)); + for (auto exportedType : exports) + { + std::vector name; + uint32_t implementation = mdTokenNil; + ASSERT_THAT(GetExportedTypeProps(currentAssembly, exportedType, &name, &implementation), testing::ElementsAreArray(GetExportedTypeProps(baselineAssembly, exportedType))); + ASSERT_THAT( + FindExportedTypeByName(currentAssembly, name.data(), implementation), + testing::ElementsAreArray(FindExportedTypeByName(baselineAssembly, name.data(), implementation))); + } + + TokenList resources; + ASSERT_EQUAL_AND_SET(resources, EnumManifestResources(baselineAssembly), EnumManifestResources(currentAssembly)); + for (auto resource : resources) + { + std::vector name; + ASSERT_THAT(GetManifestResourceProps(currentAssembly, resource, &name), testing::ElementsAreArray(GetManifestResourceProps(baselineAssembly, resource))); + ASSERT_THAT(FindManifestResourceByName(currentAssembly, name.data()), testing::ElementsAreArray(FindManifestResourceByName(baselineAssembly, name.data()))); + } } + +INSTANTIATE_TEST_SUITE_P(MetaDataImportTestCore, MetadataImportTest, testing::ValuesIn(MetadataInDirectory(GetBaselineDirectory())), PrintFileBlob); + +INSTANTIATE_TEST_SUITE_P(MetaDataImportTestFx4_0, MetadataImportTest, testing::ValuesIn(MetadataInDirectory(FindFrameworkInstall("v4.0.30319"))), PrintFileBlob); +INSTANTIATE_TEST_SUITE_P(MetaDataImportTestFx2_0, MetadataImportTest, testing::ValuesIn(MetadataInDirectory(FindFrameworkInstall("v2.0.50727"))), PrintFileBlob); From 75f05519475bc0fe1e40a40ec6a6168200fabfa0 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 21 Dec 2023 14:25:13 -0800 Subject: [PATCH 13/59] Add long running import APIs tests to GTest harness. --- test/regtest/discovery.cpp | 58 ++++++++++++++++++++- test/regtest/fixtures.h | 4 ++ test/regtest/metadata.cpp | 102 ++++++++++++++++++++++++++++++++++++- 3 files changed, 161 insertions(+), 3 deletions(-) diff --git a/test/regtest/discovery.cpp b/test/regtest/discovery.cpp index 4c0afdc0..b02da24e 100644 --- a/test/regtest/discovery.cpp +++ b/test/regtest/discovery.cpp @@ -12,10 +12,27 @@ namespace { + bool read_in_file(std::filesystem::path path, malloc_span& b) + { + // Read in the entire file + std::ifstream fd{ path, std::ios::binary }; + if (!fd) + return false; + + size_t size = std::filesystem::file_size(path); + if (size == 0) + return false; + + b = { (uint8_t*)std::malloc(size), size }; + + fd.read((char*)(uint8_t*)b, b.size()); + return true; + } + malloc_span ReadMetadataFromFile(std::filesystem::path path) { malloc_span b; - if (!read_in_file(path.generic_string().c_str(), b)) + if (!read_in_file(path, b)) { std::cerr << "Failed to read in '" << path << "'\n"; return {}; @@ -75,6 +92,45 @@ namespace std::string regressionAssemblyPath; } +std::vector CoreLibs() +{ + std::vector scenarios; + + auto coreclrSystemPrivateCoreLib = std::filesystem::path(baselinePath).parent_path() / "System.Private.CoreLib.dll"; + auto b = ReadMetadataFromFile(coreclrSystemPrivateCoreLib); + if (b.size() == 0) + { + std::cerr << "Failed to read in '" << coreclrSystemPrivateCoreLib << "'\n"; + return scenarios; + } + + scenarios.push_back({ "netcoreapp", std::vector{ (uint8_t*)b, b + b.size() } }); + + auto fx4mscorlib = std::filesystem::path(FindFrameworkInstall("v4.0.30319")) / "mscorlib.dll"; + b = ReadMetadataFromFile(coreclrSystemPrivateCoreLib); + if (b.size() == 0) + { + std::cerr << "Failed to read in '" << coreclrSystemPrivateCoreLib << "'\n"; + return scenarios; + } + + scenarios.push_back({ "fx4", std::vector{ (uint8_t*)b, b + b.size() } }); + auto fx2mscorlib = std::filesystem::path(FindFrameworkInstall("v2.0.50727")) / "mscorlib.dll"; + if (std::filesystem::exists(fx2mscorlib)) + { + b = ReadMetadataFromFile(coreclrSystemPrivateCoreLib); + if (b.size() == 0) + { + std::cerr << "Failed to read in '" << coreclrSystemPrivateCoreLib << "'\n"; + return scenarios; + } + + scenarios.push_back({ "fx2", std::vector{ (uint8_t*)b, b + b.size() } }); + } + + return scenarios; +} + std::string GetBaselineDirectory() { return std::filesystem::path(baselinePath).parent_path().string(); diff --git a/test/regtest/fixtures.h b/test/regtest/fixtures.h index c5cafd14..fcd716f2 100644 --- a/test/regtest/fixtures.h +++ b/test/regtest/fixtures.h @@ -34,6 +34,8 @@ inline std::string PrintFileBlob(testing::TestParamInfo info) std::vector MetadataInDirectory(std::string directory); +std::vector CoreLibs(); + malloc_span GetRegressionAssemblyMetadata(); std::string FindFrameworkInstall(std::string version); @@ -46,6 +48,8 @@ void SetRegressionAssemblyPath(std::string path); class RegressionTest : public ::testing::TestWithParam { +protected: + using TokenList = std::vector; }; #endif // !_TEST_REGTEST_FIXTURES_H_ \ No newline at end of file diff --git a/test/regtest/metadata.cpp b/test/regtest/metadata.cpp index 0ac246f0..88b02190 100644 --- a/test/regtest/metadata.cpp +++ b/test/regtest/metadata.cpp @@ -1767,8 +1767,6 @@ TEST(FindTest, FindAPIs) class MetadataImportTest : public RegressionTest { -protected: - using TokenList = std::vector; }; TEST_P(MetadataImportTest, ImportAPIs) @@ -2000,3 +1998,103 @@ INSTANTIATE_TEST_SUITE_P(MetaDataImportTestCore, MetadataImportTest, testing::Va INSTANTIATE_TEST_SUITE_P(MetaDataImportTestFx4_0, MetadataImportTest, testing::ValuesIn(MetadataInDirectory(FindFrameworkInstall("v4.0.30319"))), PrintFileBlob); INSTANTIATE_TEST_SUITE_P(MetaDataImportTestFx2_0, MetadataImportTest, testing::ValuesIn(MetadataInDirectory(FindFrameworkInstall("v2.0.50727"))), PrintFileBlob); + +class MetaDataLongRunningTest : public RegressionTest +{ +}; + +TEST_P(MetaDataLongRunningTest, ImportAPIs) +{ + auto param = GetParam(); + void const* data = param.blob.data(); + uint32_t dataLen = (uint32_t)param.blob.size(); + + // Load metadata + dncp::com_ptr baselineImport; + ASSERT_HRESULT_SUCCEEDED(CreateImport(TestBaseline::Metadata, data, dataLen, &baselineImport)); + + dncp::com_ptr dispenser; + ASSERT_HRESULT_SUCCEEDED(GetDispenser(IID_IMetaDataDispenser, (void**)&dispenser)); + dncp::com_ptr currentImport; + ASSERT_HRESULT_SUCCEEDED(CreateImport(dispenser, data, dataLen, ¤tImport)); + + static auto VerifyFindMemberRef = [](IMetaDataImport2 * import, mdToken memberRef) -> std::vector + { + std::vector values; + + mdToken ptk; + static_char_buffer name{}; + ULONG pchMember; + PCCOR_SIGNATURE ppvSigBlob; + ULONG pcbSigBlob; + HRESULT hr = import->GetMemberRefProps(memberRef, + & ptk, + name.data(), + (ULONG)name.size(), + & pchMember, + & ppvSigBlob, + & pcbSigBlob); + values.push_back(hr); + if (hr == S_OK) + { + // We were able to get the name, now try looking up a memberRef by name and by sig + mdMemberRef lookup = mdTokenNil; + hr = import->FindMemberRef(ptk, name.data(), ppvSigBlob, pcbSigBlob, & lookup); + values.push_back(hr); + values.push_back(lookup); + lookup = mdTokenNil; + hr = import->FindMemberRef(ptk, name.data(), nullptr, 0, & lookup); + values.push_back(hr); + values.push_back(lookup); + lookup = mdTokenNil; + hr = import->FindMemberRef(ptk, nullptr, ppvSigBlob, pcbSigBlob, & lookup); + values.push_back(hr); + values.push_back(lookup); + } + return values; + }; + + size_t stride; + size_t count; + + TokenList typedefs; + ASSERT_EQUAL_AND_SET(typedefs, EnumTypeDefs(baselineImport), EnumTypeDefs(currentImport)); + count = 0; + stride = std::max(typedefs.size() / 128, (size_t)16); + for (auto typdef : typedefs) + { + if (count++ % stride != 0) + continue; + + EXPECT_THAT(EnumMemberRefs(currentImport, typdef), testing::ElementsAreArray(EnumMemberRefs(baselineImport, typdef))); + + TokenList methoddefs; + ASSERT_EQUAL_AND_SET(methoddefs, EnumMethods(baselineImport, typdef), EnumMethods(currentImport, typdef)); + for (auto methoddef : methoddefs) + { + EXPECT_THAT(EnumMethodSemantics(currentImport, methoddef), testing::ElementsAreArray(EnumMethodSemantics(baselineImport, methoddef))); + } + + EXPECT_THAT(EnumCustomAttributes(currentImport, typdef), testing::ElementsAreArray(EnumCustomAttributes(baselineImport, typdef))); + } + + TokenList typespecs; + ASSERT_EQUAL_AND_SET(typespecs, EnumTypeSpecs(baselineImport), EnumTypeSpecs(currentImport)); + count = 0; + stride = std::max(typespecs.size() / 128, (size_t)16); + for (auto typespec : typespecs) + { + if (count++ % stride != 0) + continue; + + TokenList memberrefs; + ASSERT_EQUAL_AND_SET(memberrefs, EnumMemberRefs(baselineImport, typespec), EnumMemberRefs(currentImport, typespec)); + for (auto memberref : memberrefs) + { + EXPECT_THAT(GetMemberRefProps(currentImport, memberref), testing::ElementsAreArray(GetMemberRefProps(baselineImport, memberref))); + EXPECT_THAT(VerifyFindMemberRef(currentImport, memberref), testing::ElementsAreArray(VerifyFindMemberRef(baselineImport, memberref))); + } + } +} + +INSTANTIATE_TEST_SUITE_P(MetaDataLongRunningTest_CoreLibs, MetaDataLongRunningTest, testing::ValuesIn(CoreLibs())); From 244ab1ce81791accd32ef21d1bd6a52ffbeb5d57 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 21 Dec 2023 15:03:00 -0800 Subject: [PATCH 14/59] Split large tests into separate tests per-table --- test/regtest/metadata.cpp | 372 ++++++++++++++++++++++++-------------- 1 file changed, 233 insertions(+), 139 deletions(-) diff --git a/test/regtest/metadata.cpp b/test/regtest/metadata.cpp index 88b02190..2c17ba81 100644 --- a/test/regtest/metadata.cpp +++ b/test/regtest/metadata.cpp @@ -11,9 +11,7 @@ #include namespace { - IMetaDataDispenser* g_baselineDisp; IMetaDataDispenser* g_deltaImageBuilder; - IMetaDataDispenser* g_currentDisp; HRESULT CreateImport(IMetaDataDispenser* disp, void const* data, uint32_t dataLen, IMetaDataImport2** import) { @@ -21,7 +19,7 @@ namespace return disp->OpenScopeOnMemory( data, dataLen, - CorOpenFlags::ofReadOnly, + CorOpenFlags::ofReadOnly | CorOpenFlags::ofCopyMemory, IID_IMetaDataImport2, reinterpret_cast(import)); } @@ -32,11 +30,26 @@ namespace return disp->OpenScopeOnMemory( data, dataLen, - CorOpenFlags::ofWrite, + CorOpenFlags::ofWrite | CorOpenFlags::ofCopyMemory, IID_IMetaDataEmit2, reinterpret_cast(emit)); } + void GetImports(span metadata, dncp::com_ptr& baseline, dncp::com_ptr& current) + { + ASSERT_HRESULT_SUCCEEDED(CreateImport(TestBaseline::Metadata, metadata, (uint32_t)metadata.size(), &baseline)); + + dncp::com_ptr dispenser; + ASSERT_HRESULT_SUCCEEDED(GetDispenser(IID_IMetaDataDispenser, (void**)&dispenser)); + ASSERT_HRESULT_SUCCEEDED(CreateImport(dispenser, metadata, (uint32_t)metadata.size(), ¤t)); + + } + + void GetImports(FileBlob blob, dncp::com_ptr& baseline, dncp::com_ptr& current) + { + GetImports({ blob.blob.data(), blob.blob.size() }, baseline, current); + } + template using static_enum_buffer = std::array; @@ -1630,14 +1643,8 @@ TEST(FindTest, FindAPIs) malloc_span metadata = GetRegressionAssemblyMetadata(); dncp::com_ptr baselineImport; - ASSERT_HRESULT_SUCCEEDED(CreateImport(TestBaseline::Metadata, metadata, (uint32_t)metadata.size(), &baselineImport)); - // Load metadata dncp::com_ptr currentImport; - - dncp::com_ptr dispenser; - ASSERT_HRESULT_SUCCEEDED(GetDispenser(IID_IMetaDataDispenser, (void**)&dispenser)); - - ASSERT_HRESULT_SUCCEEDED(CreateImport(dispenser, metadata, (uint32_t)metadata.size(), ¤tImport)); + ASSERT_NO_FATAL_FAILURE(GetImports(metadata, baselineImport, currentImport)); static auto FindTokenByName = [](IMetaDataImport2* import, LPCWSTR name, mdToken enclosing = mdTokenNil) -> mdToken { @@ -1769,89 +1776,128 @@ class MetadataImportTest : public RegressionTest { }; -TEST_P(MetadataImportTest, ImportAPIs) +TEST_P(MetadataImportTest, BasicApis) { - auto param = GetParam(); - void const* data = param.blob.data(); - uint32_t dataLen = (uint32_t)param.blob.size(); - - // Load metadata dncp::com_ptr baselineImport; - ASSERT_HRESULT_SUCCEEDED(CreateImport(TestBaseline::Metadata, data, dataLen, &baselineImport)); - - dncp::com_ptr dispenser; - ASSERT_HRESULT_SUCCEEDED(GetDispenser(IID_IMetaDataDispenser, (void**)&dispenser)); dncp::com_ptr currentImport; - ASSERT_HRESULT_SUCCEEDED(CreateImport(dispenser, data, dataLen, ¤tImport)); + ASSERT_NO_FATAL_FAILURE(GetImports(GetParam(), baselineImport, currentImport)); + + EXPECT_THAT(ResetEnum(currentImport), testing::ElementsAreArray(ResetEnum(baselineImport))); + EXPECT_THAT(GetScopeProps(currentImport), testing::ElementsAreArray(GetScopeProps(baselineImport))); + EXPECT_THAT(GetVersionString(currentImport), testing::ElementsAreArray(GetVersionString(baselineImport))); +} - // Verify APIs - ASSERT_THAT(ResetEnum(currentImport), testing::ElementsAreArray(ResetEnum(baselineImport))); - ASSERT_THAT(GetScopeProps(currentImport), testing::ElementsAreArray(GetScopeProps(baselineImport))); - ASSERT_THAT(GetVersionString(currentImport), testing::ElementsAreArray(GetVersionString(baselineImport))); +TEST_P(MetadataImportTest, Signature) +{ + dncp::com_ptr baselineImport; + dncp::com_ptr currentImport; + ASSERT_NO_FATAL_FAILURE(GetImports(GetParam(), baselineImport, currentImport)); TokenList sigs; ASSERT_EQUAL_AND_SET(sigs, EnumSignatures(baselineImport), EnumSignatures(currentImport)); for (auto sig : sigs) { - ASSERT_THAT(GetSigFromToken(currentImport, sig), testing::ElementsAreArray(GetSigFromToken(baselineImport, sig))); + EXPECT_THAT(GetSigFromToken(currentImport, sig), testing::ElementsAreArray(GetSigFromToken(baselineImport, sig))); } +} + +TEST_P(MetadataImportTest, UserStrings) +{ + dncp::com_ptr baselineImport; + dncp::com_ptr currentImport; + ASSERT_NO_FATAL_FAILURE(GetImports(GetParam(), baselineImport, currentImport)); TokenList userStrings; ASSERT_EQUAL_AND_SET(userStrings, EnumUserStrings(baselineImport), EnumUserStrings(currentImport)); for (auto us : userStrings) { - ASSERT_THAT(GetUserString(currentImport, us), testing::ElementsAreArray(GetUserString(baselineImport, us))); + EXPECT_THAT(GetUserString(currentImport, us), testing::ElementsAreArray(GetUserString(baselineImport, us))); } +} + +TEST_P(MetadataImportTest, CustomAttribute) +{ + dncp::com_ptr baselineImport; + dncp::com_ptr currentImport; + ASSERT_NO_FATAL_FAILURE(GetImports(GetParam(), baselineImport, currentImport)); TokenList custAttrs; ASSERT_EQUAL_AND_SET(custAttrs, EnumCustomAttributes(baselineImport), EnumCustomAttributes(currentImport)); for (auto ca : custAttrs) { - ASSERT_THAT(GetCustomAttributeProps(currentImport, ca), testing::ElementsAreArray(GetCustomAttributeProps(baselineImport, ca))); + EXPECT_THAT(GetCustomAttributeProps(currentImport, ca), testing::ElementsAreArray(GetCustomAttributeProps(baselineImport, ca))); } +} + +TEST_P(MetadataImportTest, ModuleRef) +{ + dncp::com_ptr baselineImport; + dncp::com_ptr currentImport; + ASSERT_NO_FATAL_FAILURE(GetImports(GetParam(), baselineImport, currentImport)); TokenList modulerefs; ASSERT_EQUAL_AND_SET(modulerefs, EnumModuleRefs(baselineImport), EnumModuleRefs(currentImport)); for (auto moduleref : modulerefs) { - ASSERT_THAT(GetModuleRefProps(currentImport, moduleref), testing::ElementsAreArray(GetModuleRefProps(baselineImport, moduleref))); - ASSERT_THAT(GetNameFromToken(currentImport, moduleref), testing::ElementsAreArray(GetNameFromToken(baselineImport, moduleref))); + EXPECT_THAT(GetModuleRefProps(currentImport, moduleref), testing::ElementsAreArray(GetModuleRefProps(baselineImport, moduleref))); + EXPECT_THAT(GetNameFromToken(currentImport, moduleref), testing::ElementsAreArray(GetNameFromToken(baselineImport, moduleref))); } +} + +TEST_P(MetadataImportTest, TypeRef) +{ + dncp::com_ptr baselineImport; + dncp::com_ptr currentImport; + ASSERT_NO_FATAL_FAILURE(GetImports(GetParam(), baselineImport, currentImport)); ASSERT_THAT(FindTypeRef(currentImport), testing::ElementsAreArray(FindTypeRef(baselineImport))); TokenList typerefs; ASSERT_EQUAL_AND_SET(typerefs, EnumTypeRefs(baselineImport), EnumTypeRefs(currentImport)); for (auto typeref : typerefs) { - ASSERT_THAT(GetTypeRefProps(currentImport, typeref), testing::ElementsAreArray(GetTypeRefProps(baselineImport, typeref))); - ASSERT_THAT(GetCustomAttribute_CompilerGenerated(currentImport, typeref), testing::ElementsAreArray(GetCustomAttribute_CompilerGenerated(baselineImport, typeref))); - ASSERT_THAT(GetNameFromToken(currentImport, typeref), testing::ElementsAreArray(GetNameFromToken(baselineImport, typeref))); + EXPECT_THAT(GetTypeRefProps(currentImport, typeref), testing::ElementsAreArray(GetTypeRefProps(baselineImport, typeref))); + EXPECT_THAT(GetCustomAttribute_CompilerGenerated(currentImport, typeref), testing::ElementsAreArray(GetCustomAttribute_CompilerGenerated(baselineImport, typeref))); + EXPECT_THAT(GetNameFromToken(currentImport, typeref), testing::ElementsAreArray(GetNameFromToken(baselineImport, typeref))); } +} + +TEST_P(MetadataImportTest, TypeSpec) +{ + dncp::com_ptr baselineImport; + dncp::com_ptr currentImport; + ASSERT_NO_FATAL_FAILURE(GetImports(GetParam(), baselineImport, currentImport)); TokenList typespecs; ASSERT_EQUAL_AND_SET(typespecs, EnumTypeSpecs(baselineImport), EnumTypeSpecs(currentImport)); for (auto typespec : typespecs) { - ASSERT_THAT(GetTypeSpecFromToken(currentImport, typespec), testing::ElementsAreArray(GetTypeSpecFromToken(baselineImport, typespec))); - ASSERT_THAT(GetCustomAttribute_CompilerGenerated(currentImport, typespec), testing::ElementsAreArray(GetCustomAttribute_CompilerGenerated(baselineImport, typespec))); + EXPECT_THAT(GetTypeSpecFromToken(currentImport, typespec), testing::ElementsAreArray(GetTypeSpecFromToken(baselineImport, typespec))); + EXPECT_THAT(GetCustomAttribute_CompilerGenerated(currentImport, typespec), testing::ElementsAreArray(GetCustomAttribute_CompilerGenerated(baselineImport, typespec))); } +} + +TEST_P(MetadataImportTest, TypeDefAndMembers) +{ + dncp::com_ptr baselineImport; + dncp::com_ptr currentImport; + ASSERT_NO_FATAL_FAILURE(GetImports(GetParam(), baselineImport, currentImport)); TokenList typedefs; ASSERT_EQUAL_AND_SET(typedefs, EnumTypeDefs(baselineImport), EnumTypeDefs(currentImport)); for (auto typdef : typedefs) { - ASSERT_THAT(GetTypeDefProps(currentImport, typdef), testing::ElementsAreArray(GetTypeDefProps(baselineImport, typdef))); - ASSERT_THAT(GetNameFromToken(currentImport, typdef), testing::ElementsAreArray(GetNameFromToken(baselineImport, typdef))); - ASSERT_THAT(IsGlobal(currentImport, typdef), testing::Eq(IsGlobal(baselineImport, typdef))); - ASSERT_THAT(EnumInterfaceImpls(currentImport, typdef), testing::ElementsAreArray(EnumInterfaceImpls(baselineImport, typdef))); - ASSERT_THAT(EnumPermissionSetsAndGetProps(currentImport, typdef), testing::ElementsAreArray(EnumPermissionSetsAndGetProps(baselineImport, typdef))); - ASSERT_THAT(EnumMembers(currentImport, typdef), testing::ElementsAreArray(EnumMembers(baselineImport, typdef))); - ASSERT_THAT(EnumMembersWithName(currentImport, typdef), testing::ElementsAreArray(EnumMembersWithName(baselineImport, typdef))); - ASSERT_THAT(EnumMethodsWithName(currentImport, typdef), testing::ElementsAreArray(EnumMethodsWithName(baselineImport, typdef))); - ASSERT_THAT(EnumMethodImpls(currentImport, typdef), testing::ElementsAreArray(EnumMethodImpls(baselineImport, typdef))); - ASSERT_THAT(GetNestedClassProps(currentImport, typdef), testing::ElementsAreArray(GetNestedClassProps(baselineImport, typdef))); - ASSERT_THAT(GetClassLayout(currentImport, typdef), testing::ElementsAreArray(GetClassLayout(baselineImport, typdef))); - ASSERT_THAT(GetCustomAttribute_CompilerGenerated(currentImport, typdef), testing::ElementsAreArray(GetCustomAttribute_CompilerGenerated(baselineImport, typdef))); + EXPECT_THAT(GetTypeDefProps(currentImport, typdef), testing::ElementsAreArray(GetTypeDefProps(baselineImport, typdef))); + EXPECT_THAT(GetNameFromToken(currentImport, typdef), testing::ElementsAreArray(GetNameFromToken(baselineImport, typdef))); + EXPECT_THAT(IsGlobal(currentImport, typdef), testing::Eq(IsGlobal(baselineImport, typdef))); + EXPECT_THAT(EnumInterfaceImpls(currentImport, typdef), testing::ElementsAreArray(EnumInterfaceImpls(baselineImport, typdef))); + EXPECT_THAT(EnumPermissionSetsAndGetProps(currentImport, typdef), testing::ElementsAreArray(EnumPermissionSetsAndGetProps(baselineImport, typdef))); + EXPECT_THAT(EnumMembers(currentImport, typdef), testing::ElementsAreArray(EnumMembers(baselineImport, typdef))); + EXPECT_THAT(EnumMembersWithName(currentImport, typdef), testing::ElementsAreArray(EnumMembersWithName(baselineImport, typdef))); + EXPECT_THAT(EnumMethodsWithName(currentImport, typdef), testing::ElementsAreArray(EnumMethodsWithName(baselineImport, typdef))); + EXPECT_THAT(EnumMethodImpls(currentImport, typdef), testing::ElementsAreArray(EnumMethodImpls(baselineImport, typdef))); + EXPECT_THAT(GetNestedClassProps(currentImport, typdef), testing::ElementsAreArray(GetNestedClassProps(baselineImport, typdef))); + EXPECT_THAT(GetClassLayout(currentImport, typdef), testing::ElementsAreArray(GetClassLayout(baselineImport, typdef))); + EXPECT_THAT(GetCustomAttribute_CompilerGenerated(currentImport, typdef), testing::ElementsAreArray(GetCustomAttribute_CompilerGenerated(baselineImport, typdef))); TokenList methoddefs; ASSERT_EQUAL_AND_SET(methoddefs, EnumMethods(baselineImport, typdef), EnumMethods(currentImport, typdef)); @@ -1859,32 +1905,32 @@ TEST_P(MetadataImportTest, ImportAPIs) { void const* sig = nullptr; ULONG sigLen = 0; - ASSERT_THAT(GetMethodProps(currentImport, methoddef, &sig, &sigLen), testing::ElementsAreArray(GetMethodProps(baselineImport, methoddef))); - ASSERT_THAT(GetNativeCallConvFromSig(currentImport, sig, sigLen), testing::ElementsAreArray(GetNativeCallConvFromSig(baselineImport, sig, sigLen))); - ASSERT_THAT(GetNameFromToken(currentImport, methoddef), testing::ElementsAreArray(GetNameFromToken(baselineImport, methoddef))); - ASSERT_THAT(IsGlobal(currentImport, methoddef), testing::Eq(IsGlobal(baselineImport, methoddef))); - ASSERT_THAT(GetCustomAttribute_CompilerGenerated(currentImport, methoddef), testing::ElementsAreArray(GetCustomAttribute_CompilerGenerated(baselineImport, methoddef))); + EXPECT_THAT(GetMethodProps(currentImport, methoddef, &sig, &sigLen), testing::ElementsAreArray(GetMethodProps(baselineImport, methoddef))); + EXPECT_THAT(GetNativeCallConvFromSig(currentImport, sig, sigLen), testing::ElementsAreArray(GetNativeCallConvFromSig(baselineImport, sig, sigLen))); + EXPECT_THAT(GetNameFromToken(currentImport, methoddef), testing::ElementsAreArray(GetNameFromToken(baselineImport, methoddef))); + EXPECT_THAT(IsGlobal(currentImport, methoddef), testing::Eq(IsGlobal(baselineImport, methoddef))); + EXPECT_THAT(GetCustomAttribute_CompilerGenerated(currentImport, methoddef), testing::ElementsAreArray(GetCustomAttribute_CompilerGenerated(baselineImport, methoddef))); TokenList paramdefs; ASSERT_EQUAL_AND_SET(paramdefs, EnumParams(baselineImport, methoddef), EnumParams(currentImport, methoddef)); for (auto paramdef : paramdefs) { - ASSERT_THAT(GetParamProps(currentImport, paramdef), testing::ElementsAreArray(GetParamProps(baselineImport, paramdef))); - ASSERT_THAT(GetFieldMarshal(currentImport, paramdef), testing::ElementsAreArray(GetFieldMarshal(baselineImport, paramdef))); - ASSERT_THAT(GetCustomAttribute_Nullable(currentImport, paramdef), testing::ElementsAreArray(GetCustomAttribute_Nullable(baselineImport, paramdef))); - ASSERT_THAT(GetNameFromToken(currentImport, paramdef), testing::ElementsAreArray(GetNameFromToken(baselineImport, paramdef))); + EXPECT_THAT(GetParamProps(currentImport, paramdef), testing::ElementsAreArray(GetParamProps(baselineImport, paramdef))); + EXPECT_THAT(GetFieldMarshal(currentImport, paramdef), testing::ElementsAreArray(GetFieldMarshal(baselineImport, paramdef))); + EXPECT_THAT(GetCustomAttribute_Nullable(currentImport, paramdef), testing::ElementsAreArray(GetCustomAttribute_Nullable(baselineImport, paramdef))); + EXPECT_THAT(GetNameFromToken(currentImport, paramdef), testing::ElementsAreArray(GetNameFromToken(baselineImport, paramdef))); } - ASSERT_THAT(GetParamForMethodIndex(currentImport, methoddef), testing::ElementsAreArray(GetParamForMethodIndex(baselineImport, methoddef))); - ASSERT_THAT(EnumPermissionSetsAndGetProps(currentImport, methoddef), testing::ElementsAreArray(EnumPermissionSetsAndGetProps(baselineImport, methoddef))); - ASSERT_THAT(GetPinvokeMap(currentImport, methoddef), testing::ElementsAreArray(GetPinvokeMap(baselineImport, methoddef))); - ASSERT_THAT(GetRVA(currentImport, methoddef), testing::ElementsAreArray(GetRVA(baselineImport, methoddef))); + EXPECT_THAT(GetParamForMethodIndex(currentImport, methoddef), testing::ElementsAreArray(GetParamForMethodIndex(baselineImport, methoddef))); + EXPECT_THAT(EnumPermissionSetsAndGetProps(currentImport, methoddef), testing::ElementsAreArray(EnumPermissionSetsAndGetProps(baselineImport, methoddef))); + EXPECT_THAT(GetPinvokeMap(currentImport, methoddef), testing::ElementsAreArray(GetPinvokeMap(baselineImport, methoddef))); + EXPECT_THAT(GetRVA(currentImport, methoddef), testing::ElementsAreArray(GetRVA(baselineImport, methoddef))); TokenList methodspecs; ASSERT_EQUAL_AND_SET(methodspecs, EnumMethodSpecs(baselineImport, methoddef), EnumMethodSpecs(currentImport, methoddef)); for (auto methodspec : methodspecs) { - ASSERT_THAT(GetMethodSpecProps(currentImport, methodspec), testing::ElementsAreArray(GetMethodSpecProps(baselineImport, methodspec))); + EXPECT_THAT(GetMethodSpecProps(currentImport, methodspec), testing::ElementsAreArray(GetMethodSpecProps(baselineImport, methodspec))); } } @@ -1896,11 +1942,11 @@ TEST_P(MetadataImportTest, ImportAPIs) ASSERT_THAT(GetEventProps(currentImport, eventdef, &mds), testing::ElementsAreArray(GetEventProps(baselineImport, eventdef))); for (auto md : mds) { - ASSERT_THAT(GetMethodSemantics(currentImport, eventdef, md), testing::ElementsAreArray(GetMethodSemantics(baselineImport, eventdef, md))); + EXPECT_THAT(GetMethodSemantics(currentImport, eventdef, md), testing::ElementsAreArray(GetMethodSemantics(baselineImport, eventdef, md))); } - ASSERT_THAT(GetNameFromToken(currentImport, eventdef), testing::ElementsAreArray(GetNameFromToken(baselineImport, eventdef))); - ASSERT_THAT(IsGlobal(currentImport, eventdef), testing::Eq(IsGlobal(baselineImport, eventdef))); + EXPECT_THAT(GetNameFromToken(currentImport, eventdef), testing::ElementsAreArray(GetNameFromToken(baselineImport, eventdef))); + EXPECT_THAT(IsGlobal(currentImport, eventdef), testing::Eq(IsGlobal(baselineImport, eventdef))); } TokenList properties; @@ -1911,11 +1957,11 @@ TEST_P(MetadataImportTest, ImportAPIs) ASSERT_THAT(GetPropertyProps(currentImport, props, &mds), testing::ElementsAreArray(GetPropertyProps(baselineImport, props))); for (auto md : mds) { - ASSERT_THAT(GetMethodSemantics(currentImport, props, md), testing::ElementsAreArray(GetMethodSemantics(baselineImport, props, md))); + EXPECT_THAT(GetMethodSemantics(currentImport, props, md), testing::ElementsAreArray(GetMethodSemantics(baselineImport, props, md))); } - ASSERT_THAT(GetNameFromToken(currentImport, props), testing::ElementsAreArray(GetNameFromToken(baselineImport, props))); - ASSERT_THAT(IsGlobal(currentImport, props), testing::Eq(IsGlobal(baselineImport, props))); + EXPECT_THAT(GetNameFromToken(currentImport, props), testing::ElementsAreArray(GetNameFromToken(baselineImport, props))); + EXPECT_THAT(IsGlobal(currentImport, props), testing::Eq(IsGlobal(baselineImport, props))); } ASSERT_THAT(EnumFieldsWithName(baselineImport, typdef), EnumFieldsWithName(currentImport, typdef)); @@ -1923,28 +1969,35 @@ TEST_P(MetadataImportTest, ImportAPIs) ASSERT_EQUAL_AND_SET(fielddefs, EnumFields(baselineImport, typdef), EnumFields(currentImport, typdef)); for (auto fielddef : fielddefs) { - ASSERT_THAT(GetFieldProps(currentImport, fielddef), testing::ElementsAreArray(GetFieldProps(baselineImport, fielddef))); - ASSERT_THAT(GetNameFromToken(currentImport, fielddef), testing::ElementsAreArray(GetNameFromToken(baselineImport, fielddef))); - ASSERT_THAT(IsGlobal(currentImport, fielddef), testing::Eq(IsGlobal(baselineImport, fielddef))); - ASSERT_THAT(GetPinvokeMap(currentImport, fielddef), testing::ElementsAreArray(GetPinvokeMap(baselineImport, fielddef))); - ASSERT_THAT(GetRVA(currentImport, fielddef), testing::ElementsAreArray(GetRVA(baselineImport, fielddef))); - ASSERT_THAT(GetFieldMarshal(currentImport, fielddef), testing::ElementsAreArray(GetFieldMarshal(baselineImport, fielddef))); - ASSERT_THAT(GetCustomAttribute_Nullable(currentImport, fielddef), testing::ElementsAreArray(GetCustomAttribute_Nullable(baselineImport, fielddef))); + EXPECT_THAT(GetFieldProps(currentImport, fielddef), testing::ElementsAreArray(GetFieldProps(baselineImport, fielddef))); + EXPECT_THAT(GetNameFromToken(currentImport, fielddef), testing::ElementsAreArray(GetNameFromToken(baselineImport, fielddef))); + EXPECT_THAT(IsGlobal(currentImport, fielddef), testing::Eq(IsGlobal(baselineImport, fielddef))); + EXPECT_THAT(GetPinvokeMap(currentImport, fielddef), testing::ElementsAreArray(GetPinvokeMap(baselineImport, fielddef))); + EXPECT_THAT(GetRVA(currentImport, fielddef), testing::ElementsAreArray(GetRVA(baselineImport, fielddef))); + EXPECT_THAT(GetFieldMarshal(currentImport, fielddef), testing::ElementsAreArray(GetFieldMarshal(baselineImport, fielddef))); + EXPECT_THAT(GetCustomAttribute_Nullable(currentImport, fielddef), testing::ElementsAreArray(GetCustomAttribute_Nullable(baselineImport, fielddef))); } TokenList genparams; ASSERT_EQUAL_AND_SET(genparams, EnumGenericParams(baselineImport, typdef), EnumGenericParams(currentImport, typdef)); for (auto genparam : genparams) { - ASSERT_THAT(GetGenericParamProps(currentImport, genparam), testing::ElementsAreArray(GetGenericParamProps(baselineImport, genparam))); + EXPECT_THAT(GetGenericParamProps(currentImport, genparam), testing::ElementsAreArray(GetGenericParamProps(baselineImport, genparam))); TokenList genparamconsts; ASSERT_EQUAL_AND_SET(genparamconsts, EnumGenericParamConstraints(baselineImport, genparam), EnumGenericParamConstraints(currentImport, genparam)); for (auto genparamconst : genparamconsts) { - ASSERT_THAT(GetGenericParamConstraintProps(currentImport, genparamconst), testing::ElementsAreArray(GetGenericParamConstraintProps(baselineImport, genparamconst))); + EXPECT_THAT(GetGenericParamConstraintProps(currentImport, genparamconst), testing::ElementsAreArray(GetGenericParamConstraintProps(baselineImport, genparamconst))); } } } +} + +TEST_P(MetadataImportTest, Assembly) +{ + dncp::com_ptr baselineImport; + dncp::com_ptr currentImport; + ASSERT_NO_FATAL_FAILURE(GetImports(GetParam(), baselineImport, currentImport)); dncp::com_ptr baselineAssembly; ASSERT_THAT(S_OK, baselineImport->QueryInterface(IID_IMetaDataAssemblyImport, (void**)&baselineAssembly)); @@ -1955,22 +2008,58 @@ TEST_P(MetadataImportTest, ImportAPIs) ASSERT_EQUAL_AND_SET(assemblyTokens, GetAssemblyFromScope(baselineAssembly), GetAssemblyFromScope(currentAssembly)); for (auto assembly : assemblyTokens) { - ASSERT_THAT(GetAssemblyProps(currentAssembly, assembly), testing::ElementsAreArray(GetAssemblyProps(baselineAssembly, assembly))); + EXPECT_THAT(GetAssemblyProps(currentAssembly, assembly), testing::ElementsAreArray(GetAssemblyProps(baselineAssembly, assembly))); } +} + +TEST_P(MetadataImportTest, AssemblyRef) +{ + dncp::com_ptr baselineImport; + dncp::com_ptr currentImport; + ASSERT_NO_FATAL_FAILURE(GetImports(GetParam(), baselineImport, currentImport)); + + dncp::com_ptr baselineAssembly; + ASSERT_THAT(S_OK, baselineImport->QueryInterface(IID_IMetaDataAssemblyImport, (void**)&baselineAssembly)); + dncp::com_ptr currentAssembly; + ASSERT_THAT(S_OK, currentImport->QueryInterface(IID_IMetaDataAssemblyImport, (void**)¤tAssembly)); TokenList assemblyRefs; ASSERT_EQUAL_AND_SET(assemblyRefs, EnumAssemblyRefs(baselineAssembly), EnumAssemblyRefs(currentAssembly)); for (auto assemblyRef : assemblyRefs) { - ASSERT_THAT(GetAssemblyRefProps(currentAssembly, assemblyRef), testing::ElementsAreArray(GetAssemblyRefProps(baselineAssembly, assemblyRef))); + EXPECT_THAT(GetAssemblyRefProps(currentAssembly, assemblyRef), testing::ElementsAreArray(GetAssemblyRefProps(baselineAssembly, assemblyRef))); } +} + +TEST_P(MetadataImportTest, File) +{ + dncp::com_ptr baselineImport; + dncp::com_ptr currentImport; + ASSERT_NO_FATAL_FAILURE(GetImports(GetParam(), baselineImport, currentImport)); + + dncp::com_ptr baselineAssembly; + ASSERT_THAT(S_OK, baselineImport->QueryInterface(IID_IMetaDataAssemblyImport, (void**)&baselineAssembly)); + dncp::com_ptr currentAssembly; + ASSERT_THAT(S_OK, currentImport->QueryInterface(IID_IMetaDataAssemblyImport, (void**)¤tAssembly)); TokenList files; ASSERT_EQUAL_AND_SET(files, EnumFiles(baselineAssembly), EnumFiles(currentAssembly)); for (auto file : files) { - ASSERT_THAT(GetFileProps(currentAssembly, file), testing::ElementsAreArray(GetFileProps(baselineAssembly, file))); + EXPECT_THAT(GetFileProps(currentAssembly, file), testing::ElementsAreArray(GetFileProps(baselineAssembly, file))); } +} + +TEST_P(MetadataImportTest, ExportedType) +{ + dncp::com_ptr baselineImport; + dncp::com_ptr currentImport; + ASSERT_NO_FATAL_FAILURE(GetImports(GetParam(), baselineImport, currentImport)); + + dncp::com_ptr baselineAssembly; + ASSERT_THAT(S_OK, baselineImport->QueryInterface(IID_IMetaDataAssemblyImport, (void**)&baselineAssembly)); + dncp::com_ptr currentAssembly; + ASSERT_THAT(S_OK, currentImport->QueryInterface(IID_IMetaDataAssemblyImport, (void**)¤tAssembly)); TokenList exports; ASSERT_EQUAL_AND_SET(exports, EnumExportedTypes(baselineAssembly), EnumExportedTypes(currentAssembly)); @@ -1979,20 +2068,30 @@ TEST_P(MetadataImportTest, ImportAPIs) std::vector name; uint32_t implementation = mdTokenNil; ASSERT_THAT(GetExportedTypeProps(currentAssembly, exportedType, &name, &implementation), testing::ElementsAreArray(GetExportedTypeProps(baselineAssembly, exportedType))); - ASSERT_THAT( + EXPECT_THAT( FindExportedTypeByName(currentAssembly, name.data(), implementation), testing::ElementsAreArray(FindExportedTypeByName(baselineAssembly, name.data(), implementation))); } +} + +TEST_P(MetadataImportTest, ManifestResource) +{ + dncp::com_ptr baselineImport; + dncp::com_ptr currentImport; + ASSERT_NO_FATAL_FAILURE(GetImports(GetParam(), baselineImport, currentImport)); + + dncp::com_ptr baselineAssembly; + ASSERT_THAT(S_OK, baselineImport->QueryInterface(IID_IMetaDataAssemblyImport, (void**)&baselineAssembly)); + dncp::com_ptr currentAssembly; + ASSERT_THAT(S_OK, currentImport->QueryInterface(IID_IMetaDataAssemblyImport, (void**)¤tAssembly)); TokenList resources; ASSERT_EQUAL_AND_SET(resources, EnumManifestResources(baselineAssembly), EnumManifestResources(currentAssembly)); for (auto resource : resources) { - std::vector name; - ASSERT_THAT(GetManifestResourceProps(currentAssembly, resource, &name), testing::ElementsAreArray(GetManifestResourceProps(baselineAssembly, resource))); - ASSERT_THAT(FindManifestResourceByName(currentAssembly, name.data()), testing::ElementsAreArray(FindManifestResourceByName(baselineAssembly, name.data()))); + EXPECT_THAT(GetManifestResourceProps(currentAssembly, resource), testing::ElementsAreArray(GetManifestResourceProps(baselineAssembly, resource))); } -} +} INSTANTIATE_TEST_SUITE_P(MetaDataImportTestCore, MetadataImportTest, testing::ValuesIn(MetadataInDirectory(GetBaselineDirectory())), PrintFileBlob); @@ -2001,66 +2100,54 @@ INSTANTIATE_TEST_SUITE_P(MetaDataImportTestFx2_0, MetadataImportTest, testing::V class MetaDataLongRunningTest : public RegressionTest { +protected: + TokenList VerifyFindMemberRef(IMetaDataImport2* import, mdToken memberRef) + { + std::vector values; + + mdToken ptk; + static_char_buffer name{}; + ULONG pchMember; + PCCOR_SIGNATURE ppvSigBlob; + ULONG pcbSigBlob; + HRESULT hr = import->GetMemberRefProps(memberRef, + & ptk, + name.data(), + (ULONG)name.size(), + & pchMember, + & ppvSigBlob, + & pcbSigBlob); + values.push_back(hr); + if (hr == S_OK) + { + // We were able to get the name, now try looking up a memberRef by name and by sig + mdMemberRef lookup = mdTokenNil; + hr = import->FindMemberRef(ptk, name.data(), ppvSigBlob, pcbSigBlob, & lookup); + values.push_back(hr); + values.push_back(lookup); + lookup = mdTokenNil; + hr = import->FindMemberRef(ptk, name.data(), nullptr, 0, & lookup); + values.push_back(hr); + values.push_back(lookup); + lookup = mdTokenNil; + hr = import->FindMemberRef(ptk, nullptr, ppvSigBlob, pcbSigBlob, & lookup); + values.push_back(hr); + values.push_back(lookup); + } + return values; + } }; -TEST_P(MetaDataLongRunningTest, ImportAPIs) +TEST_P(MetaDataLongRunningTest, TypeDefs) { - auto param = GetParam(); - void const* data = param.blob.data(); - uint32_t dataLen = (uint32_t)param.blob.size(); - - // Load metadata dncp::com_ptr baselineImport; - ASSERT_HRESULT_SUCCEEDED(CreateImport(TestBaseline::Metadata, data, dataLen, &baselineImport)); - - dncp::com_ptr dispenser; - ASSERT_HRESULT_SUCCEEDED(GetDispenser(IID_IMetaDataDispenser, (void**)&dispenser)); dncp::com_ptr currentImport; - ASSERT_HRESULT_SUCCEEDED(CreateImport(dispenser, data, dataLen, ¤tImport)); - - static auto VerifyFindMemberRef = [](IMetaDataImport2 * import, mdToken memberRef) -> std::vector - { - std::vector values; - - mdToken ptk; - static_char_buffer name{}; - ULONG pchMember; - PCCOR_SIGNATURE ppvSigBlob; - ULONG pcbSigBlob; - HRESULT hr = import->GetMemberRefProps(memberRef, - & ptk, - name.data(), - (ULONG)name.size(), - & pchMember, - & ppvSigBlob, - & pcbSigBlob); - values.push_back(hr); - if (hr == S_OK) - { - // We were able to get the name, now try looking up a memberRef by name and by sig - mdMemberRef lookup = mdTokenNil; - hr = import->FindMemberRef(ptk, name.data(), ppvSigBlob, pcbSigBlob, & lookup); - values.push_back(hr); - values.push_back(lookup); - lookup = mdTokenNil; - hr = import->FindMemberRef(ptk, name.data(), nullptr, 0, & lookup); - values.push_back(hr); - values.push_back(lookup); - lookup = mdTokenNil; - hr = import->FindMemberRef(ptk, nullptr, ppvSigBlob, pcbSigBlob, & lookup); - values.push_back(hr); - values.push_back(lookup); - } - return values; - }; - - size_t stride; - size_t count; + ASSERT_NO_FATAL_FAILURE(GetImports(GetParam(), baselineImport, currentImport)); TokenList typedefs; ASSERT_EQUAL_AND_SET(typedefs, EnumTypeDefs(baselineImport), EnumTypeDefs(currentImport)); - count = 0; - stride = std::max(typedefs.size() / 128, (size_t)16); + size_t count = 0; + size_t stride = std::max(typedefs.size() / 128, (size_t)16); for (auto typdef : typedefs) { if (count++ % stride != 0) @@ -2077,11 +2164,18 @@ TEST_P(MetaDataLongRunningTest, ImportAPIs) EXPECT_THAT(EnumCustomAttributes(currentImport, typdef), testing::ElementsAreArray(EnumCustomAttributes(baselineImport, typdef))); } +} + +TEST_P(MetaDataLongRunningTest, TypeSpecs) +{ + dncp::com_ptr baselineImport; + dncp::com_ptr currentImport; + ASSERT_NO_FATAL_FAILURE(GetImports(GetParam(), baselineImport, currentImport)); TokenList typespecs; ASSERT_EQUAL_AND_SET(typespecs, EnumTypeSpecs(baselineImport), EnumTypeSpecs(currentImport)); - count = 0; - stride = std::max(typespecs.size() / 128, (size_t)16); + size_t count = 0; + size_t stride = std::max(typespecs.size() / 128, (size_t)16); for (auto typespec : typespecs) { if (count++ % stride != 0) From f2c1aa978a35ecd8ce833ffcc09806b54e48c1e5 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 21 Dec 2023 15:15:11 -0800 Subject: [PATCH 15/59] Fix test that wasn't using HashByteArray when it should have. --- test/regtest/metadata.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/regtest/metadata.cpp b/test/regtest/metadata.cpp index 2c17ba81..16744f10 100644 --- a/test/regtest/metadata.cpp +++ b/test/regtest/metadata.cpp @@ -1529,7 +1529,7 @@ namespace { values.push_back(HashCharArray(name, nameLength)); values.push_back((size_t)nameLength); - values.push_back(hashLength != 0 ? (size_t)hash : 0); + values.push_back(HashByteArray(hash, hashLength)); values.push_back(hashLength); values.push_back(flags); } From c76044d6016725bc063cfab772bd35a5224d27a0 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 21 Dec 2023 16:35:40 -0800 Subject: [PATCH 16/59] Remove indirection tables test into regtest and remove Regression.UnitTests --- test/DNMD.Tests.sln | 14 +- test/Regression.DeltaBuilder/Program.cs | 140 -- .../Regression.DeltaBuilder.csproj | 16 - test/Regression.Locator/DeltaImageBuilder.cs | 121 + test/Regression.Locator/LocatorHelpers.cs | 32 + .../Regression.Locator.csproj | 3 + test/Regression.UnitTests/ImportTests.cs | 283 --- .../Properties/launchSettings.json | 8 - .../Regression.UnitTests.csproj | 40 - test/Regression.UnitTests/SymReaderTests.cs | 25 - test/regnative/CMakeLists.txt | 3 - test/regnative/perf.cpp | 14 +- test/regnative/regnative.cpp | 26 - test/regnative/regnative.hpp | 136 -- test/regnative/unit_cor.cpp | 1975 ----------------- test/regnative/unit_corsym.cpp | 10 - test/regtest/CMakeLists.txt | 2 +- test/regtest/baseline.h | 1 + test/regtest/discovery.cpp | 54 + test/regtest/fixtures.h | 4 +- test/regtest/main.cpp | 9 + test/regtest/metadata.cpp | 4 +- 22 files changed, 240 insertions(+), 2680 deletions(-) delete mode 100644 test/Regression.DeltaBuilder/Program.cs delete mode 100644 test/Regression.DeltaBuilder/Regression.DeltaBuilder.csproj create mode 100644 test/Regression.Locator/DeltaImageBuilder.cs delete mode 100644 test/Regression.UnitTests/ImportTests.cs delete mode 100644 test/Regression.UnitTests/Properties/launchSettings.json delete mode 100644 test/Regression.UnitTests/Regression.UnitTests.csproj delete mode 100644 test/Regression.UnitTests/SymReaderTests.cs delete mode 100644 test/regnative/regnative.cpp delete mode 100644 test/regnative/regnative.hpp delete mode 100644 test/regnative/unit_cor.cpp delete mode 100644 test/regnative/unit_corsym.cpp diff --git a/test/DNMD.Tests.sln b/test/DNMD.Tests.sln index 3e9f05d3..77bf035d 100644 --- a/test/DNMD.Tests.sln +++ b/test/DNMD.Tests.sln @@ -3,15 +3,11 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.5.33026.144 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Regression.UnitTests", "Regression.UnitTests\Regression.UnitTests.csproj", "{D5105B2A-6016-4B1C-A46B-5A841AE5AD26}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Regression.Performance", "Regression.Performance\Regression.Performance.csproj", "{3ED137D0-B5A2-4309-BDA5-BD9DE3FFC752}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Regression.TargetAssembly", "Regression.TargetAssembly\Regression.TargetAssembly.ilproj", "{D3BEEF3D-C137-49AC-96DD-E5B93E2F4C21}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Regression.DeltaBuilder", "Regression.DeltaBuilder\Regression.DeltaBuilder.csproj", "{6E62512A-7FB4-41DD-9E93-014D0280C77D}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Regression.Locator", "Regression.Locator\Regression.Locator.csproj", "{CD7DEF6B-7254-4541-951D-026CDB8928FD}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Regression.Locator", "Regression.Locator\Regression.Locator.csproj", "{CD7DEF6B-7254-4541-951D-026CDB8928FD}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -19,10 +15,6 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {D5105B2A-6016-4B1C-A46B-5A841AE5AD26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D5105B2A-6016-4B1C-A46B-5A841AE5AD26}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D5105B2A-6016-4B1C-A46B-5A841AE5AD26}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D5105B2A-6016-4B1C-A46B-5A841AE5AD26}.Release|Any CPU.Build.0 = Release|Any CPU {3ED137D0-B5A2-4309-BDA5-BD9DE3FFC752}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3ED137D0-B5A2-4309-BDA5-BD9DE3FFC752}.Debug|Any CPU.Build.0 = Debug|Any CPU {3ED137D0-B5A2-4309-BDA5-BD9DE3FFC752}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -31,10 +23,6 @@ Global {D3BEEF3D-C137-49AC-96DD-E5B93E2F4C21}.Debug|Any CPU.Build.0 = Debug|Any CPU {D3BEEF3D-C137-49AC-96DD-E5B93E2F4C21}.Release|Any CPU.ActiveCfg = Release|Any CPU {D3BEEF3D-C137-49AC-96DD-E5B93E2F4C21}.Release|Any CPU.Build.0 = Release|Any CPU - {6E62512A-7FB4-41DD-9E93-014D0280C77D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6E62512A-7FB4-41DD-9E93-014D0280C77D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6E62512A-7FB4-41DD-9E93-014D0280C77D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6E62512A-7FB4-41DD-9E93-014D0280C77D}.Release|Any CPU.Build.0 = Release|Any CPU {CD7DEF6B-7254-4541-951D-026CDB8928FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CD7DEF6B-7254-4541-951D-026CDB8928FD}.Debug|Any CPU.Build.0 = Debug|Any CPU {CD7DEF6B-7254-4541-951D-026CDB8928FD}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/test/Regression.DeltaBuilder/Program.cs b/test/Regression.DeltaBuilder/Program.cs deleted file mode 100644 index c770312c..00000000 --- a/test/Regression.DeltaBuilder/Program.cs +++ /dev/null @@ -1,140 +0,0 @@ -using System.Collections.Immutable; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Emit; - -/// -/// Generates images with metadata deltas for use in the regression tests. -/// -public static class Program -{ - /// - /// Generate images with metadata deltas into the output directory. - /// - /// The directory to output the base and delta images into. - public static void Main(DirectoryInfo output) - { - // Create the directory if it does not exist. - output.Create(); - - ImmutableArray imageScenarios = [ DeltaAssembly1() ]; - - foreach (DeltaAssembly scenario in imageScenarios) - { - File.WriteAllBytes(Path.Combine(output.FullName, $"{scenario.Name}.dll"), scenario.BaseImage); - Console.WriteLine($"Wrote {scenario.Name}.dll base image"); - for (int i = 0; i < scenario.MetadataDeltas.Length; i++) - { - File.WriteAllBytes(Path.Combine(output.FullName, $"{scenario.Name}.{i + 1}.mddelta"), scenario.MetadataDeltas[i]); - Console.WriteLine($"Wrote {scenario.Name}.dll metadata delta {i + 1}"); - } - } - } - - static DeltaAssembly DeltaAssembly1() - { - Compilation baselineCompilation = CSharpCompilation.Create("DeltaAssembly1") - .WithReferences(Basic.Reference.Assemblies.NetStandard20.ReferenceInfos.netstandard.Reference) - .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); - SyntaxTree sourceBase = CSharpSyntaxTree.ParseText(""" - using System; - public class Class1 - { - private int field; - public void Method(int x) - { - } - - public int Property { get; set; } - - public event EventHandler? Event; - } - """); - baselineCompilation = baselineCompilation.AddSyntaxTrees( - sourceBase, - CSharpSyntaxTree.ParseText(""" - using System; - public class Class2 - { - private int field; - public void Method(int x) - { - } - - public int Property { get; set; } - - public event EventHandler? Event; - } - """)); - - Compilation diffCompilation = baselineCompilation.ReplaceSyntaxTree( - sourceBase, - CSharpSyntaxTree.ParseText(""" - using System; - public class Class1 - { - private class Attr : Attribute { } - - private short field2; - private int field; - - [return:Attr] - public void Method(int x) - { - } - - public int Property { get; set; } - - public short Property2 { get; set; } - - public event EventHandler? Event; - - public event EventHandler? Event2; - } - """)); - - var diagnostics = baselineCompilation.GetDiagnostics(); - MemoryStream baselineImage = new(); - baselineCompilation.Emit(baselineImage, options: new EmitOptions(debugInformationFormat: DebugInformationFormat.PortablePdb)); - baselineImage.Seek(0, SeekOrigin.Begin); - - ModuleMetadata metadata = ModuleMetadata.CreateFromStream(baselineImage); - EmitBaseline baseline = EmitBaseline.CreateInitialBaseline(metadata, _ => default, _ => default, true); - - MemoryStream mddiffStream = new(); - - diffCompilation.EmitDifference( - baseline, - new[] - { - CreateSemanticEdit(SemanticEditKind.Update, baselineCompilation, diffCompilation, c => c.GetTypeByMetadataName("Class1")), - CreateSemanticEdit(SemanticEditKind.Insert, baselineCompilation, diffCompilation, c => c.GetTypeByMetadataName("Class1")!.GetMembers("field2").FirstOrDefault()), - CreateSemanticEdit(SemanticEditKind.Insert, baselineCompilation, diffCompilation, c => c.GetTypeByMetadataName("Class1")!.GetMembers("Property2").FirstOrDefault()), - CreateSemanticEdit(SemanticEditKind.Insert, baselineCompilation, diffCompilation, c => c.GetTypeByMetadataName("Class1")!.GetMembers("Event2").FirstOrDefault()), - CreateSemanticEdit(SemanticEditKind.Update, baselineCompilation, diffCompilation, c => c.GetTypeByMetadataName("Class1")!.GetMembers("Method").FirstOrDefault()), - CreateSemanticEdit(SemanticEditKind.Insert, baselineCompilation, diffCompilation, c => c.GetTypeByMetadataName("Class1")!.GetTypeMembers("Attr").FirstOrDefault()), - }, - s => - { - return false; - }, - mddiffStream, - new MemoryStream(), // il stream - new MemoryStream() // pdb diff stream - ); - - baselineImage.Seek(0, SeekOrigin.Begin); - return new DeltaAssembly( - nameof(DeltaAssembly1), - baselineImage.ToArray(), - [ mddiffStream.ToArray() ] - ); - } - - static SemanticEdit CreateSemanticEdit(SemanticEditKind editKind, Compilation baseline, Compilation diff, Func findSymbol) - { - return new SemanticEdit(editKind, findSymbol(baseline), findSymbol(diff)); - } - - record struct DeltaAssembly(string Name, byte[] BaseImage, ImmutableArray MetadataDeltas); -} \ No newline at end of file diff --git a/test/Regression.DeltaBuilder/Regression.DeltaBuilder.csproj b/test/Regression.DeltaBuilder/Regression.DeltaBuilder.csproj deleted file mode 100644 index f1bd79b1..00000000 --- a/test/Regression.DeltaBuilder/Regression.DeltaBuilder.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - Exe - net8.0 - enable - enable - - - - - - - - - diff --git a/test/Regression.Locator/DeltaImageBuilder.cs b/test/Regression.Locator/DeltaImageBuilder.cs new file mode 100644 index 00000000..25d37b25 --- /dev/null +++ b/test/Regression.Locator/DeltaImageBuilder.cs @@ -0,0 +1,121 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Emit; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Regression.Locator +{ + internal static class DeltaImageBuilder + { + public static DeltaAssembly CreateAssembly() + { + Compilation baselineCompilation = CSharpCompilation.Create("DeltaAssembly1") + .WithReferences(Basic.Reference.Assemblies.NetStandard20.ReferenceInfos.netstandard.Reference) + .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); + SyntaxTree sourceBase = CSharpSyntaxTree.ParseText(""" + using System; + public class Class1 + { + private int field; + public void Method(int x) + { + } + + public int Property { get; set; } + + public event EventHandler? Event; + } + """); + baselineCompilation = baselineCompilation.AddSyntaxTrees( + sourceBase, + CSharpSyntaxTree.ParseText(""" + using System; + public class Class2 + { + private int field; + public void Method(int x) + { + } + + public int Property { get; set; } + + public event EventHandler? Event; + } + """)); + + Compilation diffCompilation = baselineCompilation.ReplaceSyntaxTree( + sourceBase, + CSharpSyntaxTree.ParseText(""" + using System; + public class Class1 + { + private class Attr : Attribute { } + + private short field2; + private int field; + + [return:Attr] + public void Method(int x) + { + } + + public int Property { get; set; } + + public short Property2 { get; set; } + + public event EventHandler? Event; + + public event EventHandler? Event2; + } + """)); + + var diagnostics = baselineCompilation.GetDiagnostics(); + MemoryStream baselineImage = new(); + baselineCompilation.Emit(baselineImage, options: new EmitOptions(debugInformationFormat: DebugInformationFormat.PortablePdb)); + baselineImage.Seek(0, SeekOrigin.Begin); + + ModuleMetadata metadata = ModuleMetadata.CreateFromStream(baselineImage); + EmitBaseline baseline = EmitBaseline.CreateInitialBaseline(metadata, _ => default, _ => default, true); + + MemoryStream mddiffStream = new(); + + diffCompilation.EmitDifference( + baseline, + new[] + { + CreateSemanticEdit(SemanticEditKind.Update, baselineCompilation, diffCompilation, c => c.GetTypeByMetadataName("Class1")), + CreateSemanticEdit(SemanticEditKind.Insert, baselineCompilation, diffCompilation, c => c.GetTypeByMetadataName("Class1")!.GetMembers("field2").FirstOrDefault()), + CreateSemanticEdit(SemanticEditKind.Insert, baselineCompilation, diffCompilation, c => c.GetTypeByMetadataName("Class1")!.GetMembers("Property2").FirstOrDefault()), + CreateSemanticEdit(SemanticEditKind.Insert, baselineCompilation, diffCompilation, c => c.GetTypeByMetadataName("Class1")!.GetMembers("Event2").FirstOrDefault()), + CreateSemanticEdit(SemanticEditKind.Update, baselineCompilation, diffCompilation, c => c.GetTypeByMetadataName("Class1")!.GetMembers("Method").FirstOrDefault()), + CreateSemanticEdit(SemanticEditKind.Insert, baselineCompilation, diffCompilation, c => c.GetTypeByMetadataName("Class1")!.GetTypeMembers("Attr").FirstOrDefault()), + }, + s => + { + return false; + }, + mddiffStream, + new MemoryStream(), // il stream + new MemoryStream() // pdb diff stream + ); + + baselineImage.Seek(0, SeekOrigin.Begin); + return new DeltaAssembly( + baselineImage.ToArray(), + [mddiffStream.ToArray()] + ); + } + + static SemanticEdit CreateSemanticEdit(SemanticEditKind editKind, Compilation baseline, Compilation diff, Func findSymbol) + { + return new SemanticEdit(editKind, findSymbol(baseline), findSymbol(diff)); + } + + public record struct DeltaAssembly(byte[] BaseImage, ImmutableArray MetadataDeltas); + } +} diff --git a/test/Regression.Locator/LocatorHelpers.cs b/test/Regression.Locator/LocatorHelpers.cs index 82c30091..0cc5b339 100644 --- a/test/Regression.Locator/LocatorHelpers.cs +++ b/test/Regression.Locator/LocatorHelpers.cs @@ -32,4 +32,36 @@ public static unsafe class LocatorHelpers string path = Path.Combine((string)key.GetValue("InstallRoot")!, new string(version)); return (byte*)Marshal.StringToCoTaskMemUTF8(path); } + + [UnmanagedCallersOnly(EntryPoint = "GetImageAndDeltas")] + public static void GetImageAndDeltas(byte** image, uint* imageLen, uint* deltaCount, byte*** deltas, uint** deltaLengths) + { + var asm = DeltaImageBuilder.CreateAssembly(); + *imageLen = (uint)asm.BaseImage.Length; + *image = (byte*)NativeMemory.Alloc(*imageLen); + asm.BaseImage.CopyTo(new Span(*image, (int)*imageLen)); + + *deltaCount = (uint)asm.MetadataDeltas.Length; + *deltas = (byte**)NativeMemory.Alloc(*deltaCount * (uint)sizeof(byte*)); + *deltaLengths = (uint*)NativeMemory.Alloc(*deltaCount * sizeof(uint)); + + for (int i = 0; i < asm.MetadataDeltas.Length; i++) + { + (*deltas)[i] = (byte*)NativeMemory.Alloc((uint)asm.MetadataDeltas[i].Length); + asm.MetadataDeltas[i].CopyTo(new Span((*deltas)[i], asm.MetadataDeltas[i].Length)); + (*deltaLengths)[i] = (uint)asm.MetadataDeltas[i].Length; + } + } + + [UnmanagedCallersOnly(EntryPoint = "FreeImageAndDeltas")] + public static void FreeImageAndDeltas(byte* image, uint deltaCount, byte** deltas, uint* deltaLengths) + { + NativeMemory.Free(image); + for (int i = 0; i < deltaCount; i++) + { + NativeMemory.Free(deltas[i]); + } + NativeMemory.Free(deltas); + NativeMemory.Free(deltaLengths); + } } diff --git a/test/Regression.Locator/Regression.Locator.csproj b/test/Regression.Locator/Regression.Locator.csproj index f86384c1..4d6dc209 100644 --- a/test/Regression.Locator/Regression.Locator.csproj +++ b/test/Regression.Locator/Regression.Locator.csproj @@ -6,10 +6,13 @@ enable true true + true + + diff --git a/test/Regression.UnitTests/ImportTests.cs b/test/Regression.UnitTests/ImportTests.cs deleted file mode 100644 index 0f06cdcd..00000000 --- a/test/Regression.UnitTests/ImportTests.cs +++ /dev/null @@ -1,283 +0,0 @@ -using System.Buffers; -using System.Diagnostics; -using System.Text; -using System.Reflection.Metadata; -using System.Runtime.CompilerServices; -using System.Reflection.PortableExecutable; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; - -using Common; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Emit; -using Xunit; -using Xunit.Abstractions; - -namespace Regression.UnitTests -{ - public unsafe class ImportTests - { - private delegate* unmanaged _longRunningAPIs; - private delegate* unmanaged _importAPIsIndirectionTables; - - public ImportTests(ITestOutputHelper outputHelper) - { - Log = outputHelper; - - nint mod = NativeLibrary.Load(Path.Combine(AppContext.BaseDirectory, Native.Path)); - var initialize = (delegate* unmanaged)NativeLibrary.GetExport(mod, "UnitInitialize"); - int hr = initialize((void*)Dispensers.Baseline, (void*)Dispensers.DeltaImageBuilder); - if (hr < 0) - { - throw new Exception($"Initialization failed: 0x{hr:x}"); - } - - _longRunningAPIs = (delegate* unmanaged)NativeLibrary.GetExport(mod, "UnitLongRunningAPIs"); - _importAPIsIndirectionTables = (delegate* unmanaged)NativeLibrary.GetExport(mod, "UnitImportAPIsIndirectionTables"); - } - - private ITestOutputHelper Log { get; } - - public static IEnumerable CoreFrameworkLibraries() - { - var spcl = typeof(object).Assembly.Location; - var frameworkDir = Path.GetDirectoryName(spcl)!; - foreach (var managedMaybe in Directory.EnumerateFiles(frameworkDir, "*.dll")) - { - PEReader pe = new(File.OpenRead(managedMaybe)); - if (!pe.HasMetadata) - { - pe.Dispose(); - continue; - } - - yield return new object[] { Path.GetFileName(managedMaybe), pe }; - } - } - - [SupportedOSPlatform("windows")] - public static IEnumerable Net20FrameworkLibraries() - { - foreach (var managedMaybe in Directory.EnumerateFiles(Dispensers.NetFx20Dir, "*.dll")) - { - PEReader pe = new(File.OpenRead(managedMaybe)); - if (!pe.HasMetadata) - { - pe.Dispose(); - continue; - } - - yield return new object[] { Path.GetFileName(managedMaybe), pe }; - } - } - - [SupportedOSPlatform("windows")] - public static IEnumerable Net40FrameworkLibraries() - { - foreach (var managedMaybe in Directory.EnumerateFiles(Dispensers.NetFx40Dir, "*.dll")) - { - PEReader pe = new(File.OpenRead(managedMaybe)); - if (!pe.HasMetadata) - { - pe.Dispose(); - continue; - } - - yield return new object[] { Path.GetFileName(managedMaybe), pe }; - } - } - - public static IEnumerable AllCoreLibs() - { - List corelibs = new() { typeof(object).Assembly.Location }; - - if (OperatingSystem.IsWindows()) - { - corelibs.Add(Path.Combine(Dispensers.NetFx20Dir, "mscorlib.dll")); - corelibs.Add(Path.Combine(Dispensers.NetFx40Dir, "mscorlib.dll")); - } - - foreach (var corelibMaybe in corelibs) - { - if (!File.Exists(corelibMaybe)) - { - continue; - } - - PEReader pe = new(File.OpenRead(corelibMaybe)); - yield return new object[] { Path.GetFileName(corelibMaybe), pe }; - } - } - - public static IEnumerable AssembliesWithDelta() - { - yield return DeltaAssembly1(); - static unsafe object[] DeltaAssembly1() - { - Compilation baselineCompilation = CSharpCompilation.Create("DeltaAssembly1") - .WithReferences(Basic.Reference.Assemblies.NetStandard20.ReferenceInfos.netstandard.Reference) - .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); - SyntaxTree sourceBase = CSharpSyntaxTree.ParseText(""" - using System; - public class Class1 - { - private int field; - public void Method(int x) - { - } - - public int Property { get; set; } - - public event EventHandler? Event; - } - """); - baselineCompilation = baselineCompilation.AddSyntaxTrees( - sourceBase, - CSharpSyntaxTree.ParseText(""" - using System; - public class Class2 - { - private int field; - public void Method(int x) - { - } - - public int Property { get; set; } - - public event EventHandler? Event; - } - """)); - - Compilation diffCompilation = baselineCompilation.ReplaceSyntaxTree( - sourceBase, - CSharpSyntaxTree.ParseText(""" - using System; - public class Class1 - { - private class Attr : Attribute { } - - private short field2; - private int field; - - [return:Attr] - public void Method(int x) - { - } - - public int Property { get; set; } - - public short Property2 { get; set; } - - public event EventHandler? Event; - - public event EventHandler? Event2; - } - """)); - - var diagnostics = baselineCompilation.GetDiagnostics(); - MemoryStream baselineImage = new(); - baselineCompilation.Emit(baselineImage, options: new EmitOptions(debugInformationFormat: DebugInformationFormat.PortablePdb)); - baselineImage.Seek(0, SeekOrigin.Begin); - - ModuleMetadata metadata = ModuleMetadata.CreateFromStream(baselineImage); - EmitBaseline baseline = EmitBaseline.CreateInitialBaseline(metadata, _ => default, _ => default, true); - - MemoryStream mddiffStream = new(); - - diffCompilation.EmitDifference( - baseline, - new[] - { - CreateSemanticEdit(SemanticEditKind.Update, baselineCompilation, diffCompilation, c => c.GetTypeByMetadataName("Class1")), - CreateSemanticEdit(SemanticEditKind.Insert, baselineCompilation, diffCompilation, c => c.GetTypeByMetadataName("Class1")!.GetMembers("field2").FirstOrDefault()), - CreateSemanticEdit(SemanticEditKind.Insert, baselineCompilation, diffCompilation, c => c.GetTypeByMetadataName("Class1")!.GetMembers("Property2").FirstOrDefault()), - CreateSemanticEdit(SemanticEditKind.Insert, baselineCompilation, diffCompilation, c => c.GetTypeByMetadataName("Class1")!.GetMembers("Event2").FirstOrDefault()), - CreateSemanticEdit(SemanticEditKind.Update, baselineCompilation, diffCompilation, c => c.GetTypeByMetadataName("Class1")!.GetMembers("Method").FirstOrDefault()), - CreateSemanticEdit(SemanticEditKind.Insert, baselineCompilation, diffCompilation, c => c.GetTypeByMetadataName("Class1")!.GetTypeMembers("Attr").FirstOrDefault()), - }, - s => - { - return false; - }, - mddiffStream, - new MemoryStream(), // il stream - new MemoryStream() // pdb diff stream - ); - - baselineImage.Seek(0, SeekOrigin.Begin); - PEReader baselineReader = new PEReader(baselineImage); - return new object[] - { - nameof(DeltaAssembly1), - baselineReader, - new Memory[] - { - mddiffStream.ToArray() - } - }; - } - - static SemanticEdit CreateSemanticEdit(SemanticEditKind editKind, Compilation baseline, Compilation diff, Func findSymbol) - { - return new SemanticEdit(editKind, findSymbol(baseline), findSymbol(diff)); - } - } - - [Theory] - [MemberData(nameof(AssembliesWithDelta))] - public unsafe void ImportAPIs_AssembliesWithAppliedDeltas(string filename, PEReader deltaBaseline, IList> diffs) - { - Debug.WriteLine($"{nameof(ImportAPIs_AssembliesWithAppliedDeltas)} - {filename}"); - using var _ = deltaBaseline; - PEMemoryBlock block = deltaBaseline.GetMetadata(); - - void*[] deltaImagePointers = new void*[diffs.Count]; - int[] deltaImageLengths = new int[diffs.Count]; - MemoryHandle[] handles = new MemoryHandle[diffs.Count]; - - for (int i = 0; i < diffs.Count; i++) - { - handles[i] = diffs[i].Pin(); - deltaImagePointers[i] = handles[i].Pointer; - deltaImageLengths[i] = diffs[i].Length; - } - - try - { - fixed (void** deltaImagePointersPtr = deltaImagePointers) - fixed (int* deltaImageLengthsPtr = deltaImageLengths) - { - _importAPIsIndirectionTables( - block.Pointer, - block.Length, - deltaImagePointersPtr, - deltaImageLengthsPtr, - diffs.Count).Check(); - } - } - finally - { - for (int i = 0; i < diffs.Count; i++) - { - handles[i].Dispose(); - } - } - } - - /// - /// These APIs are very expensive to run on all managed libraries. This library only runs - /// them on the system corelibs and only on a reduced selection of the tokens. - /// - [Theory] - [MemberData(nameof(AllCoreLibs))] - public void LongRunningAPIs(string filename, PEReader managedLibrary) - { - Debug.WriteLine($"{nameof(LongRunningAPIs)} - {filename}"); - using var _lib = managedLibrary; - PEMemoryBlock block = managedLibrary.GetMetadata(); - - _longRunningAPIs(block.Pointer, block.Length).Check(); - } - } -} \ No newline at end of file diff --git a/test/Regression.UnitTests/Properties/launchSettings.json b/test/Regression.UnitTests/Properties/launchSettings.json deleted file mode 100644 index 7fa32111..00000000 --- a/test/Regression.UnitTests/Properties/launchSettings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "profiles": { - "Regression.UnitTests": { - "commandName": "Project", - "nativeDebugging": true - } - } -} \ No newline at end of file diff --git a/test/Regression.UnitTests/Regression.UnitTests.csproj b/test/Regression.UnitTests/Regression.UnitTests.csproj deleted file mode 100644 index c3ee93cb..00000000 --- a/test/Regression.UnitTests/Regression.UnitTests.csproj +++ /dev/null @@ -1,40 +0,0 @@ - - - - enable - enable - true - false - - - - - - - - - - - - - - - - - - - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - diff --git a/test/Regression.UnitTests/SymReaderTests.cs b/test/Regression.UnitTests/SymReaderTests.cs deleted file mode 100644 index a14d8fcc..00000000 --- a/test/Regression.UnitTests/SymReaderTests.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Runtime.InteropServices; - -using Common; -using Xunit; -using Xunit.Abstractions; - -namespace Regression.UnitTests -{ - public unsafe class SymReaderTests - { - private delegate* unmanaged _symReaderAPIs; - - public SymReaderTests(ITestOutputHelper outputHelper) - { - Log = outputHelper; - nint mod = NativeLibrary.Load(Path.Combine(AppContext.BaseDirectory, Native.Path)); - _symReaderAPIs = (delegate* unmanaged)NativeLibrary.GetExport(mod, "UnitSymReaderAPIs"); - } - - private ITestOutputHelper Log { get; } - - [Fact] - public void SymReaderAPIs() => _symReaderAPIs(null, 0).Check(); - } -} \ No newline at end of file diff --git a/test/regnative/CMakeLists.txt b/test/regnative/CMakeLists.txt index 7a95ea43..4a96fc7e 100644 --- a/test/regnative/CMakeLists.txt +++ b/test/regnative/CMakeLists.txt @@ -1,8 +1,5 @@ set(SOURCES ./perf.cpp - ./regnative.cpp - ./unit_cor.cpp - ./unit_corsym.cpp ) add_library(regnative diff --git a/test/regnative/perf.cpp b/test/regnative/perf.cpp index 86c49ec3..b28af438 100644 --- a/test/regnative/perf.cpp +++ b/test/regnative/perf.cpp @@ -1,4 +1,16 @@ -#include "regnative.hpp" +#include +#include +#include +#include + +#include +#include + +#ifdef _MSC_VER +#define EXPORT extern "C" __declspec(dllexport) +#else +#define EXPORT extern "C" __attribute__((__visibility__("default"))) +#endif // !_MSC_VER namespace { diff --git a/test/regnative/regnative.cpp b/test/regnative/regnative.cpp deleted file mode 100644 index c6f5ad6b..00000000 --- a/test/regnative/regnative.cpp +++ /dev/null @@ -1,26 +0,0 @@ - -#include "regnative.hpp" - -using Assert::Violation; - -Violation::Violation(char const* source, size_t line, char const* funcName, std::string const& msg) -{ - std::stringstream ss; - ss << source << "(" << line << "):'" << msg << "' in " << funcName << "."; - _message = ss.str(); -} - -void Assert::_True(bool result, char const* source, size_t line, char const* funcName) -{ - if (!result) - throw Violation{ source, line, funcName, "false" }; -} - -TestResult ConvertViolation(Assert::Violation const& v) -{ - auto msg = v.message(); - char* block = (char*)malloc(msg.length() + 1); - msg.copy(block, msg.length()); - block[msg.length()] = '\0'; - return { TestState::Fail, block, &free }; -} diff --git a/test/regnative/regnative.hpp b/test/regnative/regnative.hpp deleted file mode 100644 index 32eb22e4..00000000 --- a/test/regnative/regnative.hpp +++ /dev/null @@ -1,136 +0,0 @@ -#include -#include -#include -#include - -#include -#include - -#ifdef _MSC_VER -#define EXPORT extern "C" __declspec(dllexport) -#else -#define EXPORT extern "C" __attribute__((__visibility__("default"))) -#endif // !_MSC_VER - -#include -#include -#include -#include - -namespace Assert -{ - class Violation : public std::exception - { - std::string _message; - public: - Violation(char const* source, size_t line, char const* funcName, std::string const& msg); - - std::string const& message() const noexcept - { - return _message; - } - - char const* what() const noexcept override - { - return _message.c_str(); - } - }; - - void _True(bool result, char const* source, size_t line, char const* funcName); - - template - void _Equal(T const& expected, T const& actual, char const* source, size_t line, char const* funcName) - { - if (expected != actual) - { - std::stringstream ss; - ss << std::hex << expected << " != " << actual << std::endl; - throw Violation{ source, line, funcName, ss.str() }; - } - } - - template - T _Equal(T&& expected, T const& actual, char const* source, size_t line, char const* funcName) - { - _Equal(expected, actual, source, line, funcName); - return std::move(expected); - } - - template - std::vector _Equal(std::vector&& expected, std::vector const& actual, char const* source, size_t line, char const* funcName) - { - if (expected != actual) - { - std::stringstream ss; - if (expected.size() != actual.size()) - { - ss << "Size mismatch: " << expected.size() << " != " << actual.size() << "\n"; - ss << std::hex; - char const* d = "Expect: "; - for (auto e : expected) - { - ss << d << e; - d = ", "; - } - ss << "\n"; - d = "Actual: "; - for (auto a : actual) - { - ss << d << a; - d = ", "; - } - ss << "\n"; - } - else - { - auto iters = std::mismatch(std::begin(expected), std::end(expected), std::begin(actual), std::end(actual)); - if (iters.first != std::end(expected) || iters.second != std::end(actual)) - { - ss << "Element at " << std::distance(std::begin(expected), iters.first) << " mismatch: " - << std::hex << *iters.first << " != " << *iters.second; - } - } - throw Violation{ source, line, funcName, ss.str() }; - } - return std::move(expected); - } -} - -#define ASSERT_TRUE(e) Assert::_True((e), __FILE__, __LINE__, __func__) -#define ASSERT_EQUAL(e, a) Assert::_Equal((e), (a), __FILE__, __LINE__, __func__) -#define ASSERT_AND_RETURN(e, a) Assert::_Equal((e), (a), __FILE__, __LINE__, __func__) - -enum class TestState : uint32_t -{ - Fail = 0, - Pass, -}; - -struct TestResult final -{ - TestState State; - char const* FailureMessage; - void(*Free)(void*); -}; - -TestResult ConvertViolation(Assert::Violation const& v); - -#define BEGIN_TEST()\ - try \ - { \ - -#define END_TEST()\ - } \ - catch (Assert::Violation const& v) \ - { \ - return ConvertViolation(v); \ - } \ - return { TestState::Pass, nullptr, nullptr }; - -// Used when the test calls another test case. -#define END_DELEGATING_TEST()\ - } \ - catch (Assert::Violation const& v) \ - { \ - return ConvertViolation(v); \ - } diff --git a/test/regnative/unit_cor.cpp b/test/regnative/unit_cor.cpp deleted file mode 100644 index 06629fc8..00000000 --- a/test/regnative/unit_cor.cpp +++ /dev/null @@ -1,1975 +0,0 @@ -#include "regnative.hpp" - -#include -#include -#include -#include -#include -#include -#include - -namespace -{ - IMetaDataDispenser* g_baselineDisp; - IMetaDataDispenser* g_deltaImageBuilder; - IMetaDataDispenser* g_currentDisp; - - HRESULT CreateImport(IMetaDataDispenser* disp, void const* data, uint32_t dataLen, IMetaDataImport2** import) - { - assert(disp != nullptr && data != nullptr && dataLen > 0 && import != nullptr); - return disp->OpenScopeOnMemory( - data, - dataLen, - CorOpenFlags::ofReadOnly, - IID_IMetaDataImport2, - reinterpret_cast(import)); - } - - HRESULT CreateEmit(IMetaDataDispenser* disp, void const* data, uint32_t dataLen, IMetaDataEmit2** emit) - { - assert(disp != nullptr && data != nullptr && dataLen > 0 && emit != nullptr); - return disp->OpenScopeOnMemory( - data, - dataLen, - CorOpenFlags::ofWrite, - IID_IMetaDataEmit2, - reinterpret_cast(emit)); - } -} - -EXPORT -HRESULT UnitInitialize(IMetaDataDispenser* baseline, IMetaDataDispenserEx* deltaBuilder) -{ - HRESULT hr; - if (baseline == nullptr) - return E_INVALIDARG; - - if (deltaBuilder == nullptr) - return E_INVALIDARG; - - (void)baseline->AddRef(); - g_baselineDisp = baseline; - - (void)deltaBuilder->AddRef(); - - // We need to set the ENC mode on the delta builder to get it to apply EnC deltas - // and produce images with the indirection tables. - VARIANT vt; - V_VT(&vt) = VT_UI4; - V_UI4(&vt) = MDUpdateExtension; - if (FAILED(hr = deltaBuilder->SetOption(MetaDataSetENC, &vt))) - return hr; - - g_deltaImageBuilder = deltaBuilder; - - if (FAILED(hr = GetDispenser(IID_IMetaDataDispenser, reinterpret_cast(&g_currentDisp)))) - return hr; - - return S_OK; -} - -namespace -{ - template - using static_enum_buffer = std::array; - - template - using static_char_buffer = std::array; - - // default values recommended by http://isthe.com/chongo/tech/comp/fnv/ - uint32_t const Prime = 0x01000193; // 16777619 - uint32_t const Seed = 0x811C9DC5; // 2166136261 - // hash a single byte - uint32_t fnv1a(uint8_t oneByte, uint32_t hash = Seed) - { - return (oneByte ^ hash) * Prime; - } - - // Based on https://create.stephan-brumme.com/fnv-hash/ - uint32_t HashCharArray(static_char_buffer const& arr, uint32_t written) - { - uint32_t hash = Seed; - auto curr = std::begin(arr); - auto end = curr + written; - for (; curr < end; ++curr) - { - WCHAR c = *curr; - std::array r; - memcpy(r.data(), &c, r.size()); - for (uint8_t b : r) - hash = fnv1a(b, hash); - } - return hash; - } - - // Based on https://create.stephan-brumme.com/fnv-hash/ - uint32_t HashByteArray(void const* arr, size_t byteLength) - { - uint32_t hash = Seed; - auto curr = (uint8_t const*)arr; - auto end = curr + byteLength; - for (; curr < end; ++curr) - { - hash = fnv1a(*curr, hash); - } - return hash; - } - - std::vector GetCustomAttributeByName(IMetaDataImport2* import, LPCWSTR customAttr, mdToken tkObj) - { - std::vector values; - - void const* ppData; - ULONG pcbData; - HRESULT hr = import->GetCustomAttributeByName(tkObj, - customAttr, - &ppData, - &pcbData); - - values.push_back(hr); - if (hr == S_OK) - { - values.push_back(HashByteArray(ppData, pcbData)); - values.push_back(pcbData); - } - return values; - } - - std::vector GetCustomAttribute_Nullable(IMetaDataImport2* import, mdToken tkObj) - { - auto NullableAttrName = W("System.Runtime.CompilerServices.NullableAttribute"); - return GetCustomAttributeByName(import, NullableAttrName, tkObj); - } - - std::vector GetCustomAttribute_CompilerGenerated(IMetaDataImport2* import, mdToken tkObj) - { - auto CompilerGeneratedAttrName = W("System.Runtime.CompilerServices.CompilerGeneratedAttribute"); - return GetCustomAttributeByName(import, CompilerGeneratedAttrName, tkObj); - } - - void ValidateAndCloseEnum(IMetaDataImport2* import, HCORENUM hcorenum, ULONG expectedCount) - { - ULONG count; - ASSERT_EQUAL(S_OK, import->CountEnum(hcorenum, &count)); - ASSERT_EQUAL(count, expectedCount); - import->CloseEnum(hcorenum); - } - - std::vector EnumTypeDefs(IMetaDataImport2* import) - { - std::vector tokens; - static_enum_buffer tokensBuffer{}; - HCORENUM hcorenum{}; - ULONG returned; - while (0 == import->EnumTypeDefs(&hcorenum, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) - && returned != 0) - { - for (ULONG i = 0; i < returned; ++i) - { - tokens.push_back(tokensBuffer[i]); - } - } - ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); - return tokens; - } - - std::vector EnumTypeRefs(IMetaDataImport2* import) - { - std::vector tokens; - static_enum_buffer tokensBuffer{}; - HCORENUM hcorenum{}; - ULONG returned; - while (0 == import->EnumTypeRefs(&hcorenum, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) - && returned != 0) - { - for (ULONG i = 0; i < returned; ++i) - tokens.push_back(tokensBuffer[i]); - } - ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); - return tokens; - } - - std::vector EnumTypeSpecs(IMetaDataImport2* import) - { - std::vector tokens; - static_enum_buffer tokensBuffer{}; - HCORENUM hcorenum{}; - ULONG returned; - while (0 == import->EnumTypeSpecs(&hcorenum, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) - && returned != 0) - { - for (ULONG i = 0; i < returned; ++i) - tokens.push_back(tokensBuffer[i]); - } - ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); - return tokens; - } - - std::vector EnumModuleRefs(IMetaDataImport2* import) - { - std::vector tokens; - static_enum_buffer tokensBuffer{}; - HCORENUM hcorenum{}; - ULONG returned; - while (0 == import->EnumModuleRefs(&hcorenum, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) - && returned != 0) - { - for (ULONG i = 0; i < returned; ++i) - tokens.push_back(tokensBuffer[i]); - } - ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); - return tokens; - } - - std::vector EnumInterfaceImpls(IMetaDataImport2* import, mdTypeDef typdef) - { - std::vector tokens; - static_enum_buffer tokensBuffer{}; - HCORENUM hcorenum{}; - ULONG returned; - while (0 == import->EnumInterfaceImpls(&hcorenum, typdef, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) - && returned != 0) - { - for (ULONG i = 0; i < returned; ++i) - tokens.push_back(tokensBuffer[i]); - } - ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); - return tokens; - } - - std::vector EnumMembers(IMetaDataImport2* import, mdTypeDef typdef) - { - std::vector tokens; - static_enum_buffer tokensBuffer{}; - HCORENUM hcorenum{}; - ULONG returned; - while (0 == import->EnumMembers(&hcorenum, typdef, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) - && returned != 0) - { - for (ULONG i = 0; i < returned; ++i) - tokens.push_back(tokensBuffer[i]); - } - ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); - return tokens; - } - - std::vector EnumMembersWithName(IMetaDataImport2* import, mdTypeDef typdef, LPCWSTR memberName = W(".ctor")) - { - std::vector tokens; - static_enum_buffer tokensBuffer{}; - HCORENUM hcorenum{}; - ULONG returned; - while (0 == import->EnumMembersWithName(&hcorenum, typdef, memberName, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) - && returned != 0) - { - for (ULONG i = 0; i < returned; ++i) - tokens.push_back(tokensBuffer[i]); - } - ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); - return tokens; - } - - std::vector EnumMemberRefs(IMetaDataImport2* import, mdToken tkParent) - { - std::vector tokens; - static_enum_buffer tokensBuffer{}; - HCORENUM hcorenum{}; - ULONG returned; - while (0 == import->EnumMemberRefs(&hcorenum, tkParent, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) - && returned != 0) - { - for (ULONG i = 0; i < returned; ++i) - tokens.push_back(tokensBuffer[i]); - } - ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); - return tokens; - } - - std::vector EnumMethods(IMetaDataImport2* import, mdTypeDef typdef) - { - std::vector tokens; - static_enum_buffer tokensBuffer{}; - HCORENUM hcorenum{}; - ULONG returned; - while (0 == import->EnumMethods(&hcorenum, typdef, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) - && returned != 0) - { - for (ULONG i = 0; i < returned; ++i) - tokens.push_back(tokensBuffer[i]); - } - ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); - return tokens; - } - - std::vector EnumMethodsWithName(IMetaDataImport2* import, mdToken typdef, LPCWSTR methodName = W(".ctor")) - { - std::vector tokens; - static_enum_buffer tokensBuffer{}; - HCORENUM hcorenum{}; - ULONG returned; - while (0 == import->EnumMethodsWithName(&hcorenum, typdef, methodName, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) - && returned != 0) - { - for (ULONG i = 0; i < returned; ++i) - tokens.push_back(tokensBuffer[i]); - } - ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); - return tokens; - } - - std::vector EnumMethodImpls(IMetaDataImport2* import, mdTypeDef typdef) - { - std::vector tokens; - static_enum_buffer tokensBuffer1{}; - static_enum_buffer tokensBuffer2{}; - HCORENUM hcorenum{}; - ULONG returned; - while (0 == import->EnumMethodImpls(&hcorenum, typdef, tokensBuffer1.data(), tokensBuffer2.data(), (ULONG)tokensBuffer1.size(), &returned) - && returned != 0) - { - for (ULONG i = 0; i < returned; ++i) - { - tokens.push_back(tokensBuffer1[i]); - tokens.push_back(tokensBuffer2[i]); - } - } - ValidateAndCloseEnum(import, hcorenum, (ULONG)(tokens.size() / 2)); - return tokens; - } - - std::vector EnumMethodSemantics(IMetaDataImport2* import, mdMethodDef mb) - { - std::vector tokens; - static_enum_buffer tokensBuffer{}; - HCORENUM hcorenum{}; - ULONG returned; - while (0 == import->EnumMethodSemantics(&hcorenum, mb, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) - && returned != 0) - { - for (ULONG i = 0; i < returned; ++i) - tokens.push_back(tokensBuffer[i]); - } - ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); - return tokens; - } - - std::vector EnumParams(IMetaDataImport2* import, mdMethodDef methoddef) - { - std::vector tokens; - static_enum_buffer tokensBuffer{}; - HCORENUM hcorenum{}; - ULONG returned; - while (0 == import->EnumParams(&hcorenum, methoddef, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) - && returned != 0) - { - for (ULONG i = 0; i < returned; ++i) - tokens.push_back(tokensBuffer[i]); - } - ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); - return tokens; - } - - std::vector EnumMethodSpecs(IMetaDataImport2* import, mdToken tk) - { - std::vector tokens; - static_enum_buffer tokensBuffer{}; - HCORENUM hcorenum{}; - ULONG returned; - while (0 == import->EnumMethodSpecs(&hcorenum, tk, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) - && returned != 0) - { - for (ULONG i = 0; i < returned; ++i) - tokens.push_back(tokensBuffer[i]); - } - ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); - return tokens; - } - - std::vector EnumEvents(IMetaDataImport2* import, mdTypeDef tk) - { - std::vector tokens; - static_enum_buffer tokensBuffer{}; - HCORENUM hcorenum{}; - ULONG returned; - while (0 == import->EnumEvents(&hcorenum, tk, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) - && returned != 0) - { - for (ULONG i = 0; i < returned; ++i) - tokens.push_back(tokensBuffer[i]); - } - ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); - return tokens; - } - - std::vector EnumProperties(IMetaDataImport2* import, mdTypeDef tk) - { - std::vector tokens; - static_enum_buffer tokensBuffer{}; - HCORENUM hcorenum{}; - ULONG returned; - while (0 == import->EnumProperties(&hcorenum, tk, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) - && returned != 0) - { - for (ULONG i = 0; i < returned; ++i) - tokens.push_back(tokensBuffer[i]); - } - ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); - return tokens; - } - - std::vector EnumFields(IMetaDataImport2* import, mdTypeDef tk) - { - std::vector tokens; - static_enum_buffer tokensBuffer{}; - HCORENUM hcorenum{}; - ULONG returned; - while (0 == import->EnumFields(&hcorenum, tk, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) - && returned != 0) - { - for (ULONG i = 0; i < returned; ++i) - tokens.push_back(tokensBuffer[i]); - } - ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); - return tokens; - } - - std::vector EnumFieldsWithName(IMetaDataImport2* import, mdTypeDef tk, LPCWSTR name = W("_name")) - { - std::vector tokens; - static_enum_buffer tokensBuffer{}; - HCORENUM hcorenum{}; - ULONG returned; - while (0 == import->EnumFieldsWithName(&hcorenum, tk, name, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) - && returned != 0) - { - for (ULONG i = 0; i < returned; ++i) - tokens.push_back(tokensBuffer[i]); - } - ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); - return tokens; - } - - std::vector EnumSignatures(IMetaDataImport2* import) - { - std::vector tokens; - static_enum_buffer tokensBuffer{}; - HCORENUM hcorenum{}; - ULONG returned; - while (0 == import->EnumSignatures(&hcorenum, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) - && returned != 0) - { - for (ULONG i = 0; i < returned; ++i) - tokens.push_back(tokensBuffer[i]); - } - ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); - return tokens; - } - - std::vector EnumUserStrings(IMetaDataImport2* import) - { - std::vector tokens; - static_enum_buffer tokensBuffer{}; - HCORENUM hcorenum{}; - ULONG returned; - while (0 == import->EnumUserStrings(&hcorenum, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) - && returned != 0) - { - for (ULONG i = 0; i < returned; ++i) - tokens.push_back(tokensBuffer[i]); - } - ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); - return tokens; - } - - std::vector EnumCustomAttributes(IMetaDataImport2* import, mdToken tk = mdTokenNil, mdToken tkType = mdTokenNil) - { - std::vector tokens; - static_enum_buffer tokensBuffer{}; - HCORENUM hcorenum{}; - ULONG returned; - while (0 == import->EnumCustomAttributes(&hcorenum, tk, tkType, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) - && returned != 0) - { - for (ULONG i = 0; i < returned; ++i) - tokens.push_back(tokensBuffer[i]); - } - ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); - return tokens; - } - - std::vector EnumGenericParams(IMetaDataImport2* import, mdToken tk) - { - std::vector tokens; - static_enum_buffer tokensBuffer{}; - HCORENUM hcorenum{}; - ULONG returned; - while (0 == import->EnumGenericParams(&hcorenum, tk, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) - && returned != 0) - { - for (ULONG i = 0; i < returned; ++i) - tokens.push_back(tokensBuffer[i]); - } - ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); - return tokens; - } - - std::vector EnumGenericParamConstraints(IMetaDataImport2* import, mdGenericParam tk) - { - std::vector tokens; - static_enum_buffer tokensBuffer{}; - HCORENUM hcorenum{}; - ULONG returned; - while (0 == import->EnumGenericParamConstraints(&hcorenum, tk, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) - && returned != 0) - { - for (ULONG i = 0; i < returned; ++i) - tokens.push_back(tokensBuffer[i]); - } - ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); - return tokens; - } - - std::vector EnumPermissionSetsAndGetProps(IMetaDataImport2* import, mdToken permTk) - { - std::vector values; - static_enum_buffer tokensBuffer{}; - - // See CorDeclSecurity for actions definitions - for (int32_t action = (int32_t)dclActionNil; action <= dclMaximumValue; ++action) - { - std::vector tokens; - HCORENUM hcorenum{}; - { - ULONG returned; - while (0 == import->EnumPermissionSets(&hcorenum, permTk, action, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) - && returned != 0) - { - for (ULONG j = 0; j < returned; ++j) - { - tokens.push_back(tokensBuffer[j]); - } - } - ValidateAndCloseEnum(import, hcorenum, (ULONG)tokens.size()); - } - - for (uint32_t pk : tokens) - { - DWORD a; - void const* ppvPermission; - ULONG pcbPermission; - HRESULT hr = import->GetPermissionSetProps(pk, &a, &ppvPermission, &pcbPermission); - values.push_back(hr); - if (hr == S_OK) - { - values.push_back(a); - values.push_back(HashByteArray(ppvPermission, pcbPermission)); - values.push_back(pcbPermission); - } - } - } - return values; - } - - std::vector EnumAssemblyRefs(IMetaDataAssemblyImport* import) - { - std::vector tokens; - static_enum_buffer tokensBuffer{}; - HCORENUM hcorenum{}; - ULONG returned; - while (0 == import->EnumAssemblyRefs(&hcorenum, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) - && returned != 0) - { - for (ULONG i = 0; i < returned; ++i) - tokens.push_back(tokensBuffer[i]); - } - dncp::com_ptr mdImport; - HRESULT hr = import->QueryInterface(IID_IMetaDataImport2, (void**)&mdImport); - ASSERT_EQUAL(S_OK, hr); - ValidateAndCloseEnum(mdImport, hcorenum, (ULONG)tokens.size()); - return tokens; - } - - std::vector EnumFiles(IMetaDataAssemblyImport* import) - { - std::vector tokens; - static_enum_buffer tokensBuffer{}; - HCORENUM hcorenum{}; - ULONG returned; - while (0 == import->EnumFiles(&hcorenum, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) - && returned != 0) - { - for (ULONG i = 0; i < returned; ++i) - tokens.push_back(tokensBuffer[i]); - } - dncp::com_ptr mdImport; - HRESULT hr = import->QueryInterface(IID_IMetaDataImport2, (void**)&mdImport); - ASSERT_EQUAL(S_OK, hr); - ValidateAndCloseEnum(mdImport, hcorenum, (ULONG)tokens.size()); - return tokens; - } - - std::vector EnumExportedTypes(IMetaDataAssemblyImport* import) - { - std::vector tokens; - static_enum_buffer tokensBuffer{}; - HCORENUM hcorenum{}; - ULONG returned; - while (0 == import->EnumExportedTypes(&hcorenum, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) - && returned != 0) - { - for (ULONG i = 0; i < returned; ++i) - tokens.push_back(tokensBuffer[i]); - } - dncp::com_ptr mdImport; - HRESULT hr = import->QueryInterface(IID_IMetaDataImport2, (void**)&mdImport); - ASSERT_EQUAL(S_OK, hr); - ValidateAndCloseEnum(mdImport, hcorenum, (ULONG)tokens.size()); - return tokens; - } - - std::vector EnumManifestResources(IMetaDataAssemblyImport* import) - { - std::vector tokens; - static_enum_buffer tokensBuffer{}; - HCORENUM hcorenum{}; - ULONG returned; - while (0 == import->EnumManifestResources(&hcorenum, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) - && returned != 0) - { - for (ULONG i = 0; i < returned; ++i) - tokens.push_back(tokensBuffer[i]); - } - dncp::com_ptr mdImport; - HRESULT hr = import->QueryInterface(IID_IMetaDataImport2, (void**)&mdImport); - ASSERT_EQUAL(S_OK, hr); - ValidateAndCloseEnum(mdImport, hcorenum, (ULONG)tokens.size()); - return tokens; - } - - std::vector FindTypeRef(IMetaDataImport2* import) - { - std::vector values; - HRESULT hr; - mdToken tk; - - // The first assembly ref token typically contains System.Object and Enumerator. - mdToken const assemblyRefToken = 0x23000001; - hr = import->FindTypeRef(assemblyRefToken, W("System.Object"), &tk); - values.push_back(hr); - if (hr == S_OK) - values.push_back(tk); - - // Look for a type that won't ever exist - hr = import->FindTypeRef(assemblyRefToken, W("DoesntExist"), &tk); - values.push_back(hr); - if (hr == S_OK) - values.push_back(tk); - return values; - } - - std::vector FindTypeDefByName(IMetaDataImport2* import, LPCWSTR name, mdToken scope) - { - std::vector values; - - mdTypeDef ptd; - HRESULT hr = import->FindTypeDefByName(name, scope, &ptd); - - values.push_back(hr); - if (hr == S_OK) - values.push_back(ptd); - return values; - } - - std::vector FindExportedTypeByName(IMetaDataAssemblyImport* import, LPCWSTR name, mdToken tkImplementation) - { - std::vector values; - - mdExportedType exported; - HRESULT hr = import->FindExportedTypeByName(name, tkImplementation, &exported); - - values.push_back(hr); - if (hr == S_OK) - values.push_back(exported); - return values; - } - - std::vector FindManifestResourceByName(IMetaDataAssemblyImport* import, LPCWSTR name) - { - std::vector values; - - mdManifestResource resource; - HRESULT hr = import->FindManifestResourceByName(name, &resource); - - values.push_back(hr); - if (hr == S_OK) - values.push_back(resource); - return values; - } - - std::vector GetTypeDefProps(IMetaDataImport2* import, mdTypeDef typdef) - { - std::vector values; - - static_char_buffer name{}; - ULONG pchTypeDef; - DWORD pdwTypeDefFlags; - mdToken ptkExtends; - HRESULT hr = import->GetTypeDefProps(typdef, - name.data(), - (ULONG)name.size(), - &pchTypeDef, - &pdwTypeDefFlags, - &ptkExtends); - - values.push_back(hr); - if (hr == S_OK) - { - uint32_t hash = HashCharArray(name, pchTypeDef); - values.push_back(hash); - values.push_back(pchTypeDef); - values.push_back(pdwTypeDefFlags); - values.push_back(ptkExtends); - } - return values; - } - - std::vector GetTypeRefProps(IMetaDataImport2* import, mdTypeRef typeref) - { - std::vector values; - - static_char_buffer name{}; - mdToken tkResolutionScope; - ULONG pchTypeRef; - HRESULT hr = import->GetTypeRefProps(typeref, - &tkResolutionScope, - name.data(), - (ULONG)name.size(), - &pchTypeRef); - - values.push_back(hr); - if (hr == S_OK) - { - values.push_back(tkResolutionScope); - uint32_t hash = HashCharArray(name, pchTypeRef); - values.push_back(hash); - values.push_back(pchTypeRef); - } - return values; - } - - std::vector GetScopeProps(IMetaDataImport2* import) - { - std::vector values; - - static_char_buffer name{}; - ULONG pchName; - GUID mvid; - HRESULT hr = import->GetScopeProps( - name.data(), - (ULONG)name.size(), - &pchName, - &mvid); - - values.push_back(hr); - if (hr == S_OK) - { - uint32_t hash = HashCharArray(name, pchName); - values.push_back(hash); - values.push_back(pchName); - - std::array buffer{}; - memcpy(buffer.data(), &mvid, buffer.size()); - for (auto b : buffer) - values.push_back(b); - } - return values; - } - - std::vector GetModuleRefProps(IMetaDataImport2* import, mdModuleRef moduleref) - { - std::vector values; - - static_char_buffer name{}; - ULONG pchModuleRef; - HRESULT hr = import->GetModuleRefProps(moduleref, - name.data(), - (ULONG)name.size(), - &pchModuleRef); - - values.push_back(hr); - if (hr == S_OK) - { - uint32_t hash = HashCharArray(name, pchModuleRef); - values.push_back(hash); - values.push_back(pchModuleRef); - } - return values; - } - - std::vector GetMethodProps(IMetaDataImport2* import, mdToken tk, void const** sig = nullptr, ULONG* sigLen = nullptr) - { - std::vector values; - - mdTypeDef pClass; - static_char_buffer name{}; - ULONG pchMethod; - DWORD pdwAttr; - PCCOR_SIGNATURE ppvSigBlob; - ULONG pcbSigBlob; - ULONG pulCodeRVA; - DWORD pdwImplFlags; - HRESULT hr = import->GetMethodProps(tk, - &pClass, - name.data(), - (ULONG)name.size(), - &pchMethod, - &pdwAttr, - &ppvSigBlob, - &pcbSigBlob, - &pulCodeRVA, - &pdwImplFlags); - values.push_back(hr); - if (hr == S_OK) - { - values.push_back(pClass); - uint32_t hash = HashCharArray(name, pchMethod); - values.push_back(hash); - values.push_back(pchMethod); - values.push_back(pdwAttr); - values.push_back(HashByteArray(ppvSigBlob, pcbSigBlob)); - values.push_back(pcbSigBlob); - values.push_back(pulCodeRVA); - values.push_back(pdwImplFlags); - - if (sig != nullptr) - *sig = ppvSigBlob; - if (sigLen != nullptr) - *sigLen = pcbSigBlob; - } - return values; - } - - std::vector GetParamProps(IMetaDataImport2* import, mdToken tk) - { - std::vector values; - - mdMethodDef pmd; - ULONG pulSequence; - static_char_buffer name{}; - ULONG pchName; - DWORD pdwAttr; - DWORD pdwCPlusTypeFlag; - UVCP_CONSTANT ppValue; - ULONG pcchValue; - HRESULT hr = import->GetParamProps(tk, - &pmd, - &pulSequence, - name.data(), - (ULONG)name.size(), - &pchName, - &pdwAttr, - &pdwCPlusTypeFlag, - &ppValue, - &pcchValue); - - values.push_back(hr); - if (hr == S_OK) - { - values.push_back(pmd); - values.push_back(pulSequence); - uint32_t hash = HashCharArray(name, pchName); - values.push_back(hash); - values.push_back(pchName); - values.push_back(pdwAttr); - values.push_back(pdwCPlusTypeFlag); - values.push_back(HashByteArray(ppValue, pcchValue)); - values.push_back(pcchValue); - } - return values; - } - - std::vector GetMethodSpecProps(IMetaDataImport2* import, mdMethodSpec methodSpec) - { - std::vector values; - - mdToken parent; - PCCOR_SIGNATURE sig; - ULONG sigLen; - HRESULT hr = import->GetMethodSpecProps(methodSpec, - &parent, - &sig, - &sigLen); - - values.push_back(hr); - if (hr == S_OK) - { - values.push_back(parent); - values.push_back(HashByteArray(sig, sigLen)); - values.push_back(sigLen); - } - return values; - } - - std::vector GetMemberRefProps(IMetaDataImport2* import, mdMemberRef mr, PCCOR_SIGNATURE* sig = nullptr, ULONG* sigLen = nullptr) - { - std::vector values; - - mdToken ptk; - static_char_buffer name{}; - ULONG pchMember; - PCCOR_SIGNATURE ppvSigBlob; - ULONG pcbSigBlob; - HRESULT hr = import->GetMemberRefProps(mr, - &ptk, - name.data(), - (ULONG)name.size(), - &pchMember, - &ppvSigBlob, - &pcbSigBlob); - - values.push_back(hr); - if (hr == S_OK) - { - values.push_back(ptk); - uint32_t hash = HashCharArray(name, pchMember); - values.push_back(hash); - values.push_back(pchMember); - values.push_back(HashByteArray(ppvSigBlob, pcbSigBlob)); - values.push_back(pcbSigBlob); - - if (sig != nullptr) - *sig = ppvSigBlob; - if (sigLen != nullptr) - *sigLen = pcbSigBlob; - } - return values; - } - - std::vector GetEventProps(IMetaDataImport2* import, mdEvent tk, std::vector* methoddefs = nullptr) - { - std::vector values; - - mdTypeDef pClass; - static_char_buffer name{}; - ULONG pchEvent; - DWORD pdwEventFlags; - mdToken ptkEventType; - mdMethodDef pmdAddOn; - mdMethodDef pmdRemoveOn; - mdMethodDef pmdFire; - static_enum_buffer rmdOtherMethod; - ULONG pcOtherMethod; - HRESULT hr = import->GetEventProps(tk, - &pClass, - name.data(), - (ULONG)name.size(), - &pchEvent, - &pdwEventFlags, - &ptkEventType, - &pmdAddOn, - &pmdRemoveOn, - &pmdFire, - rmdOtherMethod.data(), - (ULONG)rmdOtherMethod.size(), - &pcOtherMethod); - values.push_back(hr); - if (hr == S_OK) - { - values.push_back(pClass); - uint32_t hash = HashCharArray(name, pchEvent); - values.push_back(hash); - values.push_back(pchEvent); - values.push_back(pdwEventFlags); - values.push_back(ptkEventType); - values.push_back(pmdAddOn); - values.push_back(pmdRemoveOn); - values.push_back(pmdFire); - - std::vector retMaybe; - for (ULONG i = 0; i < std::min(pcOtherMethod, (ULONG)rmdOtherMethod.size()); ++i) - { - values.push_back(rmdOtherMethod[i]); - retMaybe.push_back(rmdOtherMethod[i]); - } - - retMaybe.push_back(pmdAddOn); - retMaybe.push_back(pmdRemoveOn); - retMaybe.push_back(pmdFire); - - if (methoddefs != nullptr) - *methoddefs = std::move(retMaybe); - } - return values; - } - - std::vector GetPropertyProps(IMetaDataImport2* import, mdProperty tk, std::vector* methoddefs = nullptr) - { - std::vector values; - - mdTypeDef pClass; - static_char_buffer name{}; - ULONG pchProperty; - DWORD pdwPropFlags; - PCCOR_SIGNATURE sig; - ULONG sigLen; - DWORD pdwCPlusTypeFlag; - UVCP_CONSTANT ppDefaultValue; - ULONG pcchDefaultValue; - mdMethodDef pmdSetter; - mdMethodDef pmdGetter; - static_enum_buffer rmdOtherMethod{}; - ULONG pcOtherMethod; - HRESULT hr = import->GetPropertyProps(tk, - &pClass, - name.data(), - (ULONG)name.size(), - &pchProperty, - &pdwPropFlags, - &sig, - &sigLen, - &pdwCPlusTypeFlag, - &ppDefaultValue, - &pcchDefaultValue, - &pmdSetter, - &pmdGetter, - rmdOtherMethod.data(), - (ULONG)rmdOtherMethod.size(), - &pcOtherMethod); - values.push_back(hr); - if (hr == S_OK) - { - values.push_back(pClass); - uint32_t hash = HashCharArray(name, pchProperty); - values.push_back(hash); - values.push_back(pchProperty); - values.push_back(pdwPropFlags); - values.push_back(HashByteArray(sig, sigLen)); - values.push_back(sigLen); - values.push_back(pdwCPlusTypeFlag); - values.push_back(HashByteArray(ppDefaultValue, pcchDefaultValue)); - values.push_back(pcchDefaultValue); - values.push_back(pmdSetter); - values.push_back(pmdGetter); - - std::vector retMaybe; - for (ULONG i = 0; i < std::min(pcOtherMethod, (ULONG)rmdOtherMethod.size()); ++i) - { - values.push_back(rmdOtherMethod[i]); - retMaybe.push_back(rmdOtherMethod[i]); - } - - retMaybe.push_back(pmdSetter); - retMaybe.push_back(pmdGetter); - - if (methoddefs != nullptr) - *methoddefs = std::move(retMaybe); - } - return values; - } - - std::vector GetFieldProps(IMetaDataImport2* import, mdFieldDef tk, void const** sig = nullptr, ULONG* sigLen = nullptr) - { - std::vector values; - - mdTypeDef pClass; - static_char_buffer name{}; - ULONG pchField; - DWORD pdwAttr; - PCCOR_SIGNATURE ppvSigBlob; - ULONG pcbSigBlob; - DWORD pdwCPlusTypeFlag; - UVCP_CONSTANT ppValue = nullptr; - ULONG pcchValue = 0; - HRESULT hr = import->GetFieldProps(tk, - &pClass, - name.data(), - (ULONG)name.size(), - &pchField, - &pdwAttr, - &ppvSigBlob, - &pcbSigBlob, - &pdwCPlusTypeFlag, - &ppValue, - &pcchValue); - - values.push_back(hr); - if (hr == S_OK) - { - values.push_back(pClass); - uint32_t hash = HashCharArray(name, pchField); - values.push_back(hash); - values.push_back(pchField); - values.push_back(pdwAttr); - values.push_back(HashByteArray(ppvSigBlob, pcbSigBlob)); - values.push_back(pcbSigBlob); - values.push_back(pdwCPlusTypeFlag); - values.push_back(HashByteArray(ppValue, pcchValue)); - values.push_back(pcchValue); - - if (sig != nullptr) - *sig = ppvSigBlob; - if (sigLen != nullptr) - *sigLen = pcbSigBlob; - } - return values; - } - - std::vector GetCustomAttributeProps(IMetaDataImport2* import, mdCustomAttribute cv) - { - std::vector values; - - mdToken ptkObj; - mdToken ptkType; - void const* sig; - ULONG sigLen; - HRESULT hr = import->GetCustomAttributeProps(cv, - &ptkObj, - &ptkType, - &sig, - &sigLen); - - values.push_back(hr); - if (hr == S_OK) - { - values.push_back(ptkObj); - values.push_back(ptkType); - values.push_back(HashByteArray(sig, sigLen)); - values.push_back(sigLen); - } - return values; - } - - std::vector GetGenericParamProps(IMetaDataImport2* import, mdGenericParam gp) - { - std::vector values; - - ULONG pulParamSeq; - DWORD pdwParamFlags; - mdToken ptOwner; - DWORD reserved; - static_char_buffer name{}; - ULONG pchName; - HRESULT hr = import->GetGenericParamProps(gp, - &pulParamSeq, - &pdwParamFlags, - &ptOwner, - &reserved, - name.data(), - (ULONG)name.size(), - &pchName); - - values.push_back(hr); - if (hr == S_OK) - { - values.push_back(pulParamSeq); - values.push_back(pdwParamFlags); - values.push_back(ptOwner); - values.push_back(reserved); - uint32_t hash = HashCharArray(name, pchName); - values.push_back(hash); - values.push_back(pchName); - } - return values; - } - - std::vector GetGenericParamConstraintProps(IMetaDataImport2* import, mdGenericParamConstraint tk) - { - std::vector values; - - mdGenericParam ptGenericParam; - mdToken ptkConstraintType; - HRESULT hr = import->GetGenericParamConstraintProps(tk, - &ptGenericParam, - &ptkConstraintType); - - values.push_back(hr); - if (hr == S_OK) - { - values.push_back(ptGenericParam); - values.push_back(ptkConstraintType); - } - return values; - } - - std::vector GetPinvokeMap(IMetaDataImport2* import, mdToken tk) - { - std::vector values; - - DWORD pdwMappingFlags; - static_char_buffer name{}; - ULONG pchImportName; - mdModuleRef pmrImportDLL; - HRESULT hr = import->GetPinvokeMap(tk, - &pdwMappingFlags, - name.data(), - (ULONG)name.size(), - &pchImportName, - &pmrImportDLL); - - values.push_back(hr); - if (hr == S_OK) - { - values.push_back(pdwMappingFlags); - uint32_t hash = HashCharArray(name, pchImportName); - values.push_back(hash); - values.push_back(pchImportName); - values.push_back(pmrImportDLL); - } - return values; - } - - std::vector GetNativeCallConvFromSig(IMetaDataImport2* import, void const* sig, ULONG sigLen) - { - std::vector values; - - // .NET 2,4 and CoreCLR metadata imports do not handle null signatures. - if (sigLen != 0) - { - ULONG pCallConv; - HRESULT hr = import->GetNativeCallConvFromSig(sig, sigLen, &pCallConv); - - values.push_back(hr); - if (hr == S_OK) - values.push_back(pCallConv); - } - - return values; - } - - std::vector GetTypeSpecFromToken(IMetaDataImport2* import, mdTypeSpec typespec) - { - std::vector values; - - PCCOR_SIGNATURE sig; - ULONG sigLen; - HRESULT hr = import->GetTypeSpecFromToken(typespec, &sig, &sigLen); - values.push_back(hr); - if (hr == S_OK) - { - values.push_back(HashByteArray(sig, sigLen)); - values.push_back(sigLen); - } - return values; - } - - std::vector GetSigFromToken(IMetaDataImport2* import, mdSignature tkSig) - { - std::vector values; - - PCCOR_SIGNATURE sig; - ULONG sigLen; - HRESULT hr = import->GetSigFromToken(tkSig, &sig, &sigLen); - values.push_back(hr); - if (hr == S_OK) - { - values.push_back(HashByteArray(sig, sigLen)); - values.push_back(sigLen); - } - return values; - } - - std::vector GetMethodSemantics(IMetaDataImport2* import, mdToken tkEventProp, mdMethodDef methodDef) - { - std::vector values; - - DWORD pdwSemanticsFlags; - HRESULT hr = import->GetMethodSemantics(methodDef, tkEventProp, &pdwSemanticsFlags); - - values.push_back(hr); - if (hr == S_OK) - values.push_back(pdwSemanticsFlags); - - return values; - } - - std::vector GetUserString(IMetaDataImport2* import, mdString tkStr) - { - std::vector values; - - static_char_buffer name{}; - ULONG pchString; - HRESULT hr = import->GetUserString(tkStr, name.data(), (ULONG)name.size(), &pchString); - values.push_back(hr); - if (hr == S_OK) - { - uint32_t hash = HashCharArray(name, pchString); - values.push_back(hash); - values.push_back(pchString); - } - return values; - } - - std::vector GetNameFromToken(IMetaDataImport2* import, mdToken tkObj) - { - std::vector values; - - MDUTF8CSTR pszUtf8NamePtr; - HRESULT hr = import->GetNameFromToken(tkObj, &pszUtf8NamePtr); - values.push_back(hr); - if (hr == S_OK) - { - values.push_back(HashByteArray(pszUtf8NamePtr, ::strlen(pszUtf8NamePtr) + 1)); - } - return values; - } - - std::vector GetFieldMarshal(IMetaDataImport2* import, mdToken tk) - { - std::vector values; - - PCCOR_SIGNATURE sig; - ULONG sigLen; - HRESULT hr = import->GetFieldMarshal(tk, &sig, &sigLen); - values.push_back(hr); - if (hr == S_OK) - { - values.push_back(HashByteArray(sig, sigLen)); - values.push_back(sigLen); - } - return values; - } - - std::vector GetNestedClassProps(IMetaDataImport2* import, mdTypeDef tk) - { - std::vector values; - - mdTypeDef ptdEnclosingClass; - HRESULT hr = import->GetNestedClassProps(tk, &ptdEnclosingClass); - values.push_back(hr); - if (hr == S_OK) - values.push_back(ptdEnclosingClass); - - return values; - } - - std::vector GetClassLayout(IMetaDataImport2* import, mdTypeDef tk) - { - std::vector values; - - DWORD pdwPackSize; - std::vector offsets(24); - ULONG pcFieldOffset; - ULONG pulClassSize; - HRESULT hr = import->GetClassLayout(tk, - &pdwPackSize, - offsets.data(), - (ULONG)offsets.size(), - &pcFieldOffset, - &pulClassSize); - values.push_back(hr); - if (hr == S_OK) - { - values.push_back(pdwPackSize); - for (ULONG i = 0; i < std::min(pcFieldOffset, (ULONG)offsets.size()); ++i) - { - COR_FIELD_OFFSET const& o = offsets[i]; - values.push_back(o.ridOfField); - values.push_back(o.ulOffset); - } - values.push_back(pcFieldOffset); - values.push_back(pulClassSize); - } - - return values; - } - - std::vector GetRVA(IMetaDataImport2* import, mdToken tk) - { - std::vector values; - - ULONG pulCodeRVA; - DWORD pdwImplFlags; - HRESULT hr = import->GetRVA(tk, &pulCodeRVA, &pdwImplFlags); - values.push_back(hr); - if (hr == S_OK) - { - values.push_back(pulCodeRVA); - values.push_back(pdwImplFlags); - } - return values; - } - - std::vector GetParamForMethodIndex(IMetaDataImport2* import, mdToken tk) - { - std::vector values; - - mdParamDef def; - for (uint32_t i = 0; i < std::numeric_limits::max(); ++i) - { - HRESULT hr = import->GetParamForMethodIndex(tk, i, &def); - values.push_back(hr); - if (hr != S_OK) - break; - values.push_back(def); - } - return values; - } - - int32_t IsGlobal(IMetaDataImport2* import, mdToken tk) - { - int32_t pbGlobal; - HRESULT hr = import->IsGlobal(tk, &pbGlobal); - if (hr != S_OK) - return hr; - return pbGlobal; - } - - std::vector GetVersionString(IMetaDataImport2* import) - { - std::vector values; - - static_char_buffer name{}; - ULONG pccBufSize; - HRESULT hr = import->GetVersionString(name.data(), (DWORD)name.size(), &pccBufSize); - values.push_back(hr); - if (hr == S_OK) - { - uint32_t hash = HashCharArray(name, pccBufSize); - values.push_back(hash); - values.push_back(pccBufSize); - } - - return values; - } - - std::vector GetAssemblyFromScope(IMetaDataAssemblyImport* import) - { - std::vector values; - - mdAssembly mdAsm; - HRESULT hr = import->GetAssemblyFromScope(&mdAsm); - if (hr == S_OK) - values.push_back(mdAsm); - return values; - } - - std::vector GetAssemblyProps(IMetaDataAssemblyImport* import, mdAssembly mda) - { - std::vector values; - static_char_buffer name{}; - static_char_buffer locale{}; - std::vector processor(1); - std::vector osInfo(1); - - ASSEMBLYMETADATA metadata; - metadata.szLocale = locale.data(); - metadata.cbLocale = (ULONG)locale.size(); - metadata.rProcessor = processor.data(); - metadata.ulProcessor = (ULONG)processor.size(); - metadata.rOS = osInfo.data(); - metadata.ulOS = (ULONG)osInfo.size(); - - void const* publicKey; - ULONG publicKeyLength; - ULONG hashAlgId; - ULONG nameLength; - ULONG flags; - HRESULT hr = import->GetAssemblyProps(mda, &publicKey, &publicKeyLength, &hashAlgId, name.data(), (ULONG)name.size(), &nameLength, &metadata, &flags); - values.push_back(hr); - - if (hr == S_OK) - { - values.push_back(HashByteArray(publicKey, publicKeyLength)); - values.push_back(publicKeyLength); - values.push_back(hashAlgId); - values.push_back(HashCharArray(name, nameLength)); - values.push_back((size_t)nameLength); - values.push_back(metadata.usMajorVersion); - values.push_back(metadata.usMinorVersion); - values.push_back(metadata.usBuildNumber); - values.push_back(metadata.usRevisionNumber); - values.push_back(HashCharArray(locale, metadata.cbLocale)); - values.push_back(metadata.cbLocale); - values.push_back(metadata.ulProcessor); - values.push_back(metadata.ulOS); - values.push_back(flags); - } - return values; - } - - std::vector GetAssemblyRefProps(IMetaDataAssemblyImport* import, mdAssemblyRef mdar) - { - std::vector values; - static_char_buffer name{}; - static_char_buffer locale{}; - std::vector processor(1); - std::vector osInfo(1); - - ASSEMBLYMETADATA metadata; - metadata.szLocale = locale.data(); - metadata.cbLocale = (ULONG)locale.size(); - metadata.rProcessor = processor.data(); - metadata.ulProcessor = (ULONG)processor.size(); - metadata.rOS = osInfo.data(); - metadata.ulOS = (ULONG)osInfo.size(); - - void const* publicKeyOrToken; - ULONG publicKeyOrTokenLength; - ULONG nameLength; - void const* hash; - ULONG hashLength; - DWORD flags; - HRESULT hr = import->GetAssemblyRefProps(mdar, &publicKeyOrToken, &publicKeyOrTokenLength, name.data(), (ULONG)name.size(), &nameLength, &metadata, &hash, &hashLength, &flags); - values.push_back(hr); - - if (hr == S_OK) - { - values.push_back(HashByteArray(publicKeyOrToken, publicKeyOrTokenLength)); - values.push_back(publicKeyOrTokenLength); - values.push_back(HashCharArray(name, nameLength)); - values.push_back((size_t)nameLength); - values.push_back(metadata.usMajorVersion); - values.push_back(metadata.usMinorVersion); - values.push_back(metadata.usBuildNumber); - values.push_back(metadata.usRevisionNumber); - values.push_back(HashCharArray(locale, metadata.cbLocale)); - values.push_back(metadata.cbLocale); - values.push_back(metadata.ulProcessor); - values.push_back(metadata.ulOS); - values.push_back(HashByteArray(hash, hashLength)); - values.push_back(hashLength); - values.push_back(flags); - } - return values; - } - - std::vector GetFileProps(IMetaDataAssemblyImport* import, mdFile mdf) - { - std::vector values; - static_char_buffer name{}; - - ULONG nameLength; - void const* hash; - ULONG hashLength; - DWORD flags; - HRESULT hr = import->GetFileProps(mdf, name.data(), (ULONG)name.size(), &nameLength, &hash, &hashLength, &flags); - values.push_back(hr); - - if (hr == S_OK) - { - values.push_back(HashCharArray(name, nameLength)); - values.push_back((size_t)nameLength); - values.push_back(hashLength != 0 ? (size_t)hash : 0); - values.push_back(hashLength); - values.push_back(flags); - } - return values; - } - - std::vector GetExportedTypeProps(IMetaDataAssemblyImport* import, mdFile mdf, std::vector* nameBuffer = nullptr, uint32_t* implementationToken = nullptr) - { - std::vector values; - static_char_buffer name{}; - - ULONG nameLength; - mdToken implementation; - mdTypeDef typeDef; - DWORD flags; - HRESULT hr = import->GetExportedTypeProps(mdf, name.data(), (ULONG)name.size(), &nameLength, &implementation, &typeDef, &flags); - values.push_back(hr); - - if (hr == S_OK) - { - values.push_back(HashCharArray(name, nameLength)); - values.push_back(nameLength); - values.push_back(implementation); - values.push_back(typeDef); - values.push_back(flags); - - if (nameBuffer != nullptr) - *nameBuffer = { std::begin(name), std::begin(name) + nameLength }; - if (implementationToken != nullptr) - *implementationToken = implementation; - } - return values; - } - - std::vector GetManifestResourceProps(IMetaDataAssemblyImport* import, mdManifestResource mmr, std::vector* nameBuffer = nullptr) - { - std::vector values; - static_char_buffer name{}; - - ULONG nameLength; - ULONG offset; - mdToken implementation; - DWORD flags; - HRESULT hr = import->GetManifestResourceProps(mmr, name.data(), (ULONG)name.size(), &nameLength, &implementation, &offset, &flags); - values.push_back(hr); - - if (hr == S_OK) - { - values.push_back(HashCharArray(name, nameLength)); - values.push_back(nameLength); - values.push_back(implementation); - values.push_back(flags); - - if (nameBuffer != nullptr) - *nameBuffer = { std::begin(name), std::begin(name) + nameLength }; - } - return values; - } - - std::vector ResetEnum(IMetaDataImport2* import) - { - // We are going to test the ResetEnum() API using the - // EnumMembers() API because it enumerates more than one table. - std::vector tokens; - auto typedefs = EnumTypeDefs(import); - if (typedefs.size() == 0) - return tokens; - - auto tk = typedefs[0]; - HCORENUM hcorenum{}; - try - { - static auto ReadInMembers = [](IMetaDataImport2* import, HCORENUM& hcorenum, mdToken tk, std::vector& tokens) - { - static_enum_buffer tokensBuffer{}; - ULONG returned; - if (0 == import->EnumMembers(&hcorenum, tk, tokensBuffer.data(), (ULONG)tokensBuffer.size(), &returned) - && returned != 0) - { - for (ULONG i = 0; i < returned; ++i) - tokens.push_back(tokensBuffer[i]); - } - }; - - ReadInMembers(import, hcorenum, tk, tokens); - - // Determine how many we have and move to right before end - ULONG count; - ASSERT_EQUAL(S_OK, import->CountEnum(hcorenum, &count)); - if (count != 0) - { - ASSERT_EQUAL(S_OK, import->ResetEnum(hcorenum, count - 1)); - ReadInMembers(import, hcorenum, tk, tokens); - - // Fully reset the enum - ASSERT_EQUAL(S_OK, import->ResetEnum(hcorenum, 0)); - ReadInMembers(import, hcorenum, tk, tokens); - } - } - catch (...) - { - import->CloseEnum(hcorenum); - throw; - } - return tokens; - } -} - -TestResult UnitImportAPIs(void const* data, uint32_t dataLen) -{ - BEGIN_TEST(); - - // Load metadata - dncp::com_ptr baselineImport; - ASSERT_EQUAL(S_OK, CreateImport(g_baselineDisp, data, dataLen, &baselineImport)); - dncp::com_ptr currentImport; - ASSERT_EQUAL(S_OK, CreateImport(g_currentDisp, data, dataLen, ¤tImport)); - - // Verify APIs - ASSERT_EQUAL(ResetEnum(baselineImport), ResetEnum(currentImport)); - ASSERT_EQUAL(GetScopeProps(baselineImport), GetScopeProps(currentImport)); - ASSERT_EQUAL(GetVersionString(baselineImport), GetVersionString(currentImport)); - - auto sigs = ASSERT_AND_RETURN(EnumSignatures(baselineImport), EnumSignatures(currentImport)); - for (auto sig : sigs) - { - ASSERT_EQUAL(GetSigFromToken(baselineImport, sig), GetSigFromToken(currentImport, sig)); - } - - auto userStrings = ASSERT_AND_RETURN(EnumUserStrings(baselineImport), EnumUserStrings(currentImport)); - for (auto us : userStrings) - { - ASSERT_EQUAL(GetUserString(baselineImport, us), GetUserString(currentImport, us)); - } - - auto custAttrs = ASSERT_AND_RETURN(EnumCustomAttributes(baselineImport), EnumCustomAttributes(currentImport)); - for (auto ca : custAttrs) - { - ASSERT_EQUAL(GetCustomAttributeProps(baselineImport, ca), GetCustomAttributeProps(currentImport, ca)); - } - - auto modulerefs = ASSERT_AND_RETURN(EnumModuleRefs(baselineImport), EnumModuleRefs(currentImport)); - for (auto moduleref : modulerefs) - { - ASSERT_EQUAL(GetModuleRefProps(baselineImport, moduleref), GetModuleRefProps(currentImport, moduleref)); - ASSERT_EQUAL(GetNameFromToken(baselineImport, moduleref), GetNameFromToken(currentImport, moduleref)); - } - - ASSERT_EQUAL(FindTypeRef(baselineImport), FindTypeRef(currentImport)); - auto typerefs = ASSERT_AND_RETURN(EnumTypeRefs(baselineImport), EnumTypeRefs(currentImport)); - for (auto typeref : typerefs) - { - ASSERT_EQUAL(GetTypeRefProps(baselineImport, typeref), GetTypeRefProps(currentImport, typeref)); - ASSERT_EQUAL(GetCustomAttribute_CompilerGenerated(baselineImport, typeref), GetCustomAttribute_CompilerGenerated(currentImport, typeref)); - ASSERT_EQUAL(GetNameFromToken(baselineImport, typeref), GetNameFromToken(currentImport, typeref)); - } - - auto typespecs = ASSERT_AND_RETURN(EnumTypeSpecs(baselineImport), EnumTypeSpecs(currentImport)); - for (auto typespec : typespecs) - { - ASSERT_EQUAL(GetTypeSpecFromToken(baselineImport, typespec), GetTypeSpecFromToken(currentImport, typespec)); - ASSERT_EQUAL(GetCustomAttribute_CompilerGenerated(baselineImport, typespec), GetCustomAttribute_CompilerGenerated(currentImport, typespec)); - } - - auto typedefs = ASSERT_AND_RETURN(EnumTypeDefs(baselineImport), EnumTypeDefs(currentImport)); - for (auto typdef : typedefs) - { - ASSERT_EQUAL(GetTypeDefProps(baselineImport, typdef), GetTypeDefProps(currentImport, typdef)); - ASSERT_EQUAL(GetNameFromToken(baselineImport, typdef), GetNameFromToken(currentImport, typdef)); - ASSERT_EQUAL(IsGlobal(baselineImport, typdef), IsGlobal(currentImport, typdef)); - ASSERT_EQUAL(EnumInterfaceImpls(baselineImport, typdef), EnumInterfaceImpls(currentImport, typdef)); - ASSERT_EQUAL(EnumPermissionSetsAndGetProps(baselineImport, typdef), EnumPermissionSetsAndGetProps(currentImport, typdef)); - ASSERT_EQUAL(EnumMembers(baselineImport, typdef), EnumMembers(currentImport, typdef)); - ASSERT_EQUAL(EnumMembersWithName(baselineImport, typdef), EnumMembersWithName(currentImport, typdef)); - ASSERT_EQUAL(EnumMethodsWithName(baselineImport, typdef), EnumMethodsWithName(currentImport, typdef)); - ASSERT_EQUAL(EnumMethodImpls(baselineImport, typdef), EnumMethodImpls(currentImport, typdef)); - ASSERT_EQUAL(GetNestedClassProps(baselineImport, typdef), GetNestedClassProps(currentImport, typdef)); - ASSERT_EQUAL(GetClassLayout(baselineImport, typdef), GetClassLayout(currentImport, typdef)); - ASSERT_EQUAL(GetCustomAttribute_CompilerGenerated(baselineImport, typdef), GetCustomAttribute_CompilerGenerated(currentImport, typdef)); - - auto methoddefs = ASSERT_AND_RETURN(EnumMethods(baselineImport, typdef), EnumMethods(currentImport, typdef)); - for (auto methoddef : methoddefs) - { - void const* sig = nullptr; - ULONG sigLen = 0; - ASSERT_EQUAL(GetMethodProps(baselineImport, methoddef), GetMethodProps(currentImport, methoddef, &sig, &sigLen)); - ASSERT_EQUAL(GetNativeCallConvFromSig(baselineImport, sig, sigLen), GetNativeCallConvFromSig(currentImport, sig, sigLen)); - ASSERT_EQUAL(GetNameFromToken(baselineImport, methoddef), GetNameFromToken(currentImport, methoddef)); - ASSERT_EQUAL(IsGlobal(baselineImport, methoddef), IsGlobal(currentImport, methoddef)); - ASSERT_EQUAL(GetCustomAttribute_CompilerGenerated(baselineImport, methoddef), GetCustomAttribute_CompilerGenerated(currentImport, methoddef)); - - auto paramdefs = ASSERT_AND_RETURN(EnumParams(baselineImport, methoddef), EnumParams(currentImport, methoddef)); - for (auto paramdef : paramdefs) - { - ASSERT_EQUAL(GetParamProps(baselineImport, paramdef), GetParamProps(currentImport, paramdef)); - ASSERT_EQUAL(GetFieldMarshal(baselineImport, paramdef), GetFieldMarshal(currentImport, paramdef)); - ASSERT_EQUAL(GetCustomAttribute_Nullable(baselineImport, paramdef), GetCustomAttribute_Nullable(currentImport, paramdef)); - ASSERT_EQUAL(GetNameFromToken(baselineImport, paramdef), GetNameFromToken(currentImport, paramdef)); - } - - ASSERT_EQUAL(GetParamForMethodIndex(baselineImport, methoddef), GetParamForMethodIndex(currentImport, methoddef)); - ASSERT_EQUAL(EnumPermissionSetsAndGetProps(baselineImport, methoddef), EnumPermissionSetsAndGetProps(currentImport, methoddef)); - ASSERT_EQUAL(GetPinvokeMap(baselineImport, methoddef), GetPinvokeMap(currentImport, methoddef)); - ASSERT_EQUAL(GetRVA(baselineImport, methoddef), GetRVA(currentImport, methoddef)); - - auto methodspecs = ASSERT_AND_RETURN(EnumMethodSpecs(baselineImport, methoddef), EnumMethodSpecs(currentImport, methoddef)); - for (auto methodspec : methodspecs) - { - ASSERT_EQUAL(GetMethodSpecProps(baselineImport, methodspec), GetMethodSpecProps(currentImport, methodspec)); - } - } - - auto eventdefs = ASSERT_AND_RETURN(EnumEvents(baselineImport, typdef), EnumEvents(currentImport, typdef)); - for (auto eventdef : eventdefs) - { - std::vector mds; - ASSERT_EQUAL(GetEventProps(baselineImport, eventdef), GetEventProps(currentImport, eventdef, &mds)); - for (auto md : mds) - { - ASSERT_EQUAL(GetMethodSemantics(baselineImport, eventdef, md), GetMethodSemantics(currentImport, eventdef, md)); - } - - ASSERT_EQUAL(GetNameFromToken(baselineImport, eventdef), GetNameFromToken(currentImport, eventdef)); - ASSERT_EQUAL(IsGlobal(baselineImport, eventdef), IsGlobal(currentImport, eventdef)); - } - - auto properties = ASSERT_AND_RETURN(EnumProperties(baselineImport, typdef), EnumProperties(currentImport, typdef)); - for (auto props : properties) - { - std::vector mds; - ASSERT_EQUAL(GetPropertyProps(baselineImport, props), GetPropertyProps(currentImport, props, &mds)); - for (auto md : mds) - { - ASSERT_EQUAL(GetMethodSemantics(baselineImport, props, md), GetMethodSemantics(currentImport, props, md)); - } - - ASSERT_EQUAL(GetNameFromToken(baselineImport, props), GetNameFromToken(currentImport, props)); - ASSERT_EQUAL(IsGlobal(baselineImport, props), IsGlobal(currentImport, props)); - } - - ASSERT_EQUAL(EnumFieldsWithName(baselineImport, typdef), EnumFieldsWithName(currentImport, typdef)); - auto fielddefs = ASSERT_AND_RETURN(EnumFields(baselineImport, typdef), EnumFields(currentImport, typdef)); - for (auto fielddef : fielddefs) - { - ASSERT_EQUAL(GetFieldProps(baselineImport, fielddef), GetFieldProps(currentImport, fielddef)); - ASSERT_EQUAL(GetNameFromToken(baselineImport, fielddef), GetNameFromToken(currentImport, fielddef)); - ASSERT_EQUAL(IsGlobal(baselineImport, fielddef), IsGlobal(currentImport, fielddef)); - ASSERT_EQUAL(GetPinvokeMap(baselineImport, fielddef), GetPinvokeMap(currentImport, fielddef)); - ASSERT_EQUAL(GetRVA(baselineImport, fielddef), GetRVA(currentImport, fielddef)); - ASSERT_EQUAL(GetFieldMarshal(baselineImport, fielddef), GetFieldMarshal(currentImport, fielddef)); - ASSERT_EQUAL(GetCustomAttribute_Nullable(baselineImport, fielddef), GetCustomAttribute_Nullable(currentImport, fielddef)); - } - - auto genparams = ASSERT_AND_RETURN(EnumGenericParams(baselineImport, typdef), EnumGenericParams(currentImport, typdef)); - for (auto genparam : genparams) - { - ASSERT_EQUAL(GetGenericParamProps(baselineImport, genparam), GetGenericParamProps(currentImport, genparam)); - auto genparamconsts = ASSERT_AND_RETURN(EnumGenericParamConstraints(baselineImport, genparam), EnumGenericParamConstraints(currentImport, genparam)); - for (auto genparamconst : genparamconsts) - { - ASSERT_EQUAL(GetGenericParamConstraintProps(baselineImport, genparamconst), GetGenericParamConstraintProps(currentImport, genparamconst)); - } - } - } - - dncp::com_ptr baselineAssembly; - ASSERT_EQUAL(S_OK, baselineImport->QueryInterface(IID_IMetaDataAssemblyImport, (void**)&baselineAssembly)); - dncp::com_ptr currentAssembly; - ASSERT_EQUAL(S_OK, currentImport->QueryInterface(IID_IMetaDataAssemblyImport, (void**)¤tAssembly)); - - auto assemblyTokens = ASSERT_AND_RETURN(GetAssemblyFromScope(baselineAssembly), GetAssemblyFromScope(currentAssembly)); - for (auto assembly : assemblyTokens) - { - ASSERT_EQUAL(GetAssemblyProps(baselineAssembly, assembly), GetAssemblyProps(currentAssembly, assembly)); - } - - auto assemblyRefs = ASSERT_AND_RETURN(EnumAssemblyRefs(baselineAssembly), EnumAssemblyRefs(currentAssembly)); - for (auto assemblyRef : assemblyRefs) - { - ASSERT_EQUAL(GetAssemblyRefProps(baselineAssembly, assemblyRef), GetAssemblyRefProps(currentAssembly, assemblyRef)); - } - - auto files = ASSERT_AND_RETURN(EnumFiles(baselineAssembly), EnumFiles(currentAssembly)); - for (auto file : files) - { - ASSERT_EQUAL(GetFileProps(baselineAssembly, file), GetFileProps(currentAssembly, file)); - } - - auto exports = ASSERT_AND_RETURN(EnumExportedTypes(baselineAssembly), EnumExportedTypes(currentAssembly)); - for (auto exportedType : exports) - { - std::vector name; - uint32_t implementation = mdTokenNil; - ASSERT_EQUAL(GetExportedTypeProps(baselineAssembly, exportedType), GetExportedTypeProps(currentAssembly, exportedType, &name, &implementation)); - ASSERT_EQUAL( - FindExportedTypeByName(baselineAssembly, name.data(), implementation), - FindExportedTypeByName(currentAssembly, name.data(), implementation)); - } - - auto resources = ASSERT_AND_RETURN(EnumManifestResources(baselineAssembly), EnumManifestResources(currentAssembly)); - for (auto resource : resources) - { - std::vector name; - ASSERT_EQUAL(GetManifestResourceProps(baselineAssembly, resource), GetManifestResourceProps(currentAssembly, resource, &name)); - ASSERT_EQUAL(FindManifestResourceByName(baselineAssembly, name.data()), FindManifestResourceByName(currentAssembly, name.data())); - } - - END_TEST(); -} - -EXPORT -TestResult UnitLongRunningAPIs(void const* data, uint32_t dataLen) -{ - BEGIN_TEST(); - - // Load metadata - dncp::com_ptr baselineImport; - ASSERT_EQUAL(S_OK, CreateImport(g_baselineDisp, data, dataLen, &baselineImport)); - dncp::com_ptr currentImport; - ASSERT_EQUAL(S_OK, CreateImport(g_currentDisp, data, dataLen, ¤tImport)); - - static auto VerifyFindMemberRef = [](IMetaDataImport2* import, mdToken memberRef) -> std::vector - { - std::vector values; - - mdToken ptk; - static_char_buffer name{}; - ULONG pchMember; - PCCOR_SIGNATURE ppvSigBlob; - ULONG pcbSigBlob; - HRESULT hr = import->GetMemberRefProps(memberRef, - &ptk, - name.data(), - (ULONG)name.size(), - &pchMember, - &ppvSigBlob, - &pcbSigBlob); - values.push_back(hr); - if (hr == S_OK) - { - // We were able to get the name, now try looking up a memberRef by name and by sig - mdMemberRef lookup = mdTokenNil; - hr = import->FindMemberRef(ptk, name.data(), ppvSigBlob, pcbSigBlob, &lookup); - values.push_back(hr); - values.push_back(lookup); - lookup = mdTokenNil; - hr = import->FindMemberRef(ptk, name.data(), nullptr, 0, &lookup); - values.push_back(hr); - values.push_back(lookup); - lookup = mdTokenNil; - hr = import->FindMemberRef(ptk, nullptr, ppvSigBlob, pcbSigBlob, &lookup); - values.push_back(hr); - values.push_back(lookup); - } - return values; - }; - - size_t stride; - size_t count; - - auto typedefs = ASSERT_AND_RETURN(EnumTypeDefs(baselineImport), EnumTypeDefs(currentImport)); - count = 0; - stride = std::max(typedefs.size() / 128, (size_t)16); - for (auto typdef : typedefs) - { - if (count++ % stride != 0) - continue; - - ASSERT_EQUAL(EnumMemberRefs(baselineImport, typdef), EnumMemberRefs(currentImport, typdef)); - - auto methoddefs = ASSERT_AND_RETURN(EnumMethods(baselineImport, typdef), EnumMethods(currentImport, typdef)); - for (auto methoddef : methoddefs) - { - ASSERT_EQUAL(EnumMethodSemantics(baselineImport, methoddef), EnumMethodSemantics(currentImport, methoddef)); - } - - ASSERT_EQUAL(EnumCustomAttributes(baselineImport, typdef), EnumCustomAttributes(currentImport, typdef)); - } - - auto typespecs = ASSERT_AND_RETURN(EnumTypeSpecs(baselineImport), EnumTypeSpecs(currentImport)); - count = 0; - stride = std::max(typespecs.size() / 128, (size_t)16); - for (auto typespec : typespecs) - { - if (count++ % stride != 0) - continue; - - auto memberrefs = ASSERT_AND_RETURN(EnumMemberRefs(baselineImport, typespec), EnumMemberRefs(currentImport, typespec)); - for (auto memberref : memberrefs) - { - ASSERT_EQUAL(GetMemberRefProps(baselineImport, memberref), GetMemberRefProps(currentImport, memberref)); - ASSERT_EQUAL(VerifyFindMemberRef(baselineImport, memberref), VerifyFindMemberRef(currentImport, memberref)); - } - } - - END_TEST(); -} - -EXPORT -TestResult UnitImportAPIsIndirectionTables(void const* data, uint32_t dataLen, void const** deltaImages, uint32_t* deltaImageLengths, uint32_t numDeltaImages) -{ - BEGIN_TEST(); - - dncp::com_ptr baseImageEmit; - ASSERT_EQUAL(S_OK, CreateEmit(g_deltaImageBuilder, data, dataLen, &baseImageEmit)); - - for (uint32_t i = 0; i < numDeltaImages; ++i) - { - dncp::com_ptr deltaImport; - ASSERT_EQUAL(S_OK, CreateImport(g_deltaImageBuilder, deltaImages[i], deltaImageLengths[i], &deltaImport)); - ASSERT_EQUAL(S_OK, baseImageEmit->ApplyEditAndContinue(deltaImport)); - } - - DWORD compositeImageSize; - ASSERT_EQUAL(S_OK, baseImageEmit->GetSaveSize(CorSaveSize::cssAccurate, &compositeImageSize)); - - std::unique_ptr compositeImage = std::make_unique(compositeImageSize); - ASSERT_EQUAL(S_OK, baseImageEmit->SaveToMemory(compositeImage.get(), compositeImageSize)); - - return UnitImportAPIs(compositeImage.get(), compositeImageSize); - - END_DELEGATING_TEST(); -} diff --git a/test/regnative/unit_corsym.cpp b/test/regnative/unit_corsym.cpp deleted file mode 100644 index c90b61b5..00000000 --- a/test/regnative/unit_corsym.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "regnative.hpp" - -EXPORT -TestResult UnitSymReaderAPIs(void const* data, uint32_t dataLen) -{ - UNREFERENCED_PARAMETER(data); - UNREFERENCED_PARAMETER(dataLen); - BEGIN_TEST() - END_TEST() -} \ No newline at end of file diff --git a/test/regtest/CMakeLists.txt b/test/regtest/CMakeLists.txt index 038037b9..b68209b3 100644 --- a/test/regtest/CMakeLists.txt +++ b/test/regtest/CMakeLists.txt @@ -27,4 +27,4 @@ add_custom_command(TARGET regtest POST_BUILD add_custom_command(TARGET regtest POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${RegressionLocatorDirectory} $) -gtest_discover_tests(regtest DISCOVERY_MODE PRE_TEST) \ No newline at end of file +gtest_discover_tests(regtest DISCOVERY_TIMEOUT 100 DISCOVERY_MODE PRE_TEST) \ No newline at end of file diff --git a/test/regtest/baseline.h b/test/regtest/baseline.h index e357da0a..35a67217 100644 --- a/test/regtest/baseline.h +++ b/test/regtest/baseline.h @@ -13,6 +13,7 @@ namespace TestBaseline { extern dncp::com_ptr Metadata; + extern dncp::com_ptr DeltaMetadataBuilder; extern dncp::com_ptr Symbol; } diff --git a/test/regtest/discovery.cpp b/test/regtest/discovery.cpp index b02da24e..a8976558 100644 --- a/test/regtest/discovery.cpp +++ b/test/regtest/discovery.cpp @@ -131,6 +131,60 @@ std::vector CoreLibs() return scenarios; } +namespace +{ + template + struct OnExit + { + T callback; + ~OnExit() + { + callback(); + } + }; + + template + [[nodiscard]] OnExit on_scope_exit(T callback) + { + return { callback }; + } +} + +#define THROW_IF_FAILED(hr) if (FAILED(hr)) throw std::runtime_error(#hr) + +FileBlob ImageWithDelta() +{ + FileBlob imageWithDelta = { "imageWithDelta", {} }; + uint8_t* baseImage = nullptr; + uint32_t baseImageSize = 0; + uint8_t** deltas = nullptr; + uint32_t* deltaSizes = nullptr; + uint32_t numDeltas = 0; + + GetImageAndDeltas(&baseImage, &baseImageSize, &numDeltas, &deltas, &deltaSizes); + + auto _ = on_scope_exit([&]() { FreeImageAndDeltas(baseImage, numDeltas, deltas, deltaSizes); }); + + dncp::com_ptr baseline; + THROW_IF_FAILED(TestBaseline::DeltaMetadataBuilder->OpenScopeOnMemory(baseImage, baseImageSize, 0, IID_IMetaDataEmit, (IUnknown**)&baseline)); + + for (uint32_t i = 0; i < numDeltas; i++) + { + dncp::com_ptr delta; + THROW_IF_FAILED(TestBaseline::DeltaMetadataBuilder->OpenScopeOnMemory(deltas[i], deltaSizes[i], 0, IID_IMetaDataImport, (IUnknown**)&delta)); + + THROW_IF_FAILED(baseline->ApplyEditAndContinue(delta)); + } + + DWORD compositeImageSize; + THROW_IF_FAILED(baseline->GetSaveSize(CorSaveSize::cssAccurate, &compositeImageSize)); + + imageWithDelta.blob.resize(compositeImageSize); + THROW_IF_FAILED(baseline->SaveToMemory(imageWithDelta.blob.data(), compositeImageSize)); + + return imageWithDelta; +} + std::string GetBaselineDirectory() { return std::filesystem::path(baselinePath).parent_path().string(); diff --git a/test/regtest/fixtures.h b/test/regtest/fixtures.h index fcd716f2..eed29783 100644 --- a/test/regtest/fixtures.h +++ b/test/regtest/fixtures.h @@ -17,8 +17,6 @@ #include - -// TODO: Can't pre-create here as dncp::com_ptr is non-copyable, need to create in the test I guess. Change discovery to be a function that returns a vector of spans of the metadata in PE. struct FileBlob { std::string path; @@ -38,6 +36,8 @@ std::vector CoreLibs(); malloc_span GetRegressionAssemblyMetadata(); +FileBlob ImageWithDelta(); + std::string FindFrameworkInstall(std::string version); std::string GetBaselineDirectory(); diff --git a/test/regtest/main.cpp b/test/regtest/main.cpp index 37d46f8b..724898f9 100644 --- a/test/regtest/main.cpp +++ b/test/regtest/main.cpp @@ -12,6 +12,7 @@ namespace TestBaseline { dncp::com_ptr Metadata = nullptr; + dncp::com_ptr DeltaMetadataBuilder = nullptr; dncp::com_ptr Symbol = nullptr; } @@ -55,6 +56,14 @@ int main(int argc, char** argv) RETURN_IF_FAILED(getDispenser(CLSID_CorMetaDataDispenser, IID_IMetaDataDispenser, (void**)&TestBaseline::Metadata)); + RETURN_IF_FAILED(getDispenser(CLSID_CorMetaDataDispenser, IID_IMetaDataDispenserEx, (void**)&TestBaseline::DeltaMetadataBuilder)); + + VARIANT vt; + V_VT(&vt) = VT_UI4; + V_UI4(&vt) = MDUpdateExtension; + if (HRESULT hr = TestBaseline::DeltaMetadataBuilder->SetOption(MetaDataSetENC, &vt); FAILED(hr)) + return hr; + SetBaselineModulePath(coreClrPath.get()); std::cout << "Loaded metadata baseline module: " << coreClrPath.get() << std::endl; diff --git a/test/regtest/metadata.cpp b/test/regtest/metadata.cpp index 16744f10..68aa5b0a 100644 --- a/test/regtest/metadata.cpp +++ b/test/regtest/metadata.cpp @@ -2098,6 +2098,8 @@ INSTANTIATE_TEST_SUITE_P(MetaDataImportTestCore, MetadataImportTest, testing::Va INSTANTIATE_TEST_SUITE_P(MetaDataImportTestFx4_0, MetadataImportTest, testing::ValuesIn(MetadataInDirectory(FindFrameworkInstall("v4.0.30319"))), PrintFileBlob); INSTANTIATE_TEST_SUITE_P(MetaDataImportTestFx2_0, MetadataImportTest, testing::ValuesIn(MetadataInDirectory(FindFrameworkInstall("v2.0.50727"))), PrintFileBlob); +INSTANTIATE_TEST_SUITE_P(MetaDataImportTest_IndirectionTables, MetadataImportTest, testing::Values(ImageWithDelta()), PrintFileBlob); + class MetaDataLongRunningTest : public RegressionTest { protected: @@ -2191,4 +2193,4 @@ TEST_P(MetaDataLongRunningTest, TypeSpecs) } } -INSTANTIATE_TEST_SUITE_P(MetaDataLongRunningTest_CoreLibs, MetaDataLongRunningTest, testing::ValuesIn(CoreLibs())); +INSTANTIATE_TEST_SUITE_P(MetaDataLongRunningTest_CoreLibs, MetaDataLongRunningTest, testing::ValuesIn(CoreLibs()), PrintFileBlob); From 02d632682e7cf885d8d5754cc98c0c5ed2d6c16d Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 21 Dec 2023 16:46:37 -0800 Subject: [PATCH 17/59] Revert splitting the big tests into smaller tests. This makes the CTest integration (which starts a separate process for each test) way too slow, likely due to CoreCLR startup. --- test/regtest/metadata.cpp | 372 ++++++++++++++------------------------ 1 file changed, 139 insertions(+), 233 deletions(-) diff --git a/test/regtest/metadata.cpp b/test/regtest/metadata.cpp index 68aa5b0a..1c855be5 100644 --- a/test/regtest/metadata.cpp +++ b/test/regtest/metadata.cpp @@ -11,7 +11,9 @@ #include namespace { + IMetaDataDispenser* g_baselineDisp; IMetaDataDispenser* g_deltaImageBuilder; + IMetaDataDispenser* g_currentDisp; HRESULT CreateImport(IMetaDataDispenser* disp, void const* data, uint32_t dataLen, IMetaDataImport2** import) { @@ -19,7 +21,7 @@ namespace return disp->OpenScopeOnMemory( data, dataLen, - CorOpenFlags::ofReadOnly | CorOpenFlags::ofCopyMemory, + CorOpenFlags::ofReadOnly, IID_IMetaDataImport2, reinterpret_cast(import)); } @@ -30,26 +32,11 @@ namespace return disp->OpenScopeOnMemory( data, dataLen, - CorOpenFlags::ofWrite | CorOpenFlags::ofCopyMemory, + CorOpenFlags::ofWrite, IID_IMetaDataEmit2, reinterpret_cast(emit)); } - void GetImports(span metadata, dncp::com_ptr& baseline, dncp::com_ptr& current) - { - ASSERT_HRESULT_SUCCEEDED(CreateImport(TestBaseline::Metadata, metadata, (uint32_t)metadata.size(), &baseline)); - - dncp::com_ptr dispenser; - ASSERT_HRESULT_SUCCEEDED(GetDispenser(IID_IMetaDataDispenser, (void**)&dispenser)); - ASSERT_HRESULT_SUCCEEDED(CreateImport(dispenser, metadata, (uint32_t)metadata.size(), ¤t)); - - } - - void GetImports(FileBlob blob, dncp::com_ptr& baseline, dncp::com_ptr& current) - { - GetImports({ blob.blob.data(), blob.blob.size() }, baseline, current); - } - template using static_enum_buffer = std::array; @@ -1643,8 +1630,14 @@ TEST(FindTest, FindAPIs) malloc_span metadata = GetRegressionAssemblyMetadata(); dncp::com_ptr baselineImport; + ASSERT_HRESULT_SUCCEEDED(CreateImport(TestBaseline::Metadata, metadata, (uint32_t)metadata.size(), &baselineImport)); + // Load metadata dncp::com_ptr currentImport; - ASSERT_NO_FATAL_FAILURE(GetImports(metadata, baselineImport, currentImport)); + + dncp::com_ptr dispenser; + ASSERT_HRESULT_SUCCEEDED(GetDispenser(IID_IMetaDataDispenser, (void**)&dispenser)); + + ASSERT_HRESULT_SUCCEEDED(CreateImport(dispenser, metadata, (uint32_t)metadata.size(), ¤tImport)); static auto FindTokenByName = [](IMetaDataImport2* import, LPCWSTR name, mdToken enclosing = mdTokenNil) -> mdToken { @@ -1776,128 +1769,89 @@ class MetadataImportTest : public RegressionTest { }; -TEST_P(MetadataImportTest, BasicApis) +TEST_P(MetadataImportTest, ImportAPIs) { - dncp::com_ptr baselineImport; - dncp::com_ptr currentImport; - ASSERT_NO_FATAL_FAILURE(GetImports(GetParam(), baselineImport, currentImport)); + auto param = GetParam(); + void const* data = param.blob.data(); + uint32_t dataLen = (uint32_t)param.blob.size(); - EXPECT_THAT(ResetEnum(currentImport), testing::ElementsAreArray(ResetEnum(baselineImport))); - EXPECT_THAT(GetScopeProps(currentImport), testing::ElementsAreArray(GetScopeProps(baselineImport))); - EXPECT_THAT(GetVersionString(currentImport), testing::ElementsAreArray(GetVersionString(baselineImport))); -} - -TEST_P(MetadataImportTest, Signature) -{ + // Load metadata dncp::com_ptr baselineImport; + ASSERT_HRESULT_SUCCEEDED(CreateImport(TestBaseline::Metadata, data, dataLen, &baselineImport)); + + dncp::com_ptr dispenser; + ASSERT_HRESULT_SUCCEEDED(GetDispenser(IID_IMetaDataDispenser, (void**)&dispenser)); dncp::com_ptr currentImport; - ASSERT_NO_FATAL_FAILURE(GetImports(GetParam(), baselineImport, currentImport)); + ASSERT_HRESULT_SUCCEEDED(CreateImport(dispenser, data, dataLen, ¤tImport)); + + // Verify APIs + ASSERT_THAT(ResetEnum(currentImport), testing::ElementsAreArray(ResetEnum(baselineImport))); + ASSERT_THAT(GetScopeProps(currentImport), testing::ElementsAreArray(GetScopeProps(baselineImport))); + ASSERT_THAT(GetVersionString(currentImport), testing::ElementsAreArray(GetVersionString(baselineImport))); TokenList sigs; ASSERT_EQUAL_AND_SET(sigs, EnumSignatures(baselineImport), EnumSignatures(currentImport)); for (auto sig : sigs) { - EXPECT_THAT(GetSigFromToken(currentImport, sig), testing::ElementsAreArray(GetSigFromToken(baselineImport, sig))); + ASSERT_THAT(GetSigFromToken(currentImport, sig), testing::ElementsAreArray(GetSigFromToken(baselineImport, sig))); } -} - -TEST_P(MetadataImportTest, UserStrings) -{ - dncp::com_ptr baselineImport; - dncp::com_ptr currentImport; - ASSERT_NO_FATAL_FAILURE(GetImports(GetParam(), baselineImport, currentImport)); TokenList userStrings; ASSERT_EQUAL_AND_SET(userStrings, EnumUserStrings(baselineImport), EnumUserStrings(currentImport)); for (auto us : userStrings) { - EXPECT_THAT(GetUserString(currentImport, us), testing::ElementsAreArray(GetUserString(baselineImport, us))); + ASSERT_THAT(GetUserString(currentImport, us), testing::ElementsAreArray(GetUserString(baselineImport, us))); } -} - -TEST_P(MetadataImportTest, CustomAttribute) -{ - dncp::com_ptr baselineImport; - dncp::com_ptr currentImport; - ASSERT_NO_FATAL_FAILURE(GetImports(GetParam(), baselineImport, currentImport)); TokenList custAttrs; ASSERT_EQUAL_AND_SET(custAttrs, EnumCustomAttributes(baselineImport), EnumCustomAttributes(currentImport)); for (auto ca : custAttrs) { - EXPECT_THAT(GetCustomAttributeProps(currentImport, ca), testing::ElementsAreArray(GetCustomAttributeProps(baselineImport, ca))); + ASSERT_THAT(GetCustomAttributeProps(currentImport, ca), testing::ElementsAreArray(GetCustomAttributeProps(baselineImport, ca))); } -} - -TEST_P(MetadataImportTest, ModuleRef) -{ - dncp::com_ptr baselineImport; - dncp::com_ptr currentImport; - ASSERT_NO_FATAL_FAILURE(GetImports(GetParam(), baselineImport, currentImport)); TokenList modulerefs; ASSERT_EQUAL_AND_SET(modulerefs, EnumModuleRefs(baselineImport), EnumModuleRefs(currentImport)); for (auto moduleref : modulerefs) { - EXPECT_THAT(GetModuleRefProps(currentImport, moduleref), testing::ElementsAreArray(GetModuleRefProps(baselineImport, moduleref))); - EXPECT_THAT(GetNameFromToken(currentImport, moduleref), testing::ElementsAreArray(GetNameFromToken(baselineImport, moduleref))); + ASSERT_THAT(GetModuleRefProps(currentImport, moduleref), testing::ElementsAreArray(GetModuleRefProps(baselineImport, moduleref))); + ASSERT_THAT(GetNameFromToken(currentImport, moduleref), testing::ElementsAreArray(GetNameFromToken(baselineImport, moduleref))); } -} - -TEST_P(MetadataImportTest, TypeRef) -{ - dncp::com_ptr baselineImport; - dncp::com_ptr currentImport; - ASSERT_NO_FATAL_FAILURE(GetImports(GetParam(), baselineImport, currentImport)); ASSERT_THAT(FindTypeRef(currentImport), testing::ElementsAreArray(FindTypeRef(baselineImport))); TokenList typerefs; ASSERT_EQUAL_AND_SET(typerefs, EnumTypeRefs(baselineImport), EnumTypeRefs(currentImport)); for (auto typeref : typerefs) { - EXPECT_THAT(GetTypeRefProps(currentImport, typeref), testing::ElementsAreArray(GetTypeRefProps(baselineImport, typeref))); - EXPECT_THAT(GetCustomAttribute_CompilerGenerated(currentImport, typeref), testing::ElementsAreArray(GetCustomAttribute_CompilerGenerated(baselineImport, typeref))); - EXPECT_THAT(GetNameFromToken(currentImport, typeref), testing::ElementsAreArray(GetNameFromToken(baselineImport, typeref))); + ASSERT_THAT(GetTypeRefProps(currentImport, typeref), testing::ElementsAreArray(GetTypeRefProps(baselineImport, typeref))); + ASSERT_THAT(GetCustomAttribute_CompilerGenerated(currentImport, typeref), testing::ElementsAreArray(GetCustomAttribute_CompilerGenerated(baselineImport, typeref))); + ASSERT_THAT(GetNameFromToken(currentImport, typeref), testing::ElementsAreArray(GetNameFromToken(baselineImport, typeref))); } -} - -TEST_P(MetadataImportTest, TypeSpec) -{ - dncp::com_ptr baselineImport; - dncp::com_ptr currentImport; - ASSERT_NO_FATAL_FAILURE(GetImports(GetParam(), baselineImport, currentImport)); TokenList typespecs; ASSERT_EQUAL_AND_SET(typespecs, EnumTypeSpecs(baselineImport), EnumTypeSpecs(currentImport)); for (auto typespec : typespecs) { - EXPECT_THAT(GetTypeSpecFromToken(currentImport, typespec), testing::ElementsAreArray(GetTypeSpecFromToken(baselineImport, typespec))); - EXPECT_THAT(GetCustomAttribute_CompilerGenerated(currentImport, typespec), testing::ElementsAreArray(GetCustomAttribute_CompilerGenerated(baselineImport, typespec))); + ASSERT_THAT(GetTypeSpecFromToken(currentImport, typespec), testing::ElementsAreArray(GetTypeSpecFromToken(baselineImport, typespec))); + ASSERT_THAT(GetCustomAttribute_CompilerGenerated(currentImport, typespec), testing::ElementsAreArray(GetCustomAttribute_CompilerGenerated(baselineImport, typespec))); } -} - -TEST_P(MetadataImportTest, TypeDefAndMembers) -{ - dncp::com_ptr baselineImport; - dncp::com_ptr currentImport; - ASSERT_NO_FATAL_FAILURE(GetImports(GetParam(), baselineImport, currentImport)); TokenList typedefs; ASSERT_EQUAL_AND_SET(typedefs, EnumTypeDefs(baselineImport), EnumTypeDefs(currentImport)); for (auto typdef : typedefs) { - EXPECT_THAT(GetTypeDefProps(currentImport, typdef), testing::ElementsAreArray(GetTypeDefProps(baselineImport, typdef))); - EXPECT_THAT(GetNameFromToken(currentImport, typdef), testing::ElementsAreArray(GetNameFromToken(baselineImport, typdef))); - EXPECT_THAT(IsGlobal(currentImport, typdef), testing::Eq(IsGlobal(baselineImport, typdef))); - EXPECT_THAT(EnumInterfaceImpls(currentImport, typdef), testing::ElementsAreArray(EnumInterfaceImpls(baselineImport, typdef))); - EXPECT_THAT(EnumPermissionSetsAndGetProps(currentImport, typdef), testing::ElementsAreArray(EnumPermissionSetsAndGetProps(baselineImport, typdef))); - EXPECT_THAT(EnumMembers(currentImport, typdef), testing::ElementsAreArray(EnumMembers(baselineImport, typdef))); - EXPECT_THAT(EnumMembersWithName(currentImport, typdef), testing::ElementsAreArray(EnumMembersWithName(baselineImport, typdef))); - EXPECT_THAT(EnumMethodsWithName(currentImport, typdef), testing::ElementsAreArray(EnumMethodsWithName(baselineImport, typdef))); - EXPECT_THAT(EnumMethodImpls(currentImport, typdef), testing::ElementsAreArray(EnumMethodImpls(baselineImport, typdef))); - EXPECT_THAT(GetNestedClassProps(currentImport, typdef), testing::ElementsAreArray(GetNestedClassProps(baselineImport, typdef))); - EXPECT_THAT(GetClassLayout(currentImport, typdef), testing::ElementsAreArray(GetClassLayout(baselineImport, typdef))); - EXPECT_THAT(GetCustomAttribute_CompilerGenerated(currentImport, typdef), testing::ElementsAreArray(GetCustomAttribute_CompilerGenerated(baselineImport, typdef))); + ASSERT_THAT(GetTypeDefProps(currentImport, typdef), testing::ElementsAreArray(GetTypeDefProps(baselineImport, typdef))); + ASSERT_THAT(GetNameFromToken(currentImport, typdef), testing::ElementsAreArray(GetNameFromToken(baselineImport, typdef))); + ASSERT_THAT(IsGlobal(currentImport, typdef), testing::Eq(IsGlobal(baselineImport, typdef))); + ASSERT_THAT(EnumInterfaceImpls(currentImport, typdef), testing::ElementsAreArray(EnumInterfaceImpls(baselineImport, typdef))); + ASSERT_THAT(EnumPermissionSetsAndGetProps(currentImport, typdef), testing::ElementsAreArray(EnumPermissionSetsAndGetProps(baselineImport, typdef))); + ASSERT_THAT(EnumMembers(currentImport, typdef), testing::ElementsAreArray(EnumMembers(baselineImport, typdef))); + ASSERT_THAT(EnumMembersWithName(currentImport, typdef), testing::ElementsAreArray(EnumMembersWithName(baselineImport, typdef))); + ASSERT_THAT(EnumMethodsWithName(currentImport, typdef), testing::ElementsAreArray(EnumMethodsWithName(baselineImport, typdef))); + ASSERT_THAT(EnumMethodImpls(currentImport, typdef), testing::ElementsAreArray(EnumMethodImpls(baselineImport, typdef))); + ASSERT_THAT(GetNestedClassProps(currentImport, typdef), testing::ElementsAreArray(GetNestedClassProps(baselineImport, typdef))); + ASSERT_THAT(GetClassLayout(currentImport, typdef), testing::ElementsAreArray(GetClassLayout(baselineImport, typdef))); + ASSERT_THAT(GetCustomAttribute_CompilerGenerated(currentImport, typdef), testing::ElementsAreArray(GetCustomAttribute_CompilerGenerated(baselineImport, typdef))); TokenList methoddefs; ASSERT_EQUAL_AND_SET(methoddefs, EnumMethods(baselineImport, typdef), EnumMethods(currentImport, typdef)); @@ -1905,32 +1859,32 @@ TEST_P(MetadataImportTest, TypeDefAndMembers) { void const* sig = nullptr; ULONG sigLen = 0; - EXPECT_THAT(GetMethodProps(currentImport, methoddef, &sig, &sigLen), testing::ElementsAreArray(GetMethodProps(baselineImport, methoddef))); - EXPECT_THAT(GetNativeCallConvFromSig(currentImport, sig, sigLen), testing::ElementsAreArray(GetNativeCallConvFromSig(baselineImport, sig, sigLen))); - EXPECT_THAT(GetNameFromToken(currentImport, methoddef), testing::ElementsAreArray(GetNameFromToken(baselineImport, methoddef))); - EXPECT_THAT(IsGlobal(currentImport, methoddef), testing::Eq(IsGlobal(baselineImport, methoddef))); - EXPECT_THAT(GetCustomAttribute_CompilerGenerated(currentImport, methoddef), testing::ElementsAreArray(GetCustomAttribute_CompilerGenerated(baselineImport, methoddef))); + ASSERT_THAT(GetMethodProps(currentImport, methoddef, &sig, &sigLen), testing::ElementsAreArray(GetMethodProps(baselineImport, methoddef))); + ASSERT_THAT(GetNativeCallConvFromSig(currentImport, sig, sigLen), testing::ElementsAreArray(GetNativeCallConvFromSig(baselineImport, sig, sigLen))); + ASSERT_THAT(GetNameFromToken(currentImport, methoddef), testing::ElementsAreArray(GetNameFromToken(baselineImport, methoddef))); + ASSERT_THAT(IsGlobal(currentImport, methoddef), testing::Eq(IsGlobal(baselineImport, methoddef))); + ASSERT_THAT(GetCustomAttribute_CompilerGenerated(currentImport, methoddef), testing::ElementsAreArray(GetCustomAttribute_CompilerGenerated(baselineImport, methoddef))); TokenList paramdefs; ASSERT_EQUAL_AND_SET(paramdefs, EnumParams(baselineImport, methoddef), EnumParams(currentImport, methoddef)); for (auto paramdef : paramdefs) { - EXPECT_THAT(GetParamProps(currentImport, paramdef), testing::ElementsAreArray(GetParamProps(baselineImport, paramdef))); - EXPECT_THAT(GetFieldMarshal(currentImport, paramdef), testing::ElementsAreArray(GetFieldMarshal(baselineImport, paramdef))); - EXPECT_THAT(GetCustomAttribute_Nullable(currentImport, paramdef), testing::ElementsAreArray(GetCustomAttribute_Nullable(baselineImport, paramdef))); - EXPECT_THAT(GetNameFromToken(currentImport, paramdef), testing::ElementsAreArray(GetNameFromToken(baselineImport, paramdef))); + ASSERT_THAT(GetParamProps(currentImport, paramdef), testing::ElementsAreArray(GetParamProps(baselineImport, paramdef))); + ASSERT_THAT(GetFieldMarshal(currentImport, paramdef), testing::ElementsAreArray(GetFieldMarshal(baselineImport, paramdef))); + ASSERT_THAT(GetCustomAttribute_Nullable(currentImport, paramdef), testing::ElementsAreArray(GetCustomAttribute_Nullable(baselineImport, paramdef))); + ASSERT_THAT(GetNameFromToken(currentImport, paramdef), testing::ElementsAreArray(GetNameFromToken(baselineImport, paramdef))); } - EXPECT_THAT(GetParamForMethodIndex(currentImport, methoddef), testing::ElementsAreArray(GetParamForMethodIndex(baselineImport, methoddef))); - EXPECT_THAT(EnumPermissionSetsAndGetProps(currentImport, methoddef), testing::ElementsAreArray(EnumPermissionSetsAndGetProps(baselineImport, methoddef))); - EXPECT_THAT(GetPinvokeMap(currentImport, methoddef), testing::ElementsAreArray(GetPinvokeMap(baselineImport, methoddef))); - EXPECT_THAT(GetRVA(currentImport, methoddef), testing::ElementsAreArray(GetRVA(baselineImport, methoddef))); + ASSERT_THAT(GetParamForMethodIndex(currentImport, methoddef), testing::ElementsAreArray(GetParamForMethodIndex(baselineImport, methoddef))); + ASSERT_THAT(EnumPermissionSetsAndGetProps(currentImport, methoddef), testing::ElementsAreArray(EnumPermissionSetsAndGetProps(baselineImport, methoddef))); + ASSERT_THAT(GetPinvokeMap(currentImport, methoddef), testing::ElementsAreArray(GetPinvokeMap(baselineImport, methoddef))); + ASSERT_THAT(GetRVA(currentImport, methoddef), testing::ElementsAreArray(GetRVA(baselineImport, methoddef))); TokenList methodspecs; ASSERT_EQUAL_AND_SET(methodspecs, EnumMethodSpecs(baselineImport, methoddef), EnumMethodSpecs(currentImport, methoddef)); for (auto methodspec : methodspecs) { - EXPECT_THAT(GetMethodSpecProps(currentImport, methodspec), testing::ElementsAreArray(GetMethodSpecProps(baselineImport, methodspec))); + ASSERT_THAT(GetMethodSpecProps(currentImport, methodspec), testing::ElementsAreArray(GetMethodSpecProps(baselineImport, methodspec))); } } @@ -1942,11 +1896,11 @@ TEST_P(MetadataImportTest, TypeDefAndMembers) ASSERT_THAT(GetEventProps(currentImport, eventdef, &mds), testing::ElementsAreArray(GetEventProps(baselineImport, eventdef))); for (auto md : mds) { - EXPECT_THAT(GetMethodSemantics(currentImport, eventdef, md), testing::ElementsAreArray(GetMethodSemantics(baselineImport, eventdef, md))); + ASSERT_THAT(GetMethodSemantics(currentImport, eventdef, md), testing::ElementsAreArray(GetMethodSemantics(baselineImport, eventdef, md))); } - EXPECT_THAT(GetNameFromToken(currentImport, eventdef), testing::ElementsAreArray(GetNameFromToken(baselineImport, eventdef))); - EXPECT_THAT(IsGlobal(currentImport, eventdef), testing::Eq(IsGlobal(baselineImport, eventdef))); + ASSERT_THAT(GetNameFromToken(currentImport, eventdef), testing::ElementsAreArray(GetNameFromToken(baselineImport, eventdef))); + ASSERT_THAT(IsGlobal(currentImport, eventdef), testing::Eq(IsGlobal(baselineImport, eventdef))); } TokenList properties; @@ -1957,11 +1911,11 @@ TEST_P(MetadataImportTest, TypeDefAndMembers) ASSERT_THAT(GetPropertyProps(currentImport, props, &mds), testing::ElementsAreArray(GetPropertyProps(baselineImport, props))); for (auto md : mds) { - EXPECT_THAT(GetMethodSemantics(currentImport, props, md), testing::ElementsAreArray(GetMethodSemantics(baselineImport, props, md))); + ASSERT_THAT(GetMethodSemantics(currentImport, props, md), testing::ElementsAreArray(GetMethodSemantics(baselineImport, props, md))); } - EXPECT_THAT(GetNameFromToken(currentImport, props), testing::ElementsAreArray(GetNameFromToken(baselineImport, props))); - EXPECT_THAT(IsGlobal(currentImport, props), testing::Eq(IsGlobal(baselineImport, props))); + ASSERT_THAT(GetNameFromToken(currentImport, props), testing::ElementsAreArray(GetNameFromToken(baselineImport, props))); + ASSERT_THAT(IsGlobal(currentImport, props), testing::Eq(IsGlobal(baselineImport, props))); } ASSERT_THAT(EnumFieldsWithName(baselineImport, typdef), EnumFieldsWithName(currentImport, typdef)); @@ -1969,35 +1923,28 @@ TEST_P(MetadataImportTest, TypeDefAndMembers) ASSERT_EQUAL_AND_SET(fielddefs, EnumFields(baselineImport, typdef), EnumFields(currentImport, typdef)); for (auto fielddef : fielddefs) { - EXPECT_THAT(GetFieldProps(currentImport, fielddef), testing::ElementsAreArray(GetFieldProps(baselineImport, fielddef))); - EXPECT_THAT(GetNameFromToken(currentImport, fielddef), testing::ElementsAreArray(GetNameFromToken(baselineImport, fielddef))); - EXPECT_THAT(IsGlobal(currentImport, fielddef), testing::Eq(IsGlobal(baselineImport, fielddef))); - EXPECT_THAT(GetPinvokeMap(currentImport, fielddef), testing::ElementsAreArray(GetPinvokeMap(baselineImport, fielddef))); - EXPECT_THAT(GetRVA(currentImport, fielddef), testing::ElementsAreArray(GetRVA(baselineImport, fielddef))); - EXPECT_THAT(GetFieldMarshal(currentImport, fielddef), testing::ElementsAreArray(GetFieldMarshal(baselineImport, fielddef))); - EXPECT_THAT(GetCustomAttribute_Nullable(currentImport, fielddef), testing::ElementsAreArray(GetCustomAttribute_Nullable(baselineImport, fielddef))); + ASSERT_THAT(GetFieldProps(currentImport, fielddef), testing::ElementsAreArray(GetFieldProps(baselineImport, fielddef))); + ASSERT_THAT(GetNameFromToken(currentImport, fielddef), testing::ElementsAreArray(GetNameFromToken(baselineImport, fielddef))); + ASSERT_THAT(IsGlobal(currentImport, fielddef), testing::Eq(IsGlobal(baselineImport, fielddef))); + ASSERT_THAT(GetPinvokeMap(currentImport, fielddef), testing::ElementsAreArray(GetPinvokeMap(baselineImport, fielddef))); + ASSERT_THAT(GetRVA(currentImport, fielddef), testing::ElementsAreArray(GetRVA(baselineImport, fielddef))); + ASSERT_THAT(GetFieldMarshal(currentImport, fielddef), testing::ElementsAreArray(GetFieldMarshal(baselineImport, fielddef))); + ASSERT_THAT(GetCustomAttribute_Nullable(currentImport, fielddef), testing::ElementsAreArray(GetCustomAttribute_Nullable(baselineImport, fielddef))); } TokenList genparams; ASSERT_EQUAL_AND_SET(genparams, EnumGenericParams(baselineImport, typdef), EnumGenericParams(currentImport, typdef)); for (auto genparam : genparams) { - EXPECT_THAT(GetGenericParamProps(currentImport, genparam), testing::ElementsAreArray(GetGenericParamProps(baselineImport, genparam))); + ASSERT_THAT(GetGenericParamProps(currentImport, genparam), testing::ElementsAreArray(GetGenericParamProps(baselineImport, genparam))); TokenList genparamconsts; ASSERT_EQUAL_AND_SET(genparamconsts, EnumGenericParamConstraints(baselineImport, genparam), EnumGenericParamConstraints(currentImport, genparam)); for (auto genparamconst : genparamconsts) { - EXPECT_THAT(GetGenericParamConstraintProps(currentImport, genparamconst), testing::ElementsAreArray(GetGenericParamConstraintProps(baselineImport, genparamconst))); + ASSERT_THAT(GetGenericParamConstraintProps(currentImport, genparamconst), testing::ElementsAreArray(GetGenericParamConstraintProps(baselineImport, genparamconst))); } } } -} - -TEST_P(MetadataImportTest, Assembly) -{ - dncp::com_ptr baselineImport; - dncp::com_ptr currentImport; - ASSERT_NO_FATAL_FAILURE(GetImports(GetParam(), baselineImport, currentImport)); dncp::com_ptr baselineAssembly; ASSERT_THAT(S_OK, baselineImport->QueryInterface(IID_IMetaDataAssemblyImport, (void**)&baselineAssembly)); @@ -2008,58 +1955,22 @@ TEST_P(MetadataImportTest, Assembly) ASSERT_EQUAL_AND_SET(assemblyTokens, GetAssemblyFromScope(baselineAssembly), GetAssemblyFromScope(currentAssembly)); for (auto assembly : assemblyTokens) { - EXPECT_THAT(GetAssemblyProps(currentAssembly, assembly), testing::ElementsAreArray(GetAssemblyProps(baselineAssembly, assembly))); + ASSERT_THAT(GetAssemblyProps(currentAssembly, assembly), testing::ElementsAreArray(GetAssemblyProps(baselineAssembly, assembly))); } -} - -TEST_P(MetadataImportTest, AssemblyRef) -{ - dncp::com_ptr baselineImport; - dncp::com_ptr currentImport; - ASSERT_NO_FATAL_FAILURE(GetImports(GetParam(), baselineImport, currentImport)); - - dncp::com_ptr baselineAssembly; - ASSERT_THAT(S_OK, baselineImport->QueryInterface(IID_IMetaDataAssemblyImport, (void**)&baselineAssembly)); - dncp::com_ptr currentAssembly; - ASSERT_THAT(S_OK, currentImport->QueryInterface(IID_IMetaDataAssemblyImport, (void**)¤tAssembly)); TokenList assemblyRefs; ASSERT_EQUAL_AND_SET(assemblyRefs, EnumAssemblyRefs(baselineAssembly), EnumAssemblyRefs(currentAssembly)); for (auto assemblyRef : assemblyRefs) { - EXPECT_THAT(GetAssemblyRefProps(currentAssembly, assemblyRef), testing::ElementsAreArray(GetAssemblyRefProps(baselineAssembly, assemblyRef))); + ASSERT_THAT(GetAssemblyRefProps(currentAssembly, assemblyRef), testing::ElementsAreArray(GetAssemblyRefProps(baselineAssembly, assemblyRef))); } -} - -TEST_P(MetadataImportTest, File) -{ - dncp::com_ptr baselineImport; - dncp::com_ptr currentImport; - ASSERT_NO_FATAL_FAILURE(GetImports(GetParam(), baselineImport, currentImport)); - - dncp::com_ptr baselineAssembly; - ASSERT_THAT(S_OK, baselineImport->QueryInterface(IID_IMetaDataAssemblyImport, (void**)&baselineAssembly)); - dncp::com_ptr currentAssembly; - ASSERT_THAT(S_OK, currentImport->QueryInterface(IID_IMetaDataAssemblyImport, (void**)¤tAssembly)); TokenList files; ASSERT_EQUAL_AND_SET(files, EnumFiles(baselineAssembly), EnumFiles(currentAssembly)); for (auto file : files) { - EXPECT_THAT(GetFileProps(currentAssembly, file), testing::ElementsAreArray(GetFileProps(baselineAssembly, file))); + ASSERT_THAT(GetFileProps(currentAssembly, file), testing::ElementsAreArray(GetFileProps(baselineAssembly, file))); } -} - -TEST_P(MetadataImportTest, ExportedType) -{ - dncp::com_ptr baselineImport; - dncp::com_ptr currentImport; - ASSERT_NO_FATAL_FAILURE(GetImports(GetParam(), baselineImport, currentImport)); - - dncp::com_ptr baselineAssembly; - ASSERT_THAT(S_OK, baselineImport->QueryInterface(IID_IMetaDataAssemblyImport, (void**)&baselineAssembly)); - dncp::com_ptr currentAssembly; - ASSERT_THAT(S_OK, currentImport->QueryInterface(IID_IMetaDataAssemblyImport, (void**)¤tAssembly)); TokenList exports; ASSERT_EQUAL_AND_SET(exports, EnumExportedTypes(baselineAssembly), EnumExportedTypes(currentAssembly)); @@ -2068,30 +1979,20 @@ TEST_P(MetadataImportTest, ExportedType) std::vector name; uint32_t implementation = mdTokenNil; ASSERT_THAT(GetExportedTypeProps(currentAssembly, exportedType, &name, &implementation), testing::ElementsAreArray(GetExportedTypeProps(baselineAssembly, exportedType))); - EXPECT_THAT( + ASSERT_THAT( FindExportedTypeByName(currentAssembly, name.data(), implementation), testing::ElementsAreArray(FindExportedTypeByName(baselineAssembly, name.data(), implementation))); } -} - -TEST_P(MetadataImportTest, ManifestResource) -{ - dncp::com_ptr baselineImport; - dncp::com_ptr currentImport; - ASSERT_NO_FATAL_FAILURE(GetImports(GetParam(), baselineImport, currentImport)); - - dncp::com_ptr baselineAssembly; - ASSERT_THAT(S_OK, baselineImport->QueryInterface(IID_IMetaDataAssemblyImport, (void**)&baselineAssembly)); - dncp::com_ptr currentAssembly; - ASSERT_THAT(S_OK, currentImport->QueryInterface(IID_IMetaDataAssemblyImport, (void**)¤tAssembly)); TokenList resources; ASSERT_EQUAL_AND_SET(resources, EnumManifestResources(baselineAssembly), EnumManifestResources(currentAssembly)); for (auto resource : resources) { - EXPECT_THAT(GetManifestResourceProps(currentAssembly, resource), testing::ElementsAreArray(GetManifestResourceProps(baselineAssembly, resource))); + std::vector name; + ASSERT_THAT(GetManifestResourceProps(currentAssembly, resource, &name), testing::ElementsAreArray(GetManifestResourceProps(baselineAssembly, resource))); + ASSERT_THAT(FindManifestResourceByName(currentAssembly, name.data()), testing::ElementsAreArray(FindManifestResourceByName(baselineAssembly, name.data()))); } -} +} INSTANTIATE_TEST_SUITE_P(MetaDataImportTestCore, MetadataImportTest, testing::ValuesIn(MetadataInDirectory(GetBaselineDirectory())), PrintFileBlob); @@ -2102,54 +2003,66 @@ INSTANTIATE_TEST_SUITE_P(MetaDataImportTest_IndirectionTables, MetadataImportTes class MetaDataLongRunningTest : public RegressionTest { -protected: - TokenList VerifyFindMemberRef(IMetaDataImport2* import, mdToken memberRef) - { - std::vector values; - - mdToken ptk; - static_char_buffer name{}; - ULONG pchMember; - PCCOR_SIGNATURE ppvSigBlob; - ULONG pcbSigBlob; - HRESULT hr = import->GetMemberRefProps(memberRef, - & ptk, - name.data(), - (ULONG)name.size(), - & pchMember, - & ppvSigBlob, - & pcbSigBlob); - values.push_back(hr); - if (hr == S_OK) - { - // We were able to get the name, now try looking up a memberRef by name and by sig - mdMemberRef lookup = mdTokenNil; - hr = import->FindMemberRef(ptk, name.data(), ppvSigBlob, pcbSigBlob, & lookup); - values.push_back(hr); - values.push_back(lookup); - lookup = mdTokenNil; - hr = import->FindMemberRef(ptk, name.data(), nullptr, 0, & lookup); - values.push_back(hr); - values.push_back(lookup); - lookup = mdTokenNil; - hr = import->FindMemberRef(ptk, nullptr, ppvSigBlob, pcbSigBlob, & lookup); - values.push_back(hr); - values.push_back(lookup); - } - return values; - } }; -TEST_P(MetaDataLongRunningTest, TypeDefs) +TEST_P(MetaDataLongRunningTest, ImportAPIs) { + auto param = GetParam(); + void const* data = param.blob.data(); + uint32_t dataLen = (uint32_t)param.blob.size(); + + // Load metadata dncp::com_ptr baselineImport; + ASSERT_HRESULT_SUCCEEDED(CreateImport(TestBaseline::Metadata, data, dataLen, &baselineImport)); + + dncp::com_ptr dispenser; + ASSERT_HRESULT_SUCCEEDED(GetDispenser(IID_IMetaDataDispenser, (void**)&dispenser)); dncp::com_ptr currentImport; - ASSERT_NO_FATAL_FAILURE(GetImports(GetParam(), baselineImport, currentImport)); + ASSERT_HRESULT_SUCCEEDED(CreateImport(dispenser, data, dataLen, ¤tImport)); + + static auto VerifyFindMemberRef = [](IMetaDataImport2 * import, mdToken memberRef) -> std::vector + { + std::vector values; + + mdToken ptk; + static_char_buffer name{}; + ULONG pchMember; + PCCOR_SIGNATURE ppvSigBlob; + ULONG pcbSigBlob; + HRESULT hr = import->GetMemberRefProps(memberRef, + & ptk, + name.data(), + (ULONG)name.size(), + & pchMember, + & ppvSigBlob, + & pcbSigBlob); + values.push_back(hr); + if (hr == S_OK) + { + // We were able to get the name, now try looking up a memberRef by name and by sig + mdMemberRef lookup = mdTokenNil; + hr = import->FindMemberRef(ptk, name.data(), ppvSigBlob, pcbSigBlob, & lookup); + values.push_back(hr); + values.push_back(lookup); + lookup = mdTokenNil; + hr = import->FindMemberRef(ptk, name.data(), nullptr, 0, & lookup); + values.push_back(hr); + values.push_back(lookup); + lookup = mdTokenNil; + hr = import->FindMemberRef(ptk, nullptr, ppvSigBlob, pcbSigBlob, & lookup); + values.push_back(hr); + values.push_back(lookup); + } + return values; + }; + + size_t stride; + size_t count; TokenList typedefs; ASSERT_EQUAL_AND_SET(typedefs, EnumTypeDefs(baselineImport), EnumTypeDefs(currentImport)); - size_t count = 0; - size_t stride = std::max(typedefs.size() / 128, (size_t)16); + count = 0; + stride = std::max(typedefs.size() / 128, (size_t)16); for (auto typdef : typedefs) { if (count++ % stride != 0) @@ -2166,18 +2079,11 @@ TEST_P(MetaDataLongRunningTest, TypeDefs) EXPECT_THAT(EnumCustomAttributes(currentImport, typdef), testing::ElementsAreArray(EnumCustomAttributes(baselineImport, typdef))); } -} - -TEST_P(MetaDataLongRunningTest, TypeSpecs) -{ - dncp::com_ptr baselineImport; - dncp::com_ptr currentImport; - ASSERT_NO_FATAL_FAILURE(GetImports(GetParam(), baselineImport, currentImport)); TokenList typespecs; ASSERT_EQUAL_AND_SET(typespecs, EnumTypeSpecs(baselineImport), EnumTypeSpecs(currentImport)); - size_t count = 0; - size_t stride = std::max(typespecs.size() / 128, (size_t)16); + count = 0; + stride = std::max(typespecs.size() / 128, (size_t)16); for (auto typespec : typespecs) { if (count++ % stride != 0) From 6d91926bcee3f1f162b84181eb26a34e97fc2328 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 21 Dec 2023 16:46:54 -0800 Subject: [PATCH 18/59] Remove unused globals --- test/regtest/metadata.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/regtest/metadata.cpp b/test/regtest/metadata.cpp index 1c855be5..86ded99f 100644 --- a/test/regtest/metadata.cpp +++ b/test/regtest/metadata.cpp @@ -11,10 +11,6 @@ #include namespace { - IMetaDataDispenser* g_baselineDisp; - IMetaDataDispenser* g_deltaImageBuilder; - IMetaDataDispenser* g_currentDisp; - HRESULT CreateImport(IMetaDataDispenser* disp, void const* data, uint32_t dataLen, IMetaDataImport2** import) { assert(disp != nullptr && data != nullptr && dataLen > 0 && import != nullptr); From edfa01072d26c21ae5c0080c21c6afa13dba7b65 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 21 Dec 2023 17:10:45 -0800 Subject: [PATCH 19/59] Start converting regnative to an native exe-based perf benchmark --- test/CMakeLists.txt | 4 ++-- test/regnative/CMakeLists.txt | 21 ------------------ test/regperf/CMakeLists.txt | 33 ++++++++++++++++++++++++++++ test/{regnative => regperf}/perf.cpp | 20 +++++++++++++++++ 4 files changed, 55 insertions(+), 23 deletions(-) delete mode 100644 test/regnative/CMakeLists.txt create mode 100644 test/regperf/CMakeLists.txt rename test/{regnative => regperf}/perf.cpp (90%) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1f683f33..7e8a1151 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,7 +1,8 @@ # Configure the compiler include(../configure.cmake) +include(ImportManagedComponents.cmake) -add_subdirectory(regnative/) +add_subdirectory(regperf) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -19,7 +20,6 @@ FetchContent_Declare( set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) FetchContent_MakeAvailable(googletest) -include(ImportManagedComponents.cmake) include(GoogleTest) diff --git a/test/regnative/CMakeLists.txt b/test/regnative/CMakeLists.txt deleted file mode 100644 index 4a96fc7e..00000000 --- a/test/regnative/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -set(SOURCES - ./perf.cpp -) - -add_library(regnative - SHARED - ${SOURCES} -) - -target_link_libraries(regnative - dnmd::interfaces - dncp::dncp) - -if(NOT MSVC) - target_link_libraries(regnative dncp::winhdrs) -endif() - -install(TARGETS regnative - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib - RUNTIME DESTINATION bin) \ No newline at end of file diff --git a/test/regperf/CMakeLists.txt b/test/regperf/CMakeLists.txt new file mode 100644 index 00000000..22754317 --- /dev/null +++ b/test/regperf/CMakeLists.txt @@ -0,0 +1,33 @@ +set(SOURCES + ./perf.cpp +) + +add_executable(regperf + ${SOURCES} +) + +target_link_libraries(regperf + dnmd::interfaces + dncp::dncp + Regression.Locator) + +if(NOT MSVC) + target_link_libraries(regperf dncp::winhdrs) +endif() + +include(FetchContent) +FetchContent_Declare( + benchmark + GIT_REPOSITORY + https://github.com/google/benchmark.git + GIT_TAG + v1.8.3 +) + +set(BENCHMARK_ENABLE_TESTING OFF) +FetchContent_MakeAvailable(benchmark) + +target_link_libraries(regperf benchmark::benchmark) + +install(DIRECTORY ${RegressionLocatorDirectory} DESTINATION bin) +install(TARGETS regperf DESTINATION bin) \ No newline at end of file diff --git a/test/regnative/perf.cpp b/test/regperf/perf.cpp similarity index 90% rename from test/regnative/perf.cpp rename to test/regperf/perf.cpp index b28af438..b70c6130 100644 --- a/test/regnative/perf.cpp +++ b/test/regperf/perf.cpp @@ -6,12 +6,20 @@ #include #include +#include + +#ifdef _WIN32 +#define DNNE_API_OVERRIDE __declspec(dllimport) +#endif +#include + #ifdef _MSC_VER #define EXPORT extern "C" __declspec(dllexport) #else #define EXPORT extern "C" __attribute__((__visibility__("default"))) #endif // !_MSC_VER + namespace { void const* g_data; @@ -230,3 +238,15 @@ HRESULT PerfCurrentGetCustomAttributeByName(int iter) } return S_OK; } + +#define RETURN_IF_FAILED(x) { auto hr = x; if (FAILED(hr)) return hr; } + +int main(int argc, char** argv) +{ + // TODO: Refactor regtest/pal to be a static lib that we can use from here. + // TODO: Can we use nethost to discover hostfxr and use hostfxr APIs to discover the baseline? + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + return 0; +} \ No newline at end of file From 318b64208ae07c2083d42e070bba869c5d8a3722 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 22 Dec 2023 10:51:25 -0800 Subject: [PATCH 20/59] Move benchmark suite to be driven by google/benchmark in native code. --- test/CMakeLists.txt | 23 ++- test/DNMD.Tests.sln | 6 - test/Regression.Performance/Program.cs | 112 ----------- .../Regression.Performance.csproj | 20 -- test/regpal/CMakeLists.txt | 9 + test/regpal/pal.cpp | 97 ++++++++++ test/regpal/pal.hpp | 14 ++ test/regperf/CMakeLists.txt | 12 +- test/regperf/perf.cpp | 183 ++++++++---------- test/regtest/CMakeLists.txt | 4 +- test/regtest/discovery.cpp | 20 +- test/regtest/main.cpp | 33 +--- test/regtest/pal.cpp | 26 --- test/regtest/pal.hpp | 7 - 14 files changed, 221 insertions(+), 345 deletions(-) delete mode 100644 test/Regression.Performance/Program.cs delete mode 100644 test/Regression.Performance/Regression.Performance.csproj create mode 100644 test/regpal/CMakeLists.txt create mode 100644 test/regpal/pal.cpp create mode 100644 test/regpal/pal.hpp delete mode 100644 test/regtest/pal.cpp delete mode 100644 test/regtest/pal.hpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7e8a1151..a7abf9eb 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -2,13 +2,8 @@ include(../configure.cmake) include(ImportManagedComponents.cmake) -add_subdirectory(regperf) - -set(CMAKE_CXX_STANDARD 14) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - if (POLICY CMP0135) - cmake_policy(SET CMP0135 NEW) # Set timestamps in downloaded archives to the time of download. + cmake_policy(SET CMP0135 NEW) # Set timestamps in downloaded archives to the time of download. endif() include(FetchContent) @@ -18,9 +13,21 @@ FetchContent_Declare( ) # For Windows: Prevent overriding the parent project's compiler/linker settings set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) -FetchContent_MakeAvailable(googletest) +FetchContent_Declare( + benchmark + GIT_REPOSITORY + https://github.com/google/benchmark.git + GIT_TAG + v1.8.3 +) + +FetchContent_MakeAvailable(googletest benchmark) include(GoogleTest) -add_subdirectory(regtest) \ No newline at end of file +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +add_subdirectory(regpal) +add_subdirectory(regperf) +add_subdirectory(regtest) diff --git a/test/DNMD.Tests.sln b/test/DNMD.Tests.sln index 77bf035d..a519c9ad 100644 --- a/test/DNMD.Tests.sln +++ b/test/DNMD.Tests.sln @@ -3,8 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.5.33026.144 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Regression.Performance", "Regression.Performance\Regression.Performance.csproj", "{3ED137D0-B5A2-4309-BDA5-BD9DE3FFC752}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Regression.TargetAssembly", "Regression.TargetAssembly\Regression.TargetAssembly.ilproj", "{D3BEEF3D-C137-49AC-96DD-E5B93E2F4C21}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Regression.Locator", "Regression.Locator\Regression.Locator.csproj", "{CD7DEF6B-7254-4541-951D-026CDB8928FD}" @@ -15,10 +13,6 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {3ED137D0-B5A2-4309-BDA5-BD9DE3FFC752}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3ED137D0-B5A2-4309-BDA5-BD9DE3FFC752}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3ED137D0-B5A2-4309-BDA5-BD9DE3FFC752}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3ED137D0-B5A2-4309-BDA5-BD9DE3FFC752}.Release|Any CPU.Build.0 = Release|Any CPU {D3BEEF3D-C137-49AC-96DD-E5B93E2F4C21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D3BEEF3D-C137-49AC-96DD-E5B93E2F4C21}.Debug|Any CPU.Build.0 = Debug|Any CPU {D3BEEF3D-C137-49AC-96DD-E5B93E2F4C21}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/test/Regression.Performance/Program.cs b/test/Regression.Performance/Program.cs deleted file mode 100644 index 411e716e..00000000 --- a/test/Regression.Performance/Program.cs +++ /dev/null @@ -1,112 +0,0 @@ -using System.Diagnostics; -using System.Reflection.PortableExecutable; -using System.Runtime.InteropServices; - -using Common; - -namespace Regression.Performance -{ - public unsafe class Compare - { - private PEReader _peReader; - private PEMemoryBlock _metadataBlock; - - delegate* unmanaged _Initialize; - - private readonly struct Scenario - { - public string Name { get; init; } - public delegate* unmanaged Baseline { get; init; } - public delegate* unmanaged Current { get; init; } - } - - List _scenarios = new(); - - public Compare(string currentPath) - { - // Load System.Private.CoreLib - var spcl = typeof(object).Assembly.Location; - _peReader = new(File.OpenRead(spcl)); - _metadataBlock = _peReader.GetMetadata(); - - // Acquire native functions - nint mod = NativeLibrary.Load(currentPath); - _Initialize = (delegate* unmanaged)NativeLibrary.GetExport(mod, "PerfInitialize"); - - string[] scenarioNames = new[] - { - "CreateImport", - "EnumTypeDefs", - "GetScopeProps", - "EnumUserStrings", - "GetCustomAttributeByName", - }; - - // Look up each scenario test export. - foreach (var name in scenarioNames) - { - _scenarios.Add(new Scenario() - { - Name = name, - Baseline = (delegate* unmanaged)NativeLibrary.GetExport(mod, $"PerfBaseline{name}"), - Current = (delegate* unmanaged)NativeLibrary.GetExport(mod, $"PerfCurrent{name}"), - }); - } - - int hr = _Initialize(_metadataBlock.Pointer, _metadataBlock.Length, Dispensers.Baseline); - if (hr < 0) - { - throw new Exception($"Initialization failed: 0x{hr:x}"); - } - } - - public void Run(int iter) - { - int hr; - const int width = 12; - var sw = new Stopwatch(); - - foreach (var scenario in _scenarios) - { - Console.WriteLine(scenario.Name); - sw.Restart(); - hr = scenario.Baseline(iter); - Console.WriteLine($" Baseline: {sw.ElapsedMilliseconds,width}"); - if (hr < 0) throw new Exception($"Failure 0x{hr:x}"); - sw.Restart(); - hr = scenario.Current(iter); - Console.WriteLine($" Current: {sw.ElapsedMilliseconds,width}"); - if (hr < 0) throw new Exception($"Failure 0x{hr:x}"); - } - } - } - - public class Program - { - public static void Main(string[] args) - { - string regnativePath; - if (args.Length > 0) - { - regnativePath = args[0]; - } - else - { - regnativePath = - OperatingSystem.IsWindows() ? "regnative.dll" - : OperatingSystem.IsMacOS() ? "libregnative.dylib" - : "libregnative.so"; - regnativePath = Path.Combine(AppContext.BaseDirectory, regnativePath); - } - - var test = new Compare(regnativePath); - - Console.WriteLine("Warm-up"); - test.Run(100); - - const int iter = 100_000; - Console.WriteLine($"\nRun iterations - {iter}"); - test.Run(iter); - } - } -} diff --git a/test/Regression.Performance/Regression.Performance.csproj b/test/Regression.Performance/Regression.Performance.csproj deleted file mode 100644 index f2ef8624..00000000 --- a/test/Regression.Performance/Regression.Performance.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - Exe - enable - enable - true - - false - - - - - - - - - - - diff --git a/test/regpal/CMakeLists.txt b/test/regpal/CMakeLists.txt new file mode 100644 index 00000000..70528172 --- /dev/null +++ b/test/regpal/CMakeLists.txt @@ -0,0 +1,9 @@ +set(SOURCES + ./pal.cpp +) + +add_library(regpal STATIC ${SOURCES}) + +target_link_libraries(regpal PUBLIC Regression.Locator dncp::dncp) + +target_include_directories(regpal PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) \ No newline at end of file diff --git a/test/regpal/pal.cpp b/test/regpal/pal.cpp new file mode 100644 index 00000000..6b8f6797 --- /dev/null +++ b/test/regpal/pal.cpp @@ -0,0 +1,97 @@ +#include "pal.hpp" +#include + +#ifdef _WIN32 +#define DNNE_API_OVERRIDE __declspec(dllimport) +#endif +#include + +#ifdef _WIN32 +#define NOMINMAX +#include +#else +#include +#endif + +#include +#include + + +namespace +{ + void* LoadModule(char const* path) + { +#ifdef _WIN32 + return LoadLibraryA(path); +#else + return dlopen(path, RTLD_LAZY); +#endif + } + + void* GetSymbol(void* module, char const* name) + { +#ifdef _WIN32 + return GetProcAddress((HMODULE)module, name); +#else + return dlsym(module, name); +#endif + } + + using MetaDataGetDispenser = HRESULT(STDMETHODCALLTYPE*)(REFCLSID, REFIID, LPVOID*); + + MetaDataGetDispenser LoadGetDispenser() + { + // TODO: Can we use nethost to discover hostfxr and use hostfxr APIs to discover the baseline? + dncp::cotaskmem_ptr coreClrPath{ (char*)GetCoreClrPath() }; + if (coreClrPath == nullptr) + { + std::cerr << "Failed to get coreclr path" << std::endl; + return nullptr; + } + + auto mod = LoadModule(coreClrPath.get()); + if (mod == nullptr) + { + std::cerr << "Failed to load metadata baseline module: " << coreClrPath.get() << std::endl; + return nullptr; + } + + auto getDispenser = (MetaDataGetDispenser)GetSymbol(mod, "MetaDataGetDispenser"); + if (getDispenser == nullptr) + { + std::cerr << "Failed to find MetaDataGetDispenser in module: " << coreClrPath.get() << std::endl; + return nullptr; + } + + return getDispenser; + } + + MetaDataGetDispenser GetDispenser = LoadGetDispenser(); +} + +HRESULT pal::GetBaselineMetadataDispenser(IMetaDataDispenser** dispenser) +{ + if (GetDispenser == nullptr) + { + return E_FAIL; + } + + return GetDispenser(CLSID_CorMetaDataDispenser, IID_IMetaDataDispenser, (void**)dispenser); +} + +bool pal::ReadFile(std::filesystem::path path, malloc_span& b) +{ + // Read in the entire file + std::ifstream fd{ path, std::ios::binary }; + if (!fd) + return false; + + size_t size = std::filesystem::file_size(path); + if (size == 0) + return false; + + b = { (uint8_t*)std::malloc(size), size }; + + fd.read((char*)(uint8_t*)b, b.size()); + return true; +} diff --git a/test/regpal/pal.hpp b/test/regpal/pal.hpp new file mode 100644 index 00000000..bc601c0d --- /dev/null +++ b/test/regpal/pal.hpp @@ -0,0 +1,14 @@ +#ifndef _TEST_REGPAL_PAL_H_ +#define _TEST_REGPAL_PAL_H_ + +#include +#include +#include + +namespace pal +{ + HRESULT GetBaselineMetadataDispenser(IMetaDataDispenser** dispenser); + bool ReadFile(std::filesystem::path path, malloc_span& b); +} + +#endif // !_TEST_REGPAL_PAL_H_ \ No newline at end of file diff --git a/test/regperf/CMakeLists.txt b/test/regperf/CMakeLists.txt index 22754317..60fb6395 100644 --- a/test/regperf/CMakeLists.txt +++ b/test/regperf/CMakeLists.txt @@ -9,23 +9,13 @@ add_executable(regperf target_link_libraries(regperf dnmd::interfaces dncp::dncp + regpal Regression.Locator) if(NOT MSVC) target_link_libraries(regperf dncp::winhdrs) endif() -include(FetchContent) -FetchContent_Declare( - benchmark - GIT_REPOSITORY - https://github.com/google/benchmark.git - GIT_TAG - v1.8.3 -) - -set(BENCHMARK_ENABLE_TESTING OFF) -FetchContent_MakeAvailable(benchmark) target_link_libraries(regperf benchmark::benchmark) diff --git a/test/regperf/perf.cpp b/test/regperf/perf.cpp index b70c6130..eea01027 100644 --- a/test/regperf/perf.cpp +++ b/test/regperf/perf.cpp @@ -1,13 +1,17 @@ -#include -#include -#include -#include - +#include #include #include +#include #include +#include +#include +#include +#include +#include +#include + #ifdef _WIN32 #define DNNE_API_OVERRIDE __declspec(dllimport) #endif @@ -19,6 +23,7 @@ #define EXPORT extern "C" __attribute__((__visibility__("default"))) #endif // !_MSC_VER +#define RETURN_IF_FAILED(x) { auto hr = x; if (FAILED(hr)) return hr; } namespace { @@ -89,162 +94,128 @@ namespace } } -EXPORT HRESULT PerfInitialize( void const* data, - uint32_t dataLen, - IMetaDataDispenser* baseline) + uint32_t dataLen) { - if (data == nullptr || baseline == nullptr) + if (data == nullptr) return E_INVALIDARG; g_data = data; g_dataLen = dataLen; - (void)baseline->AddRef(); - g_baselineDisp = baseline; - - HRESULT hr; - if (FAILED(hr = CreateImport(g_baselineDisp, &g_baselineImport))) - return hr; - - if (FAILED(hr = GetDispenser(IID_IMetaDataDispenser, reinterpret_cast(&g_currentDisp)))) - return hr; + RETURN_IF_FAILED(CreateImport(g_baselineDisp, &g_baselineImport)); - if (FAILED(hr = CreateImport(g_currentDisp, &g_currentImport))) - return hr; + RETURN_IF_FAILED(CreateImport(g_currentDisp, &g_currentImport)); return S_OK; } -EXPORT -HRESULT PerfBaselineCreateImport(int iter) +void CreateImport(benchmark::State& state, IMetaDataDispenser* disp) { - HRESULT hr; IMetaDataImport* import; - for (int i = 0; i < iter; ++i) + for (auto _ : state) { - if (FAILED(hr = CreateImport(g_baselineDisp, &import))) - return hr; - (void)import->Release(); + if (SUCCEEDED(CreateImport(disp, &import))) + (void)import->Release(); } - return S_OK; } -EXPORT -HRESULT PerfCurrentCreateImport(int iter) -{ - HRESULT hr; - IMetaDataImport* import; - for (int i = 0; i < iter; ++i) - { - if (FAILED(hr = CreateImport(g_currentDisp, &import))) - return hr; - (void)import->Release(); - } - return S_OK; -} +BENCHMARK_CAPTURE(CreateImport, BaselineCreateImport, g_baselineDisp); +BENCHMARK_CAPTURE(CreateImport, CurrentCreateImport, g_currentDisp); -EXPORT -HRESULT PerfBaselineEnumTypeDefs(int iter) -{ - HRESULT hr; - for (int i = 0; i < iter; ++i) - { - if (FAILED(hr = EnumTypeDefs(g_baselineImport))) - return hr; - } - return S_OK; -} +#define IMPORT_BENCHMARK(func) \ + BENCHMARK_CAPTURE(func, Baseline##func, g_baselineImport); \ + BENCHMARK_CAPTURE(func, Current##func, g_currentImport); -EXPORT -HRESULT PerfCurrentEnumTypeDefs(int iter) +void EnumTypeDefs(benchmark::State& state, IMetaDataImport* import) { - HRESULT hr; - for (int i = 0; i < iter; ++i) + for (auto _ : state) { - if (FAILED(hr = EnumTypeDefs(g_currentImport))) - return hr; + if (FAILED(EnumTypeDefs(import))) + { + state.SkipWithError("Failed to enumerate typedefs"); + } } - return S_OK; } -EXPORT -HRESULT PerfBaselineGetScopeProps(int iter) +IMPORT_BENCHMARK(EnumTypeDefs); + +void GetScopeProps(benchmark::State& state, IMetaDataImport* import) { HRESULT hr; - for (int i = 0; i < iter; ++i) + for (auto _ : state) { - if (FAILED(hr = GetScopeProps(g_baselineImport))) - return hr; + if (FAILED(hr = GetScopeProps(import))) + { + state.SkipWithError("Failed to get scope props"); + } } - return S_OK; } -EXPORT -HRESULT PerfCurrentGetScopeProps(int iter) +IMPORT_BENCHMARK(GetScopeProps); + +void EnumUserStrings(benchmark::State& state, IMetaDataImport* import) { HRESULT hr; - for (int i = 0; i < iter; ++i) + for (auto _ : state) { - if (FAILED(hr = GetScopeProps(g_currentImport))) - return hr; + if (FAILED(hr = EnumUserStrings(import))) + { + state.SkipWithError("Failed to enumerate user strings"); + } } - return S_OK; } -EXPORT -HRESULT PerfBaselineEnumUserStrings(int iter) +IMPORT_BENCHMARK(EnumUserStrings); + +void EnumCustomAttributeByName(benchmark::State& state, IMetaDataImport* import) { HRESULT hr; - for (int i = 0; i < iter; ++i) + for (auto _ : state) { - if (FAILED(hr = EnumUserStrings(g_baselineImport))) - return hr; + if (FAILED(hr = GetCustomAttributeByName(import))) + { + state.SkipWithError("Failed to get custom attributes"); + } } - return S_OK; } -EXPORT -HRESULT PerfCurrentEnumUserStrings(int iter) +IMPORT_BENCHMARK(EnumCustomAttributeByName); + +int main(int argc, char** argv) { - HRESULT hr; - for (int i = 0; i < iter; ++i) + RETURN_IF_FAILED(pal::GetBaselineMetadataDispenser(&g_baselineDisp)); + RETURN_IF_FAILED(GetDispenser(IID_IMetaDataDispenser, reinterpret_cast(&g_currentDisp))); + dncp::cotaskmem_ptr coreClrPath{ (char*)GetCoreClrPath() }; + if (coreClrPath == nullptr) { - if (FAILED(hr = EnumUserStrings(g_currentImport))) - return hr; + std::cerr << "Failed to get coreclr path" << std::endl; + return -1; } - return S_OK; -} -EXPORT -HRESULT PerfBaselineGetCustomAttributeByName(int iter) -{ - for (int i = 0; i < iter; ++i) + std::filesystem::path dataImagePath = coreClrPath.get(); + dataImagePath.replace_filename("System.Private.CoreLib.dll"); + + std::cerr << "Loading System.Private.CoreLib from: " << dataImagePath << std::endl; + + malloc_span dataImage; + if (!pal::ReadFile(dataImagePath, dataImage)) { - if (S_FALSE != GetCustomAttributeByName(g_baselineImport)) - return E_FAIL; + std::cerr << "Failed to read System.Private.CoreLib" << std::endl; + return -1; } - return S_OK; -} -EXPORT -HRESULT PerfCurrentGetCustomAttributeByName(int iter) -{ - for (int i = 0; i < iter; ++i) + if (!get_metadata_from_pe(dataImage)) { - if (S_FALSE != GetCustomAttributeByName(g_currentImport)) - return E_FAIL; + std::cerr << "Failed to get metadata from System.Private.CoreLib" << std::endl; + return -1; } - return S_OK; -} -#define RETURN_IF_FAILED(x) { auto hr = x; if (FAILED(hr)) return hr; } + RETURN_IF_FAILED(PerfInitialize( + dataImage, + (uint32_t)dataImage.size())); -int main(int argc, char** argv) -{ - // TODO: Refactor regtest/pal to be a static lib that we can use from here. - // TODO: Can we use nethost to discover hostfxr and use hostfxr APIs to discover the baseline? benchmark::Initialize(&argc, argv); benchmark::RunSpecifiedBenchmarks(); benchmark::Shutdown(); diff --git a/test/regtest/CMakeLists.txt b/test/regtest/CMakeLists.txt index b68209b3..136fe519 100644 --- a/test/regtest/CMakeLists.txt +++ b/test/regtest/CMakeLists.txt @@ -2,17 +2,15 @@ set(HEADERS ./baseline.h ./fixtures.h ./asserts.h - ./pal.hpp ) set(SOURCES ./main.cpp ./discovery.cpp - ./pal.cpp ./metadata.cpp ) add_executable(regtest ${SOURCES} ${HEADERS}) -target_link_libraries(regtest PRIVATE dnmd::interfaces gtest gmock dncp::dncp) # Reference gmock for better collection assertions +target_link_libraries(regtest PRIVATE dnmd::interfaces gtest gmock dncp::dncp regpal) # Reference gmock for better collection assertions set_target_properties(regtest PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON) # Require C++17 for the tests so we can use std::filesystem. if (NOT WIN32) diff --git a/test/regtest/discovery.cpp b/test/regtest/discovery.cpp index a8976558..e417efcd 100644 --- a/test/regtest/discovery.cpp +++ b/test/regtest/discovery.cpp @@ -1,5 +1,6 @@ #include "fixtures.h" #include "baseline.h" +#include #include #include @@ -12,27 +13,10 @@ namespace { - bool read_in_file(std::filesystem::path path, malloc_span& b) - { - // Read in the entire file - std::ifstream fd{ path, std::ios::binary }; - if (!fd) - return false; - - size_t size = std::filesystem::file_size(path); - if (size == 0) - return false; - - b = { (uint8_t*)std::malloc(size), size }; - - fd.read((char*)(uint8_t*)b, b.size()); - return true; - } - malloc_span ReadMetadataFromFile(std::filesystem::path path) { malloc_span b; - if (!read_in_file(path, b)) + if (!pal::ReadFile(path, b)) { std::cerr << "Failed to read in '" << path << "'\n"; return {}; diff --git a/test/regtest/main.cpp b/test/regtest/main.cpp index 724898f9..15adcd0d 100644 --- a/test/regtest/main.cpp +++ b/test/regtest/main.cpp @@ -16,11 +16,6 @@ namespace TestBaseline dncp::com_ptr Symbol = nullptr; } -namespace -{ - using MetaDataGetDispenser = HRESULT(__stdcall*)(REFCLSID, REFIID, LPVOID*); -} - #define RETURN_IF_FAILED(x) { auto hr = x; if (FAILED(hr)) return hr; } class ThrowListener final : public testing::EmptyTestEventListener { @@ -33,30 +28,11 @@ class ThrowListener final : public testing::EmptyTestEventListener { int main(int argc, char** argv) { - dncp::cotaskmem_ptr coreClrPath{ (char*)GetCoreClrPath() }; - if (coreClrPath == nullptr) - { - std::cout << "Failed to get coreclr path" << std::endl; - return -1; - } + RETURN_IF_FAILED(pal::GetBaselineMetadataDispenser(&TestBaseline::Metadata)); - auto mod = LoadModule(coreClrPath.get()); - if (mod == nullptr) - { - std::cout << "Failed to load metadata baseline module: " << coreClrPath.get() << std::endl; - return -1; - } - - auto getDispenser = (MetaDataGetDispenser)GetSymbol(mod, "MetaDataGetDispenser"); - if (getDispenser == nullptr) - { - std::cout << "Failed to find MetaDataGetDispenser in module: " << coreClrPath.get() << std::endl; - return -1; - } - - RETURN_IF_FAILED(getDispenser(CLSID_CorMetaDataDispenser, IID_IMetaDataDispenser, (void**)&TestBaseline::Metadata)); - - RETURN_IF_FAILED(getDispenser(CLSID_CorMetaDataDispenser, IID_IMetaDataDispenserEx, (void**)&TestBaseline::DeltaMetadataBuilder)); + dncp::com_ptr deltaBuilder; + RETURN_IF_FAILED(pal::GetBaselineMetadataDispenser(&deltaBuilder)); + RETURN_IF_FAILED(deltaBuilder->QueryInterface(IID_IMetaDataDispenserEx, (void**)&TestBaseline::DeltaMetadataBuilder)); VARIANT vt; V_VT(&vt) = VT_UI4; @@ -64,6 +40,7 @@ int main(int argc, char** argv) if (HRESULT hr = TestBaseline::DeltaMetadataBuilder->SetOption(MetaDataSetENC, &vt); FAILED(hr)) return hr; + dncp::cotaskmem_ptr coreClrPath{ (char*)GetCoreClrPath() }; SetBaselineModulePath(coreClrPath.get()); std::cout << "Loaded metadata baseline module: " << coreClrPath.get() << std::endl; diff --git a/test/regtest/pal.cpp b/test/regtest/pal.cpp deleted file mode 100644 index 9b4e7545..00000000 --- a/test/regtest/pal.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include "pal.hpp" -#ifdef _WIN32 -#define NOMINMAX -#include -#else -#include -#include -#endif - -void* LoadModule(char const* path) -{ -#ifdef _WIN32 - return LoadLibraryA(path); -#else - return dlopen(path, RTLD_LAZY); -#endif -} - -void* GetSymbol(void* module, char const* name) -{ -#ifdef _WIN32 - return GetProcAddress((HMODULE)module, name); -#else - return dlsym(module, name); -#endif -} \ No newline at end of file diff --git a/test/regtest/pal.hpp b/test/regtest/pal.hpp deleted file mode 100644 index 346e3777..00000000 --- a/test/regtest/pal.hpp +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef _TEST_REGTEST_PAL_H_ -#define _TEST_REGTEST_PAL_H_ - -void* LoadModule(char const* path); -void* GetSymbol(void* module, char const* name); - -#endif // !_TEST_REGTEST_PAL_H_ \ No newline at end of file From 0524a01d22ca870fe5cf4cefc27b01c4ae43b375 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 22 Dec 2023 11:45:00 -0800 Subject: [PATCH 21/59] Lazily read in/generate metadata files and pass around spans of the files so we aren't allocating a ton and slowing down test discovery and execution. --- test/CMakeLists.txt | 2 + test/regtest/CMakeLists.txt | 2 +- test/regtest/discovery.cpp | 204 +++++++++++++++++++++--------------- test/regtest/fixtures.h | 40 ++++--- test/regtest/metadata.cpp | 20 ++-- 5 files changed, 157 insertions(+), 111 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a7abf9eb..9bfc2697 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -22,6 +22,8 @@ FetchContent_Declare( v1.8.3 ) +# Don't build the tests for the benchmark library. +set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "" FORCE) FetchContent_MakeAvailable(googletest benchmark) include(GoogleTest) diff --git a/test/regtest/CMakeLists.txt b/test/regtest/CMakeLists.txt index 136fe519..0990a68c 100644 --- a/test/regtest/CMakeLists.txt +++ b/test/regtest/CMakeLists.txt @@ -25,4 +25,4 @@ add_custom_command(TARGET regtest POST_BUILD add_custom_command(TARGET regtest POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${RegressionLocatorDirectory} $) -gtest_discover_tests(regtest DISCOVERY_TIMEOUT 100 DISCOVERY_MODE PRE_TEST) \ No newline at end of file +gtest_discover_tests(regtest) \ No newline at end of file diff --git a/test/regtest/discovery.cpp b/test/regtest/discovery.cpp index e417efcd..0d49f5ad 100644 --- a/test/regtest/discovery.cpp +++ b/test/regtest/discovery.cpp @@ -2,7 +2,8 @@ #include "baseline.h" #include #include -#include +#include +#include #include @@ -11,31 +12,90 @@ #endif #include +#define THROW_IF_FAILED(hr) if (FAILED(hr)) throw std::runtime_error(#hr) + + namespace { + std::string baselinePath; + std::string regressionAssemblyPath; + + template + struct OnExit + { + T callback; + ~OnExit() + { + callback(); + } + }; + + template + [[nodiscard]] OnExit on_scope_exit(T callback) + { + return { callback }; + } + malloc_span ReadMetadataFromFile(std::filesystem::path path) { malloc_span b; - if (!pal::ReadFile(path, b)) + if (!pal::ReadFile(path, b) + || !get_metadata_from_pe(b)) { - std::cerr << "Failed to read in '" << path << "'\n"; return {}; } - if (!get_metadata_from_pe(b)) + return b; + } + + malloc_span CreateImageWithAppliedDelta() + { + uint8_t* baseImage = nullptr; + uint32_t baseImageSize = 0; + uint8_t** deltas = nullptr; + uint32_t* deltaSizes = nullptr; + uint32_t numDeltas = 0; + + GetImageAndDeltas(&baseImage, &baseImageSize, &numDeltas, &deltas, &deltaSizes); + + auto _ = on_scope_exit([&]() { FreeImageAndDeltas(baseImage, numDeltas, deltas, deltaSizes); }); + + dncp::com_ptr baseline; + THROW_IF_FAILED(TestBaseline::DeltaMetadataBuilder->OpenScopeOnMemory(baseImage, baseImageSize, 0, IID_IMetaDataEmit, (IUnknown**)&baseline)); + + for (uint32_t i = 0; i < numDeltas; i++) { - std::cerr << "Failed to read '" << path << "' as PE\n"; - return {}; + dncp::com_ptr delta; + THROW_IF_FAILED(TestBaseline::DeltaMetadataBuilder->OpenScopeOnMemory(deltas[i], deltaSizes[i], 0, IID_IMetaDataImport, (IUnknown**)&delta)); + + THROW_IF_FAILED(baseline->ApplyEditAndContinue(delta)); } - return b; + malloc_span imageWithDelta; + DWORD compositeImageSize; + THROW_IF_FAILED(baseline->GetSaveSize(CorSaveSize::cssAccurate, &compositeImageSize)); + + imageWithDelta = { (uint8_t*)malloc(compositeImageSize), compositeImageSize }; + + THROW_IF_FAILED(baseline->SaveToMemory(imageWithDelta, compositeImageSize)); + + return imageWithDelta; + } + + malloc_span GetMetadataFromKey(std::string key) + { + if (key == DeltaImageKey) + { + return CreateImageWithAppliedDelta(); + } + return {}; } } -std::vector MetadataInDirectory(std::string directory) +std::vector MetadataFilesInDirectory(std::string directory) { - std::vector scenarios; - + std::vector scenarios; + if (!std::filesystem::exists(directory)) { std::cerr << "Directory '" << directory << "' does not exist\n"; @@ -50,9 +110,6 @@ std::vector MetadataInDirectory(std::string directory) auto ext = path.extension(); if (ext == ".dll") { - FileBlob scenario; - scenario.path = path.filename().generic_string(); - malloc_span b = ReadMetadataFromFile(path); if (b.size() == 0) @@ -61,8 +118,7 @@ std::vector MetadataInDirectory(std::string directory) continue; } - scenario.blob = std::vector{ (uint8_t*)b, b + b.size() }; - scenarios.push_back(std::move(scenario)); + scenarios.emplace_back(MetadataFile::Kind::OnDisk, path.generic_string()); } } } @@ -70,103 +126,81 @@ std::vector MetadataInDirectory(std::string directory) return scenarios; } -namespace +std::vector CoreLibFiles() { - std::string baselinePath; - std::string regressionAssemblyPath; -} + std::vector scenarios; -std::vector CoreLibs() -{ - std::vector scenarios; - - auto coreclrSystemPrivateCoreLib = std::filesystem::path(baselinePath).parent_path() / "System.Private.CoreLib.dll"; - auto b = ReadMetadataFromFile(coreclrSystemPrivateCoreLib); - if (b.size() == 0) - { - std::cerr << "Failed to read in '" << coreclrSystemPrivateCoreLib << "'\n"; - return scenarios; - } - - scenarios.push_back({ "netcoreapp", std::vector{ (uint8_t*)b, b + b.size() } }); - - auto fx4mscorlib = std::filesystem::path(FindFrameworkInstall("v4.0.30319")) / "mscorlib.dll"; - b = ReadMetadataFromFile(coreclrSystemPrivateCoreLib); - if (b.size() == 0) - { - std::cerr << "Failed to read in '" << coreclrSystemPrivateCoreLib << "'\n"; - return scenarios; - } + scenarios.emplace_back(MetadataFile::Kind::OnDisk, (std::filesystem::path(baselinePath).parent_path() / "System.Private.CoreLib.dll").generic_string()); + scenarios.emplace_back(MetadataFile::Kind::OnDisk, (std::filesystem::path(FindFrameworkInstall("v4.0.30319")) / "mscorlib.dll").generic_string()); - scenarios.push_back({ "fx4", std::vector{ (uint8_t*)b, b + b.size() } }); auto fx2mscorlib = std::filesystem::path(FindFrameworkInstall("v2.0.50727")) / "mscorlib.dll"; if (std::filesystem::exists(fx2mscorlib)) { - b = ReadMetadataFromFile(coreclrSystemPrivateCoreLib); - if (b.size() == 0) - { - std::cerr << "Failed to read in '" << coreclrSystemPrivateCoreLib << "'\n"; - return scenarios; - } - - scenarios.push_back({ "fx2", std::vector{ (uint8_t*)b, b + b.size() } }); + scenarios.emplace_back(MetadataFile::Kind::OnDisk, fx2mscorlib.generic_string()); } - return scenarios; } namespace { - template - struct OnExit + std::mutex metadataCacheMutex; + + struct MetadataFileHash { - T callback; - ~OnExit() + size_t operator()(const MetadataFile& file) const { - callback(); + return std::hash{}(file.pathOrKey); } }; - template - [[nodiscard]] OnExit on_scope_exit(T callback) - { - return { callback }; - } + std::unordered_map, MetadataFileHash> metadataCache; } -#define THROW_IF_FAILED(hr) if (FAILED(hr)) throw std::runtime_error(#hr) - -FileBlob ImageWithDelta() +span GetMetadataForFile(MetadataFile file) { - FileBlob imageWithDelta = { "imageWithDelta", {} }; - uint8_t* baseImage = nullptr; - uint32_t baseImageSize = 0; - uint8_t** deltas = nullptr; - uint32_t* deltaSizes = nullptr; - uint32_t numDeltas = 0; - - GetImageAndDeltas(&baseImage, &baseImageSize, &numDeltas, &deltas, &deltaSizes); - - auto _ = on_scope_exit([&]() { FreeImageAndDeltas(baseImage, numDeltas, deltas, deltaSizes); }); + std::lock_guard lock{ metadataCacheMutex }; + auto it = metadataCache.find(file); + if (it != metadataCache.end()) + { + return it->second; + } - dncp::com_ptr baseline; - THROW_IF_FAILED(TestBaseline::DeltaMetadataBuilder->OpenScopeOnMemory(baseImage, baseImageSize, 0, IID_IMetaDataEmit, (IUnknown**)&baseline)); - - for (uint32_t i = 0; i < numDeltas; i++) + malloc_span b; + if (file.kind == MetadataFile::Kind::OnDisk) { - dncp::com_ptr delta; - THROW_IF_FAILED(TestBaseline::DeltaMetadataBuilder->OpenScopeOnMemory(deltas[i], deltaSizes[i], 0, IID_IMetaDataImport, (IUnknown**)&delta)); + auto path = std::filesystem::path(baselinePath).parent_path() / file.pathOrKey; + b = ReadMetadataFromFile(path); + } + else + { + b = GetMetadataFromKey(file.pathOrKey.c_str()); + } - THROW_IF_FAILED(baseline->ApplyEditAndContinue(delta)); + if (b.size() == 0) + { + return {}; } - DWORD compositeImageSize; - THROW_IF_FAILED(baseline->GetSaveSize(CorSaveSize::cssAccurate, &compositeImageSize)); + span spanToReturn = b; - imageWithDelta.blob.resize(compositeImageSize); - THROW_IF_FAILED(baseline->SaveToMemory(imageWithDelta.blob.data(), compositeImageSize)); + auto [_, inserted] = metadataCache.emplace(std::move(file), std::move(b)); + assert(inserted); + return spanToReturn; +} - return imageWithDelta; +std::string PrintName(testing::TestParamInfo info) +{ + std::string name; + if (info.param.kind == MetadataFile::Kind::OnDisk) + { + name = std::filesystem::path(info.param.pathOrKey).stem().generic_string(); + std::replace(name.begin(), name.end(), '.', '_'); + } + else + { + name = info.param.pathOrKey + "_InMemory"; + } + return name; } std::string GetBaselineDirectory() diff --git a/test/regtest/fixtures.h b/test/regtest/fixtures.h index eed29783..c048be9b 100644 --- a/test/regtest/fixtures.h +++ b/test/regtest/fixtures.h @@ -13,30 +13,38 @@ #include #include -#include - #include -struct FileBlob +struct MetadataFile final { - std::string path; - std::vector blob; + enum class Kind + { + OnDisk, + Generated + } kind; + + MetadataFile(Kind kind, std::string pathOrKey) + : pathOrKey(std::move(pathOrKey)), kind(kind) {} + + std::string pathOrKey; + + bool operator==(const MetadataFile& rhs) const noexcept + { + return kind == rhs.kind && pathOrKey == rhs.pathOrKey; + } }; -inline std::string PrintFileBlob(testing::TestParamInfo info) -{ - std::string name = info.param.path; - std::replace(name.begin(), name.end(), '.', '_'); - return name; -} +inline static std::string DeltaImageKey = "DeltaImage"; -std::vector MetadataInDirectory(std::string directory); +std::string PrintName(testing::TestParamInfo info); -std::vector CoreLibs(); +std::vector MetadataFilesInDirectory(std::string directory); -malloc_span GetRegressionAssemblyMetadata(); +std::vector CoreLibFiles(); -FileBlob ImageWithDelta(); +span GetMetadataForFile(MetadataFile file); + +malloc_span GetRegressionAssemblyMetadata(); std::string FindFrameworkInstall(std::string version); @@ -46,7 +54,7 @@ void SetBaselineModulePath(std::string path); void SetRegressionAssemblyPath(std::string path); -class RegressionTest : public ::testing::TestWithParam +class RegressionTest : public ::testing::TestWithParam { protected: using TokenList = std::vector; diff --git a/test/regtest/metadata.cpp b/test/regtest/metadata.cpp index 86ded99f..cea67b26 100644 --- a/test/regtest/metadata.cpp +++ b/test/regtest/metadata.cpp @@ -1768,8 +1768,9 @@ class MetadataImportTest : public RegressionTest TEST_P(MetadataImportTest, ImportAPIs) { auto param = GetParam(); - void const* data = param.blob.data(); - uint32_t dataLen = (uint32_t)param.blob.size(); + span blob = GetMetadataForFile(param); + void const* data = blob; + uint32_t dataLen = (uint32_t)blob.size(); // Load metadata dncp::com_ptr baselineImport; @@ -1990,12 +1991,12 @@ TEST_P(MetadataImportTest, ImportAPIs) } } -INSTANTIATE_TEST_SUITE_P(MetaDataImportTestCore, MetadataImportTest, testing::ValuesIn(MetadataInDirectory(GetBaselineDirectory())), PrintFileBlob); +INSTANTIATE_TEST_SUITE_P(MetaDataImportTestCore, MetadataImportTest, testing::ValuesIn(MetadataFilesInDirectory(GetBaselineDirectory())), PrintName); -INSTANTIATE_TEST_SUITE_P(MetaDataImportTestFx4_0, MetadataImportTest, testing::ValuesIn(MetadataInDirectory(FindFrameworkInstall("v4.0.30319"))), PrintFileBlob); -INSTANTIATE_TEST_SUITE_P(MetaDataImportTestFx2_0, MetadataImportTest, testing::ValuesIn(MetadataInDirectory(FindFrameworkInstall("v2.0.50727"))), PrintFileBlob); +INSTANTIATE_TEST_SUITE_P(MetaDataImportTestFx4_0, MetadataImportTest, testing::ValuesIn(MetadataFilesInDirectory(FindFrameworkInstall("v4.0.30319"))), PrintName); +INSTANTIATE_TEST_SUITE_P(MetaDataImportTestFx2_0, MetadataImportTest, testing::ValuesIn(MetadataFilesInDirectory(FindFrameworkInstall("v2.0.50727"))), PrintName); -INSTANTIATE_TEST_SUITE_P(MetaDataImportTest_IndirectionTables, MetadataImportTest, testing::Values(ImageWithDelta()), PrintFileBlob); +INSTANTIATE_TEST_SUITE_P(MetaDataImportTest_IndirectionTables, MetadataImportTest, testing::Values(MetadataFile{ MetadataFile::Kind::Generated, DeltaImageKey }), PrintName); class MetaDataLongRunningTest : public RegressionTest { @@ -2004,8 +2005,9 @@ class MetaDataLongRunningTest : public RegressionTest TEST_P(MetaDataLongRunningTest, ImportAPIs) { auto param = GetParam(); - void const* data = param.blob.data(); - uint32_t dataLen = (uint32_t)param.blob.size(); + span blob = GetMetadataForFile(param); + void const* data = blob; + uint32_t dataLen = (uint32_t)blob.size(); // Load metadata dncp::com_ptr baselineImport; @@ -2095,4 +2097,4 @@ TEST_P(MetaDataLongRunningTest, ImportAPIs) } } -INSTANTIATE_TEST_SUITE_P(MetaDataLongRunningTest_CoreLibs, MetaDataLongRunningTest, testing::ValuesIn(CoreLibs()), PrintFileBlob); +INSTANTIATE_TEST_SUITE_P(MetaDataLongRunningTest_CoreLibs, MetaDataLongRunningTest, testing::ValuesIn(CoreLibFiles()), PrintName); From f0ffaa0339c60a163e45adf285587b0046a955eb Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 22 Dec 2023 12:17:10 -0800 Subject: [PATCH 22/59] Use WIL and lookup the .NET Framework install location in regtest, not in Regression.Locator --- test/Regression.Locator/LocatorHelpers.cs | 14 -------------- test/regtest/CMakeLists.txt | 16 ++++++++++++++++ test/regtest/discovery.cpp | 17 +++++++---------- 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/test/Regression.Locator/LocatorHelpers.cs b/test/Regression.Locator/LocatorHelpers.cs index 0cc5b339..89ccf7da 100644 --- a/test/Regression.Locator/LocatorHelpers.cs +++ b/test/Regression.Locator/LocatorHelpers.cs @@ -19,20 +19,6 @@ public static unsafe class LocatorHelpers return (byte*)Marshal.StringToCoTaskMemUTF8(path); } - [SupportedOSPlatform("windows")] - [UnmanagedCallersOnly(EntryPoint = "GetFrameworkPath")] - public static byte* GetFrameworkPath([DNNE.C99Type("char const*")]sbyte* version) - { - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - return null; - } - - using var key = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\.NETFramework")!; - string path = Path.Combine((string)key.GetValue("InstallRoot")!, new string(version)); - return (byte*)Marshal.StringToCoTaskMemUTF8(path); - } - [UnmanagedCallersOnly(EntryPoint = "GetImageAndDeltas")] public static void GetImageAndDeltas(byte** image, uint* imageLen, uint* deltaCount, byte*** deltas, uint** deltaLengths) { diff --git a/test/regtest/CMakeLists.txt b/test/regtest/CMakeLists.txt index 0990a68c..d2e5a286 100644 --- a/test/regtest/CMakeLists.txt +++ b/test/regtest/CMakeLists.txt @@ -17,6 +17,22 @@ if (NOT WIN32) target_link_libraries(regtest PRIVATE dncp::winhdrs) endif() +if (WIN32) + FetchContent_Declare( + wil + GIT_REPOSITORY + https://github.com/microsoft/wil.git + GIT_TAG + v1.0.231216.1 + ) + + set(WIL_BUILD_PACKAGING OFF CACHE BOOL "" FORCE) + set(WIL_BUILD_TESTS OFF CACHE BOOL "" FORCE) + + FetchContent_MakeAvailable(wil) + target_link_libraries(regtest PRIVATE WIL) +endif() + target_link_libraries(regtest PRIVATE Regression.Locator) add_custom_command(TARGET regtest POST_BUILD diff --git a/test/regtest/discovery.cpp b/test/regtest/discovery.cpp index 0d49f5ad..5ced72e5 100644 --- a/test/regtest/discovery.cpp +++ b/test/regtest/discovery.cpp @@ -5,6 +5,10 @@ #include #include +#ifdef _WIN32 +#include +#endif + #include #ifdef _WIN32 @@ -12,9 +16,6 @@ #endif #include -#define THROW_IF_FAILED(hr) if (FAILED(hr)) throw std::runtime_error(#hr) - - namespace { std::string baselinePath; @@ -226,13 +227,9 @@ malloc_span GetRegressionAssemblyMetadata() std::string FindFrameworkInstall(std::string version) { #ifdef _WIN32 - dncp::cotaskmem_ptr installPath { (char*)GetFrameworkPath(version.c_str()) }; - if (installPath == nullptr) - { - std::cerr << "Failed to find framework install for version: " << version << "\n"; - return {}; - } - return installPath.get(); + auto key = wil::reg::create_unique_key(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\.NETFramework"); + std::filesystem::path installPath{ wil::reg::get_value_string(key.get(), L"InstallRoot") }; + return (installPath / version).generic_string(); #else return {}; #endif From 2fd13f68cc5fadd00e12263aeac72c3a0a5a824e Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 22 Dec 2023 14:20:30 -0800 Subject: [PATCH 23/59] Manually produce a minimal image with indirection tables at runtime using IMetaDataEmit instead of using Roslyn to remove another managed dependency. --- src/interfaces/metadataimport.cpp | 2 +- test/Regression.Locator/DeltaImageBuilder.cs | 121 ------------------ test/Regression.Locator/LocatorHelpers.cs | 36 +----- .../Regression.Locator.csproj | 2 - test/regtest/discovery.cpp | 118 +++++++++++++---- test/regtest/fixtures.h | 2 +- test/regtest/metadata.cpp | 2 +- 7 files changed, 97 insertions(+), 186 deletions(-) delete mode 100644 test/Regression.Locator/DeltaImageBuilder.cs diff --git a/src/interfaces/metadataimport.cpp b/src/interfaces/metadataimport.cpp index 0c60f1ea..de232412 100644 --- a/src/interfaces/metadataimport.cpp +++ b/src/interfaces/metadataimport.cpp @@ -2727,7 +2727,7 @@ HRESULT STDMETHODCALLTYPE MetadataImportRO::GetCustomAttributeByName( mdcursor_t cursor; uint32_t count; if (!md_create_cursor(_md_ptr.get(), mdtid_CustomAttribute, &cursor, &count)) - return CLDB_E_RECORD_NOTFOUND; + return S_FALSE; // If no custom attributes are defined, treat it the same as if the attribute is not found. char buffer[1024]; pal::StringConvert cvt{ szName, buffer }; diff --git a/test/Regression.Locator/DeltaImageBuilder.cs b/test/Regression.Locator/DeltaImageBuilder.cs deleted file mode 100644 index 25d37b25..00000000 --- a/test/Regression.Locator/DeltaImageBuilder.cs +++ /dev/null @@ -1,121 +0,0 @@ -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Emit; -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Regression.Locator -{ - internal static class DeltaImageBuilder - { - public static DeltaAssembly CreateAssembly() - { - Compilation baselineCompilation = CSharpCompilation.Create("DeltaAssembly1") - .WithReferences(Basic.Reference.Assemblies.NetStandard20.ReferenceInfos.netstandard.Reference) - .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); - SyntaxTree sourceBase = CSharpSyntaxTree.ParseText(""" - using System; - public class Class1 - { - private int field; - public void Method(int x) - { - } - - public int Property { get; set; } - - public event EventHandler? Event; - } - """); - baselineCompilation = baselineCompilation.AddSyntaxTrees( - sourceBase, - CSharpSyntaxTree.ParseText(""" - using System; - public class Class2 - { - private int field; - public void Method(int x) - { - } - - public int Property { get; set; } - - public event EventHandler? Event; - } - """)); - - Compilation diffCompilation = baselineCompilation.ReplaceSyntaxTree( - sourceBase, - CSharpSyntaxTree.ParseText(""" - using System; - public class Class1 - { - private class Attr : Attribute { } - - private short field2; - private int field; - - [return:Attr] - public void Method(int x) - { - } - - public int Property { get; set; } - - public short Property2 { get; set; } - - public event EventHandler? Event; - - public event EventHandler? Event2; - } - """)); - - var diagnostics = baselineCompilation.GetDiagnostics(); - MemoryStream baselineImage = new(); - baselineCompilation.Emit(baselineImage, options: new EmitOptions(debugInformationFormat: DebugInformationFormat.PortablePdb)); - baselineImage.Seek(0, SeekOrigin.Begin); - - ModuleMetadata metadata = ModuleMetadata.CreateFromStream(baselineImage); - EmitBaseline baseline = EmitBaseline.CreateInitialBaseline(metadata, _ => default, _ => default, true); - - MemoryStream mddiffStream = new(); - - diffCompilation.EmitDifference( - baseline, - new[] - { - CreateSemanticEdit(SemanticEditKind.Update, baselineCompilation, diffCompilation, c => c.GetTypeByMetadataName("Class1")), - CreateSemanticEdit(SemanticEditKind.Insert, baselineCompilation, diffCompilation, c => c.GetTypeByMetadataName("Class1")!.GetMembers("field2").FirstOrDefault()), - CreateSemanticEdit(SemanticEditKind.Insert, baselineCompilation, diffCompilation, c => c.GetTypeByMetadataName("Class1")!.GetMembers("Property2").FirstOrDefault()), - CreateSemanticEdit(SemanticEditKind.Insert, baselineCompilation, diffCompilation, c => c.GetTypeByMetadataName("Class1")!.GetMembers("Event2").FirstOrDefault()), - CreateSemanticEdit(SemanticEditKind.Update, baselineCompilation, diffCompilation, c => c.GetTypeByMetadataName("Class1")!.GetMembers("Method").FirstOrDefault()), - CreateSemanticEdit(SemanticEditKind.Insert, baselineCompilation, diffCompilation, c => c.GetTypeByMetadataName("Class1")!.GetTypeMembers("Attr").FirstOrDefault()), - }, - s => - { - return false; - }, - mddiffStream, - new MemoryStream(), // il stream - new MemoryStream() // pdb diff stream - ); - - baselineImage.Seek(0, SeekOrigin.Begin); - return new DeltaAssembly( - baselineImage.ToArray(), - [mddiffStream.ToArray()] - ); - } - - static SemanticEdit CreateSemanticEdit(SemanticEditKind editKind, Compilation baseline, Compilation diff, Func findSymbol) - { - return new SemanticEdit(editKind, findSymbol(baseline), findSymbol(diff)); - } - - public record struct DeltaAssembly(byte[] BaseImage, ImmutableArray MetadataDeltas); - } -} diff --git a/test/Regression.Locator/LocatorHelpers.cs b/test/Regression.Locator/LocatorHelpers.cs index 89ccf7da..1bb12f95 100644 --- a/test/Regression.Locator/LocatorHelpers.cs +++ b/test/Regression.Locator/LocatorHelpers.cs @@ -1,6 +1,4 @@ using System.Runtime.InteropServices; -using System.Runtime.Versioning; -using Microsoft.Win32; namespace Regression.Locator; public static unsafe class LocatorHelpers @@ -13,41 +11,9 @@ public static unsafe class LocatorHelpers } [UnmanagedCallersOnly(EntryPoint = "GetRegressionTargetAssemblyPath")] - public static byte* GetCoreLibPath() + public static byte* GetRegressionTargetAssemblyPath() { string path = Path.Combine(Path.GetDirectoryName(typeof(LocatorHelpers).Assembly.Location!)!, "Regression.TargetAssembly.dll"); return (byte*)Marshal.StringToCoTaskMemUTF8(path); } - - [UnmanagedCallersOnly(EntryPoint = "GetImageAndDeltas")] - public static void GetImageAndDeltas(byte** image, uint* imageLen, uint* deltaCount, byte*** deltas, uint** deltaLengths) - { - var asm = DeltaImageBuilder.CreateAssembly(); - *imageLen = (uint)asm.BaseImage.Length; - *image = (byte*)NativeMemory.Alloc(*imageLen); - asm.BaseImage.CopyTo(new Span(*image, (int)*imageLen)); - - *deltaCount = (uint)asm.MetadataDeltas.Length; - *deltas = (byte**)NativeMemory.Alloc(*deltaCount * (uint)sizeof(byte*)); - *deltaLengths = (uint*)NativeMemory.Alloc(*deltaCount * sizeof(uint)); - - for (int i = 0; i < asm.MetadataDeltas.Length; i++) - { - (*deltas)[i] = (byte*)NativeMemory.Alloc((uint)asm.MetadataDeltas[i].Length); - asm.MetadataDeltas[i].CopyTo(new Span((*deltas)[i], asm.MetadataDeltas[i].Length)); - (*deltaLengths)[i] = (uint)asm.MetadataDeltas[i].Length; - } - } - - [UnmanagedCallersOnly(EntryPoint = "FreeImageAndDeltas")] - public static void FreeImageAndDeltas(byte* image, uint deltaCount, byte** deltas, uint* deltaLengths) - { - NativeMemory.Free(image); - for (int i = 0; i < deltaCount; i++) - { - NativeMemory.Free(deltas[i]); - } - NativeMemory.Free(deltas); - NativeMemory.Free(deltaLengths); - } } diff --git a/test/Regression.Locator/Regression.Locator.csproj b/test/Regression.Locator/Regression.Locator.csproj index 4d6dc209..1b991916 100644 --- a/test/Regression.Locator/Regression.Locator.csproj +++ b/test/Regression.Locator/Regression.Locator.csproj @@ -11,8 +11,6 @@ - - diff --git a/test/regtest/discovery.cpp b/test/regtest/discovery.cpp index 5ced72e5..0adf607f 100644 --- a/test/regtest/discovery.cpp +++ b/test/regtest/discovery.cpp @@ -48,46 +48,114 @@ namespace return b; } - - malloc_span CreateImageWithAppliedDelta() + + // Create an image with indirection tables, like an image that has had a delta applied to it. + // This is used to test that the importer can handle out-of-order rows. + // This image is intentinally minimal as our other regression tests cover more full-filled metadata scenarios. + malloc_span CreateImageWithIndirectionTables() { - uint8_t* baseImage = nullptr; - uint32_t baseImageSize = 0; - uint8_t** deltas = nullptr; - uint32_t* deltaSizes = nullptr; - uint32_t numDeltas = 0; + dncp::com_ptr image; + THROW_IF_FAILED(TestBaseline::DeltaMetadataBuilder->DefineScope(CLSID_CorMetaDataRuntime, 0, IID_IMetaDataEmit, (IUnknown**)&image)); + + THROW_IF_FAILED(image->SetModuleProps(W("IndirectionTables.dll"))); + + dncp::com_ptr assemblyEmit; + THROW_IF_FAILED(image->QueryInterface(IID_IMetaDataAssemblyEmit, (void**)&assemblyEmit)); - GetImageAndDeltas(&baseImage, &baseImageSize, &numDeltas, &deltas, &deltaSizes); + ASSEMBLYMETADATA assemblyMetadata = { 0 }; - auto _ = on_scope_exit([&]() { FreeImageAndDeltas(baseImage, numDeltas, deltas, deltaSizes); }); + mdAssemblyRef systemRuntimeRef; + THROW_IF_FAILED(assemblyEmit->DefineAssemblyRef(nullptr, 0, W("System.Runtime"), &assemblyMetadata, nullptr, 0, 0, &systemRuntimeRef)); - dncp::com_ptr baseline; - THROW_IF_FAILED(TestBaseline::DeltaMetadataBuilder->OpenScopeOnMemory(baseImage, baseImageSize, 0, IID_IMetaDataEmit, (IUnknown**)&baseline)); + mdTypeRef systemObject; + THROW_IF_FAILED(image->DefineTypeRefByName(systemRuntimeRef, W("System.Object"), &systemObject)); - for (uint32_t i = 0; i < numDeltas; i++) - { - dncp::com_ptr delta; - THROW_IF_FAILED(TestBaseline::DeltaMetadataBuilder->OpenScopeOnMemory(deltas[i], deltaSizes[i], 0, IID_IMetaDataImport, (IUnknown**)&delta)); + // Define two types so we can define out-of-order rows. + mdTypeDef type1; + THROW_IF_FAILED(image->DefineTypeDef(W("Type1"), tdSealed, systemObject, nullptr, &type1)); - THROW_IF_FAILED(baseline->ApplyEditAndContinue(delta)); - } + mdTypeDef type2; + THROW_IF_FAILED(image->DefineTypeDef(W("Type2"), tdSealed, systemObject, nullptr, &type2)); + + // Define a signature that has two parameters and a return type. + // This will provide us with enough structure to define out-of-order Param rows. + std::array signature = { (uint8_t)IMAGE_CEE_CS_CALLCONV_DEFAULT, (uint8_t)0x02, (uint8_t)ELEMENT_TYPE_I4, (uint8_t)ELEMENT_TYPE_I2, (uint8_t)ELEMENT_TYPE_I8}; + + mdMethodDef method1; + THROW_IF_FAILED(image->DefineMethod(type1, W("Method1"), 0, signature.data(), (ULONG)signature.size(), 0, 0, &method1)); + + mdParamDef param1; + THROW_IF_FAILED(image->DefineParam(method1, 2, W("Param2"), 0, 0, nullptr, 0, ¶m1)); + + // Define the Param row for the first parameter after we've already defined the second parameter. + mdParamDef paramOutOfOrder; + THROW_IF_FAILED(image->DefineParam(method1, 1, W("Param1"), 0, 0, nullptr, 0, ¶mOutOfOrder)); + + mdMethodDef method2; + THROW_IF_FAILED(image->DefineMethod(type2, W("Method2"), 0, signature.data(), (ULONG)signature.size(), 0, 0, &method2)); + + // Define a method on the first type after we've already defined a method on the second type. + mdMethodDef methodOutOfOrder; + THROW_IF_FAILED(image->DefineMethod(type1, W("MethodOutOfOrder"), 0, signature.data(), (ULONG)signature.size(), 0, 0, &methodOutOfOrder)); + + std::array fieldSignature = { (uint8_t)IMAGE_CEE_CS_CALLCONV_FIELD, (uint8_t)ELEMENT_TYPE_I4 }; - malloc_span imageWithDelta; - DWORD compositeImageSize; - THROW_IF_FAILED(baseline->GetSaveSize(CorSaveSize::cssAccurate, &compositeImageSize)); + mdFieldDef field1; + THROW_IF_FAILED(image->DefineField(type1, W("Field1"), 0, fieldSignature.data(), (ULONG)fieldSignature.size(), 0, nullptr, 0, &field1)); + + mdFieldDef field2; + THROW_IF_FAILED(image->DefineField(type2, W("Field2"), 0, fieldSignature.data(), (ULONG)fieldSignature.size(), 0, nullptr, 0, &field2)); + + // Define a field on the first type after we've already defined a field on the second type. + mdFieldDef fieldOutOfOrder; + THROW_IF_FAILED(image->DefineField(type1, W("FieldOutOfOrder"), 0, fieldSignature.data(), (ULONG)fieldSignature.size(), 0, nullptr, 0, &fieldOutOfOrder)); + + std::array propertySignature = { (uint8_t)IMAGE_CEE_CS_CALLCONV_PROPERTY, (uint8_t)ELEMENT_TYPE_I4 }; + std::array getterSignature = { (uint8_t)IMAGE_CEE_CS_CALLCONV_DEFAULT, (uint8_t)0x00, (uint8_t)ELEMENT_TYPE_I4 }; + + mdMethodDef getter1; + THROW_IF_FAILED(image->DefineMethod(type1, W("get_Property1"), 0, getterSignature.data(), (ULONG)getterSignature.size(), 0, 0, &getter1)); + + mdProperty property1; + THROW_IF_FAILED(image->DefineProperty(type1, W("Property1"), 0, propertySignature.data(), (ULONG)propertySignature.size(), 0, nullptr, 0, getter1, mdMethodDefNil, nullptr, &property1)); + + mdMethodDef getter2; + THROW_IF_FAILED(image->DefineMethod(type2, W("get_Property2"), 0, getterSignature.data(), (ULONG)getterSignature.size(), 0, 0, &getter2)); + + mdProperty property2; + THROW_IF_FAILED(image->DefineProperty(type2, W("Property2"), 0, propertySignature.data(), (ULONG)propertySignature.size(), 0, nullptr, 0, getter2, mdMethodDefNil, nullptr, &property2)); + + // Define a property on the first type after we've already defined a property on the second type. + mdProperty propertyOutOfOrder; + THROW_IF_FAILED(image->DefineProperty(type1, W("PropertyOutOfOrder"), 0, propertySignature.data(), (ULONG)propertySignature.size(), 0, nullptr, 0, mdMethodDefNil, mdMethodDefNil, nullptr, &propertyOutOfOrder)); + + mdTypeRef eventHandlerRef; + THROW_IF_FAILED(image->DefineTypeRefByName(systemRuntimeRef, W("System.EventHandler"), &eventHandlerRef)); - imageWithDelta = { (uint8_t*)malloc(compositeImageSize), compositeImageSize }; + mdEvent event1; + THROW_IF_FAILED(image->DefineEvent(type1, W("Event1"), 0, eventHandlerRef, mdMethodDefNil, mdMethodDefNil, mdMethodDefNil, nullptr, &event1)); + + mdEvent event2; + THROW_IF_FAILED(image->DefineEvent(type2, W("Event2"), 0, eventHandlerRef, mdMethodDefNil, mdMethodDefNil, mdMethodDefNil, nullptr, &event2)); + + // Define an event on the first type after we've already defined an event on the second type. + mdEvent eventOutOfOrder; + THROW_IF_FAILED(image->DefineEvent(type1, W("EventOutOfOrder"), 0, eventHandlerRef, mdMethodDefNil, mdMethodDefNil, mdMethodDefNil, nullptr, &eventOutOfOrder)); + + ULONG size; + THROW_IF_FAILED(image->GetSaveSize(cssAccurate, &size)); - THROW_IF_FAILED(baseline->SaveToMemory(imageWithDelta, compositeImageSize)); + malloc_span imageWithIndirectionTables{ (uint8_t*)malloc(size), size }; + THROW_IF_FAILED(image->SaveToMemory(imageWithIndirectionTables, size)); - return imageWithDelta; + return imageWithIndirectionTables; } malloc_span GetMetadataFromKey(std::string key) { - if (key == DeltaImageKey) + if (key == IndirectionTablesKey) { - return CreateImageWithAppliedDelta(); + return CreateImageWithIndirectionTables(); } return {}; } diff --git a/test/regtest/fixtures.h b/test/regtest/fixtures.h index c048be9b..17df523b 100644 --- a/test/regtest/fixtures.h +++ b/test/regtest/fixtures.h @@ -34,7 +34,7 @@ struct MetadataFile final } }; -inline static std::string DeltaImageKey = "DeltaImage"; +inline static std::string IndirectionTablesKey = "IndirectionTables"; std::string PrintName(testing::TestParamInfo info); diff --git a/test/regtest/metadata.cpp b/test/regtest/metadata.cpp index cea67b26..cc6adaf3 100644 --- a/test/regtest/metadata.cpp +++ b/test/regtest/metadata.cpp @@ -1996,7 +1996,7 @@ INSTANTIATE_TEST_SUITE_P(MetaDataImportTestCore, MetadataImportTest, testing::Va INSTANTIATE_TEST_SUITE_P(MetaDataImportTestFx4_0, MetadataImportTest, testing::ValuesIn(MetadataFilesInDirectory(FindFrameworkInstall("v4.0.30319"))), PrintName); INSTANTIATE_TEST_SUITE_P(MetaDataImportTestFx2_0, MetadataImportTest, testing::ValuesIn(MetadataFilesInDirectory(FindFrameworkInstall("v2.0.50727"))), PrintName); -INSTANTIATE_TEST_SUITE_P(MetaDataImportTest_IndirectionTables, MetadataImportTest, testing::Values(MetadataFile{ MetadataFile::Kind::Generated, DeltaImageKey }), PrintName); +INSTANTIATE_TEST_SUITE_P(MetaDataImportTest_IndirectionTables, MetadataImportTest, testing::Values(MetadataFile{ MetadataFile::Kind::Generated, IndirectionTablesKey }), PrintName); class MetaDataLongRunningTest : public RegressionTest { From e327198e9a2d1668056db4cb79af22beee6fcac7 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 22 Dec 2023 14:33:15 -0800 Subject: [PATCH 24/59] Reference winhdrs from regpal --- test/regpal/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/regpal/CMakeLists.txt b/test/regpal/CMakeLists.txt index 70528172..7e3256d8 100644 --- a/test/regpal/CMakeLists.txt +++ b/test/regpal/CMakeLists.txt @@ -6,4 +6,8 @@ add_library(regpal STATIC ${SOURCES}) target_link_libraries(regpal PUBLIC Regression.Locator dncp::dncp) +if (NOT WIN32) + target_link_libraries(regpal PUBLIC dncp::winhdrs) +endif() + target_include_directories(regpal PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) \ No newline at end of file From a4f5625a9fd0c61591edb7e4064d2bacd4f7dba3 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Sun, 24 Dec 2023 21:02:53 -0800 Subject: [PATCH 25/59] Add dncp include, define iids in pal.cpp for tests, increase discovery timeout for windows --- test/regpal/pal.cpp | 2 +- test/regpal/pal.hpp | 1 + test/regtest/CMakeLists.txt | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/test/regpal/pal.cpp b/test/regpal/pal.cpp index 6b8f6797..e5cd369a 100644 --- a/test/regpal/pal.cpp +++ b/test/regpal/pal.cpp @@ -1,5 +1,5 @@ +#define DNCP_DEFINE_GUID #include "pal.hpp" -#include #ifdef _WIN32 #define DNNE_API_OVERRIDE __declspec(dllimport) diff --git a/test/regpal/pal.hpp b/test/regpal/pal.hpp index bc601c0d..1c63ca81 100644 --- a/test/regpal/pal.hpp +++ b/test/regpal/pal.hpp @@ -1,6 +1,7 @@ #ifndef _TEST_REGPAL_PAL_H_ #define _TEST_REGPAL_PAL_H_ +#include #include #include #include diff --git a/test/regtest/CMakeLists.txt b/test/regtest/CMakeLists.txt index d2e5a286..4319d9fa 100644 --- a/test/regtest/CMakeLists.txt +++ b/test/regtest/CMakeLists.txt @@ -41,4 +41,4 @@ add_custom_command(TARGET regtest POST_BUILD add_custom_command(TARGET regtest POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${RegressionLocatorDirectory} $) -gtest_discover_tests(regtest) \ No newline at end of file +gtest_discover_tests(regtest DISCOVERY_TIMEOUT 10) \ No newline at end of file From 56a49c46eb6f58bb3368d5660386e37e772a3a28 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Sun, 24 Dec 2023 21:11:08 -0800 Subject: [PATCH 26/59] Add includes for standard type definitions --- test/regpal/pal.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/regpal/pal.hpp b/test/regpal/pal.hpp index 1c63ca81..e7294ebe 100644 --- a/test/regpal/pal.hpp +++ b/test/regpal/pal.hpp @@ -1,6 +1,8 @@ #ifndef _TEST_REGPAL_PAL_H_ #define _TEST_REGPAL_PAL_H_ +#include +#include #include #include #include From 7f436476f790376df55f55798a30b26a4cc8269c Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 27 Dec 2023 10:15:18 -0800 Subject: [PATCH 27/59] Fix current errors in unix/macos builds --- src/inc/internal/dnmd_tools_platform.hpp | 1 + test/regpal/pal.hpp | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/src/inc/internal/dnmd_tools_platform.hpp b/src/inc/internal/dnmd_tools_platform.hpp index efb69f18..78b9a00d 100644 --- a/src/inc/internal/dnmd_tools_platform.hpp +++ b/src/inc/internal/dnmd_tools_platform.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "dnmd_platform.hpp" #include "span.hpp" diff --git a/test/regpal/pal.hpp b/test/regpal/pal.hpp index e7294ebe..8d2a7ac5 100644 --- a/test/regpal/pal.hpp +++ b/test/regpal/pal.hpp @@ -4,6 +4,11 @@ #include #include #include + +#ifndef BUILD_WINDOWS +#include +#endif + #include #include #include From 58a0eeeafd75adbcb6564a2f87fbd0ce383c7d49 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 27 Dec 2023 10:18:32 -0800 Subject: [PATCH 28/59] DNNE doesn't add the lib prefix on non-Windows --- test/ImportManagedComponents.cmake | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/ImportManagedComponents.cmake b/test/ImportManagedComponents.cmake index 3991384a..82797cb1 100644 --- a/test/ImportManagedComponents.cmake +++ b/test/ImportManagedComponents.cmake @@ -14,12 +14,12 @@ if (WIN32) IMPORTED_IMPLIB_RELEASE ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/release/Regression.LocatorNE.lib) elseif (APPLE) set_target_properties(Regression.Locator PROPERTIES - IMPORTED_LOCATION_DEBUG ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/debug/libRegression.LocatorNE.dylib - IMPORTED_LOCATION_RELEASE ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/release/libRegression.LocatorNE.dylib) + IMPORTED_LOCATION_DEBUG ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/debug/Regression.LocatorNE.dylib + IMPORTED_LOCATION_RELEASE ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/release/Regression.LocatorNE.dylib) else() set_target_properties(Regression.Locator PROPERTIES - IMPORTED_LOCATION_DEBUG ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/debug/libRegression.LocatorNE.so - IMPORTED_LOCATION_RELEASE ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/release/libRegression.LocatorNE.so) + IMPORTED_LOCATION_DEBUG ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/debug/Regression.LocatorNE.so + IMPORTED_LOCATION_RELEASE ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/release/Regression.LocatorNE.so) endif() set_target_properties(Regression.Locator PROPERTIES From b9c07d97439a84f565e233212047b24652973d47 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 27 Dec 2023 10:28:10 -0800 Subject: [PATCH 29/59] Set define to avoid windows.h include in corsym.h --- test/regtest/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/regtest/CMakeLists.txt b/test/regtest/CMakeLists.txt index 4319d9fa..06cd946c 100644 --- a/test/regtest/CMakeLists.txt +++ b/test/regtest/CMakeLists.txt @@ -13,6 +13,8 @@ add_executable(regtest ${SOURCES} ${HEADERS}) target_link_libraries(regtest PRIVATE dnmd::interfaces gtest gmock dncp::dncp regpal) # Reference gmock for better collection assertions set_target_properties(regtest PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON) # Require C++17 for the tests so we can use std::filesystem. +target_compile_definitions(regtest PRIVATE COM_NO_WINDOWS_H) + if (NOT WIN32) target_link_libraries(regtest PRIVATE dncp::winhdrs) endif() From c8b0db7ab046c85399f9f4ff0e6ea702d720c707 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 27 Dec 2023 11:23:42 -0800 Subject: [PATCH 30/59] Fix build on Windows --- test/regpal/pal.hpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/regpal/pal.hpp b/test/regpal/pal.hpp index 8d2a7ac5..d10eda7c 100644 --- a/test/regpal/pal.hpp +++ b/test/regpal/pal.hpp @@ -3,12 +3,15 @@ #include #include -#include -#ifndef BUILD_WINDOWS +#ifdef BUILD_WINDOWS +#define NOMINMAX +#include +#else #include #endif +#include #include #include #include From 4c1bb24394de4e587d41cfc662aaa45dbaab247e Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 27 Dec 2023 11:29:39 -0800 Subject: [PATCH 31/59] Fix references to IMAGE_DEBUG_DIRECTORY in regtest and use the defines we set up in configure.cmake --- test/regpal/pal.cpp | 11 ++++------- test/regperf/perf.cpp | 2 +- test/regtest/baseline.h | 4 +++- test/regtest/discovery.cpp | 6 +++--- test/regtest/fixtures.h | 2 +- test/regtest/main.cpp | 2 +- 6 files changed, 13 insertions(+), 14 deletions(-) diff --git a/test/regpal/pal.cpp b/test/regpal/pal.cpp index e5cd369a..917b5ce5 100644 --- a/test/regpal/pal.cpp +++ b/test/regpal/pal.cpp @@ -1,15 +1,12 @@ #define DNCP_DEFINE_GUID #include "pal.hpp" -#ifdef _WIN32 +#ifdef BUILD_WINDOWS #define DNNE_API_OVERRIDE __declspec(dllimport) #endif #include -#ifdef _WIN32 -#define NOMINMAX -#include -#else +#ifndef BUILD_WINDOWS #include #endif @@ -21,7 +18,7 @@ namespace { void* LoadModule(char const* path) { -#ifdef _WIN32 +#ifdef BUILD_WINDOWS return LoadLibraryA(path); #else return dlopen(path, RTLD_LAZY); @@ -30,7 +27,7 @@ namespace void* GetSymbol(void* module, char const* name) { -#ifdef _WIN32 +#ifdef BUILD_WINDOWS return GetProcAddress((HMODULE)module, name); #else return dlsym(module, name); diff --git a/test/regperf/perf.cpp b/test/regperf/perf.cpp index eea01027..cd2deb42 100644 --- a/test/regperf/perf.cpp +++ b/test/regperf/perf.cpp @@ -12,7 +12,7 @@ #include #include -#ifdef _WIN32 +#ifdef BUILD_WINDOWS #define DNNE_API_OVERRIDE __declspec(dllimport) #endif #include diff --git a/test/regtest/baseline.h b/test/regtest/baseline.h index 35a67217..2d126160 100644 --- a/test/regtest/baseline.h +++ b/test/regtest/baseline.h @@ -1,9 +1,11 @@ #ifndef _TEST_REGTEST_BASELINE_H_ #define _TEST_REGTEST_BASELINE_H_ -#if defined(_MSC_VER) +#if BUILD_WINDOWS #define NOMINMAX #include +#else +#include #endif #include diff --git a/test/regtest/discovery.cpp b/test/regtest/discovery.cpp index 0adf607f..60042cbe 100644 --- a/test/regtest/discovery.cpp +++ b/test/regtest/discovery.cpp @@ -5,13 +5,13 @@ #include #include -#ifdef _WIN32 +#ifdef BUILD_WINDOWS #include #endif #include -#ifdef _WIN32 +#ifdef BUILD_WINDOWS #define DNNE_API_OVERRIDE __declspec(dllimport) #endif #include @@ -294,7 +294,7 @@ malloc_span GetRegressionAssemblyMetadata() std::string FindFrameworkInstall(std::string version) { -#ifdef _WIN32 +#ifdef BUILD_WINDOWS auto key = wil::reg::create_unique_key(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\.NETFramework"); std::filesystem::path installPath{ wil::reg::get_value_string(key.get(), L"InstallRoot") }; return (installPath / version).generic_string(); diff --git a/test/regtest/fixtures.h b/test/regtest/fixtures.h index 17df523b..ab4acb97 100644 --- a/test/regtest/fixtures.h +++ b/test/regtest/fixtures.h @@ -24,7 +24,7 @@ struct MetadataFile final } kind; MetadataFile(Kind kind, std::string pathOrKey) - : pathOrKey(std::move(pathOrKey)), kind(kind) {} + : kind(kind), pathOrKey(std::move(pathOrKey)) {} std::string pathOrKey; diff --git a/test/regtest/main.cpp b/test/regtest/main.cpp index 15adcd0d..14033b0d 100644 --- a/test/regtest/main.cpp +++ b/test/regtest/main.cpp @@ -4,7 +4,7 @@ #include "fixtures.h" #include "pal.hpp" -#ifdef _WIN32 +#ifdef BUILD_WINDOWS #define DNNE_API_OVERRIDE __declspec(dllimport) #endif #include From fa3babad754c95ddcc981497fef28a8342ce9b77 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 27 Dec 2023 11:34:00 -0800 Subject: [PATCH 32/59] Add THROW_IF_FAILED macro for non-Windows and increase test discovery timeout for windows --- test/regtest/CMakeLists.txt | 2 +- test/regtest/discovery.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/test/regtest/CMakeLists.txt b/test/regtest/CMakeLists.txt index 06cd946c..7b2a2e4e 100644 --- a/test/regtest/CMakeLists.txt +++ b/test/regtest/CMakeLists.txt @@ -43,4 +43,4 @@ add_custom_command(TARGET regtest POST_BUILD add_custom_command(TARGET regtest POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${RegressionLocatorDirectory} $) -gtest_discover_tests(regtest DISCOVERY_TIMEOUT 10) \ No newline at end of file +gtest_discover_tests(regtest DISCOVERY_TIMEOUT 20) \ No newline at end of file diff --git a/test/regtest/discovery.cpp b/test/regtest/discovery.cpp index 60042cbe..7333b921 100644 --- a/test/regtest/discovery.cpp +++ b/test/regtest/discovery.cpp @@ -7,6 +7,8 @@ #ifdef BUILD_WINDOWS #include +#else +#define THROW_IF_FAILED(x) do { HRESULT hr = (x); if (FAILED(hr)) { throw std::runtime_error("Failed HR when running '" #x "'"); } } while (false) #endif #include From 92b28d4e01dc7d6c6e768a22a3984408540b8f49 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 27 Dec 2023 11:41:33 -0800 Subject: [PATCH 33/59] These CI machines are slow --- test/regtest/CMakeLists.txt | 2 +- test/regtest/fixtures.h | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/test/regtest/CMakeLists.txt b/test/regtest/CMakeLists.txt index 7b2a2e4e..9fdb9999 100644 --- a/test/regtest/CMakeLists.txt +++ b/test/regtest/CMakeLists.txt @@ -43,4 +43,4 @@ add_custom_command(TARGET regtest POST_BUILD add_custom_command(TARGET regtest POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${RegressionLocatorDirectory} $) -gtest_discover_tests(regtest DISCOVERY_TIMEOUT 20) \ No newline at end of file +gtest_discover_tests(regtest DISCOVERY_TIMEOUT 60) \ No newline at end of file diff --git a/test/regtest/fixtures.h b/test/regtest/fixtures.h index ab4acb97..2e1d21fd 100644 --- a/test/regtest/fixtures.h +++ b/test/regtest/fixtures.h @@ -3,9 +3,11 @@ #include -#if defined(_MSC_VER) +#ifdef BUILD_WINDOWS #define NOMINMAX #include +#else +#include #endif #include From 7fdc511699a29c7311397d6360b411818c9f4ab1 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 27 Dec 2023 11:50:13 -0800 Subject: [PATCH 34/59] Polyfill HRESULT asserts that gtest doesn't provide on non-Windows --- test/regtest/metadata.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/regtest/metadata.cpp b/test/regtest/metadata.cpp index cc6adaf3..1dadc706 100644 --- a/test/regtest/metadata.cpp +++ b/test/regtest/metadata.cpp @@ -9,6 +9,14 @@ #include #include + +#ifndef BUILD_WINDOWS +#define EXPECT_HRESULT_SUCCEEDED(hr) EXPECT_THAT((hr), testing::Ge(S_OK)) +#define EXPECT_HRESULT_FAILED(hr) EXPECT_THAT((hr), testing::Lt(0)) +#define ASSERT_HRESULT_SUCCEEDED(hr) ASSERT_THAT((hr), testing::Ge(S_OK)) +#define ASSERT_HRESULT_FAILED(hr) ASSERT_THAT((hr), testing::Lt(0)) +#endif + namespace { HRESULT CreateImport(IMetaDataDispenser* disp, void const* data, uint32_t dataLen, IMetaDataImport2** import) From 16d596bcf4b7de7560ba4827208d63f1da91974e Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 27 Dec 2023 11:51:38 -0800 Subject: [PATCH 35/59] Add some logging because discovery shouldn't take this long --- test/regtest/discovery.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/regtest/discovery.cpp b/test/regtest/discovery.cpp index 7333b921..b6ceb4ed 100644 --- a/test/regtest/discovery.cpp +++ b/test/regtest/discovery.cpp @@ -165,6 +165,7 @@ namespace std::vector MetadataFilesInDirectory(std::string directory) { + std::cerr << "Discovering metadata files in directory: " << directory << "\n"; std::vector scenarios; if (!std::filesystem::exists(directory)) From 10eccbf872e77c88bf6d4b2ad06411c2b36ee565 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 27 Dec 2023 11:53:39 -0800 Subject: [PATCH 36/59] Remove unused function --- test/regtest/metadata.cpp | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/test/regtest/metadata.cpp b/test/regtest/metadata.cpp index 1dadc706..d1bf74d6 100644 --- a/test/regtest/metadata.cpp +++ b/test/regtest/metadata.cpp @@ -30,17 +30,6 @@ namespace reinterpret_cast(import)); } - HRESULT CreateEmit(IMetaDataDispenser* disp, void const* data, uint32_t dataLen, IMetaDataEmit2** emit) - { - assert(disp != nullptr && data != nullptr && dataLen > 0 && emit != nullptr); - return disp->OpenScopeOnMemory( - data, - dataLen, - CorOpenFlags::ofWrite, - IID_IMetaDataEmit2, - reinterpret_cast(emit)); - } - template using static_enum_buffer = std::array; From 93480b292e7c4c2bd22de328bad018f88becf8d1 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 27 Dec 2023 12:04:30 -0800 Subject: [PATCH 37/59] Add more logging to see why discovery is taking a long time --- test/regtest/discovery.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/regtest/discovery.cpp b/test/regtest/discovery.cpp index b6ceb4ed..f3a066e0 100644 --- a/test/regtest/discovery.cpp +++ b/test/regtest/discovery.cpp @@ -56,6 +56,7 @@ namespace // This image is intentinally minimal as our other regression tests cover more full-filled metadata scenarios. malloc_span CreateImageWithIndirectionTables() { + std::cerr << "Creating image with indirection tables" << std::endl; dncp::com_ptr image; THROW_IF_FAILED(TestBaseline::DeltaMetadataBuilder->DefineScope(CLSID_CorMetaDataRuntime, 0, IID_IMetaDataEmit, (IUnknown**)&image)); @@ -165,12 +166,12 @@ namespace std::vector MetadataFilesInDirectory(std::string directory) { - std::cerr << "Discovering metadata files in directory: " << directory << "\n"; + std::cerr << "Discovering metadata files in directory: " << directory << std::endl; std::vector scenarios; if (!std::filesystem::exists(directory)) { - std::cerr << "Directory '" << directory << "' does not exist\n"; + std::cerr << "Directory '" << directory << "' does not exist" << std::endl; return scenarios; } @@ -200,6 +201,7 @@ std::vector MetadataFilesInDirectory(std::string directory) std::vector CoreLibFiles() { + std::cerr << "Discovering CoreLib files" << std::endl; std::vector scenarios; scenarios.emplace_back(MetadataFile::Kind::OnDisk, (std::filesystem::path(baselinePath).parent_path() / "System.Private.CoreLib.dll").generic_string()); @@ -297,6 +299,7 @@ malloc_span GetRegressionAssemblyMetadata() std::string FindFrameworkInstall(std::string version) { + std::cerr << "Discovering framework install for version: " << version << std::endl; #ifdef BUILD_WINDOWS auto key = wil::reg::create_unique_key(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\.NETFramework"); std::filesystem::path installPath{ wil::reg::get_value_string(key.get(), L"InstallRoot") }; From e0251dceca5e8658433b3a68b2e4f9cd9ef41ee1 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 27 Dec 2023 13:46:11 -0800 Subject: [PATCH 38/59] Add more diagnostics and fix path for Unix platforms --- test/ImportManagedComponents.cmake | 2 +- test/regtest/discovery.cpp | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/test/ImportManagedComponents.cmake b/test/ImportManagedComponents.cmake index 82797cb1..a3615e19 100644 --- a/test/ImportManagedComponents.cmake +++ b/test/ImportManagedComponents.cmake @@ -25,4 +25,4 @@ endif() set_target_properties(Regression.Locator PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/$>/) -set(RegressionLocatorDirectory ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/$/) +set(RegressionLocatorDirectory ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/$>/) diff --git a/test/regtest/discovery.cpp b/test/regtest/discovery.cpp index f3a066e0..c5c7e5a6 100644 --- a/test/regtest/discovery.cpp +++ b/test/regtest/discovery.cpp @@ -56,7 +56,7 @@ namespace // This image is intentinally minimal as our other regression tests cover more full-filled metadata scenarios. malloc_span CreateImageWithIndirectionTables() { - std::cerr << "Creating image with indirection tables" << std::endl; + std::cout << "Creating image with indirection tables" << std::endl; dncp::com_ptr image; THROW_IF_FAILED(TestBaseline::DeltaMetadataBuilder->DefineScope(CLSID_CorMetaDataRuntime, 0, IID_IMetaDataEmit, (IUnknown**)&image)); @@ -166,12 +166,12 @@ namespace std::vector MetadataFilesInDirectory(std::string directory) { - std::cerr << "Discovering metadata files in directory: " << directory << std::endl; + std::cout << "Discovering metadata files in directory: " << directory << std::endl; std::vector scenarios; if (!std::filesystem::exists(directory)) { - std::cerr << "Directory '" << directory << "' does not exist" << std::endl; + std::cout << "Directory '" << directory << "' does not exist" << std::endl; return scenarios; } @@ -191,6 +191,12 @@ std::vector MetadataFilesInDirectory(std::string directory) continue; } +#ifdef BUILD_WINDOWS + std::wcout << "Found file: " << entry.path().filename() << std::endl; +#else + std::cout << "Found file: " << entry.path().filename() << std::endl; +#endif + scenarios.emplace_back(MetadataFile::Kind::OnDisk, path.generic_string()); } } @@ -201,7 +207,7 @@ std::vector MetadataFilesInDirectory(std::string directory) std::vector CoreLibFiles() { - std::cerr << "Discovering CoreLib files" << std::endl; + std::cout << "Discovering CoreLib files" << std::endl; std::vector scenarios; scenarios.emplace_back(MetadataFile::Kind::OnDisk, (std::filesystem::path(baselinePath).parent_path() / "System.Private.CoreLib.dll").generic_string()); @@ -257,7 +263,7 @@ span GetMetadataForFile(MetadataFile file) span spanToReturn = b; - auto [_, inserted] = metadataCache.emplace(std::move(file), std::move(b)); + [[maybe_unused]] auto [_, inserted] = metadataCache.emplace(std::move(file), std::move(b)); assert(inserted); return spanToReturn; } @@ -299,7 +305,7 @@ malloc_span GetRegressionAssemblyMetadata() std::string FindFrameworkInstall(std::string version) { - std::cerr << "Discovering framework install for version: " << version << std::endl; + std::cout << "Discovering framework install for version: " << version << std::endl; #ifdef BUILD_WINDOWS auto key = wil::reg::create_unique_key(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\.NETFramework"); std::filesystem::path installPath{ wil::reg::get_value_string(key.get(), L"InstallRoot") }; From c074d079c4206a76d0ad339b65764ed0e88a5342 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 27 Dec 2023 13:53:18 -0800 Subject: [PATCH 39/59] Significantly increase discovery time --- test/regtest/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/regtest/CMakeLists.txt b/test/regtest/CMakeLists.txt index 9fdb9999..49ae0dc0 100644 --- a/test/regtest/CMakeLists.txt +++ b/test/regtest/CMakeLists.txt @@ -43,4 +43,4 @@ add_custom_command(TARGET regtest POST_BUILD add_custom_command(TARGET regtest POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${RegressionLocatorDirectory} $) -gtest_discover_tests(regtest DISCOVERY_TIMEOUT 60) \ No newline at end of file +gtest_discover_tests(regtest DISCOVERY_TIMEOUT 600) \ No newline at end of file From 3e8210d1c110bf121402149c9ab39f831046027e Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 27 Dec 2023 14:31:25 -0800 Subject: [PATCH 40/59] Set no-config imported locations and correctly handle reading R2Rd images (which have masked Machine bits in their PE for non-Windows) --- src/inc/internal/dnmd_platform.hpp | 7 +++++ src/inc/internal/dnmd_tools_platform.hpp | 39 +++++++++++++++++++++--- test/ImportManagedComponents.cmake | 4 +++ test/regpal/CMakeLists.txt | 4 +++ test/regpal/pal.cpp | 2 +- test/regpal/pal.hpp | 10 +----- test/regtest/baseline.h | 10 +----- test/regtest/discovery.cpp | 3 ++ test/regtest/fixtures.h | 11 +------ 9 files changed, 57 insertions(+), 33 deletions(-) diff --git a/src/inc/internal/dnmd_platform.hpp b/src/inc/internal/dnmd_platform.hpp index 767fc9ef..4942af47 100644 --- a/src/inc/internal/dnmd_platform.hpp +++ b/src/inc/internal/dnmd_platform.hpp @@ -13,6 +13,13 @@ #endif // !BUILD_WINDOWS +// Machine code masks for native (R2R) images +#define IMAGE_FILE_MACHINE_OS_MASK_APPLE 0x4644 +#define IMAGE_FILE_MACHINE_OS_MASK_FREEBSD 0xADC4 +#define IMAGE_FILE_MACHINE_OS_MASK_LINUX 0x7B79 +#define IMAGE_FILE_MACHINE_OS_MASK_NETBSD 0x1993 +#define IMAGE_FILE_MACHINE_OS_MASK_SUN 0x1992 + #include #include diff --git a/src/inc/internal/dnmd_tools_platform.hpp b/src/inc/internal/dnmd_tools_platform.hpp index 78b9a00d..d7c0de13 100644 --- a/src/inc/internal/dnmd_tools_platform.hpp +++ b/src/inc/internal/dnmd_tools_platform.hpp @@ -86,6 +86,33 @@ inline bool write_out_file(char const* file, malloc_span b) return true; } +inline bool find_pe_image_bitness(uint16_t machine, uint8_t& bitness) +{ +#define MAKE_MACHINE_CASE(x) \ + case ((x) ^ IMAGE_FILE_MACHINE_OS_MASK_APPLE): \ + case ((x) ^ IMAGE_FILE_MACHINE_OS_MASK_FREEBSD): \ + case ((x) ^ IMAGE_FILE_MACHINE_OS_MASK_LINUX): \ + case ((x) ^ IMAGE_FILE_MACHINE_OS_MASK_NETBSD): \ + case ((x) ^ IMAGE_FILE_MACHINE_OS_MASK_SUN): \ + case (x) + + switch (machine) + { + MAKE_MACHINE_CASE(IMAGE_FILE_MACHINE_I386): + MAKE_MACHINE_CASE(IMAGE_FILE_MACHINE_ARM): + bitness = 32; + return true; + MAKE_MACHINE_CASE(IMAGE_FILE_MACHINE_AMD64): + MAKE_MACHINE_CASE(IMAGE_FILE_MACHINE_ARM64): + bitness = 64; + return true; + default: + return false; + } + +#undef MAKE_MACHINE_CASE +} + inline bool get_metadata_from_pe(malloc_span& b) { if (b.size() < sizeof(IMAGE_DOS_HEADER)) @@ -112,8 +139,13 @@ inline bool get_metadata_from_pe(malloc_span& b) uint16_t section_header_count; uint8_t* section_header_begin; auto nt_header_any = (PIMAGE_NT_HEADERS)(b + dos_header->e_lfanew); - if (nt_header_any->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64 - || nt_header_any->FileHeader.Machine == IMAGE_FILE_MACHINE_ARM64) + uint16_t machine = nt_header_any->FileHeader.Machine; + + uint8_t bitness; + if (!find_pe_image_bitness(machine, bitness)) + return false; + + if (bitness == 64) { auto nt_header64 = (PIMAGE_NT_HEADERS64)nt_header_any; if (remaining_pe_size < sizeof(*nt_header64)) @@ -123,8 +155,7 @@ inline bool get_metadata_from_pe(malloc_span& b) section_header_begin = (uint8_t*)&nt_header64[1]; dotnet_dir = &nt_header64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR]; } - else if (nt_header_any->FileHeader.Machine == IMAGE_FILE_MACHINE_I386 - || nt_header_any->FileHeader.Machine == IMAGE_FILE_MACHINE_ARM) + else if (bitness == 32) { auto nt_header32 = (PIMAGE_NT_HEADERS32)nt_header_any; if (remaining_pe_size < sizeof(*nt_header32)) diff --git a/test/ImportManagedComponents.cmake b/test/ImportManagedComponents.cmake index a3615e19..8e428d4f 100644 --- a/test/ImportManagedComponents.cmake +++ b/test/ImportManagedComponents.cmake @@ -8,16 +8,20 @@ add_library(Regression.Locator IMPORTED SHARED) if (WIN32) set_target_properties(Regression.Locator PROPERTIES + IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/debug/Regression.LocatorNE.dll IMPORTED_LOCATION_DEBUG ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/debug/Regression.LocatorNE.dll IMPORTED_LOCATION_RELEASE ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/release/Regression.LocatorNE.dll + IMPORTED_IMPLIB ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/debug/Regression.LocatorNE.lib IMPORTED_IMPLIB_DEBUG ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/debug/Regression.LocatorNE.lib IMPORTED_IMPLIB_RELEASE ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/release/Regression.LocatorNE.lib) elseif (APPLE) set_target_properties(Regression.Locator PROPERTIES + IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/debug/Regression.LocatorNE.dylib IMPORTED_LOCATION_DEBUG ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/debug/Regression.LocatorNE.dylib IMPORTED_LOCATION_RELEASE ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/release/Regression.LocatorNE.dylib) else() set_target_properties(Regression.Locator PROPERTIES + IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/debug/Regression.LocatorNE.so IMPORTED_LOCATION_DEBUG ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/debug/Regression.LocatorNE.so IMPORTED_LOCATION_RELEASE ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/release/Regression.LocatorNE.so) endif() diff --git a/test/regpal/CMakeLists.txt b/test/regpal/CMakeLists.txt index 7e3256d8..ecdbe595 100644 --- a/test/regpal/CMakeLists.txt +++ b/test/regpal/CMakeLists.txt @@ -10,4 +10,8 @@ if (NOT WIN32) target_link_libraries(regpal PUBLIC dncp::winhdrs) endif() +if (NOT WIN32 AND NOT APPLE) + target_link_libraries(regpal PUBLIC dl) +endif() + target_include_directories(regpal PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) \ No newline at end of file diff --git a/test/regpal/pal.cpp b/test/regpal/pal.cpp index 917b5ce5..25d64d93 100644 --- a/test/regpal/pal.cpp +++ b/test/regpal/pal.cpp @@ -79,7 +79,7 @@ HRESULT pal::GetBaselineMetadataDispenser(IMetaDataDispenser** dispenser) bool pal::ReadFile(std::filesystem::path path, malloc_span& b) { // Read in the entire file - std::ifstream fd{ path, std::ios::binary }; + std::ifstream fd{ path, std::ios::binary | std::ios::in }; if (!fd) return false; diff --git a/test/regpal/pal.hpp b/test/regpal/pal.hpp index d10eda7c..8ad42e6f 100644 --- a/test/regpal/pal.hpp +++ b/test/regpal/pal.hpp @@ -4,15 +4,7 @@ #include #include -#ifdef BUILD_WINDOWS -#define NOMINMAX -#include -#else -#include -#endif - -#include -#include +#include #include #include diff --git a/test/regtest/baseline.h b/test/regtest/baseline.h index 2d126160..036b3aaf 100644 --- a/test/regtest/baseline.h +++ b/test/regtest/baseline.h @@ -1,15 +1,7 @@ #ifndef _TEST_REGTEST_BASELINE_H_ #define _TEST_REGTEST_BASELINE_H_ -#if BUILD_WINDOWS -#define NOMINMAX -#include -#else -#include -#endif -#include - -#include +#include #include namespace TestBaseline diff --git a/test/regtest/discovery.cpp b/test/regtest/discovery.cpp index c5c7e5a6..64f66be7 100644 --- a/test/regtest/discovery.cpp +++ b/test/regtest/discovery.cpp @@ -211,6 +211,8 @@ std::vector CoreLibFiles() std::vector scenarios; scenarios.emplace_back(MetadataFile::Kind::OnDisk, (std::filesystem::path(baselinePath).parent_path() / "System.Private.CoreLib.dll").generic_string()); + +#ifdef BUILD_WINDOWS scenarios.emplace_back(MetadataFile::Kind::OnDisk, (std::filesystem::path(FindFrameworkInstall("v4.0.30319")) / "mscorlib.dll").generic_string()); auto fx2mscorlib = std::filesystem::path(FindFrameworkInstall("v2.0.50727")) / "mscorlib.dll"; @@ -218,6 +220,7 @@ std::vector CoreLibFiles() { scenarios.emplace_back(MetadataFile::Kind::OnDisk, fx2mscorlib.generic_string()); } +#endif return scenarios; } diff --git a/test/regtest/fixtures.h b/test/regtest/fixtures.h index 2e1d21fd..ccfe2c66 100644 --- a/test/regtest/fixtures.h +++ b/test/regtest/fixtures.h @@ -3,16 +3,7 @@ #include -#ifdef BUILD_WINDOWS -#define NOMINMAX -#include -#else -#include -#endif -#include - -#include -#include +#include #include #include From ec4089ce1091e0c7946285767c96048fb4e11ef7 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 27 Dec 2023 14:39:54 -0800 Subject: [PATCH 41/59] Don't validate the value of the reserved out parameter (as its value is unspecified) --- test/regtest/metadata.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/regtest/metadata.cpp b/test/regtest/metadata.cpp index d1bf74d6..fed30745 100644 --- a/test/regtest/metadata.cpp +++ b/test/regtest/metadata.cpp @@ -1124,7 +1124,8 @@ namespace values.push_back(pulParamSeq); values.push_back(pdwParamFlags); values.push_back(ptOwner); - values.push_back(reserved); + // We don't care about reserved + // as its value is unspecified uint32_t hash = HashCharArray(name, pchName); values.push_back(hash); values.push_back(pchName); From 83ec060498ec93811d6ed8080148031f0b6b16e4 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 27 Dec 2023 14:48:17 -0800 Subject: [PATCH 42/59] Add name overrides to avoid duplicate names for the corelib-only tests. --- test/regtest/discovery.cpp | 10 +++++++--- test/regtest/fixtures.h | 5 +++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/test/regtest/discovery.cpp b/test/regtest/discovery.cpp index 64f66be7..1d34d904 100644 --- a/test/regtest/discovery.cpp +++ b/test/regtest/discovery.cpp @@ -210,15 +210,15 @@ std::vector CoreLibFiles() std::cout << "Discovering CoreLib files" << std::endl; std::vector scenarios; - scenarios.emplace_back(MetadataFile::Kind::OnDisk, (std::filesystem::path(baselinePath).parent_path() / "System.Private.CoreLib.dll").generic_string()); + scenarios.emplace_back(MetadataFile::Kind::OnDisk, (std::filesystem::path(baselinePath).parent_path() / "System.Private.CoreLib.dll").generic_string(), "System_Private_CoreLib"); #ifdef BUILD_WINDOWS - scenarios.emplace_back(MetadataFile::Kind::OnDisk, (std::filesystem::path(FindFrameworkInstall("v4.0.30319")) / "mscorlib.dll").generic_string()); + scenarios.emplace_back(MetadataFile::Kind::OnDisk, (std::filesystem::path(FindFrameworkInstall("v4.0.30319")) / "mscorlib.dll").generic_string(), "4_0_mscorlib"); auto fx2mscorlib = std::filesystem::path(FindFrameworkInstall("v2.0.50727")) / "mscorlib.dll"; if (std::filesystem::exists(fx2mscorlib)) { - scenarios.emplace_back(MetadataFile::Kind::OnDisk, fx2mscorlib.generic_string()); + scenarios.emplace_back(MetadataFile::Kind::OnDisk, fx2mscorlib.generic_string(), "2_0_mscorlib"); } #endif return scenarios; @@ -273,6 +273,10 @@ span GetMetadataForFile(MetadataFile file) std::string PrintName(testing::TestParamInfo info) { + if (info.param.testNameOverride.size() > 0) + { + return info.param.testNameOverride; + } std::string name; if (info.param.kind == MetadataFile::Kind::OnDisk) { diff --git a/test/regtest/fixtures.h b/test/regtest/fixtures.h index ccfe2c66..825e74b1 100644 --- a/test/regtest/fixtures.h +++ b/test/regtest/fixtures.h @@ -16,10 +16,11 @@ struct MetadataFile final Generated } kind; - MetadataFile(Kind kind, std::string pathOrKey) - : kind(kind), pathOrKey(std::move(pathOrKey)) {} + MetadataFile(Kind kind, std::string pathOrKey, std::string testNameOverride = "") + : kind(kind), pathOrKey(std::move(pathOrKey)), testNameOverride(testNameOverride) {} std::string pathOrKey; + std::string testNameOverride; bool operator==(const MetadataFile& rhs) const noexcept { From 9b2215d8acb2240cf43e68c6e2f5d7e7a3b043dc Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 27 Dec 2023 14:53:21 -0800 Subject: [PATCH 43/59] Don't install testing dependencies --- test/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9bfc2697..194d3fba 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -13,6 +13,7 @@ FetchContent_Declare( ) # For Windows: Prevent overriding the parent project's compiler/linker settings set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +set(INSTALL_GTEST OFF CACHE BOOL "" FORCE) FetchContent_Declare( benchmark @@ -24,6 +25,7 @@ FetchContent_Declare( # Don't build the tests for the benchmark library. set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "" FORCE) +set(BENCHMARK_ENABLE_INSTALL OFF CACHE BOOL "" FORCE) FetchContent_MakeAvailable(googletest benchmark) include(GoogleTest) From 0dcbfceb56d05f130fdf69d08652ea2166758e37 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 27 Dec 2023 16:03:22 -0800 Subject: [PATCH 44/59] index on no-dotnet-test: 9b2215d Don't install testing dependencies --- test/CMakeLists.txt | 2 +- test/FindNetHost.cmake | 20 +++++++ test/FindNetHostDir.proj | 8 +++ test/ImportManagedComponents.cmake | 32 ----------- test/regpal/CMakeLists.txt | 2 +- test/regpal/pal.cpp | 87 +++++++++++++++++++++++++----- test/regpal/pal.hpp | 1 + test/regperf/CMakeLists.txt | 3 +- test/regperf/perf.cpp | 11 ++-- test/regtest/CMakeLists.txt | 12 +++-- test/regtest/discovery.cpp | 13 +++-- test/regtest/fixtures.h | 3 +- test/regtest/main.cpp | 24 +++------ 13 files changed, 132 insertions(+), 86 deletions(-) create mode 100644 test/FindNetHost.cmake create mode 100644 test/FindNetHostDir.proj delete mode 100644 test/ImportManagedComponents.cmake diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 194d3fba..e0a14508 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,6 +1,6 @@ # Configure the compiler include(../configure.cmake) -include(ImportManagedComponents.cmake) +include(FindNetHost.cmake) if (POLICY CMP0135) cmake_policy(SET CMP0135 NEW) # Set timestamps in downloaded archives to the time of download. diff --git a/test/FindNetHost.cmake b/test/FindNetHost.cmake new file mode 100644 index 00000000..d6f0e24a --- /dev/null +++ b/test/FindNetHost.cmake @@ -0,0 +1,20 @@ +execute_process( + COMMAND dotnet msbuild FindNetHostDir.proj -t:OutputNetHostDir -nologo + OUTPUT_VARIABLE NET_HOST_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE + COMMAND_ERROR_IS_FATAL ANY + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) + +string(STRIP ${NET_HOST_DIR} NET_HOST_DIR) + +add_library(nethost IMPORTED STATIC) +target_compile_definitions(nethost INTERFACE NETHOST_USE_AS_STATIC) +target_include_directories(nethost INTERFACE ${NET_HOST_DIR}) + +if (WIN32) + set_target_properties(nethost PROPERTIES + IMPORTED_LOCATION ${NET_HOST_DIR}/libnethost.lib) +else() + set_target_properties(nethost PROPERTIES + IMPORTED_LOCATION ${NET_HOST_DIR}/libnethost.a) +endif() diff --git a/test/FindNetHostDir.proj b/test/FindNetHostDir.proj new file mode 100644 index 00000000..ddb2a1ba --- /dev/null +++ b/test/FindNetHostDir.proj @@ -0,0 +1,8 @@ + + + net8.0 + + + + + \ No newline at end of file diff --git a/test/ImportManagedComponents.cmake b/test/ImportManagedComponents.cmake deleted file mode 100644 index 8e428d4f..00000000 --- a/test/ImportManagedComponents.cmake +++ /dev/null @@ -1,32 +0,0 @@ -execute_process( - COMMAND dotnet build -c Debug - COMMAND dotnet build -c Release - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} -) - -add_library(Regression.Locator IMPORTED SHARED) - -if (WIN32) - set_target_properties(Regression.Locator PROPERTIES - IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/debug/Regression.LocatorNE.dll - IMPORTED_LOCATION_DEBUG ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/debug/Regression.LocatorNE.dll - IMPORTED_LOCATION_RELEASE ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/release/Regression.LocatorNE.dll - IMPORTED_IMPLIB ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/debug/Regression.LocatorNE.lib - IMPORTED_IMPLIB_DEBUG ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/debug/Regression.LocatorNE.lib - IMPORTED_IMPLIB_RELEASE ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/release/Regression.LocatorNE.lib) -elseif (APPLE) - set_target_properties(Regression.Locator PROPERTIES - IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/debug/Regression.LocatorNE.dylib - IMPORTED_LOCATION_DEBUG ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/debug/Regression.LocatorNE.dylib - IMPORTED_LOCATION_RELEASE ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/release/Regression.LocatorNE.dylib) -else() - set_target_properties(Regression.Locator PROPERTIES - IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/debug/Regression.LocatorNE.so - IMPORTED_LOCATION_DEBUG ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/debug/Regression.LocatorNE.so - IMPORTED_LOCATION_RELEASE ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/release/Regression.LocatorNE.so) -endif() - -set_target_properties(Regression.Locator PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/$>/) - -set(RegressionLocatorDirectory ${CMAKE_BINARY_DIR}/managed/bin/Regression.Locator/$>/) diff --git a/test/regpal/CMakeLists.txt b/test/regpal/CMakeLists.txt index ecdbe595..2d56b49f 100644 --- a/test/regpal/CMakeLists.txt +++ b/test/regpal/CMakeLists.txt @@ -4,7 +4,7 @@ set(SOURCES add_library(regpal STATIC ${SOURCES}) -target_link_libraries(regpal PUBLIC Regression.Locator dncp::dncp) +target_link_libraries(regpal PUBLIC nethost dncp::dncp) if (NOT WIN32) target_link_libraries(regpal PUBLIC dncp::winhdrs) diff --git a/test/regpal/pal.cpp b/test/regpal/pal.cpp index 25d64d93..8aebf316 100644 --- a/test/regpal/pal.cpp +++ b/test/regpal/pal.cpp @@ -1,10 +1,8 @@ #define DNCP_DEFINE_GUID #include "pal.hpp" -#ifdef BUILD_WINDOWS -#define DNNE_API_OVERRIDE __declspec(dllimport) -#endif -#include +#include +#include #ifndef BUILD_WINDOWS #include @@ -12,16 +10,25 @@ #include #include +#include +#include + +using std::filesystem::path; +#ifdef BUILD_WINDOWS +#define X(str) std::wstring_view{L##str} +#else +#define X(str) std::string_view{str} +#endif namespace { - void* LoadModule(char const* path) + void* LoadModule(path path) { #ifdef BUILD_WINDOWS - return LoadLibraryA(path); + return LoadLibraryW(path.c_str()); #else - return dlopen(path, RTLD_LAZY); + return dlopen(path.c_str(), RTLD_LAZY); #endif } @@ -39,24 +46,24 @@ namespace MetaDataGetDispenser LoadGetDispenser() { // TODO: Can we use nethost to discover hostfxr and use hostfxr APIs to discover the baseline? - dncp::cotaskmem_ptr coreClrPath{ (char*)GetCoreClrPath() }; - if (coreClrPath == nullptr) + auto coreClrPath = pal::GetCoreClrPath(); + if (coreClrPath.empty()) { std::cerr << "Failed to get coreclr path" << std::endl; return nullptr; } - auto mod = LoadModule(coreClrPath.get()); + auto mod = LoadModule(coreClrPath); if (mod == nullptr) { - std::cerr << "Failed to load metadata baseline module: " << coreClrPath.get() << std::endl; + std::cerr << "Failed to load metadata baseline module: " << coreClrPath << std::endl; return nullptr; } auto getDispenser = (MetaDataGetDispenser)GetSymbol(mod, "MetaDataGetDispenser"); if (getDispenser == nullptr) { - std::cerr << "Failed to find MetaDataGetDispenser in module: " << coreClrPath.get() << std::endl; + std::cerr << "Failed to find MetaDataGetDispenser in module: " << coreClrPath << std::endl; return nullptr; } @@ -76,7 +83,7 @@ HRESULT pal::GetBaselineMetadataDispenser(IMetaDataDispenser** dispenser) return GetDispenser(CLSID_CorMetaDataDispenser, IID_IMetaDataDispenser, (void**)dispenser); } -bool pal::ReadFile(std::filesystem::path path, malloc_span& b) +bool pal::ReadFile(path path, malloc_span& b) { // Read in the entire file std::ifstream fd{ path, std::ios::binary | std::ios::in }; @@ -92,3 +99,57 @@ bool pal::ReadFile(std::filesystem::path path, malloc_span& b) fd.read((char*)(uint8_t*)b, b.size()); return true; } + +path pal::GetCoreClrPath() +{ + int result = 0; + size_t bufferSize = 4096; + std::unique_ptr hostfxr_path{nullptr}; + do + { + hostfxr_path.reset(new char_t[bufferSize]); + result = get_hostfxr_path(hostfxr_path.get(), &bufferSize, nullptr); + } while (result != 0); + + void* hostfxrModule = LoadModule(hostfxr_path.get()); + if (hostfxrModule == nullptr) + { + std::cerr << "Failed to load hostfxr module: " << hostfxr_path.get() << std::endl; + return {}; + } + + path coreClrPath = {}; + auto getDotnetEnvironmentInfo = (hostfxr_get_dotnet_environment_info_fn)GetSymbol(hostfxrModule, "hostfxr_get_dotnet_environment_info"); + if (getDotnetEnvironmentInfo( + nullptr, + nullptr, + [](const hostfxr_dotnet_environment_info* info, void* result_context) + { + path& coreClrPath = *(path*)result_context; + for (size_t i = 0; i < info->framework_count; ++i) + { + if (info->frameworks[i].name == X("Microsoft.NETCore.App")) + { + coreClrPath = info->frameworks[i].path; + coreClrPath /= info->frameworks[i].version; +#ifdef BUILD_WINDOWS + coreClrPath /= "coreclr.dll"; +#elif BUILD_MACOS + coreClrPath /= "libcoreclr.dylib"; +#elif BUILD_UNIX + coreClrPath /= "libcoreclr.so"; +#else +#error "Unknown platform, cannot determine name for CoreCLR executable" +#endif + } + } + }, + &coreClrPath + ) != 0) + { + std::cout << "Failed to get dotnet environment info" << std::endl; + return {}; + } + + return coreClrPath; +} diff --git a/test/regpal/pal.hpp b/test/regpal/pal.hpp index 8ad42e6f..7ac13e16 100644 --- a/test/regpal/pal.hpp +++ b/test/regpal/pal.hpp @@ -10,6 +10,7 @@ namespace pal { + std::filesystem::path GetCoreClrPath(); HRESULT GetBaselineMetadataDispenser(IMetaDataDispenser** dispenser); bool ReadFile(std::filesystem::path path, malloc_span& b); } diff --git a/test/regperf/CMakeLists.txt b/test/regperf/CMakeLists.txt index 60fb6395..46541ae8 100644 --- a/test/regperf/CMakeLists.txt +++ b/test/regperf/CMakeLists.txt @@ -9,8 +9,7 @@ add_executable(regperf target_link_libraries(regperf dnmd::interfaces dncp::dncp - regpal - Regression.Locator) + regpal) if(NOT MSVC) target_link_libraries(regperf dncp::winhdrs) diff --git a/test/regperf/perf.cpp b/test/regperf/perf.cpp index cd2deb42..434fafbf 100644 --- a/test/regperf/perf.cpp +++ b/test/regperf/perf.cpp @@ -12,11 +12,6 @@ #include #include -#ifdef BUILD_WINDOWS -#define DNNE_API_OVERRIDE __declspec(dllimport) -#endif -#include - #ifdef _MSC_VER #define EXPORT extern "C" __declspec(dllexport) #else @@ -187,14 +182,14 @@ int main(int argc, char** argv) { RETURN_IF_FAILED(pal::GetBaselineMetadataDispenser(&g_baselineDisp)); RETURN_IF_FAILED(GetDispenser(IID_IMetaDataDispenser, reinterpret_cast(&g_currentDisp))); - dncp::cotaskmem_ptr coreClrPath{ (char*)GetCoreClrPath() }; - if (coreClrPath == nullptr) + auto coreClrPath = pal::GetCoreClrPath(); + if (coreClrPath.empty()) { std::cerr << "Failed to get coreclr path" << std::endl; return -1; } - std::filesystem::path dataImagePath = coreClrPath.get(); + std::filesystem::path dataImagePath = std::move(coreClrPath); dataImagePath.replace_filename("System.Private.CoreLib.dll"); std::cerr << "Loading System.Private.CoreLib from: " << dataImagePath << std::endl; diff --git a/test/regtest/CMakeLists.txt b/test/regtest/CMakeLists.txt index 49ae0dc0..cb7f9f78 100644 --- a/test/regtest/CMakeLists.txt +++ b/test/regtest/CMakeLists.txt @@ -35,12 +35,16 @@ if (WIN32) target_link_libraries(regtest PRIVATE WIL) endif() -target_link_libraries(regtest PRIVATE Regression.Locator) +add_custom_target(Regression.TargetAssembly + dotnet build ${CMAKE_CURRENT_SOURCE_DIR}/../Regression.TargetAssembly/Regression.TargetAssembly.ilproj -c $ + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/managed/bin/Regression.TargetAssembly/$>/Regression.TargetAssembly.dll ${CMAKE_CURRENT_BINARY_DIR}/Regression.TargetAssembly.dll + BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/Regression.TargetAssembly.dll + COMMENT "Building Regression.TargetAssembly.dll" + ) -add_custom_command(TARGET regtest POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy $ $) +add_dependencies(regtest Regression.TargetAssembly) add_custom_command(TARGET regtest POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_directory ${RegressionLocatorDirectory} $) + COMMAND ${CMAKE_COMMAND} -E copy $ $) gtest_discover_tests(regtest DISCOVERY_TIMEOUT 600) \ No newline at end of file diff --git a/test/regtest/discovery.cpp b/test/regtest/discovery.cpp index 1d34d904..18c064a4 100644 --- a/test/regtest/discovery.cpp +++ b/test/regtest/discovery.cpp @@ -16,11 +16,10 @@ #ifdef BUILD_WINDOWS #define DNNE_API_OVERRIDE __declspec(dllimport) #endif -#include namespace { - std::string baselinePath; + std::filesystem::path baselinePath; std::string regressionAssemblyPath; template @@ -210,7 +209,7 @@ std::vector CoreLibFiles() std::cout << "Discovering CoreLib files" << std::endl; std::vector scenarios; - scenarios.emplace_back(MetadataFile::Kind::OnDisk, (std::filesystem::path(baselinePath).parent_path() / "System.Private.CoreLib.dll").generic_string(), "System_Private_CoreLib"); + scenarios.emplace_back(MetadataFile::Kind::OnDisk, (baselinePath.parent_path() / "System.Private.CoreLib.dll").generic_string(), "System_Private_CoreLib"); #ifdef BUILD_WINDOWS scenarios.emplace_back(MetadataFile::Kind::OnDisk, (std::filesystem::path(FindFrameworkInstall("v4.0.30319")) / "mscorlib.dll").generic_string(), "4_0_mscorlib"); @@ -251,7 +250,7 @@ span GetMetadataForFile(MetadataFile file) malloc_span b; if (file.kind == MetadataFile::Kind::OnDisk) { - auto path = std::filesystem::path(baselinePath).parent_path() / file.pathOrKey; + auto path = baselinePath.parent_path() / file.pathOrKey; b = ReadMetadataFromFile(path); } else @@ -292,12 +291,12 @@ std::string PrintName(testing::TestParamInfo info) std::string GetBaselineDirectory() { - return std::filesystem::path(baselinePath).parent_path().string(); + return baselinePath.parent_path().string(); } -void SetBaselineModulePath(std::string path) +void SetBaselineModulePath(std::filesystem::path path) { - baselinePath = path; + baselinePath = std::move(path); } void SetRegressionAssemblyPath(std::string path) diff --git a/test/regtest/fixtures.h b/test/regtest/fixtures.h index 825e74b1..292f8883 100644 --- a/test/regtest/fixtures.h +++ b/test/regtest/fixtures.h @@ -6,6 +6,7 @@ #include #include +#include #include struct MetadataFile final @@ -44,7 +45,7 @@ std::string FindFrameworkInstall(std::string version); std::string GetBaselineDirectory(); -void SetBaselineModulePath(std::string path); +void SetBaselineModulePath(std::filesystem::path path); void SetRegressionAssemblyPath(std::string path); diff --git a/test/regtest/main.cpp b/test/regtest/main.cpp index 14033b0d..c52c19b9 100644 --- a/test/regtest/main.cpp +++ b/test/regtest/main.cpp @@ -4,11 +4,6 @@ #include "fixtures.h" #include "pal.hpp" -#ifdef BUILD_WINDOWS -#define DNNE_API_OVERRIDE __declspec(dllimport) -#endif -#include - namespace TestBaseline { dncp::com_ptr Metadata = nullptr; @@ -40,21 +35,16 @@ int main(int argc, char** argv) if (HRESULT hr = TestBaseline::DeltaMetadataBuilder->SetOption(MetaDataSetENC, &vt); FAILED(hr)) return hr; - dncp::cotaskmem_ptr coreClrPath{ (char*)GetCoreClrPath() }; - SetBaselineModulePath(coreClrPath.get()); - - std::cout << "Loaded metadata baseline module: " << coreClrPath.get() << std::endl; + auto coreClrPath = pal::GetCoreClrPath(); + std::cout << "Loaded metadata baseline module: " << coreClrPath.generic_string() << std::endl; + SetBaselineModulePath(std::move(coreClrPath)); - dncp::cotaskmem_ptr regressionAssemblyPath{ (char*)GetRegressionTargetAssemblyPath() }; - if (regressionAssemblyPath == nullptr) - { - std::cout << "Failed to get regression assembly path" << std::endl; - return -1; - } + std::filesystem::path regressionAssemblyPath = argv[0]; + regressionAssemblyPath = regressionAssemblyPath.parent_path() / "Regression.TargetAssembly.dll"; - SetRegressionAssemblyPath(regressionAssemblyPath.get()); + SetRegressionAssemblyPath(regressionAssemblyPath.generic_string()); - std::cout << "Regression assembly path: " << regressionAssemblyPath.get() << std::endl; + std::cout << "Regression assembly path: " << regressionAssemblyPath.generic_string() << std::endl; ::testing::InitGoogleTest(&argc, argv); testing::UnitTest::GetInstance()->listeners().Append(new ThrowListener); From e391dd48a9eee0c73ad219d779ac7acfc12cc2f8 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 27 Dec 2023 16:04:07 -0800 Subject: [PATCH 45/59] Fail on any build failure --- test/ImportManagedComponents.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/test/ImportManagedComponents.cmake b/test/ImportManagedComponents.cmake index 8e428d4f..2fdb2960 100644 --- a/test/ImportManagedComponents.cmake +++ b/test/ImportManagedComponents.cmake @@ -2,6 +2,7 @@ execute_process( COMMAND dotnet build -c Debug COMMAND dotnet build -c Release WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND_ERROR_IS_FATAL ANY ) add_library(Regression.Locator IMPORTED SHARED) From 8a82c120c65f166171d3b82a097a1c6f4564935e Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 28 Dec 2023 15:25:05 -0800 Subject: [PATCH 46/59] Initialize the runtime on non-Windows --- test/regpal/pal.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test/regpal/pal.cpp b/test/regpal/pal.cpp index 8aebf316..e6e61815 100644 --- a/test/regpal/pal.cpp +++ b/test/regpal/pal.cpp @@ -43,6 +43,15 @@ namespace using MetaDataGetDispenser = HRESULT(STDMETHODCALLTYPE*)(REFCLSID, REFIID, LPVOID*); + using CoreCLRInitialize = int(STDMETHODCALLTYPE*)( + const char* exePath, + const char* appDomainFriendlyName, + int propertyCount, + const char** propertyKeys, + const char** propertyValues, + void** hostHandle, + unsigned int* domainId); + MetaDataGetDispenser LoadGetDispenser() { // TODO: Can we use nethost to discover hostfxr and use hostfxr APIs to discover the baseline? @@ -59,6 +68,20 @@ namespace std::cerr << "Failed to load metadata baseline module: " << coreClrPath << std::endl; return nullptr; } +#ifndef BUILD_WINDOWS + // On non-Windows, the metadata APIs in CoreCLR don't work until the PAL is initialized. + // Initialize the runtime just enough to load the PAL. + auto init = (CoreCLRInitialize)GetSymbol(mod, "coreclr_initialize"); + if (init == nullptr) + { + std::cerr << "Failed to find coreclr_initialize in module: " << coreClrPath << std::endl; + return nullptr; + } + + const char* propertyKeys[] = { "TRUSTED_PLATFORM_ASSEMBLIES" }; + const char* propertyValues[] = { coreClrPath.c_str() }; + init("regpal", "regpal", 1, propertyKeys, propertyValues, nullptr, nullptr); +#endif auto getDispenser = (MetaDataGetDispenser)GetSymbol(mod, "MetaDataGetDispenser"); if (getDispenser == nullptr) From a889abf89697fa99de6081b92dd07939944b41bc Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 28 Dec 2023 15:27:57 -0800 Subject: [PATCH 47/59] Remove resolved TODO --- test/regpal/pal.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/test/regpal/pal.cpp b/test/regpal/pal.cpp index e6e61815..68214525 100644 --- a/test/regpal/pal.cpp +++ b/test/regpal/pal.cpp @@ -54,7 +54,6 @@ namespace MetaDataGetDispenser LoadGetDispenser() { - // TODO: Can we use nethost to discover hostfxr and use hostfxr APIs to discover the baseline? auto coreClrPath = pal::GetCoreClrPath(); if (coreClrPath.empty()) { From a7a53c4cf4fab31d122f1aa20c37f9bb194e9947 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 28 Dec 2023 15:36:51 -0800 Subject: [PATCH 48/59] Set env var to suppress first-time-use message --- test/FindNetHost.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/FindNetHost.cmake b/test/FindNetHost.cmake index b4e5e5bc..c141d7a4 100644 --- a/test/FindNetHost.cmake +++ b/test/FindNetHost.cmake @@ -1,5 +1,5 @@ execute_process( - COMMAND dotnet msbuild FindNetHostDir.proj -t:OutputNetHostDir -nologo + COMMAND ${CMAKE_COMMAND} -E env DOTNET_NOLOGO=1 dotnet msbuild FindNetHostDir.proj -t:OutputNetHostDir -nologo OUTPUT_VARIABLE NET_HOST_DIR OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND_ERROR_IS_FATAL ANY From a2abedd12bf7e09be9e7e51d26611d1e1da8a49e Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 28 Dec 2023 15:52:03 -0800 Subject: [PATCH 49/59] Run dotnet --info for diagnostics --- .github/workflows/main.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 82a07ac9..77b96dac 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -29,6 +29,9 @@ jobs: with: dotnet-version: '8.0.x' dotnet-quality: 'preview' + + - name: List dotnet information + run: dotnet --info - name: Build Vendored Dependencies if: ${{ !matrix.use-vendored-libs }} From cc166297912efcf92f3b8d30e4aed87aba398100 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 28 Dec 2023 15:55:18 -0800 Subject: [PATCH 50/59] Dump more diagnostic --- test/regpal/pal.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/regpal/pal.cpp b/test/regpal/pal.cpp index 68214525..880e9c1a 100644 --- a/test/regpal/pal.cpp +++ b/test/regpal/pal.cpp @@ -152,6 +152,7 @@ path pal::GetCoreClrPath() { if (info->frameworks[i].name == X("Microsoft.NETCore.App")) { + std::cout << "Found Microsoft.NETCore.App (" << info->frameworks[i].version << "): " << info->frameworks[i].path << std::endl; coreClrPath = info->frameworks[i].path; coreClrPath /= info->frameworks[i].version; #ifdef BUILD_WINDOWS From b94f4db031e5a6339bfc8980a7fafa428856ab08 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 28 Dec 2023 15:58:15 -0800 Subject: [PATCH 51/59] Move the diagnostic --- test/regpal/pal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/regpal/pal.cpp b/test/regpal/pal.cpp index 880e9c1a..03a5cdfa 100644 --- a/test/regpal/pal.cpp +++ b/test/regpal/pal.cpp @@ -150,9 +150,9 @@ path pal::GetCoreClrPath() path& coreClrPath = *(path*)result_context; for (size_t i = 0; i < info->framework_count; ++i) { + std::cout << "Found framework: " << info->frameworks[i].name << " (" << info->frameworks[i].version << "): " << info->frameworks[i].path << std::endl; if (info->frameworks[i].name == X("Microsoft.NETCore.App")) { - std::cout << "Found Microsoft.NETCore.App (" << info->frameworks[i].version << "): " << info->frameworks[i].path << std::endl; coreClrPath = info->frameworks[i].path; coreClrPath /= info->frameworks[i].version; #ifdef BUILD_WINDOWS From faba662e5af4d5f33e2eb3f9a8be5f038bae82f7 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 28 Dec 2023 16:07:03 -0800 Subject: [PATCH 52/59] Add more logging --- test/regpal/pal.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/regpal/pal.cpp b/test/regpal/pal.cpp index 03a5cdfa..160a214c 100644 --- a/test/regpal/pal.cpp +++ b/test/regpal/pal.cpp @@ -148,9 +148,10 @@ path pal::GetCoreClrPath() [](const hostfxr_dotnet_environment_info* info, void* result_context) { path& coreClrPath = *(path*)result_context; + std::cerr << "Found " << info->framework_count << " frameworks" << std::endl; for (size_t i = 0; i < info->framework_count; ++i) { - std::cout << "Found framework: " << info->frameworks[i].name << " (" << info->frameworks[i].version << "): " << info->frameworks[i].path << std::endl; + std::cerr << "Found framework: " << info->frameworks[i].name << " (" << info->frameworks[i].version << "): " << info->frameworks[i].path << std::endl; if (info->frameworks[i].name == X("Microsoft.NETCore.App")) { coreClrPath = info->frameworks[i].path; @@ -170,9 +171,11 @@ path pal::GetCoreClrPath() &coreClrPath ) != 0) { - std::cout << "Failed to get dotnet environment info" << std::endl; + std::cerr << "Failed to get dotnet environment info" << std::endl; return {}; } + std::cerr << "Found CoreCLR at: " << coreClrPath << std::endl; + return coreClrPath; } From dd45d85e07bbb63c33d11957a0b58fc6ec59a962 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 28 Dec 2023 16:10:14 -0800 Subject: [PATCH 53/59] Dump hostfxr path --- test/regpal/pal.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/regpal/pal.cpp b/test/regpal/pal.cpp index 160a214c..c9bbf3c6 100644 --- a/test/regpal/pal.cpp +++ b/test/regpal/pal.cpp @@ -132,6 +132,8 @@ path pal::GetCoreClrPath() hostfxr_path.reset(new char_t[bufferSize]); result = get_hostfxr_path(hostfxr_path.get(), &bufferSize, nullptr); } while (result != 0); + + std::cerr << "Found hostfxr at: " << hostfxr_path.get() << std::endl; void* hostfxrModule = LoadModule(hostfxr_path.get()); if (hostfxrModule == nullptr) From 2fd064ee6902a60e3404d09b36a9396df15a6de3 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 29 Dec 2023 10:49:54 -0800 Subject: [PATCH 54/59] Specify dotnet root based on hostfxr that is discovered. --- test/regpal/pal.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/test/regpal/pal.cpp b/test/regpal/pal.cpp index c9bbf3c6..c2d12123 100644 --- a/test/regpal/pal.cpp +++ b/test/regpal/pal.cpp @@ -133,27 +133,32 @@ path pal::GetCoreClrPath() result = get_hostfxr_path(hostfxr_path.get(), &bufferSize, nullptr); } while (result != 0); - std::cerr << "Found hostfxr at: " << hostfxr_path.get() << std::endl; - + path hostFxrPath = hostfxr_path.get(); void* hostfxrModule = LoadModule(hostfxr_path.get()); if (hostfxrModule == nullptr) { - std::cerr << "Failed to load hostfxr module: " << hostfxr_path.get() << std::endl; + std::cerr << "Failed to load hostfxr module: " << hostFxrPath << std::endl; return {}; } + + // The hostfxr path is in the form: /host/fxr//hostfxr.dll + // We need to get the dotnet root, which is 3 levels up + // We need to do this because hostfxr_get_dotnet_environment_info only returns information + // for a globally-installed dotnet if we don't pass a path to the dotnet root. + // The macOS machines on GitHub Actions don't have dotnet globally installed. + path dotnetRoot = hostFxrPath.parent_path().parent_path().parent_path().parent_path(); + path coreClrPath = {}; auto getDotnetEnvironmentInfo = (hostfxr_get_dotnet_environment_info_fn)GetSymbol(hostfxrModule, "hostfxr_get_dotnet_environment_info"); if (getDotnetEnvironmentInfo( - nullptr, + dotnetRoot.c_str(), nullptr, [](const hostfxr_dotnet_environment_info* info, void* result_context) { path& coreClrPath = *(path*)result_context; - std::cerr << "Found " << info->framework_count << " frameworks" << std::endl; for (size_t i = 0; i < info->framework_count; ++i) { - std::cerr << "Found framework: " << info->frameworks[i].name << " (" << info->frameworks[i].version << "): " << info->frameworks[i].path << std::endl; if (info->frameworks[i].name == X("Microsoft.NETCore.App")) { coreClrPath = info->frameworks[i].path; @@ -177,7 +182,5 @@ path pal::GetCoreClrPath() return {}; } - std::cerr << "Found CoreCLR at: " << coreClrPath << std::endl; - return coreClrPath; } From 05857f11809cebe8c483d9b6e3adc2a11ec9012c Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 29 Dec 2023 11:56:04 -0800 Subject: [PATCH 55/59] Remove unused locator project now that we're using nethost. --- test/DNMD.Tests.sln | 6 ------ test/Regression.Locator/LocatorHelpers.cs | 19 ------------------ .../Regression.Locator.csproj | 20 ------------------- 3 files changed, 45 deletions(-) delete mode 100644 test/Regression.Locator/LocatorHelpers.cs delete mode 100644 test/Regression.Locator/Regression.Locator.csproj diff --git a/test/DNMD.Tests.sln b/test/DNMD.Tests.sln index a519c9ad..1c895967 100644 --- a/test/DNMD.Tests.sln +++ b/test/DNMD.Tests.sln @@ -5,8 +5,6 @@ VisualStudioVersion = 17.5.33026.144 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Regression.TargetAssembly", "Regression.TargetAssembly\Regression.TargetAssembly.ilproj", "{D3BEEF3D-C137-49AC-96DD-E5B93E2F4C21}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Regression.Locator", "Regression.Locator\Regression.Locator.csproj", "{CD7DEF6B-7254-4541-951D-026CDB8928FD}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -17,10 +15,6 @@ Global {D3BEEF3D-C137-49AC-96DD-E5B93E2F4C21}.Debug|Any CPU.Build.0 = Debug|Any CPU {D3BEEF3D-C137-49AC-96DD-E5B93E2F4C21}.Release|Any CPU.ActiveCfg = Release|Any CPU {D3BEEF3D-C137-49AC-96DD-E5B93E2F4C21}.Release|Any CPU.Build.0 = Release|Any CPU - {CD7DEF6B-7254-4541-951D-026CDB8928FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CD7DEF6B-7254-4541-951D-026CDB8928FD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CD7DEF6B-7254-4541-951D-026CDB8928FD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CD7DEF6B-7254-4541-951D-026CDB8928FD}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/test/Regression.Locator/LocatorHelpers.cs b/test/Regression.Locator/LocatorHelpers.cs deleted file mode 100644 index 1bb12f95..00000000 --- a/test/Regression.Locator/LocatorHelpers.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Runtime.InteropServices; -namespace Regression.Locator; - -public static unsafe class LocatorHelpers -{ - [UnmanagedCallersOnly(EntryPoint = "GetCoreClrPath")] - public static byte* GetCoreClrPath() - { - string path = Path.Combine(Path.GetDirectoryName(typeof(object).Assembly.Location!)!, OperatingSystem.IsWindows() ? "coreclr.dll" : "libcoreclr.so"); - return (byte*)Marshal.StringToCoTaskMemUTF8(path); - } - - [UnmanagedCallersOnly(EntryPoint = "GetRegressionTargetAssemblyPath")] - public static byte* GetRegressionTargetAssemblyPath() - { - string path = Path.Combine(Path.GetDirectoryName(typeof(LocatorHelpers).Assembly.Location!)!, "Regression.TargetAssembly.dll"); - return (byte*)Marshal.StringToCoTaskMemUTF8(path); - } -} diff --git a/test/Regression.Locator/Regression.Locator.csproj b/test/Regression.Locator/Regression.Locator.csproj deleted file mode 100644 index 1b991916..00000000 --- a/test/Regression.Locator/Regression.Locator.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - net8.0 - enable - enable - true - true - true - - - - - - - - - - - From 25543f369efeacaed01d8c7ee09d1ac880c54adf Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 29 Dec 2023 11:57:50 -0800 Subject: [PATCH 56/59] Remove configuration limitations, we don't need them any more as we aren't importing a DNNE-based lib --- CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 41ba6750..ad428c6b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,6 @@ cmake_minimum_required(VERSION 3.10) project(dnmd LANGUAGES C CXX) -set(CMAKE_CONFIGURATION_TYPES Debug;Release) option(INCLUDE_VENDORED_LIBS "Include vendored libraries (submodules) instead of discovering dependencies through packages." ON) set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}) From afe73e3696b09f7224afc898a97c9646df2b6cc9 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 2 Jan 2024 11:13:32 -0800 Subject: [PATCH 57/59] Apply suggestions from code review --- test/regtest/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/regtest/CMakeLists.txt b/test/regtest/CMakeLists.txt index 67d17003..78cf3dae 100644 --- a/test/regtest/CMakeLists.txt +++ b/test/regtest/CMakeLists.txt @@ -52,4 +52,4 @@ if(WIN32) COMMAND ${CMAKE_COMMAND} -E copy $ $) endif() -gtest_discover_tests(regtest DISCOVERY_TIMEOUT 600) \ No newline at end of file +gtest_discover_tests(regtest DISCOVERY_TIMEOUT 1200) \ No newline at end of file From 044509280b556ed2acd5c293a658829362942204 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 26 Jan 2024 11:48:25 -0800 Subject: [PATCH 58/59] PR feedback --- src/inc/internal/dnmd_platform.hpp | 1 + test/CMakeLists.txt | 5 +++- test/FindNetHostDir.proj | 3 --- test/regpal/pal.cpp | 38 +++++++++++++++--------------- test/regperf/perf.cpp | 6 ++--- test/regtest/main.cpp | 15 +++++++----- 6 files changed, 36 insertions(+), 32 deletions(-) diff --git a/src/inc/internal/dnmd_platform.hpp b/src/inc/internal/dnmd_platform.hpp index 4942af47..215a35e9 100644 --- a/src/inc/internal/dnmd_platform.hpp +++ b/src/inc/internal/dnmd_platform.hpp @@ -14,6 +14,7 @@ #endif // !BUILD_WINDOWS // Machine code masks for native (R2R) images +// See pedecoder.h in CoreCLR #define IMAGE_FILE_MACHINE_OS_MASK_APPLE 0x4644 #define IMAGE_FILE_MACHINE_OS_MASK_FREEBSD 0xADC4 #define IMAGE_FILE_MACHINE_OS_MASK_LINUX 0x7B79 diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e0a14508..fb59f89f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -9,7 +9,10 @@ endif() include(FetchContent) FetchContent_Declare( googletest - URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip + GIT_REPOSITORY + https://github.com/google/googletest.git + GIT_TAG + v1.14.0 ) # For Windows: Prevent overriding the parent project's compiler/linker settings set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) diff --git a/test/FindNetHostDir.proj b/test/FindNetHostDir.proj index ddb2a1ba..c2130191 100644 --- a/test/FindNetHostDir.proj +++ b/test/FindNetHostDir.proj @@ -1,7 +1,4 @@ - - net8.0 - diff --git a/test/regpal/pal.cpp b/test/regpal/pal.cpp index c2d12123..0d8e2595 100644 --- a/test/regpal/pal.cpp +++ b/test/regpal/pal.cpp @@ -16,9 +16,9 @@ using std::filesystem::path; #ifdef BUILD_WINDOWS -#define X(str) std::wstring_view{L##str} +#define W(str) std::wstring_view{L##str} #else -#define X(str) std::string_view{str} +#define W(str) std::string_view{str} #endif namespace @@ -26,31 +26,31 @@ namespace void* LoadModule(path path) { #ifdef BUILD_WINDOWS - return LoadLibraryW(path.c_str()); -#else - return dlopen(path.c_str(), RTLD_LAZY); + return ::LoadLibraryW(path.c_str()); +#else + return ::dlopen(path.c_str(), RTLD_LAZY); #endif } void* GetSymbol(void* module, char const* name) { #ifdef BUILD_WINDOWS - return GetProcAddress((HMODULE)module, name); -#else - return dlsym(module, name); + return ::GetProcAddress((HMODULE)module, name); +#else + return ::dlsym(module, name); #endif } using MetaDataGetDispenser = HRESULT(STDMETHODCALLTYPE*)(REFCLSID, REFIID, LPVOID*); using CoreCLRInitialize = int(STDMETHODCALLTYPE*)( - const char* exePath, - const char* appDomainFriendlyName, - int propertyCount, - const char** propertyKeys, - const char** propertyValues, - void** hostHandle, - unsigned int* domainId); + char const* exePath, + char const* appDomainFriendlyName, + int propertyCount, + char const** propertyKeys, + char const** propertyValues, + void** hostHandle, + uint32* domainId); MetaDataGetDispenser LoadGetDispenser() { @@ -77,8 +77,8 @@ namespace return nullptr; } - const char* propertyKeys[] = { "TRUSTED_PLATFORM_ASSEMBLIES" }; - const char* propertyValues[] = { coreClrPath.c_str() }; + char const* propertyKeys[] = { "TRUSTED_PLATFORM_ASSEMBLIES" }; + char const* propertyValues[] = { coreClrPath.c_str() }; init("regpal", "regpal", 1, propertyKeys, propertyValues, nullptr, nullptr); #endif @@ -126,7 +126,7 @@ path pal::GetCoreClrPath() { int result = 0; size_t bufferSize = 4096; - std::unique_ptr hostfxr_path{nullptr}; + std::unique_ptr hostfxr_path; do { hostfxr_path.reset(new char_t[bufferSize]); @@ -159,7 +159,7 @@ path pal::GetCoreClrPath() path& coreClrPath = *(path*)result_context; for (size_t i = 0; i < info->framework_count; ++i) { - if (info->frameworks[i].name == X("Microsoft.NETCore.App")) + if (info->frameworks[i].name == W("Microsoft.NETCore.App")) { coreClrPath = info->frameworks[i].path; coreClrPath /= info->frameworks[i].version; diff --git a/test/regperf/perf.cpp b/test/regperf/perf.cpp index 434fafbf..518a177b 100644 --- a/test/regperf/perf.cpp +++ b/test/regperf/perf.cpp @@ -198,13 +198,13 @@ int main(int argc, char** argv) if (!pal::ReadFile(dataImagePath, dataImage)) { std::cerr << "Failed to read System.Private.CoreLib" << std::endl; - return -1; + return EXIT_FAILURE; } if (!get_metadata_from_pe(dataImage)) { std::cerr << "Failed to get metadata from System.Private.CoreLib" << std::endl; - return -1; + return EXIT_FAILURE; } RETURN_IF_FAILED(PerfInitialize( @@ -214,5 +214,5 @@ int main(int argc, char** argv) benchmark::Initialize(&argc, argv); benchmark::RunSpecifiedBenchmarks(); benchmark::Shutdown(); - return 0; + return EXIT_SUCCESS; } \ No newline at end of file diff --git a/test/regtest/main.cpp b/test/regtest/main.cpp index c52c19b9..d07c5497 100644 --- a/test/regtest/main.cpp +++ b/test/regtest/main.cpp @@ -13,12 +13,15 @@ namespace TestBaseline #define RETURN_IF_FAILED(x) { auto hr = x; if (FAILED(hr)) return hr; } -class ThrowListener final : public testing::EmptyTestEventListener { - void OnTestPartResult(const testing::TestPartResult& result) override { - if (result.fatally_failed()) { - throw testing::AssertionException(result); - } - } +class ThrowListener final : public testing::EmptyTestEventListener +{ + void OnTestPartResult(testing::TestPartResult const& result) override + { + if (result.fatally_failed()) + { + throw testing::AssertionException(result); + } + } }; int main(int argc, char** argv) From 9f68f497fbbb3f226db45076a5ba7a18bf7cb841 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 26 Jan 2024 12:11:01 -0800 Subject: [PATCH 59/59] Don't crack open files for discovery. Use a heuristic instead --- test/regpal/pal.cpp | 8 ++++---- test/regtest/CMakeLists.txt | 2 +- test/regtest/discovery.cpp | 21 ++++++++++++++++++--- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/test/regpal/pal.cpp b/test/regpal/pal.cpp index 0d8e2595..353c3f9c 100644 --- a/test/regpal/pal.cpp +++ b/test/regpal/pal.cpp @@ -16,9 +16,9 @@ using std::filesystem::path; #ifdef BUILD_WINDOWS -#define W(str) std::wstring_view{L##str} +#define W_StringView(str) std::wstring_view{L##str} #else -#define W(str) std::string_view{str} +#define W_StringView(str) std::string_view{str} #endif namespace @@ -50,7 +50,7 @@ namespace char const** propertyKeys, char const** propertyValues, void** hostHandle, - uint32* domainId); + uint32_t* domainId); MetaDataGetDispenser LoadGetDispenser() { @@ -159,7 +159,7 @@ path pal::GetCoreClrPath() path& coreClrPath = *(path*)result_context; for (size_t i = 0; i < info->framework_count; ++i) { - if (info->frameworks[i].name == W("Microsoft.NETCore.App")) + if (info->frameworks[i].name == W_StringView("Microsoft.NETCore.App")) { coreClrPath = info->frameworks[i].path; coreClrPath /= info->frameworks[i].version; diff --git a/test/regtest/CMakeLists.txt b/test/regtest/CMakeLists.txt index 78cf3dae..5eed48dc 100644 --- a/test/regtest/CMakeLists.txt +++ b/test/regtest/CMakeLists.txt @@ -52,4 +52,4 @@ if(WIN32) COMMAND ${CMAKE_COMMAND} -E copy $ $) endif() -gtest_discover_tests(regtest DISCOVERY_TIMEOUT 1200) \ No newline at end of file +gtest_discover_tests(regtest) \ No newline at end of file diff --git a/test/regtest/discovery.cpp b/test/regtest/discovery.cpp index 18c064a4..4b30b48b 100644 --- a/test/regtest/discovery.cpp +++ b/test/regtest/discovery.cpp @@ -182,11 +182,26 @@ std::vector MetadataFilesInDirectory(std::string directory) auto ext = path.extension(); if (ext == ".dll") { - malloc_span b = ReadMetadataFromFile(path); + // Some of the DLLs in our search paths are native, + // so we need to filter to the managed ones. + // We could try opening them and skip them if they don't have any metadata, + // but that is slow and we don't want to do that for test discovery. + // Instead, we'll use the following heuristic to determine if the DLL is managed: + // - If the file name contains '.Native.', then it's not managed + // - If the file name contains '.Thunk.', then it's not managed + // - If the file name starts with 'System.' or 'Microsoft.', then it's managed + + auto fileName = path.filename().generic_string(); + + if (fileName.find(".Native.") != std::string::npos + || fileName.find(".Thunk.") != std::string::npos) + { + continue; + } - if (b.size() == 0) + if (fileName.find("System.") != 0 + && fileName.find("Microsoft.") != 0) { - // Some DLLs don't have metadata, so skip them. continue; }