name: test
# å•元测试矩阵(å‚考 NewLife.Core 的跨平å°åšæ³•ï¼‰ï¼šç›®æ ‡æ¡†æž¶ × æ“作系统/指令集
# TFM:net10.0(最新)与 net8.0ï¼ˆæœ€ä½Žå—æ”¯æŒè¿è¡Œæ—¶ï¼‰ï¼›netstandard2.1/netstandard2.0/net45 ç”±å…¨è§£å†³æ–¹æ¡ˆç¼–è¯‘æ ¡éªŒæ‰¿æ‹…
# OS/架构:win-x64ã€linux-x64ã€linux-arm64 为阻æ–é—¨ç¦ï¼›win-arm64 / osx-arm64 / osx-x64 / musl 为观察期
# linux-loongarch64(LA64)→ ubuntu-latest + QEMU 仿真 + 龙芯 SDK(社区平å°ï¼Œæ— 官方 runner;观察期)
# 触å‘:push / PR / 手动(workflow_dispatch);ä¸é‡‡ç”¨å¤œé—´å®šæ—¶â€”—GitHub 为镜åƒä»“åº“ï¼ŒåŒæ¥æŽ¨é€å³è§¦å‘。
# 用例零外部æœåŠ¡ä¾èµ–(Samples 在 Testing 环境跳过外部连接)。
on:
push:
branches: [ '*' ]
pull_request:
branches: [ '*' ]
workflow_dispatch:
jobs:
# å…¨è§£å†³æ–¹æ¡ˆç¼–è¯‘æ ¡éªŒï¼ˆå« Samples/Benchmark/AotSmoke ç‰éžæµ‹è¯•工程)+ 陿€é—¨ç¦
# é—¨ç¦ä¸¤é¡¹ï¼šè¿è¡Œæ—¶æ¶ˆæ¯å¿…须为英文ã€å¤šè¯è¨€è¯è¡¨ä¸€è‡´ï¼ˆREADME è¯è¨€ä¸ŽæŒ‡æ ‡ç”± readme.yml 负责)
build:
name: build solution
runs-on: windows-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- name: Setup dotNET
uses: actions/setup-dotnet@v4
with:
# è§£å†³æ–¹æ¡ˆå« net10.0-windows(Samples/Zero.Desktop),需对应 SDK æä¾› WindowsDesktop ç›®æ ‡åŒ…
dotnet-version: |
8.x
10.x
cache: true
cache-dependency-path: '**/*.csproj'
- name: Build
shell: pwsh
# æºç”Ÿæˆå™¨å·¥ç¨‹æ—¢ä½œä¸ºè§£å†³æ–¹æ¡ˆæ¡ç›®ã€åˆè¢«å¤šç›®æ ‡å·¥ç¨‹å¼•用生æˆï¼Œä¸¤ç±»å®žä¾‹ä¼šè¢«å¹¶è¡Œé‡å¤ç”Ÿæˆï¼Œ
# å¶å‘ CS2012(åŒä¸€ obj 输出被两个编译器进程写入);é‡è¯•一次å³å¯ï¼ˆç¬¬äºŒæ¬¡å·²æ˜¯å¢žé‡æž„建)
run: |
dotnet build NewLife.Remoting.slnx -c Release
if ($LASTEXITCODE -ne 0) {
Write-Host '首次构建失败,é‡è¯•一次(已知并行é‡å¤ç”Ÿæˆç«žæ€ï¼‰'
dotnet build NewLife.Remoting.slnx -c Release
if ($LASTEXITCODE -ne 0) { exit 1 }
}
- name: Check English runtime messages
shell: pwsh
run: pwsh -File ./Tools/Scripts/check-english-exceptions.ps1
- name: Check locale tables
shell: pwsh
run: pwsh -File ./Tools/Scripts/check-locales.ps1
# 测试矩阵:æ¯ä¸ª TFM × OS 独立 job,job 间并行且互ä¸äº‰æŠ¢å®¿ä¸»èµ„æºï¼ˆè¿›ç¨‹/端å£/CPU),
# æ¯ä¸ª job åªæž„å»ºè‡ªèº«ç›®æ ‡æ¡†æž¶ï¼ŒçœåŽ»å…¨è§£å†³æ–¹æ¡ˆæž„å»ºçš„å¢™é’Ÿå¼€é”€ï¼ˆç¼–è¯‘æ ¡éªŒç”± build job 承担)
test:
name: test (${{ matrix.name }})
runs-on: ${{ matrix.os }}
timeout-minutes: 40
continue-on-error: ${{ matrix.experimental == true }}
strategy:
fail-fast: false
matrix:
include:
# ============ Windows x64(阻æ–é—¨ç¦ï¼‰ ============
- name: win-x64-net10.0
os: windows-latest
framework: net10.0
dotnet: |
8.x
10.x
- name: win-x64-net8.0
os: windows-latest
framework: net8.0
dotnet: |
8.x
10.x
# ============ Linux x64(阻æ–é—¨ç¦ï¼‰ ============
- name: linux-x64-net10.0
os: ubuntu-latest
framework: net10.0
dotnet: |
8.x
10.x
- name: linux-x64-net8.0
os: ubuntu-latest
framework: net8.0
dotnet: |
8.x
10.x
# ============ Linux arm64(原生 runner,阻æ–é—¨ç¦ï¼‰ ============
- name: linux-arm64-net10.0
os: ubuntu-24.04-arm
framework: net10.0
dotnet: |
8.x
10.x
- name: linux-arm64-net8.0
os: ubuntu-24.04-arm
framework: net8.0
dotnet: |
8.x
10.x
# ============ Windows arm64(观察期) ============
# System.Data.SQLite æ— win-arm64 æž„å»ºï¼Œè·³è¿‡æ ‡æœ‰ Requires=Sqlite çš„ RPC 链路用例
- name: win-arm64-net10.0
os: windows-11-arm
framework: net10.0
dotnet: 10.x
filter: '--filter "Requires!=Sqlite"'
experimental: true
# ============ macOS Apple Silicon(观察期) ============
# System.Data.SQLite æ— osx-arm64 æž„å»ºï¼Œè·³è¿‡æ ‡æœ‰ Requires=Sqlite çš„ RPC 链路用例
- name: osx-arm64-net10.0
os: macos-latest
framework: net10.0
dotnet: 10.x
filter: '--filter "Requires!=Sqlite"'
experimental: true
# ============ macOS Intel(观察期) ============
- name: osx-x64-net10.0
os: macos-15-intel
framework: net10.0
dotnet: 10.x
experimental: true
steps:
- uses: actions/checkout@v4
- name: Setup dotNET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ matrix.dotnet }}
cache: true
cache-dependency-path: '**/*.csproj'
- name: Build
run: |
dotnet build NewLife.Remoting -c Release -f ${{ matrix.framework }}
dotnet build NewLife.Remoting.Extensions -c Release -f ${{ matrix.framework }}
- name: Test (XUnitTest)
# blame-hang:用例挂起时超时转储,便于事åŽå®šä½ï¼ˆæ£å¸¸å• job 约 1 分钟,远å°äºŽ 15 分钟阈值);
# dump-type å–值 full/mini/none 区分大å°å†™ï¼Œå†™ Mini 会让 dotnet test ç›´æŽ¥æŠ¥å‚æ•°é”™è¯¯ã€æ•´ä¸ª job 秒退
run: dotnet test XUnitTest -c Release -f ${{ matrix.framework }} --blame-hang-timeout 15m --blame-hang-dump-type mini ${{ matrix.filter }} --logger "trx;LogFileName=XUnitTest-${{ matrix.name }}.trx" --results-directory TestResults
- name: Upload Test Results
if: always()
uses: actions/upload-artifact@v4
with:
name: test-results-${{ matrix.name }}
path: TestResults/*.trx
if-no-files-found: ignore
# æºç”Ÿæˆå™¨å•测:与 RID æ— å…³ï¼Œç‹¬ç«‹ job é¿å…在矩阵里é‡å¤æ‰§è¡Œ
test-source-generator:
name: test (source generator)
runs-on: windows-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- name: Setup dotNET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 10.x
cache: true
cache-dependency-path: '**/*.csproj'
- name: Test (XUnitTest.SourceGenerator)
run: dotnet test XUnitTest.SourceGenerator -c Release --logger "trx;LogFileName=SourceGenerator.trx" --results-directory TestResults
- name: Upload Test Results
if: always()
uses: actions/upload-artifact@v4
with:
name: test-results-source-generator
path: TestResults/*.trx
if-no-files-found: ignore
# Samples é›†æˆæµ‹è¯•(ZeroServer/IoTZero 端到端,18 项):win-x64 / linux-x64 硬约æŸã€‚
# 2026-10-16 曾在 GitHub runner 上 11/18 å¤±è´¥ï¼ˆæœ¬åœ°å…¨ç»¿ï¼‰ï¼Œæ ¹å› å·²ä¿®ï¼šâ‘ å†·å¯åŠ¨é¦–ä¸ªç™»å½•è¶…è¿‡å®¢æˆ·ç«¯ 1s 滚动超时
# → 按递增超时é‡è¯•;② IoTZero 预置产å“åŽå®žä½“缓å˜å¼‚æ¥åˆ·æ–°ä¸Žæ’入竞争 → å¼ºåˆ¶åŒæ¥åˆ·æ–°ï¼ˆFIX-1 å·²é—环)。
samples:
name: samples (${{ matrix.name }})
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
include:
- name: win-x64
os: windows-latest
- name: linux-x64
os: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup dotNET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 10.x
cache: true
cache-dependency-path: '**/*.csproj'
- name: Test (XUnitTest.Samples)
run: dotnet test XUnitTest.Samples -c Release --logger "trx;LogFileName=Samples-${{ matrix.name }}.trx" --results-directory TestResults
- name: Upload Test Results
if: always()
uses: actions/upload-artifact@v4
with:
name: samples-results-${{ matrix.name }}
path: TestResults/*.trx
if-no-files-found: ignore
# musl(Alpine)容器矩阵(观察期):覆盖 linux-musl-x64 / linux-musl-arm64 ä¸¤ç§ libc å˜ä½“
# 容器内 actions/setup-dotnet ä¸å¯ç”¨ï¼Œæ”¹ç”¨ dotnet-install.sh;宿主先 warm NuGet 缓å˜å¹¶æŒ‚载进容器å¤ç”¨
test-musl:
name: test (musl-${{ matrix.arch }})
runs-on: ${{ matrix.os }}
timeout-minutes: 40
continue-on-error: true
strategy:
fail-fast: false
matrix:
include:
- arch: x64
os: ubuntu-latest
# System.Data.SQLite æ— linux-musl-arm64 æž„å»ºï¼Œè·³è¿‡æ ‡æœ‰ Requires=Sqlite çš„ RPC 链路用例
- arch: arm64
os: ubuntu-24.04-arm
filter: '--filter "Requires!=Sqlite"'
steps:
- uses: actions/checkout@v4
- name: Setup dotNET (warm NuGet cache)
uses: actions/setup-dotnet@v4
with:
dotnet-version: 10.x
cache: true
cache-dependency-path: '**/*.csproj'
- name: Test (alpine, net10.0)
run: |
docker run --rm -v "$PWD:/src" -v "$HOME/.nuget/packages:/nuget-packages" -e NUGET_PACKAGES=/nuget-packages -w /src alpine:3.20 sh -c '
set -e
apk add --no-cache bash curl icu-libs krb5-libs libgcc libstdc++ tzdata >/dev/null
curl -sSL https://dot.net/v1/dotnet-install.sh -o /tmp/dotnet-install.sh
bash /tmp/dotnet-install.sh --channel 10.0 --install-dir /usr/share/dotnet >/dev/null
ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet
export DOTNET_CLI_TELEMETRY_OPTOUT=1
dotnet test XUnitTest -f net10.0 -c Release --nologo ${{ matrix.filter }}
'
# 龙芯 LA64(linux-loongarch64ï¼‰ï¼šæ— å®˜æ–¹ runner / runtime pack / NativeAOT,QEMU 用户æ€ä»¿çœŸæ‰§è¡Œï¼ˆè§‚察期)。
# å‚考 numpy çš„è·¨æž¶æž„åšæ³•:①tonistiigi/binfmt 注册较新的 qemu-user-static(qemu 10.2.3ï¼Œå« loong64ï¼›
# 9.2.2 的龙芯仿真å˜åœ¨ç¼ºé™·ï¼Œæ›¾åœ¨ MSBuild/Regex 处触å‘å†…å˜æŸå);
# â‘¡ 业务在 loong64 Debian å®¹å™¨å†…æ‰§è¡Œâ€”â€”å®¹å™¨æ ¹æ–‡ä»¶ç³»ç»Ÿçš„ glibc/ld.so 与龙芯 SDK 匹é…ï¼ˆå®¿ä¸»äº¤å‰ glibc 曾é‡
# 「unexpected reloc typeã€ï¼‰ï¼›â‘¢ 龙芯 SDK(github.com/loongson/dotnet)由宿主下载åŽè¿›å…¥é¢„构建镜åƒï¼ˆ.github/la64)。
# 还原/编译/测试拆分为独立æ¥éª¤ï¼Œä¾¿äºŽæ¸è¿›æŸ¥çœ‹æ—¥å¿—ï¼›å„æ¥éª¤å¸¦é‡è¯•ï¼Œè§„é¿ QEMU 仿真的å¶å‘内å˜/指令缺陷。
# 主测试集容错执行(失败先收集 TRX ä¸é˜»æ–;稳定åŽè½¬ç¡¬çº¦æŸï¼‰ã€‚
test-loongarch64:
name: test (linux-la64 qemu)
runs-on: ubuntu-latest
timeout-minutes: 180
continue-on-error: true
steps:
- uses: actions/checkout@v4
- name: Initialize binfmt_misc (QEMU 10.2.3)
run: docker run --rm --privileged tonistiigi/binfmt:qemu-v10.2.3-68 --install all
- name: Build LoongArch64 SDK image
run: |
curl -fL -o .github/la64/dotnet-sdk-10.0.113-linux-loongarch64.tar.gz \
https://github.com/loongson/dotnet/releases/download/v10.0.113-loongarch64/dotnet-sdk-10.0.113-linux-loongarch64.tar.gz
for i in 1 2; do
echo "=== image build attempt $i ==="
docker build --platform linux/loong64 -t la64-dotnet:10.0.113 .github/la64 && exit 0
echo "image build attempt $i failed"
done
exit 1
- name: Restore (LoongArch64)
run: |
for i in 1 2 3; do
echo "=== restore attempt $i ==="
docker run --rm --platform linux/loong64 -v "$PWD:/src" -v "$PWD/.qemu/nuget:/nuget" -w /src la64-dotnet:10.0.113 \
bash -c 'dotnet nuget add source https://lnuget.loongnix.cn/v3/index.json -n loongnuget --allow-insecure-connections || true; dotnet --info; timeout -k 60s 30m dotnet restore XUnitTest --disable-parallel' && exit 0
echo "restore attempt $i failed"
done
exit 1
- name: Build (LoongArch64)
run: |
for i in 1 2 3; do
echo "=== build attempt $i ==="
docker run --rm --platform linux/loong64 -v "$PWD:/src" -v "$PWD/.qemu/nuget:/nuget" -w /src la64-dotnet:10.0.113 \
bash -c 'timeout -k 60s 40m dotnet build XUnitTest -f net10.0 -c Release --no-restore --nologo' && exit 0
echo "build attempt $i failed"
done
exit 1
- name: Prepare xunit console runner
# VSTest çš„ PlatformEnvironment.get_Architecture() ä¸è®¤è¯† loongarch64(NotSupportedException),
# dotnet test åœ¨é¾™èŠ¯ä¸Šæ— æ³•è¿è¡Œï¼›æ”¹ç”¨ä¸ä¾èµ– VSTest çš„ xunit 控制å°è¿è¡Œå™¨ï¼ˆçº¯æ‰˜ç®¡ä»£ç ï¼Œæ— æž¶æž„å‡è®¾ï¼‰ã€‚
# è¿è¡Œå™¨è‡ªå¸¦çš„ runtimeconfig åªå£°æ˜Ž NETCore.App,测试程åºé›†ä¾èµ–çš„ AspNetCore.App(控制器/HttpContext
# 用例)ä¸åœ¨è¿›ç¨‹å…±äº«æ¡†æž¶å†…,会抛 FileNotFoundException(Microsoft.AspNetCore.Mvc.Core)ï¼›æ¤å¤„显å¼è¡¥ä¸Šï¼Œ
# ç”± DOTNET_ROLL_FORWARD=LatestMajor æå‡åˆ°å®¹å™¨å†…çš„ 10.0.x。
# 下载到 RUNNER_TEMP 而éžå·¥ä½œåŒºï¼šå·¥ä½œåŒºå†…çš„ .qemu 由容器以 root åˆ›å»ºï¼Œå®¿ä¸»ç”¨æˆ·æ— æ³•å†™å…¥
run: |
mkdir -p "$RUNNER_TEMP/xunit"
curl -fL -o "$RUNNER_TEMP/xunit.nupkg" https://www.nuget.org/api/v2/package/xunit.runner.console/2.9.3
unzip -q -o "$RUNNER_TEMP/xunit.nupkg" "tools/*" -d "$RUNNER_TEMP/xunit"
printf '%s' '{"runtimeOptions":{"tfm":"net6.0","rollForward":"LatestMajor","frameworks":[{"name":"Microsoft.NETCore.App","version":"6.0.0"},{"name":"Microsoft.AspNetCore.App","version":"6.0.0"}]}}' \
> "$RUNNER_TEMP/xunit/tools/net6.0/xunit.console.runtimeconfig.json"
ls -l "$RUNNER_TEMP/xunit/tools/net6.0/xunit.console.dll"
echo "=== container shared frameworks ==="
docker run --rm --platform linux/loong64 la64-dotnet:10.0.113 ls /usr/share/dotnet/shared
- name: Run Unit Tests (LoongArch64,观察期,容错执行)
# -notrait Requires=Sqlite:XCode 的 SQLite 驱动(System.Data.SQLite)没有 loongarch64 构建,
# æ’ä»¶ä¸‹è½½æº x.newlifex.com äº¦æ— å¯¹åº”åŒ…ï¼Œä¾èµ–å»ºè¡¨çš„æ ·ä¾‹ RPC é›†æˆæµ‹è¯•åœ¨é¾™èŠ¯ä¸Šæ— æ³•è¿è¡Œã€‚
run: |
for i in 1 2 3; do
echo "=== test attempt $i ==="
docker run --rm --platform linux/loong64 -v "$PWD:/src" -v "$PWD/.qemu/nuget:/nuget" -v "$RUNNER_TEMP/xunit:/xunit" -w /src la64-dotnet:10.0.113 \
bash -c 'mkdir -p /src/TestResults; timeout -k 60s 80m dotnet /xunit/tools/net6.0/xunit.console.dll /src/Bin/UnitTest/net10.0/XUnitTest.dll -parallel none -notrait Requires=Sqlite -xml /src/TestResults/loongarch64.xml' && exit 0
echo "test attempt $i failed"
done
exit 1
- name: Upload Test Results
if: always()
uses: actions/upload-artifact@v4
with:
name: test-results-loongarch64
path: TestResults/*
if-no-files-found: ignore
|