Skip to content

Add a precompilation workload #58

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jan 23, 2025
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
28 changes: 8 additions & 20 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ on:
tags: '*'
pull_request:

permissions:
contents: read
actions: write

jobs:
test:
name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }}
Expand All @@ -15,37 +19,21 @@ jobs:
fail-fast: false
matrix:
include:
- { os: ubuntu-latest, version: '1.0', arch: x64}
- { os: ubuntu-latest, version: '1.1', arch: x64}
- { os: ubuntu-latest, version: '1.2', arch: x64}
- { os: ubuntu-latest, version: '1.3', arch: x64}
- { os: ubuntu-latest, version: '1.4', arch: x64}
- { os: ubuntu-latest, version: '1.5', arch: x64}
- { os: ubuntu-latest, version: '1.6', arch: x64}
- { os: ubuntu-latest, version: '^1.7.0-0', arch: x64}
- { os: ubuntu-latest, version: '1.10', arch: x64}
- { os: ubuntu-latest, version: 'nightly', arch: x64}
- { os: ubuntu-latest, version: '1', arch: x86 }
- { os: windows-latest, version: '1', arch: x64}
- { os: macOS-latest, version: '1', arch: x64}

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4

- uses: julia-actions/setup-julia@v1
- uses: julia-actions/setup-julia@v2
with:
version: ${{ matrix.version }}
arch: ${{ matrix.arch }}

- uses: actions/cache@v1
env:
cache-name: cache-artifacts
with:
path: ~/.julia/artifacts
key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }}
restore-keys: |
${{ runner.os }}-test-${{ env.cache-name }}-
${{ runner.os }}-test-
${{ runner.os }}-
- uses: julia-actions/cache@v2

- run: |
git config --global user.name Tester
Expand Down
9 changes: 2 additions & 7 deletions .github/workflows/Documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,10 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: julia-actions/setup-julia@latest
with:
version: '1'
- uses: julia-actions/cache@v2
- name: Install dependencies
run: |
julia --project=docs/ -e '
using Pkg
Pkg.develop([PackageSpec(path=joinpath(pwd(), "InlineTest")),
PackageSpec(path=pwd())])
Pkg.instantiate()'
julia --project=docs/ -e 'using Pkg; Pkg.instantiate()'
- name: Build and deploy
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # For authentication with GitHub Actions token
Expand Down
6 changes: 4 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
name = "ReTest"
uuid = "e0db7c4e-2690-44b9-bad6-7687da720f89"
authors = ["Rafael Fourquet <[email protected]>"]
version = "0.3.3"
version = "0.3.4"

[deps]
Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b"
InlineTest = "bd334432-b1e7-49c7-a2dc-dd9149e4ebd6"
PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Sockets = "6462fe0b-24de-5631-8697-dd941f90decc"
Expand All @@ -14,7 +15,8 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
[compat]
InlineTest = "=0.2.0"
Revise = "3.1"
julia = "1"
PrecompileTools = "1.2.1"
julia = "1.10"

[extras]
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
Expand Down
4 changes: 4 additions & 0 deletions docs/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@ ReTest = "e0db7c4e-2690-44b9-bad6-7687da720f89"

[compat]
Documenter = "1"

[sources]
ReTest = { path = ".." }
InlineTest = { path = "../InlineTest" }
5 changes: 3 additions & 2 deletions docs/make.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
using Documenter, ReTest

makedocs(sitename = "ReTest.jl",
modules = [ReTest, ReTest.InlineTest])
makedocs(; sitename = "ReTest.jl",
modules = [ReTest, ReTest.InlineTest],
warnonly=[:missing_docs])


deploydocs(
Expand Down
33 changes: 17 additions & 16 deletions src/ReTest.jl
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,10 @@ const ArgType = Union{Module,PatternX,AbstractString,AbstractArray,Tuple,Symbol,
Pair{Module,
<:Union{PatternX,AbstractString,AbstractArray,Tuple}}}

# Holds the seed to set before each testset. This is not thread-safe, but it's
# not normal/intended to call retest() concurrently anyway.
const test_seed = Ref{Any}(false)

const retest_defaults = (
dry = false,
stats = false,
Expand Down Expand Up @@ -1089,22 +1093,12 @@ function retest(@nospecialize(args::ArgType...);
end

if seed !== false
let seedstr =
if seed === true
# seed!(nothing) doesn't work on old Julia, so we can't just set
# `seed = nothing` and interpolate `seed` directly in includestr
""
else
string(seed)
end,
includestr = """
using Random
Random.seed!($seedstr)
nothing
"""
# can't use `@everywhere using Random`, as here is not toplevel
@everywhere Base.include_string(Main, $includestr)
end
includestr = """
import ReTest
ReTest.test_seed[] = $seed
"""

@everywhere Base.include_string(Main, $includestr)
end

@sync for wrkr in workers()
Expand Down Expand Up @@ -1413,6 +1407,11 @@ function process_args(@nospecialize(args);
# tests is passed to retest in order to run tests in its submodules
filter!(m -> isdefined(m, INLINE_TEST), modules)

# Remove the precompilation module if we're not precompiling
if ccall(:jl_generating_output, Cint, ()) == 0
filter!(m -> nameof(m) !== :_ReTestPrecompileTests, modules)
end

shuffle && shuffle!(modules)

########## process verbose
Expand Down Expand Up @@ -1749,4 +1748,6 @@ function runtests(tests::String="")
Pkg.test("ReTest", test_args=Vector{String}(split(tests)))
end

include("precompile.jl")

end # module ReTest
50 changes: 50 additions & 0 deletions src/precompile.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import PrecompileTools: @setup_workload, @compile_workload

module _ReTestPrecompile
const x = 1
end

module _ReTestPrecompileTests
import .._ReTestPrecompile
using ..ReTest

@testset "precompilation workload" begin
@test _ReTestPrecompile.x == 1
@test false
end

end # _ReTestPrecompileTestsModule


@setup_workload begin
stderr_pipe = Pipe()
stdout_pipe = Pipe()

@compile_workload begin
try
redirect_stdio(; stderr=stderr_pipe, stdout=stdout_pipe) do
retest(_ReTestPrecompile, _ReTestPrecompileTests; recursive=false, stats=true, spin=true)
end
catch ex
close(stderr_pipe.in)
close(stdout_pipe.in)

if !(ex isa Test.TestSetException)
stdout_str = read(stdout_pipe, String)
stderr_str = read(stderr_pipe, String)

@error "Precompilation failed, this is the captured stdout ($(length(stdout_str)) chars):"
println(stdout_str)

@error "And this is the captured stderr ($(length(stderr_str)) chars):"
println(stderr_str)

rethrow()
end
finally
empty!(ReTest.TESTED_MODULES)
close(stderr_pipe)
close(stdout_pipe)
end
end
end
49 changes: 32 additions & 17 deletions src/testset.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ using Distributed: myid, nworkers

import InlineTest: @testset

import ..ReTest
using ..ReTest: Pattern, Marks, matches, setresult!

# mostly copied from Test stdlib
Expand Down Expand Up @@ -514,17 +515,19 @@ function testset_beginend(mod::Module, isfinal::Bool, pat::Pattern, id::Int64, d
put!($(chan.preview), ($id, $desc))
end
push_testset(ts)

# we reproduce the logic of guardseed, but this function
# cannot be used as it changes slightly the semantic of @testset,
# by wrapping the body in a function
local RNG = default_rng()
local oldrng = copy(RNG)
local default_rng_orig = copy(default_rng())
@static if VERSION >= v"1.11"
local tls_seed_orig = copy(Random.get_tls_seed())
end

try
# RNG is re-seeded with its own seed to ease reproduce a failed test
if VERSION >= v"1.7.0-DEV.1225"
Random.seed!(Random.GLOBAL_SEED)
else
Random.seed!(RNG.seed)
# RNG is re-seeded with the desired seed for the test
if ReTest.test_seed[] !== false
Random.seed!(ReTest.test_seed[])
end
let
ts.timed = @stats $stats $(esc(tests))
Expand All @@ -536,14 +539,19 @@ function testset_beginend(mod::Module, isfinal::Bool, pat::Pattern, id::Int64, d
record(ts, Error(:nontest_error, Expr(:tuple), err,
current_exceptions(), $(QuoteNode(source))))
finally
copy!(RNG, oldrng)
copy!(default_rng(), default_rng_orig)
@static if VERSION >= v"1.11"
copy!(Random.get_tls_seed(), tls_seed_orig)
end

setresult!($marks, ts.subject, !anyfailed(ts))
pop_testset()
ret = finish(ts, $chan)
end
ret
end
end

# preserve outer location if possible
if tests isa Expr && tests.head === :block &&
!isempty(tests.args) && tests.args[1] isa LineNumberNode
Expand Down Expand Up @@ -578,7 +586,7 @@ function testset_forloop(mod::Module, isfinal::Bool, pat::Pattern, id::Int64,
break
end
# it's 1000 times faster to copy from tmprng rather than calling Random.seed!
copy!(RNG, tmprng)
copy!(default_rng(), tmprng)
end
ts = ts0
if nworkers() == 1 && get_testset_depth() == 0 && $(chan.preview) !== nothing
Expand All @@ -600,19 +608,22 @@ function testset_forloop(mod::Module, isfinal::Bool, pat::Pattern, id::Int64,
end
end
end

quote
local arr = Vector{Any}()
local first_iteration = true
local iter = 0
local ts
local RNG = default_rng()
local oldrng = copy(RNG)
if VERSION >= v"1.7.0-DEV.1225"
Random.seed!(Random.GLOBAL_SEED)
else
Random.seed!(RNG.seed)

local default_rng_orig = copy(default_rng())
@static if VERSION >= v"1.11"
local tls_seed_orig = copy(Random.get_tls_seed())
end

local tmprng = copy(default_rng())
if ReTest.test_seed[] !== false
tmprng = copy(Random.seed!(ReTest.test_seed[]))
end
local tmprng = copy(RNG)
try
let
$(Expr(:for, Expr(:block, [esc(v) for v in loops]...), blk))
Expand All @@ -623,7 +634,11 @@ function testset_forloop(mod::Module, isfinal::Bool, pat::Pattern, id::Int64,
pop_testset()
push!(arr, finish(ts, $chan))
end
copy!(RNG, oldrng)

copy!(default_rng(), default_rng_orig)
@static if VERSION >= v"1.11"
copy!(Random.get_tls_seed(), tls_seed_orig)
end
end
arr
end
Expand Down
29 changes: 17 additions & 12 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ function check(rx, list; implicit=false)
if implicit
expected = "Main.M\n" * expected
end
actual = readchomp(io)
actual = strip(readchomp(io))
if isempty(expected)
@test startswith(actual, "No matching tests for module")
else
Expand Down Expand Up @@ -1836,17 +1836,22 @@ end
restore_file!(load_path_file)
end

# test lazy=true
empty!(Hijack.RUN)
ReTest.hijack("./Hijack/test/lazy.jl", :HijackLazy, lazy=true)
retest(HijackLazy)
@test Hijack.RUN == [1, 3]

# test lazy=:brutal
empty!(Hijack.RUN)
ReTest.hijack("./Hijack/test/lazy.jl", :HijackBrutal, lazy=:brutal)
retest(HijackBrutal)
@test Hijack.RUN == [3]
# These two tests currently just spin forever
if false
@warn "Skipping some hijack tests because they cause Revise to get into an infinite loop"

# test lazy=true
empty!(Hijack.RUN)
ReTest.hijack("./Hijack/test/lazy.jl", :HijackLazy, lazy=true)
retest(HijackLazy)
@test Hijack.RUN == [1, 3]

# test lazy=:brutal
empty!(Hijack.RUN)
ReTest.hijack("./Hijack/test/lazy.jl", :HijackBrutal, lazy=:brutal)
retest(HijackBrutal)
@test Hijack.RUN == [3]
end

# test lazy=:wrong
empty!(Hijack.RUN)
Expand Down
Loading