.NET runtime 的 CoreCLR 测试工程怎么配:CLRTestKind、CLRTestPriority 与压力测试避坑指南 .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在 .NET runtime 仓库里提交一个 CoreCLR 测试真正的工作量多半在工程文件里的那十几个属性上。CLRTestKind、CLRTestPriority、GCStressIncompatible这些开关决定了你的测试工程会不会被生成执行脚本、能否进入批量构建以及它能在 GCStress、JIT 压力、R2R、NativeAOT 这些 CI 变体里活下来几个。配置写错测试往往不会报错而是被 CI 矩阵静默跳过最后只留下一句没人看的 SKIPPING EXECUTION。理解这组属性的默认值是怎么推导的、它们之间谁压过谁是读懂 src/tests/ 整个测试基础设施的前提。一个 CLRTestKind 属性如何决定工程被构建还是只被运行CLRTestKind把测试工程分成四种角色BuildAndRun、BuildOnly、RunOnly、SharedLibrary。名字很直白但它的真正价值在于每个值都会在 src/tests/Directory.Build.targets 里被展开成一组内部开关——GenerateRunScript控制生成不生成执行脚本_CLRTestCompilesSource控制编译不编译源码_CLRTestBuildsExecutable控制产不产可执行文件。工程里没显式写CLRTestKind时默认值由条件表达式推导推导依据是OutputTypeCLRTestKind Condition$(CLRTestKind) and $(OutputType) ExeBuildAndRun/CLRTestKind CLRTestKind Condition$(CLRTestKind) SharedLibrary/SharedLibrary 类型 CLRTestPriority Condition$(CLRTestPriority) 0/CLRTestPriority也就是可执行程序默认构建并运行类库默认共享库优先级默认 0。三种典型取法绝大多数测试什么都不写直接吃BuildAndRun默认只想编译校验不执行的比如 src/tests/Common/test_dependencies/test_dependencies.csproj 就声明了BuildOnly显式写明BuildOnly同一份可执行文件想换几组命令行参数反复跑时复制出几个RunOnly工程各自用CLRTestProjectToRun指向真正产出二进制的那个工程——构建系统会把它转成ProjectReference先行构建再为RunOnly工程生成脚本。最小可运行配置就是一个csproj头部PropertyGroup CLRTestKindBuildAndRun/CLRTestKind CLRTestPriority0/CLRTestPriority /PropertyGroup种类决定了产物形态接下来 CI 批量跑测试时还要按优先级筛一遍。优先级阈值如何过滤批量构建CI 不会每次都跑全量测试CLRTestPriority就是分级执行的依据。设计意图是最重要的子集应当也是最小的子集所以默认 0新测试需要显式降级。批量构建时判定链里有一条按阈值过滤的规则_WillCLRTestProjectBuild Condition$(BuildAllProjects) true And $(CLRTestPriority) lt; $(CLRTestPriorityToBuild)true/_WillCLRTestProjectBuildCLRTestPriorityToBuild由 CI 任务给阈值数值只进不出你选了 20、1、2 全部会跑。看真实工程能更直观——src/tests/GC/API/GC/Collect.csproj 的完整内容不长PropertyGroup RequiresProcessIsolationtrue/RequiresProcessIsolation CLRTestPriority1/CLRTestPriority /PropertyGroup !-- ... -- ItemGroup ProjectReference Include$(TestLibraryProjectPath) / /ItemGroup它声明自己没那么核心只保证进入优先级 ≤ 1的批量任务。注意优先级只影响构建范围不影响单测触发——单独跑它照样构建执行。压力矩阵下的兼容开关谁砍构建、谁砍运行同一工程进入压力矩阵后还要在 GCStress、JIT 压力、R2R、NativeAOT 等变体下各活一遍。每个变体对应一个开关开关的差别在于有的直接砍掉构建有的让生成的脚本自己跳过执行。GCStress 的注入点在 src/tests/Common/CLRTest.Jit.targets 生成的执行脚本里——只要 CI 传入了压力参数脚本就会带上对应环境变量。JitOptimizationSensitive同理用来把结果依赖优化行为的工程从 JIT 压力运行中摘出去。而NativeAotIncompatible走的是构建判定链TestBuildMode nativeaot时该工程根本不进构建连脚本都不会生成。一个自动推导值得留意工程里任何源文件带了反汇编检查HasDisasmCheck构建系统会直接替你补上GCStressIncompatibletrue。原因是压力模式下 GC 插入改变了代码形态CHECK 断言没法稳定复现与其让 CI 红一片不如默认排除。R2R 这条线有两个属性容易配反。R2RIncompatibletrue的工程照常构建执行脚本会在开头生成这样一段if [ ! -z ${RunCrossGen2x} ]; then echo SKIPPING EXECUTION BECAUSE the test is incompatible with ReadyToRun exit 0 fi非 R2R 轮次照跑R2R 轮次安静退出 0。另一个方向是AlwaysUseCrossGen2true表示这个测试就是专门验证 crossgen2 的脚本会强制置RunCrossGen21与CompositeBuildMode1走复合编译。这两个属性在 src/tests/Common/CLRTest.CrossGen.targets 里是互斥的Error TextSetting both AlwaysUseCrossGen2 to true and CrossGenTest to false is non-sensical Condition$(CrossGenTest) false and $(AlwaysUseCrossGen2) true/同时设置时构建直接报错——前者要求必须以 crossgen2 编译后者要求该工程不参与 R2R 编译语义上无法共存。跨平台编译约束托管代码必须在 macOS 上先编译通过还有一条不写在工程属性里的硬约束。CI 里所有测试的托管部分统一在 macOS 上构建再分发到各目标平台跑子集所以托管代码不得包含依赖目标平台的条件编译、依赖目标平台的条件包含、依赖目标平台的DefineConstants。测试用到的System.Private.CoreLibAPI 必须在所有平台可编译不支持的平台用抛PlatformNotSupportedException的桩实现占位。这条违反的后果不是 CI 失败而是其他平台的托管构建在合并后才发现挂了。场景一新增一个 JIT 回归测试并跑通 CI最常见的情况——测试只需要默认编译参数把源文件直接丢进 src/tests/JIT/Regression_ro_2/ 目录。这个运行器工程递归包含目录下所有.cs文件你不需要动工程文件也不需要单独建目录。文件头写上许可证横幅构建一下确认被收进去、脚本生成了测试就进了 CI。需要独立编译参数、原生依赖或进程隔离时在src/tests/JIT/Regression_2/Runtime_issue/下新建工程声明CLRTestKind与CLRTestPriority再按兼容性需要挂开关。测试用例两种写法用 xunit 的[Fact]基础设施的源生成器会为工程合成Main入口逐个调用它们或写自定义Main成功返回 100、失败返回其他值——100 是执行脚本约定的默认成功退出码自定义CLRTestExitCode才能改它。大型合并运行器的条纹切分参数如果你的测试量级像 src/tests/JIT/Methodical/Methodical_d1.csproj 这种合并运行器HasMergedInTeststrue/HasMergedInTests !-- ... -- MergedWrapperProjectReference Includea*/**/*_d.??proj / MergedWrapperProjectReference Includeb*/**/*_d.??proj /用通配符批量引入子工程。GCStress 下全量跑太久时把NumberOfStripesToUseInStress调大合并运行器默认 1上限 99构建系统按该数值生成条纹标记文件CI 压力任务据此把大测试组切开并行。本地手动复现某一条纹用-stripe whichStripe totalStripes参数或设置TEST_HARNESS_STRIPE_TO_EXECUTE值形如.whichStripe.totalStripes条纹下标从 0 开始。⚠️常见误区新增工程时从模板复制却忘了删AssemblyName会让构建系统对后台怎么处理这个测试产生误判同理新依赖的 NuGet 包应加进 test_dependencies.csproj 集中管理而不是散落在各测试工程里。场景二排查某个测试在特定平台上被静默跳过测试消失的排查顺序基本固定。先打开 csproj看CLRTestTargetUnsupported的条件表达式在不在你这个平台为真看有没有写死DisableProjectBuild。再对照判定链里_WillCLRTestProjectBuild的覆盖顺序阈值 →CLRTestTargetUnsupported→DisableProjectBuild→NativeAotIncompatible等模式互斥项后者覆盖前者确认是哪条把工程挡在了构建之外。最后看输出目录没有这个工程的标记文件说明构建阶段就被跳过了压根没进执行环节。如果脚本生成了、执行阶段才跳过那就是执行策略不兼容去搜脚本里的SKIPPING EXECUTION文案——R2R 不兼容、反汇编检查轮次互斥都会走这条安静退出的路。属性速查与排障思路属性默认值作用与行为CLRTestKind由OutputType推导Exe→BuildAndRun其余 →SharedLibrary决定脚本生成、源码编译、可执行产物三组开关CLRTestPriority0批量构建阈值过滤只进不出CLRTestProjectToRun空RunOnly工程指向的被引用工程自动转ProjectReferenceCLRTestTargetUnsupported空条件为真时该目标不构建不运行DisableProjectBuild空无条件取消构建GCStressIncompatible空反汇编检查工程被自动置true排除 GCStress 运行JitOptimizationSensitive空排除 JIT 压力运行NativeAotIncompatible空NativeAOT 模式下不进构建IlasmRoundTripIncompatible/UnloadabilityIncompatible空分别排除 ilasm 往返、可回收程序集测试R2RIncompatible空照常构建脚本在 R2R 轮次打印跳过并退出 0AlwaysUseCrossGen2/CrossGenTest空 /true强制 crossgen2 复合编译 / 退出标准 R2R两者矛盾时构建报错NumberOfStripesToUseInStress合并运行器为1压力条纹数上限 99超限构建报错CLRTestExitCode100自定义Main的成功退出码约定现象对上了就能快速收敛工程静默不构建先查CLRTestTargetUnsupported的条件表达式和NativeAotIncompatible是否命中当前TestBuildMode脚本生成了却安静退出多半是R2RIncompatible或轮次互斥在作怪直接看脚本输出里的跳过文案压力矩阵上构建直接爆掉优先怀疑AlwaysUseCrossGen2与CrossGenTest的组合或条纹数超过 99 的上限。判定链的最终实现都在Directory.Build.targets的_WillCLRTestProjectBuild一段把那条链从上往下读一遍所有为什么没跑都能对号入座。【免费下载链接】runtime.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.项目地址: https://gitcode.com/GitHub_Trending/runtime6/runtime创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考