diff --git a/Project.toml b/Project.toml index 7beb2da..436c903 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "FileCheck" uuid = "4e644321-382b-4b05-b0b6-5d23c3d944fb" authors = ["Tim Besard "] -version = "1.0" +version = "1.1" [deps] LLVM_jll = "86de99a1-58d6-5da7-8064-bd56ce2e322c" diff --git a/src/FileCheck.jl b/src/FileCheck.jl index c5f7ccc..a8f367d 100644 --- a/src/FileCheck.jl +++ b/src/FileCheck.jl @@ -25,6 +25,7 @@ function filecheck_exe(; adjust_PATH::Bool=true, adjust_LIBPATH::Bool=true) end function filecheck(f, input; + throws::Union{Nothing, Type{<:Exception}}=nothing, match_full_lines::Bool=false, strict_whitespace::Bool=false, ignore_case::Bool=false, @@ -50,14 +51,33 @@ function filecheck(f, input; read(pipe, String) end result = nothing + thrown = false io = IOContext(pipe) - stats = redirect_stdio(; stdout=io, stderr=io) do + redirect_stdio(; stdout=io, stderr=io) do put!(pipe_initialized, nothing) - result = f(input) + if throws !== nothing + try + result = f(input) + catch e + e isa throws || rethrow() + thrown = true + Base.display_error(io, e, catch_backtrace()) + end + else + result = f(input) + end end - if result !== nothing - println(io) - print(io, result) + if throws !== nothing + if !thrown + close(pipe.in) + fetch(reader) + error("Expected $throws to be thrown, but no exception was thrown") + end + else + if result !== nothing + println(io) + print(io, result) + end end close(pipe.in) output = fetch(reader) @@ -228,6 +248,10 @@ These are forwarded to LLVM's FileCheck as CLI flags: - `check_prefixes::Vector{String}`: Use custom check prefixes (`--check-prefixes`). - `defines::Dict{String,String}`: Define FileCheck variables (`-Dkey=value`). - `allow_empty::Bool`: Allow empty check files (`--allow-empty`). +- `throws::Type{<:Exception}`: Expect the expression to throw an exception of this type. + The error and backtrace are printed in standard Julia format and can be verified with + `@check*` directives. Raises an error if no exception is thrown; re-throws if the wrong + type is caught. # Examples @@ -248,6 +272,11 @@ end @check literal=true "foo {{bar}}" print("foo {{bar}}") end + +@test @filecheck throws=ArgumentError begin + @check "ArgumentError: bad" + throw(ArgumentError("bad")) +end ``` """ macro filecheck(args...) diff --git a/test/runtests.jl b/test/runtests.jl index 2307da4..416cb5b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -162,6 +162,47 @@ end end end +@testset "throws" begin + # Basic: catch expected exception, verify error message + @test @filecheck throws=ErrorException begin + @check "ERROR: TestError" + error("TestError") + end + + # Verify backtrace is included + @test @filecheck throws=ErrorException begin + @check "ERROR: TestError" + @check "Stacktrace:" + error("TestError") + end + + # ArgumentError type + @test @filecheck throws=ArgumentError begin + @check "ArgumentError: bad arg" + throw(ArgumentError("bad arg")) + end + + # Output before exception is also captured + @test @filecheck throws=ErrorException begin + @check "before" + @check "ERROR: boom" + println("before") + error("boom") + end + + # No exception thrown when expected → error + @test_throws "no exception was thrown" @filecheck throws=ErrorException begin + @check "hello" + "hello" + end + + # Wrong exception type → rethrown + @test_throws ArgumentError @filecheck throws=ErrorException begin + @check "hello" + throw(ArgumentError("wrong type")) + end +end + @testset "conditional checks" begin # cond=true: check is included @test @filecheck begin