Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/grouped-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,14 @@ jobs:
fail-fast: false
matrix:
include: ${{ fromJson(needs.detect.outputs.matrix) }}
name: "${{ matrix.group }} (julia ${{ matrix.version }}, ${{ join(matrix.runner, ' ') }})"
name: "${{ matrix.group }} (julia ${{ matrix.version }}, ${{ join(matrix.runner, ' ') }}${{ matrix.arch != '' && format(', {0}', matrix.arch) || '' }})"
uses: "SciML/.github/.github/workflows/tests.yml@v1"
with:
project: "."
group: "${{ matrix.group }}"
group-env-name: "${{ inputs.group-env-name }}"
julia-version: "${{ matrix.version }}"
julia-arch: "${{ matrix.arch }}"
runner: ${{ toJson(matrix.runner) }}
timeout-minutes: ${{ matrix.timeout }}
num-threads: "${{ matrix.num_threads }}"
Expand Down
48 changes: 37 additions & 11 deletions scripts/compute_affected_sublibraries.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@
# group runs once per OS, e.g. ["ubuntu-latest","windows-latest",
# "macos-latest"]). Empty -> use `runner`. Don't combine with a
# custom `runner`; if both set, the OS axis wins.
# arch — string or array of Julia CPU architectures (root matrix only;
# the group runs once per arch, e.g. ["x64","x86"] to add a
# 32-bit lane). Empty -> the runner's native arch. Forwarded to
# tests.yml's `julia-arch`, so a section like `arch = ["x86"]`
# with `os = ["ubuntu-latest"]` yields a single 32-bit cell.
# group — string; the GROUP env value dispatched to the package's
# runtests.jl for this section, defaulting to the section name.
# Lets a section named e.g. "Core 32-bit" run the "Core" body
# (so its group folder / core file resolves) while carrying its
# own distinct arch/os axis and its own CI job name. Root matrix
# only.
# timeout — integer, job timeout in minutes (default: 120)
# num_threads — integer, JULIA_NUM_THREADS (default: 1)
# local_only — boolean (default: false). When true, the group is skipped
Expand Down Expand Up @@ -67,7 +78,7 @@
#
# With the --root-matrix flag the output is the ROOT package's group matrix,
# read from <repo>/test/test_groups.toml (NOT under lib/), as a JSON array of
# {group, version, runner, timeout, num_threads, continue_on_error}. This is not
# {group, version, runner, arch, timeout, num_threads, continue_on_error}. This is not
# diff-filtered (the root package runs all its groups every push/PR) and needs
# no lib/ directory, so ordinary single packages can use it too. When no
# test/test_groups.toml exists the default is a single "Core" group on
Expand Down Expand Up @@ -152,6 +163,8 @@ struct TestGroupConfig
local_only::Bool
continue_on_error::Bool
os::Vector{String} # root matrix only: OS axis (group runs once per os); empty = use `runner`
arch::Vector{String} # root matrix only: arch axis (group runs once per arch); empty = runner's native arch
dispatch_group::String # root matrix only: GROUP env dispatched to runtests; empty = section name
end

function parse_test_group(config::AbstractDict)
Expand All @@ -163,7 +176,12 @@ function parse_test_group(config::AbstractDict)
local_only = Bool(get(config, "local_only", false))
continue_on_error = Bool(get(config, "continue_on_error", false))
os = convert(Vector{String}, get(config, "os", String[]))
return TestGroupConfig(versions, runner, timeout, num_threads, local_only, continue_on_error, os)
arch_raw = get(config, "arch", String[])
arch = arch_raw isa Vector ? convert(Vector{String}, arch_raw) : String[String(arch_raw)]
dispatch_group = String(get(config, "group", ""))
return TestGroupConfig(
versions, runner, timeout, num_threads, local_only, continue_on_error, os, arch, dispatch_group,
)
end

function load_test_groups(lib_dir::String, pkg::String)
Expand All @@ -173,7 +191,7 @@ function load_test_groups(lib_dir::String, pkg::String)
return Dict{String, TestGroupConfig}(name => parse_test_group(config) for (name, config) in toml)
end
return Dict{String, TestGroupConfig}(
k => TestGroupConfig(v, "ubuntu-latest", 120, 1, false, false, String[]) for (k, v) in DEFAULT_TEST_GROUPS
k => TestGroupConfig(v, "ubuntu-latest", 120, 1, false, false, String[], String[], "") for (k, v) in DEFAULT_TEST_GROUPS
)
end

Expand All @@ -195,7 +213,7 @@ function load_root_test_groups(repo_root::String)
isempty(groups) || return groups
end
return Dict{String, TestGroupConfig}(
k => TestGroupConfig(v, "ubuntu-latest", 120, 1, false, false, String[]) for (k, v) in DEFAULT_ROOT_GROUPS
k => TestGroupConfig(v, "ubuntu-latest", 120, 1, false, false, String[], String[], "") for (k, v) in DEFAULT_ROOT_GROUPS
)
end

Expand Down Expand Up @@ -381,18 +399,25 @@ function build_root_matrix(repo_root::String)
entries = []
for group_name in sort!(collect(keys(groups)))
config = groups[group_name]
dispatch = isempty(config.dispatch_group) ? group_name : config.dispatch_group
runners = isempty(config.os) ? Any[config.runner] : Any[o for o in config.os]
# Empty arch = one cell on the runner's native arch (emitted as "", which
# tests.yml falls back from to runner.arch). A non-empty list adds a cell
# per arch, e.g. ["x64", "x86"] for a 32-bit lane.
arches = isempty(config.arch) ? String[""] : config.arch
vers = group_name == "QA" ? QA_VERSIONS : config.versions
for ver in vers
for runner in runners
push!(
entries,
(;
group = group_name, version = ver, runner = runner,
timeout = config.timeout, num_threads = config.num_threads,
continue_on_error = config.continue_on_error,
for arch in arches
push!(
entries,
(;
group = dispatch, version = ver, runner = runner, arch = arch,
timeout = config.timeout, num_threads = config.num_threads,
continue_on_error = config.continue_on_error,
)
)
)
end
end
end
end
Expand All @@ -406,6 +431,7 @@ function print_root_matrix(entries)
print("{\"group\":\"", entry.group, "\",\"version\":\"", entry.version, "\",\"runner\":")
json_value(entry.runner)
print(
",\"arch\":\"", entry.arch, "\"",
",\"timeout\":", entry.timeout, ",\"num_threads\":", entry.num_threads,
",\"continue_on_error\":", entry.continue_on_error ? "true" : "false", "}",
)
Expand Down
65 changes: 65 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,71 @@ end
@test length(gpu) == 1 && only(gpu).runner == ["self-hosted", "Linux", "X64", "gpu"]
end

@testset "root matrix: arch axis and group alias (32-bit lane)" begin
d = mktempdir()
mkpath(joinpath(d, "test"))
write(
joinpath(d, "test", "test_groups.toml"), """
[Core]
versions = ["lts", "1"]
os = ["ubuntu-latest", "windows-latest"]

["Core 32-bit"]
group = "Core"
versions = ["1"]
os = ["ubuntu-latest"]
arch = "x86"

[QA]
versions = ["1"]
"""
)
m = build_root_matrix(d)
# Native Core cells carry an empty arch (tests.yml falls back to runner.arch).
native = filter(e -> e.group == "Core" && e.arch == "", m)
@test length(native) == 4 # 2 versions × 2 OSes
@test Set((e.version, e.runner) for e in native) ==
Set((v, o) for v in ["lts", "1"] for o in ["ubuntu-latest", "windows-latest"])
# The aliased "Core 32-bit" section dispatches GROUP=Core but adds exactly one
# x86 cell on ubuntu, so runtests.jl's folder resolves to the Core body.
x86 = filter(e -> e.arch == "x86", m)
@test length(x86) == 1
@test only(x86).group == "Core" && only(x86).version == "1" && only(x86).runner == "ubuntu-latest"
# QA stays native.
@test all(e -> e.arch == "", filter(e -> e.group == "QA", m))
end

@testset "root matrix: arch as a list fans out per arch" begin
d = mktempdir()
mkpath(joinpath(d, "test"))
write(
joinpath(d, "test", "test_groups.toml"), """
[Core]
versions = ["1"]
arch = ["x64", "x86"]
"""
)
m = build_root_matrix(d)
@test Set(e.arch for e in m) == Set(["x64", "x86"])
@test length(m) == 2
end

@testset "root matrix: --root-matrix CLI emits arch field" begin
d = mktempdir()
mkpath(joinpath(d, "test"))
write(
joinpath(d, "test", "test_groups.toml"), """
["Core 32-bit"]
group = "Core"
versions = ["1"]
arch = "x86"
"""
)
out = read(pipeline(IOBuffer(""), `$(Base.julia_cmd()) $SCRIPT $d --root-matrix`), String)
@test occursin("\"arch\":\"x86\"", out)
@test occursin("\"group\":\"Core\"", out)
end

@testset "root matrix faithfully reproduces OrdinaryDiffEq's embedded matrix" begin
# ODE's root CI.yml is 17 groups × [lts,1,pre] minus excludes (AD->lts only,
# ODEInterfaceRegression->lts only). QA is centrally clamped to v1 only (see
Expand Down
Loading