
CoreCLR 测试配置全解在 .NET runtime 仓库中新增 CoreCLR 测试的完整方法【免费下载链接】runtime.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.项目地址: https://gitcode.com/GitHub_Trending/runtime6/runtime以 dotnet/runtime 仓库的 CoreCLR 测试基础设施为对象本文讲清楚每个测试工程必须打的标签CLRTestKind、优先级分层CLRTestPriority、各类兼容开关以及构建系统如何据此决定构不构建、跑不跑让你新增的测试一次就进入正确的构建与执行流程。新增测试后构建系统视而不见怎么办你往 src/tests 下丢了一个新的测试工程本地跑通了CI 上却压根没执行——这不是测试写错了而是构建系统的判定逻辑把它跳过了。整个 CoreCLR 测试体系的核心就是 src/tests/Directory.Build.targets 里一个名为_WillCLRTestProjectBuild的布尔判定它综合优先级、目标平台、构建模式等多个属性决定工程是正常构建还是导入 src/tests/Common/nobuild.targets该文件把Build、Compile等目标全部替换为空实现构建直接空转。所以配置测试的本质就是管理这些属性。下面按实际操作顺序走一遍先给工程打标签再定优先级然后处理平台与编译模式的兼容开关最后看底层机制与本地验证方法。给测试工程打哪种标签CLRTestKind 的四种取值每个测试工程都必须归属BuildOnly、RunOnly、BuildAndRun、SharedLibrary四种种类之一由CLRTestKind属性声明。不写的话构建系统会按OutputType自动推断——src/tests/Directory.Build.targets 第 19-20 行CLRTestKind Condition$(CLRTestKind) and $(OutputType) ExeBuildAndRun/CLRTestKind CLRTestKind Condition$(CLRTestKind) SharedLibrary/CLRTestKind即输出为Exe的工程默认BuildAndRun类库默认SharedLibrary。四种取值的行为差异如下CLRTestKind是否编译源码是否产出可执行文件是否生成执行脚本典型用途BuildAndRun默认是是是绝大多数普通测试BuildOnly是是否只验证能编译、或构建依赖库RunOnly否否是复用别的工程的可执行文件换一组命令行参数再跑SharedLibrary是否否供多个测试共用的托管测试库具体开关在 src/tests/Directory.Build.targets 第 30-64 行的Choose块里逐一定义例如BuildOnly会设置_CLRTestNeedsToRunfalse、GenerateRunScriptfalse。⚠️RunOnly是唯一不编译源码的种类它靠CLRTestProjectToRun指向真正要运行的工程。构建系统会把这个引用转成ProjectReference先把依赖工程构建出来第 84-88 行再为RunOnly工程自己生成执行脚本同时它会设置SkipSigningtrue跳过程序集签名。脚本生成时src/tests/Common/CLRTest.Execute.targets 的FetchExternalProperties目标会区分两种情况RunOnly用[MSBuild]::MakeRelative算出被引用工程的输出路径作为InputAssemblyNameBuildAndRun则直接用$(AssemblyName).dll。测试该放在哪里JIT 回归测试的归属规则用优化编译不带调试信息的 JIT 回归测试直接丢进 src/tests/JIT/Regression_ro_2/ 即可Regression_ro_2.csproj会递归包含该目录下所有.cs文件命名约定是Runtime_issue_number.cs相关文件可放子目录分组。其他源码编译型回归运行器沿用同一约定各自保留既有分区与编译设置被多个运行器共享的源码放在其中一个运行器目录里由其余运行器显式包含。但如果你的测试需要不同的编译设置、独立的程序集、原生依赖或进程隔离就不能放进源码通配source-glob目录而要在 src/tests/JIT/Regression_2/ 下建独立工程命名Runtime_issue_number/。Regression_2.csproj以递归发现工程的方式管理它们Regression_3.csproj同理管理既有的GitHub_*、DevDiv_*等基于工程的测试。每个工程的源码和配套文件放在一起不要混入源码通配运行器的目录。从零建一个 C# 测试工程的步骤复制一个现成测试如 src/tests/Exceptions/Finalization/Finalizer.csproj到新目录。删掉AssemblyName属性——不删会让构建系统后台处理测试的方式产生歧义。设置CLRTestKind/CLRTestPriority。加源文件用 xUnit 的Fact特性写用例[Fact] public void MyTest() { /* ... */ }测试工程的Main入口由源生成器 src/tests/Common/XUnitWrapperGenerator 构造它找出所有Fact标记的方法并从生成的Main里调用它们。注入逻辑在 src/tests/Directory.Build.targets 第 537-543 行满足条件的 C# 工程会自动引用该生成器作为Analyzer。也可以自己写Main。约定是成功返回100任何非100值都表示失败static public int Main(string[] notUsed) { try { /* 测试场景 */ } catch (Exception e) { Console.WriteLine($Test Failure: {e}); return 101; } return 100; }这个100与 src/tests/Common/CLRTest.Execute.targets 中默认的CLRTestExitCode100/CLRTestExitCode一致——执行脚本就是靠它判定测试成败。按需声明依赖托管引用写ProjectReference Include../ManagedDll.csproj /原生引用写CMakeProjectReference Include../NativeDll/CMakeLists.txt /。后者会被ResolveCMakeNativeProjectReference目标识别并转成NativeProjectReference构建后二进制经CopyNativeProjectBinaries目标复制到测试输出目录。构建测试。按本地重跑的方式验证见文末。另外两条硬性约束所有测试源文件必须带 MIT 许可证横幅Licensed to the .NET Foundation under one or more agreements...托管部分必须在任何平台都能编译——CI 里所有测试的托管部分都在 OSX 上构建、各目标平台只运行子集所以不得包含依赖目标平台的条件编译代码、条件包含文件或条件DefineConstants/。用到System.Private.CoreLib的类型时不支持的平台上应有桩实现直接throw new PlatformNotSupportedException()。测试因活跃 bug 失败时加[ActiveIssue]特性。需要配置哪些开关优先级与兼容排除优先级怎么定低值先跑显式降级测试默认就是 priority 0src/tests/Directory.Build.targets 第 21 行最重要的子集应当也是最小的子集。想降级就显式写CLRTestPriority2/CLRTestPriority。低优先级值的测试总伴随高优先级值一起跑选择运行 priority 2 时0、1、2 全部执行。真实示例见 src/tests/GC/API/GC/Collect.csprojCLRTestPriority1/CLRTestPriority与 src/tests/GC/API/GC/Collect_Aggressive.csproj显式CLRTestPriority0/CLRTestPriority。优先级还参与选择性构建当BuildAllProjectstrue时只有CLRTestPriority CLRTestPriorityToBuild的工程才会被真正构建_WillCLRTestProjectBuild判定之一从而支持只构建/运行到某一优先级为止的批量操作。平台不可用时怎么声明属性效果最小示例CLRTestTargetUnsupported在特定目标平台上不构建也不运行可条件化CLRTestTargetUnsupported Condition$(TargetArchitecture) arm64true/CLRTestTargetUnsupportedDisableProjectBuild无条件禁用构建DisableProjectBuildtrue/DisableProjectBuild两者都会把_WillCLRTestProjectBuild置为 false第 100-101 行。该判定还覆盖一批衍生场景MonoAotIncompatible在 mono 的 fullaot 变体下禁用构建、NativeAotIncompatible在TestBuildMode nativeaot时禁用构建、IsGCSimulatorTest的测试不参与 NativeAOT 构建、AlwaysUseCrossGen2与 mono 变体或 sanitizer 组合时也禁用构建。各编译/压力模式的排除开关这些属性全部写在测试工程 csproj 中把测试从特定运行模式里排除属性排除的模式写法GCStressIncompatibleGCStress 运行GCStressIncompatibletrue/GCStressIncompatibleJitOptimizationSensitiveJIT 压力运行JitOptimizationSensitivetrue/JitOptimizationSensitiveNativeAotIncompatibleNativeAOT 运行NativeAotIncompatibletrue/NativeAotIncompatibleIlasmRoundTripIncompatibleilasm 往返测试同时隐含ManagedIlasmRoundTripIncompatibleIlasmRoundTripIncompatibletrue/IlasmRoundTripIncompatibleUnloadabilityIncompatible可卸载性collectible 程序集测试UnloadabilityIncompatibletrue/UnloadabilityIncompatibleR2RIncompatibleReadyToRun 测试运行R2RIncompatibletrue/R2RIncompatible两个属性值得多看一眼。第一GCStress 本身靠环境变量DOTNET_GCStress注入src/tests/Common/CLRTest.Jit.targets 末尾会把它写进执行脚本。而如果你的源文件带反汇编检查HasDisasmCheck元数据该文件第 116 行会自动补上GCStressIncompatibletrue——反汇编比对与 GCStress 扰动出的代码形态天然冲突不必手动处理。第二围绕 crossgen2 / R2R 还有两个反向开关AlwaysUseCrossGen2true/AlwaysUseCrossGen2表示测试专门验证 crossgen2、所有模式下都按此编译CrossGenTestfalse/CrossGenTest表示即使正在跑 R2R 测试通过该测试也不以标准 R2R 方式编译。两者都实现在 src/tests/Common/CLRTest.CrossGen.targets注意第 26 行的联动设置AlwaysUseCrossGen2true会自动标记IlasmRoundTripIncompatibletrue而同时设置AlwaysUseCrossGen2true和CrossGenTestfalse会直接报错 Setting both AlwaysUseCrossGen2 to true and CrossGenTest to false is non-sensical第 47 行。它背后发生了什么构建判定与脚本生成把前面散落的属性串起来整个流水线是这样的判定是否构建。src/tests/Directory.Build.targets 第 94-107 行计算_WillCLRTestProjectBuild非批量构建BuildAllProjects ! true默认为 true批量构建时再依次检查优先级、CLRTestTargetUnsupported、DisableProjectBuild、NativeAotIncompatible、IsGCSimulatorTest、AlwaysUseCrossGen2的组合。判定为 false 时导入 src/tests/Common/nobuild.targetsBuild/Compile等目标空转——这就是你测试被跳过时最该去查的分支。生成执行脚本。CreateExecuteScript目标第 284-287 行在_WillCLRTestProjectBuild true且GenerateRunScript ! false时触发GenerateExecutionScriptsInternal统一产出 BatchWindows与 BashUnix两份脚本内容包含执行前/后命令片段、环境变量、退出码检查。R2R / crossgen2 脚本。src/tests/Common/CLRTest.CrossGen.targets 生成脚本会先检查若R2RIncompatibletrue且处于 R2R 模式直接输出 SKIPPING EXECUTION BECAUSE the test is incompatible with ReadyToRun 并exit 0若AlwaysUseCrossGen2true则设置RunCrossGen21与CompositeBuildMode1走 composite 编译。脚本还会生成响应文件、调用$CORE_ROOT/crossgen2/crossgen2、再用 R2RDump 对产物做--header --sc --val校验支持--composite、--targetarch、--targetos、--method-layout:random、--hot-cold-splitting、--synthesize-random-mibc合成 PGO 数据等参数。JIT 附加流程。src/tests/Common/CLRTest.Jit.targets 负责 ilasm 往返脚本、反汇编检查SuperFileCheck、SuperPMI 收集以及前述的DOTNET_GCStress环境变量注入。标记文件。GenerateMarkerFiles目标会为各类属性落盘标记文件如$(AssemblyName).IlasmRoundTripIncompatible、$(AssemblyName).NoMonoAot供 CI 侧的测试调度器按类型筛选。合并测试运行器要注意什么大规模测试通常收敛进合并测试运行器merged test runner以 src/tests/JIT/Methodical/Methodical_d1.csproj 为模板工程里用HasMergedInTeststrue/HasMergedInTests声明合并属性用MergedWrapperProjectReference的通配符如a*/**/*_d.??proj批量引入子工程再显式Compile包含共享源文件。如果新运行器包含大量测试、GC Stress 下跑不完设置NumberOfStripesToUseInStress10/NumberOfStripesToUseInStress把它切成多条条纹并行执行。生效逻辑在 src/tests/Directory.Build.targets 的GenerateMarkerFiles目标合并运行器默认 1 条然后按该数值生成形如$(MSBuildProjectName).MergedTestAssembly.stripe.total.MergedTestAssemblyForStress的标记文件上限 99超出直接报错。本地手动复现问题时合并运行器支持标准命令行参数[testFilterString] [-stripe whichStripe totalStripes]testFilterString目前只支持dotnet test --filter的简单形式测试完全限定名的子串条纹控制也可以用环境变量TEST_HARNESS_STRIPE_TO_EXECUTE值必须形如.whichStripe.totalStripes。whichStripe是从 0 开始的下标。怎么验证测试真的跑通了✅ 按重跑失败测试的流程来先构建测试再按失败报告里给出的命令执行对应脚本。CI 的失败报告如 docs/workflow/ci/failed-test.md 所述格式会给出完整的重跑命令并明确展示期望与实际退出码——这正是前面CLRTestExitCode约定的用武之地图中Expected: 100, Actual: 10就是测试没走成功返回 100的约定报告底部 To run the test 段落直接给出可复制的重跑命令本地执行后退出码回到 100 即为通过。NuGet 依赖统一加到 src/tests/Common/test_dependencies/test_dependencies.csproj该工程本身是BuildOnly种类它集中承载测试依赖包并负责把库运行时文件汇集到 Core_Root。属性速查与源码索引属性默认值说明CLRTestKindBuildAndRunOutputTypeExe时测试种类BuildOnly/RunOnly/BuildAndRun/SharedLibraryCLRTestPriority0优先级低值总随高值一同运行降级需显式设置CLRTestTargetUnsupported空按条件禁用特定目标的构建与运行DisableProjectBuild空无条件禁用构建GCStressIncompatible空排除 GCStress 运行JitOptimizationSensitive空排除 JIT 压力运行NativeAotIncompatible空排除 NativeAOT 运行IlasmRoundTripIncompatible空排除 ilasm 往返测试UnloadabilityIncompatible空排除 collectible 程序集测试R2RIncompatible空排除 ReadyToRun 运行AlwaysUseCrossGen2空强制以 crossgen2 方式编译自动排除 ilasm 往返CrossGenTesttrue置false时 R2R 测试通过中也不做标准 R2R 编译NumberOfStripesToUseInStress1合并运行器GC Stress 下的条纹切分数上限 99CLRTestExitCode100测试成功退出码遇到问题时按顺序查这几个文件src/tests/Directory.Build.targets——统一入口种类/优先级默认值、_WillCLRTestProjectBuild判定、标记文件生成、XUnit 源生成器注入、NativeAOT 配置。src/tests/Common/CLRTest.Execute.targets——Batch/Bash 执行脚本生成核心InputAssemblyName解析、前/后命令片段。src/tests/Common/CLRTest.CrossGen.targets——crossgen2 脚本响应文件、composite 模式、R2RDump 校验。src/tests/Common/CLRTest.Jit.targets——ilasm 往返、反汇编检查、SuperPMI 收集、DOTNET_GCStress注入。src/tests/Common/nobuild.targets 与 src/tests/Common/runonly.targets——构建被跳过与 RunOnly 依赖构建的最小实现。src/tests/Common/test_dependencies/test_dependencies.csproj——测试公共 NuGet 依赖的集中管理。src/tests/JIT/Methodical/Methodical_d1.csproj 与 src/tests/GC/API/GC/Collect.csproj——合并运行器与优先级设置的现实样例。【免费下载链接】runtime.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.项目地址: https://gitcode.com/GitHub_Trending/runtime6/runtime创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考