首页 / 资讯中心 / 文章详情

.NET runtime CoreCLR 测试配置深度指南:CLRTestKind、CLRTestPriority 与测试工程编写规范

.NET runtime CoreCLR 测试配置深度指南:CLRTestKind、CLRTestPriority 与测试工程编写规范 ★ FEATURED ARTICLE
.NET runtime CoreCLR 测试配置深度指南CLRTestKind、CLRTestPriority 与测试工程编写规范【免费下载链接】runtime.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.项目地址: https://gitcode.com/GitHub_Trending/runtime6/runtime你在src/tests下新建一个 CoreCLR 测试工程后CI 会替它决定要不要编译、要不要生成执行脚本、GCStress 跑遍时跳不跳过、R2R 模式下用不用 crossgen2 预编译。这些决定全部来自工程里的几个 MSBuild 属性——CLRTestKind与CLRTestPriority是入口其余不兼容开关是修正项。本文拆解这套配置体系每个属性的定义、正确写法以及底层判定发生在哪个 targets 文件的哪一行让你配置一次到位、跳过得来也查得清。机制总览一个属性如何走完判定 → 执行所有测试工程的属性声明都先汇入src/tests/Directory.Build.targets做统一判定再分流到执行脚本生成。先记住这张分层图后文每个概念都在其中占位csproj 中声明: CLRTestKind / CLRTestPriority / 各种 *Incompatible 属性 │ ▼ src/tests/Directory.Build.targets ├─ 推导默认值未显式设置时按 OutputType 推断 KindPriority 默认 0 ├─ 构建门禁 _WillCLRTestProjectBuild ──false──▶ nobuild.targetsBuild 空转不生成任何产物 └─ true ▼ src/tests/Common/CLRTest.Execute.targets ├─ 生成 .cmdWindows/ .shUnix执行脚本 ├─ CLRTest.Jit.targets ── ilasm 往返、反汇编检查、GCStress 环境变量 └─ CLRTest.CrossGen.targets ── R2R / crossgen2 预编译与 R2RDump 校验两个值得先记住的设计身份与门禁分离CLRTestKind回答这个工程是什么形态CLRTestPriority回答它属于第几梯队而CLRTestTargetUnsupported、NativeAotIncompatible一类属性回答这次构建里要不要它。三者最终都汇进同一个_WillCLRTestProjectBuild判定。构建时决策 运行时自跳过不兼容属性不是在 CI 脚本里过滤工程而是把SKIPPING EXECUTION ...逻辑直接写进执行脚本。同一个产物因此可以安全地跑在多种模式里——模式不匹配时脚本自己退出。概念拆解五个属性组从身份到形态1. CLRTestKind工程是构建、运行还是两者都做定义每个测试工程必须归属四种形态之一由CLRTestKind声明CLRTestKind编译源码构建可执行文件生成执行脚本典型用途BuildOnly是是否编译期校验、供 RunOnly 复用的产物RunOnly否否是同一可执行文件配不同命令行参数BuildAndRun是是是绝大多数普通测试默认值SharedLibrary是否否多个测试共用的托管库作用Kind 是构建系统一切行为的总开关——它决定脚本是否生成、依赖工程是否先行构建、签名是否跳过。配置写法多数工程不需要显式设置默认推导逻辑在 src/tests/Directory.Build.targets 第 19-20 行CLRTestKind Condition$(CLRTestKind) and $(OutputType) ExeBuildAndRun/CLRTestKind CLRTestKind Condition$(CLRTestKind) SharedLibrary/SharedLibrary即输出为Exe的工程默认BuildAndRun类库默认SharedLibrary。需要特殊形态时才显式写CLRTestKindRunOnly/CLRTestKind。底层实现第 30-64 行的Choose块把四种 Kind 翻译成_CLRTestCompilesSource/_CLRTestNeedsToRun/GenerateRunScript/_CLRTestBuildsExecutable四个内部开关。RunOnly的依赖工程机制在第 84-88 行当需要运行但不构建可执行文件时CLRTestProjectToRun被自动转换为ProjectReference保证被引用的工程先构建。随后 src/tests/Common/CLRTest.Execute.targets 的FetchExternalProperties目标第 100-101 行用[MSBuild]::MakeRelative把被引用工程的输出算成InputAssemblyNameBuildAndRun则直接用$(AssemblyName).dll。真实示例src/tests/GC/Scenarios/GCSimulator/GCSimulator_100.csproj 是一个RunOnly工程通过CLRTestProjectToRunGCSimulator.csproj/CLRTestProjectToRun复用同一可执行文件并用CLRTestExecutionArguments传入不同的模拟器参数。2. CLRTestPriority测试梯队与选择性构建定义测试按重要性分梯队priority 0 必须是最重要且最小的子集。默认所有测试为 priority 0想要降级必须显式声明。作用既控制运行范围也控制构建范围。低优先级值总是与高优先级值一起跑——选 priority 2 的运行0、1、2 全部执行。配置写法CLRTestPriority2/CLRTestPriority !-- 显式降级不写则默认 0 --底层实现默认值在 src/tests/Directory.Build.targets 第 21 行。选择性构建在第 94-107 行BuildAllProjectstrue时只有CLRTestPriority CLRTestPriorityToBuild的工程才置_WillCLRTestProjectBuildtrue其余工程连同 IL 目标一起被跳过第 126 行SkipImportILTargets。这就是只构建到某一优先级为止的批量操作基础。仓库里的现实示例src/tests/GC/API/GC/Collect.csproj 显式写了CLRTestPriority1/CLRTestPriority因为该测试要求独占的进程级 GC 状态而 src/tests/GC/API/GC/Collect_Aggressive.csproj 留在默认的 priority 0。3. 构建门禁这个工程本次要不要构建定义一组条件开关命中任意一条工程就不参与构建、不生成执行脚本。作用把某平台跑不了 / 某构建模式不适用从 CI 流水线配置里解耦出来直接写在工程自身。配置写法条件写法按需选用!-- 在特定目标平台不构建也不运行 -- CLRTestTargetUnsupported Condition$(TargetArchitecture) arm64true/CLRTestTargetUnsupported !-- 无条件禁用构建 -- DisableProjectBuildtrue/DisableProjectBuild !-- NativeAOT 模式不适用 -- NativeAotIncompatibletrue/NativeAotIncompatible底层实现判定全部集中在 src/tests/Directory.Build.targets 第 94-107 行的_WillCLRTestProjectBuild链条中。除了上面三个属性还自动处理了一批衍生场景MonoAotIncompatible在 mono fullaot 变体下禁用第 15 行、IsGCSimulatorTest与TestBuildModenativeaot互斥第 103 行、AlwaysUseCrossGen2在 mono、nativeaot、sanitizer 模式下禁用构建第 104-106 行与概念 5 联动。判定为不构建时文件末尾第 561 行导入 src/tests/Common/nobuild.targets——它把Build、Compile等目标全部替换为空操作构建空转通过但不产出任何东西。4. 压力模式不兼容属性让执行脚本自己跳过定义一组把测试从特定运行模式排除的开关。它们不影响构建只影响执行脚本内容。作用CI 会用 GCStress、JIT stress、ilasm 往返、可卸载程序集等模式反复跑同一批测试。有些测试与某种模式的代码形态根本不相容比如反汇编检查遇上 GCStress 注入的分配点强行跑只会误报。配置写法属性排除的模式写法GCStressIncompatibleGCStress 运行GCStressIncompatibletrue/GCStressIncompatibleJitOptimizationSensitiveJIT stress / TieredCompilation 开启JitOptimizationSensitivetrue/JitOptimizationSensitiveNativeAotIncompatibleNativeAOT 运行NativeAotIncompatibletrue/NativeAotIncompatibleIlasmRoundTripIncompatibleilasm 往返测试IlasmRoundTripIncompatibletrue/IlasmRoundTripIncompatibleUnloadabilityIncompatible可卸载collectible 程序集测试UnloadabilityIncompatibletrue/UnloadabilityIncompatibleR2RIncompatibleReadyToRun 运行R2RIncompatibletrue/R2RIncompatible底层实现按三个文件分述GCStresssrc/tests/Common/CLRTest.Jit.targets 第 496-499 行把RunWithGcStress写进执行脚本的DOTNET_GCStress环境变量。同一文件第 108-117 行的GetDisasmCheckData目标会扫描带HasDisasmCheck元数据的源文件有反汇编检查的工程被自动标记GCStressIncompatible第 116 行——你不需要、也不应该手写这一条。JIT 敏感src/tests/Common/CLRTest.Execute.Bash.targets 第 105-115 行Batch 版在第 98-119 行在脚本前置环境兼容性检查TieredCompilation 未禁用时输出SKIPPING EXECUTION BECAUSE ... marked JitOptimizationSensitive并退出。ilasm 往返IlasmRoundTripIncompatibletrue时构建会落一个标记文件GenerateMarkerFiles目标src/tests/Directory.Build.targets 第 445-447 行ilasm 往返脚本CLRTest.Jit.targets第 283 行起的IlasmRoundTripCommand看到标记就跳过该程序集。5. R2R 与 crossgen2预编译测试的高级属性定义围绕 ReadyToRun 的四个属性R2RIncompatible整体排除、AlwaysUseCrossGen2强制以 crossgen2 方式编译、CrossGenTest默认true置false表示 R2R 测试遍中也不做标准 R2R 编译、CompositeCrossGen2TestExtraArguments/CrossGen2TestExtraArguments附加 crossgen2 参数。作用让专测 crossgen2 行为的工程在所有模式下都以 R2R 身份出场同时避免与 ilasm 往返等不相容的模式冲突。配置写法仓库里最完整的范本是 src/tests/readytorun/crossgen2/crossgen2smoke.csprojIlasmRoundTripIncompatibletrue/IlasmRoundTripIncompatible AlwaysUseCrossGen2true/AlwaysUseCrossGen2 CrossGen2TestExtraArguments$(CrossGen2TestExtraArguments) --generate-unboxing-stubs/CrossGen2TestExtraArguments底层实现src/tests/Common/CLRTest.CrossGen.targets 第 26 行规定AlwaysUseCrossGen2true自动蕴含IlasmRoundTripIncompatibletruecrossgen2 产物与 ilasm 往返不相容无需手动写。执行脚本生成时的三分支R2RIncompatibletrue且运行模式要求 R2R → 脚本直接输出SKIPPING EXECUTION BECAUSE the test is incompatible with ReadyToRun后退出第 50-57 行AlwaysUseCrossGen2true→ 设置RunCrossGen21与CompositeBuildMode1走 composite 编译流程产物还要过 R2RDump 的--header --sc --val校验冲突规则如果同时设置AlwaysUseCrossGen2true和CrossGenTestfalse构建直接报错Setting both AlwaysUseCrossGen2 to true and CrossGenTest to false is non-sensical第 47 行——两者语义互相矛盾。6. 合并测试运行器一个入口跑几百个测试定义merged test runner 把目录下成百上千个小测试聚合成单个可执行文件运行由IsMergedTestRunnerAssembly运行器自身声明与HasMergedInTests声明其吞并了子工程标识。作用避免每个小测试各自起进程的开销同时保留-stripe切分能力让 GCStress 这类慢模式下大测试组也能切条并行。配置写法以 src/tests/JIT/Methodical/Methodical_d1.csproj 为模板核心结构HasMergedInTeststrue/HasMergedInTests !-- 按通配符批量引入子工程 -- MergedWrapperProjectReference Includea*/**/*_d.??proj / !-- GCStress 下耗时过长时切分成 10 条 stripe -- NumberOfStripesToUseInStress10/NumberOfStripesToUseInStress底层实现src/tests/Directory.Build.targets 第 318-331 行的GenerateMarkerFiles目标合并运行器未显式设置时默认 1 条 stripe第 319-321 行随后生成形如name.MergedTestAssembly.stripe.total.MergedTestAssemblyForStress的标记文件第 426-431 行CI 的压力测试按标记文件把测试组切开并行执行stripe 数超过 99 会直接报构建错误。运行时的参数解析在 src/tests/Common/XUnitWrapperLibrary/TestFilter.cs 第 128-156 行。实战场景新增一个 C# 测试工程以 src/tests/Exceptions/Finalization/Finalizer.csproj 为模板复制到src/tests下的新目录务必移除模板里的AssemblyName属性——保留它会让构建系统幕后处理测试的方式产生混淆按需设置CLRTestKind/CLRTestPriority普通测试两者都可以不写走默认推导加源文件。测试用例用 Xunit 的[Fact]特性标记构建系统通过 src/tests/Directory.Build.targets 第 537-543 行把XUnitWrapperGenerator源生成器注入为Analyzer它会自动发现所有[Fact]方法并从生成的Main中调用。也可以手写Main——成功返回 100失败返回任何非 100 值100 这个默认值定义在 src/tests/Common/CLRTest.Execute.targets 第 37 行CLRTestExitCode声明依赖托管引用用ProjectReference Include../ManagedDll.csproj /CMake 原生库用CMakeProjectReference Include../NativeDll/CMakeLists.txt /——后者由ResolveCMakeNativeProjectReference目标识别并转成NativeProjectReference构建后由CopyNativeProjectBinaries把原生二进制复制进测试输出目录构建并按重跑失败测试的既有流程验证新测试真的被执行。新增一个 JIT 回归测试常规情况优化编译、无调试信息的回归测试直接往src/tests/JIT/Regression_ro_2/目录放Runtime_issue_number.cs即可。src/tests/JIT/Regression_ro_2/Regression_ro_2.csproj 用Compile Include**\*.cs /递归吞并目录下全部 C# 文件——不需要改工程文件也不需要为单个源文件建目录相关辅助文件可以放子目录分组。进阶情况需要不同编译设置、独立程序集、原生依赖或进程隔离的测试放在src/tests/JIT/Regression_2/Runtime_issue_number/下建独立工程。Regression_2.csproj以递归发现工程的方式组织它们不直接编译源码Regression_3.csproj类似地管理既有的GitHub_*、DevDiv_*系列工程。注意不要把这类工程的源码放进源文件通配运行器的目录否则会被直接编译进运行器。本地运行合并测试运行器合并运行器支持的标准命令行形态[testFilterString] [-stripe whichStripe totalStripe]testFilterString是除-stripe外的任意字符串目前仅支持dotnet test --filter的简单子串形式。条纹控制有两种等价方式-stripe 3 10whichStripe从 0 计或环境变量TEST_HARNESS_STRIPE_TO_EXECUTE.3.10必须带首尾点号。排查测试被跳过的三步法先确认它到底构建了没有。被门禁拦下的工程走nobuild.targetsBuild是空转的——产物目录里没有 dll 与执行脚本是构建被拒而非运行被跳。检查_WillCLRTestProjectBuild链条src/tests/Directory.Build.targets 第 94-107 行里的每个条件CLRTestTargetUnsupported、DisableProjectBuild、NativeAotIncompatible、AlwaysUseCrossGen2与各模式的组合。再看执行脚本里的自跳过逻辑。用MSBuild -preprocess或直接读生成的.sh/.cmd找SKIPPING EXECUTION BECAUSE文案R2RIncompatible R2R 模式、JitOptimizationSensitive TieredCompilation 未禁用、GC 模拟器/长跑 GC 的开关不匹配都在这里退出。最后核对退出码约定。默认CLRTestExitCode是 100如果你给手写Main的测试设了别的成功码要确认执行脚本侧ExpectedExitCode标记文件GenerateMarkerFiles第 454-456 行同步修改否则跑对了也会被判失败。配置速查属性默认值作用适用场景冲突规则CLRTestKindBuildAndRunOutputTypeExe时否则SharedLibrary决定编译/构建/脚本生成行为特殊形态RunOnly 等才需显式设置—CLRTestPriority0测试梯队低值随高值一起运行显式降级不显式升级 CLRTestPriorityToBuild时不构建CLRTestProjectToRun空RunOnly 工程指向真正运行的工程同一可执行文件多种参数仅CLRTestKindRunOnly有意义CLRTestTargetUnsupported空条件禁用某目标的构建与运行平台/架构不支持置 true 即走 nobuildDisableProjectBuild空无条件禁用构建工程整体停用优先级最高的门禁GCStressIncompatible空排除 GCStress依赖稳定 GC 行为的测试HasDisasmCheck时自动置 true勿手写JitOptimizationSensitive空排除 JIT stress对寄存器分配/优化路径敏感要求DOTNET_TieredCompilation禁用NativeAotIncompatible空排除 NativeAOT托管桩在 AOT 下不可用置 true 即 nativeaot 模式不构建IlasmRoundTripIncompatible空排除 ilasm 往返产物无法可靠往返自动蕴含ManagedIlasmRoundTripIncompatibleUnloadabilityIncompatible空排除 collectible 程序集测试依赖静态程序集生命期—R2RIncompatible空排除 R2R 运行R2R 产物行为不相容R2R 模式下脚本自跳过AlwaysUseCrossGen2空强制 crossgen2 编译专测 crossgen2 的工程与CrossGenTestfalse同设直接报错CrossGenTesttruefalse时 R2R 遍中不做标准 R2R 编译需绕过 R2R 的测试见上行NumberOfStripesToUseInStress1仅合并运行器GCStress 下切分的 stripe 数大测试组压力测试上限 99超出报构建错误CLRTestExitCode100成功退出码手写Main的非标准约定合并运行器可用ExpectedExitCode标记覆盖源码导读src/tests/Directory.Build.targets —— 所有测试工程的统一入口Kind/Priority 默认值推导、四形态开关、_WillCLRTestProjectBuild构建判定、stripe 标记文件生成、XUnit 源生成器注入。src/tests/Common/CLRTest.Execute.targets —— 执行脚本生成核心InputAssemblyName决策RunOnly 相对路径 vs BuildAndRun 直用 dll、默认CLRTestExitCode100。src/tests/Common/CLRTest.CrossGen.targets —— crossgen2/R2R 脚本响应文件、composite 模式、R2RDump 校验、矛盾配置报错。src/tests/Common/CLRTest.Jit.targets —— ilasm 往返脚本、反汇编检查SuperFileCheck 前后置命令、GCStress 环境变量注入、SuperPMI 收集。src/tests/Common/nobuild.targets 与 src/tests/Common/runonly.targets —— 被门禁拦截时让构建空转RunOnly 工程的只构建依赖 建输出目录特殊构建。src/tests/Common/XUnitWrapperLibrary/TestFilter.cs —— 合并运行器的测试过滤与-stripe/TEST_HARNESS_STRIPE_TO_EXECUTE解析。src/tests/Common/test_dependencies/test_dependencies.csproj —— 测试公共 NuGet 依赖的集中承载工程BuildOnly新增包引用改这里。示例工程src/tests/JIT/Methodical/Methodical_d1.csproj合并运行器模板、src/tests/JIT/Regression_ro_2/Regression_ro_2.csproj源文件通配运行器、src/tests/GC/API/GC/Collect.csprojPriority 降级、src/tests/readytorun/crossgen2/crossgen2smoke.csprojcrossgen2 属性全量用法。建议按Directory.Build.targets → CLRTest.Execute.targets → 你手头那个行为异常的 targets 文件的顺序精读先看懂属性如何变成判定再顺着判定落到脚本片段测试被跳过、脚本没生成、R2R 行为不对这类问题基本都能在这条链路上对号入座。【免费下载链接】runtime.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.项目地址: https://gitcode.com/GitHub_Trending/runtime6/runtime创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?
咨询建站