diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f5425ce5..d28ed05d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,14 +46,34 @@ jobs: - run: julia --color=yes .ci/test_and_change_uuid.jl - uses: julia-actions/julia-buildpkg@v1 - uses: julia-actions/julia-runtest@v1 - env: - SPARSEARRAYS_AQUA_TEST: true - uses: julia-actions/julia-processcoverage@v1 - uses: codecov/codecov-action@v4 with: file: lcov.info token: ${{ secrets.CODECOV_TOKEN }} + aqua-test: + runs-on: ${{ matrix.os }} + timeout-minutes: 60 + strategy: + matrix: + version: + - '1.10' + os: + - ubuntu-latest + arch: + - x64 + steps: + - uses: actions/checkout@v4 + - uses: julia-actions/setup-julia@v2 + with: + version: ${{ matrix.version }} + arch: ${{ matrix.arch }} + - uses: julia-actions/cache@v2 + - run: julia --color=yes .ci/test_and_change_uuid.jl + - name: Run Aqua and ambiguity tests + run: | + julia --color=yes -e 'using Pkg; Pkg.activate(temp=true); Pkg.develop(path=pwd()); Pkg.add(name="Aqua", version="0.8"); include(joinpath(pwd(), "test", "ambiguous.jl"))' docs: runs-on: ubuntu-latest steps: diff --git a/Project.toml b/Project.toml index fac1058d..cdab5d0f 100644 --- a/Project.toml +++ b/Project.toml @@ -15,6 +15,7 @@ Dates = "<0.0.1, 1" InteractiveUtils = "<0.0.1, 1" Libdl = "<0.0.1, 1" LinearAlgebra = "<0.0.1, 1" +ParallelTestRunner = "2" Pkg = "<0.0.1, 1" Printf = "<0.0.1, 1" Random = "<0.0.1, 1" @@ -27,9 +28,10 @@ julia = "1.10" Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" +ParallelTestRunner = "d3525ed8-44d0-4b2c-a655-542cee43accc" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Aqua", "Dates", "InteractiveUtils", "Pkg", "Printf", "Test"] +test = ["Aqua", "Dates", "InteractiveUtils", "ParallelTestRunner", "Pkg", "Printf", "Test"] diff --git a/test/runtests.jl b/test/runtests.jl index 3eb1de97..9064b495 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,49 +1,22 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -using Test, LinearAlgebra, SparseArrays -if Base.get_bool_env("SPARSEARRAYS_AQUA_TEST", false) - include("ambiguous.jl") -end +using Test, LinearAlgebra, SparseArrays -for file in readlines(joinpath(@__DIR__, "testgroups")) - file == "" && continue # skip empty lines - include(file * ".jl") -end +testfiles = [file * ".jl" for file in readlines(joinpath(@__DIR__, "testgroups")) if file != ""] if Base.USE_GPL_LIBS + push!(testfiles, "threads_suite.jl") +end - nt = @static if isdefined(Threads, :maxthreadid) - Threads.maxthreadid() - else - Threads.nthreads() - end - - # Test multithreaded execution - @testset "threaded SuiteSparse tests" verbose = true begin - @testset "threads = $nt" begin - include("threads.jl") - end - # test both nthreads==1 and nthreads>1. spawn a process to test whichever - # case we are not running currently. - other_nthreads = nt == 1 ? 4 : 1 - @testset "threads = $other_nthreads" begin - let p, cmd = `$(Base.julia_cmd()) --depwarn=error --startup-file=no threads.jl` - p = run( - pipeline( - setenv( - cmd, - "JULIA_NUM_THREADS" => other_nthreads, - dir=@__DIR__()), - stdout = stdout, - stderr = stderr), - wait = false) - if !success(p) - error("SuiteSparse threads test failed with nthreads == $other_nthreads") - else - @test true # mimic the one @test in threads.jl - end - end - end - end - +# ParallelTestRunner comes from the Pkg.test target; Julia base CI runs this +# file without it and falls back to the serial path. +if Base.find_package("ParallelTestRunner") !== nothing + using ParallelTestRunner + # Auto CPU thread count detection in ParallelTestRunner is bad + push!(ARGS, "--jobs=$(Sys.CPU_THREADS)") + testsuite = Dict{String,Expr}(splitext(f)[1] => :(include($(joinpath(@__DIR__, f)))) + for f in testfiles) + runtests(SparseArrays, ARGS; testsuite) +else + foreach(include, testfiles) end diff --git a/test/sparsematrix_constructors_indexing.jl b/test/sparsematrix_constructors_indexing.jl index a6f1a75c..fd0656ad 100644 --- a/test/sparsematrix_constructors_indexing.jl +++ b/test/sparsematrix_constructors_indexing.jl @@ -821,7 +821,9 @@ end times = Float64[0,0,0] best = [typemax(Float64), 0] for searchtype in [0, 1, 2] - GC.gc() + # stabilizes the debug timings below, but forces 180 full + # collections across these loops which dominates the file's GC time + # GC.gc() tres = @timed test_getindex_algs(S, I, J, searchtype) res[searchtype+1] = tres[1] times[searchtype+1] = tres[2] @@ -877,9 +879,9 @@ end for I in IA Isorted = sort(I) for S in SA - GC.gc() + # GC.gc() # see comment above ru = @timed S[I, J] - GC.gc() + # GC.gc() rs = @timed S[Isorted, Jsorted] if debug @printf(" %7d | %7d | %7d | %4.2e | %4.2e | %4.2e | %4.2e |\n", round(Int,nnz(S)/size(S, 2)), length(I), length(J), rs[2], ru[2], rs[3], ru[3]) diff --git a/test/sparsevector.jl b/test/sparsevector.jl index b93a4963..2bd1e9ec 100644 --- a/test/sparsevector.jl +++ b/test/sparsevector.jl @@ -1281,6 +1281,10 @@ end floattypes = (Float32, Float64, BigFloat) complextypes = (ComplexF32, ComplexF64) eltypes = (inttypes..., floattypes..., complextypes...) + # The full eltype cross product compiles thousands of specializations + # (several CI minutes); do it only for the core types and pair the + # remaining eltypes with Float64. + coretypes = (Int64, Float64, ComplexF64) for eltypemat in eltypes (densemat, sparsemat) = eltypemat in inttypes ? (denseintmat, sparseintmat) : @@ -1294,6 +1298,8 @@ end LinearAlgebra.UnitLowerTriangular(sparsemat), LinearAlgebra.UnitUpperTriangular(sparsemat) ) for eltypevec in eltypes + (eltypemat in coretypes && eltypevec in coretypes) || + eltypemat == Float64 || eltypevec == Float64 || continue spvecs = eltypevec in inttypes ? sparseintvecs : eltypevec in floattypes ? sparsefloatvecs : eltypevec in complextypes && sparsecomplexvecs diff --git a/test/threads_suite.jl b/test/threads_suite.jl new file mode 100644 index 00000000..5d2d3424 --- /dev/null +++ b/test/threads_suite.jl @@ -0,0 +1,36 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +using Test, SparseArrays + +nt = @static if isdefined(Threads, :maxthreadid) + Threads.maxthreadid() +else + Threads.nthreads() +end + +@testset "threaded SuiteSparse tests" verbose = true begin + @testset "threads = $nt" begin + include("threads.jl") + end + # test both nthreads==1 and nthreads>1. spawn a process to test whichever + # case we are not running currently. + other_nthreads = nt == 1 ? 4 : 1 + @testset "threads = $other_nthreads" begin + let p, cmd = `$(Base.julia_cmd()) --depwarn=error --startup-file=no threads.jl` + p = run( + pipeline( + setenv( + cmd, + "JULIA_NUM_THREADS" => other_nthreads, + dir=@__DIR__()), + stdout = stdout, + stderr = stderr), + wait = false) + if !success(p) + error("SuiteSparse threads test failed with nthreads == $other_nthreads") + else + @test true # mimic the one @test in threads.jl + end + end + end +end