Skip to content

Commit e8295b0

Browse files
Do all filtering of testitems at AST level (#179)
* WIP do all filtering at AST level * Bump version * Update tests to better reflect rai macro Specifically `name` is required, `tags` is supported, `code` must be passed as a keyword argument. * fixup! WIP do all filtering at AST level * Delete internal tests for unsupported usage
1 parent f9cf56c commit e8295b0

File tree

6 files changed

+63
-47
lines changed

6 files changed

+63
-47
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "ReTestItems"
22
uuid = "817f1d60-ba6b-4fd5-9520-3cf149f6a823"
3-
version = "1.25.1"
3+
version = "1.26.0"
44

55
[deps]
66
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"

src/filtering.jl

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,6 @@ function (f::TestItemFilter)(ti::TestItemMetadata)
1919
return f.shouldrun(ti)::Bool && _shouldrun(f.tags, ti) && _shouldrun(f.name, ti)
2020
end
2121

22-
# This method is needed for filtering `TestItem`s that were created by
23-
# `___RAI_MACRO_NAME_DONT_USE` and so couldn't be filtered from the AST.
24-
# TODO: drop this method when we no longer support `___RAI_MACRO_NAME_DONT_USE`
25-
function (f::TestItemFilter)(ti::TestItem)
26-
return f.shouldrun(ti)::Bool && _shouldrun(f.tags, ti) && _shouldrun(f.name, ti)
27-
end
28-
2922
_shouldrun(name::AbstractString, ti) = name == ti.name
3023
_shouldrun(pattern::Regex, ti) = contains(ti.name, pattern)
3124
_shouldrun(tags::AbstractVector{Symbol}, ti) = issubset(tags, ti.tags)
@@ -57,17 +50,21 @@ end
5750
# having to maintain our own version of that code.
5851
function filter_testitem(f, expr)
5952
@assert expr.head == :macrocall
60-
if expr.args[1] !== Symbol("@testitem")
53+
macro_name = expr.args[1]
54+
if macro_name === Symbol("@testitem")
55+
# `@testitem` have have at least: macro_name, line_number, name, body
56+
length(expr.args) < 4 && return expr
57+
name = try_get_name(expr)
58+
name === nothing && return expr
59+
tags = try_get_tags(expr)
60+
tags === nothing && return expr
61+
ti = TestItemMetadata(name, tags)
62+
return f(ti) ? expr : nothing
63+
elseif macro_name === Symbol("@testsetup")
6164
return expr
65+
elseif macro_name === ___RAI_MACRO_NAME_DONT_USE # TODO: drop this branch when we can
66+
return __filter_rai(f, expr)
6267
end
63-
# `@testitem` have have at least: macro_name, line_number, name, body
64-
length(expr.args) < 4 && return expr
65-
name = try_get_name(expr)
66-
name === nothing && return expr
67-
tags = try_get_tags(expr)
68-
tags === nothing && return expr
69-
ti = TestItemMetadata(name, tags)
70-
return f(ti) ? expr : nothing
7168
end
7269

7370
# Extract the name from a `@testitem`, return `nothing` if name is not of the expected type.
@@ -104,6 +101,34 @@ end
104101
# Macro used by RAI (corporate sponsor of this package)
105102
# TODO: drop support for this when RAI codebase is fully switched to ReTestItems.jl
106103
const ___RAI_MACRO_NAME_DONT_USE = Symbol("@test_rel")
104+
# This logic is very similar to `try_get_tags` but we want to keep the two separate
105+
# so that this code is easy to delete when we drop support for the RAI macro.
106+
function __filter_rai(f, expr)
107+
@assert expr.head == :macrocall && expr.args[1] == ___RAI_MACRO_NAME_DONT_USE
108+
name = nothing
109+
tags = Symbol[]
110+
for args in expr.args[2:end]
111+
if args isa Expr && args.head == :(=) && args.args[1] == :name && args.args[2] isa String
112+
name = args.args[2]
113+
elseif args isa Expr && args.head == :(=) && args.args[1] == :tags
114+
tags_arg = args.args[2]
115+
if tags_arg isa Expr && tags_arg.head == :vect
116+
for tag in tags_arg.args
117+
if tag isa QuoteNode && tag.value isa Symbol
118+
push!(tags, tag.value)
119+
else
120+
return expr
121+
end
122+
end
123+
else
124+
return expr
125+
end
126+
end
127+
end
128+
name === nothing && return expr
129+
ti = TestItemMetadata(name, tags)
130+
return f(ti) ? expr : nothing
131+
end
107132

108133
# Check if the expression is a macrocall as expected.
109134
# NOTE: Only `@testitem` and `@testsetup` calls are officially supported.

src/testcontext.jl

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -32,29 +32,16 @@ mutable struct TestContext
3232
TestContext(name, ntestitems) = new(name, ntestitems)
3333
end
3434

35-
# FilteredVector applies a filtering function `f` to items
36-
# when you try to `push!` and only puts if `f` returns true.
37-
# TODO: drop this when all filtering is done at the AST level
38-
# i.e. when we drop support for _RAI_MACRO_NAME
39-
struct FilteredVector{T} <: AbstractVector{T}
40-
f::Any
41-
vec::Vector{T}
42-
end
43-
44-
Base.push!(x::FilteredVector, y) = x.f(y) && push!(x.vec, y)
45-
Base.size(x::FilteredVector) = size(x.vec)
46-
Base.getindex(x::FilteredVector, i) = x.vec[i]
47-
4835
struct FileNode
4936
path::String
5037
testset::DefaultTestSet
5138
junit::Union{JUnitTestSuite,Nothing}
52-
testitems::FilteredVector{TestItem} # sorted by line number within file
39+
testitems::Vector{TestItem} # sorted by line number within file
5340
end
5441

5542
function FileNode(path, f=Returns(true); report::Bool=false, verbose::Bool=false)
5643
junit = report ? JUnitTestSuite(path) : nothing
57-
return FileNode(path, DefaultTestSet(path; verbose), junit, FilteredVector(f, TestItem[]))
44+
return FileNode(path, DefaultTestSet(path; verbose), junit, TestItem[])
5845
end
5946

6047
Base.push!(f::FileNode, ti::TestItem) = push!(f.testitems, ti)

test/integrationtests.jl

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -835,12 +835,17 @@ end
835835
@assert ReTestItems.___RAI_MACRO_NAME_DONT_USE == Symbol("@test_rel")
836836
@eval begin
837837
macro test_rel(args...)
838-
if length(args) == 1
839-
name = "anon"
840-
ex = args[1]
841-
else
842-
name = args[1]
843-
ex = args[2]
838+
local name::String
839+
local ex::Expr
840+
local tags = Symbol[]
841+
for arg in args
842+
if arg.head == :(=) && arg.args[1] == :name
843+
name = arg.args[2]
844+
elseif arg.head == :(=) && arg.args[1] == :tags
845+
tags = arg.args[2]
846+
elseif arg.head == :(=) && arg.args[1] == :code
847+
ex = arg.args[2]
848+
end
844849
end
845850
quote
846851
@testitem $(name) begin
@@ -856,7 +861,9 @@ end
856861
@test n_tests(results) == 3
857862
results = encased_testset(() -> runtests(file; name="ti"))
858863
@test n_tests(results) == 1
859-
results = encased_testset(() -> runtests(file; name="anon"))
864+
results = encased_testset(() -> runtests(file; name=r"other"))
865+
@test n_tests(results) == 2
866+
results = encased_testset(() -> runtests(file; tags=[:xyz]))
860867
@test n_tests(results) == 1
861868
results = encased_testset(() -> runtests(filter_func, file))
862869
@test n_tests(results) == 0

test/internals.jl

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -328,13 +328,10 @@ end
328328
using ReTestItems: filter_testitem
329329
ti = :(@testitem "TI" tags=[:foo, :bar] begin; @test true; end)
330330
ts = :(@testsetup module TS; x = 1; end)
331-
tx = :(@other_macro "TX" tags=[:foo, :bar] begin; @test true; end)
332331
@test filter_testitem(Returns(true), ti) == ti
333332
@test filter_testitem(Returns(false), ti) == nothing
334333
@test filter_testitem(Returns(true), ts) == ts
335334
@test filter_testitem(Returns(false), ts) == ts
336-
@test filter_testitem(Returns(true), tx) == tx
337-
@test filter_testitem(Returns(false), tx) == tx
338335
end
339336
@testset "try_get_name" begin
340337
using ReTestItems: try_get_name

test/testfiles/_support_rai_test.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ end
1010
end
1111

1212
# this macro is defined in `integrationtests.jl` where this file is run.
13-
@test_rel begin
13+
@test_rel(name="other A", code=begin
1414
@test 1 == 1
15-
end
15+
end)
1616

17-
@test_rel "other" begin
18-
@test 1 == 1
19-
end
17+
@test_rel(tags=[:xyz], name="other B", code=begin
18+
@test 2 == 2
19+
end)

0 commit comments

Comments
 (0)