Skip to content

Commit b1e24c7

Browse files
committed
Try to filter test items without constructing the AST
1 parent 9da139d commit b1e24c7

File tree

3 files changed

+80
-1
lines changed

3 files changed

+80
-1
lines changed

Project.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,25 @@ version = "1.34.0"
44

55
[deps]
66
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
7+
JuliaSyntax = "70703baa-626e-46a2-a12c-08ffd08c73b4"
78
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
89
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
910
Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
1011
Sockets = "6462fe0b-24de-5631-8697-dd941f90decc"
12+
StringViews = "354b36f9-a18e-4713-926e-db85100087ba"
1113
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
1214
TestEnv = "1e6cf692-eddd-4d53-88a5-2d735e33781b"
1315

1416
[compat]
1517
Dates = "1"
18+
JuliaSyntax = "1"
1619
Logging = "1"
1720
Pkg = "1"
1821
Profile = "1"
1922
Random = "1"
2023
Serialization = "1"
2124
Sockets = "1"
25+
StringViews = "1"
2226
Test = "1"
2327
TestEnv = "1.8"
2428
julia = "1.8"

src/ReTestItems.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ include("junit_xml.jl")
102102
include("testcontext.jl")
103103
include("log_capture.jl")
104104
include("filtering.jl")
105+
include("include_test_file.jl")
105106

106107
function __init__()
107108
if ccall(:jl_generating_output, Cint, ()) == 0 # not precompiling
@@ -869,7 +870,7 @@ function include_task(walkdir_channel, setup_channel, project_root, ti_filter)
869870
for (file_path, file_node) in walkdir_channel
870871
@debugv 1 "Including test items from file `$(file_path)`"
871872
task_local_storage(:__RE_TEST_ITEMS__, (file_node, empty!(testitem_names))) do
872-
Base.include(ti_filter, Main, file_path)
873+
include_test_file(ti_filter, file_path)
873874
end
874875
end
875876
end

src/include_test_file.jl

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
using JuliaSyntax: ParseStream, @K_str, build_tree, bump_trivia, kind, parse!, peek_full_token, peek_token
2+
using StringViews
3+
4+
function include_test_file(ti_filter, path::String)
5+
bytes = read(path)
6+
stream = ParseStream(bytes)
7+
tls = task_local_storage()
8+
tls[:SOURCE_PATH] = path # This is also done by Base.include
9+
try
10+
while true
11+
bump_trivia(stream, skip_newlines=true)
12+
t = peek_token(stream, 1)
13+
k = kind(t)
14+
k == K"EndMarker" && break
15+
if k == K"@"
16+
tf = peek_full_token(stream, 2)
17+
v = @inbounds @view(bytes[tf.first_byte:tf.last_byte])
18+
if v == b"testitem" ; _eval_from_stream(stream, path, ti_filter, bytes)
19+
elseif v == b"testsetup"; _eval_from_stream(stream, path)
20+
elseif v == b"test_rel" ; _eval_from_stream(stream, path, ti_filter)
21+
else
22+
error("Test files must only include `@testitem` and `@testsetup` calls, got an `@$(StringView(v))` at $(path)") # TODO
23+
end
24+
else
25+
error("Test files must only include `@testitem` and `@testsetup` calls, got a $t at $(path)") # TODO
26+
end
27+
empty!(stream)
28+
end
29+
finally
30+
delete!(tls, :SOURCE_PATH)
31+
end
32+
end
33+
34+
_contains(s::AbstractString, pattern::Regex) = occursin(pattern, s)
35+
_contains(s::AbstractString, pattern::AbstractString) = s == pattern
36+
37+
# unconditionally eval
38+
function _eval_from_stream(stream, path)
39+
parse!(stream; rule=:statement)
40+
ast = build_tree(Expr, stream; filename=path)
41+
Core.eval(Main, ast)
42+
return nothing
43+
end
44+
45+
# test_rel -> apply ti_filter on the parsed ast
46+
function _eval_from_stream(stream, path, ti_filter)
47+
parse!(stream; rule=:statement)
48+
ast = build_tree(Expr, stream; filename=path)
49+
filtered = ti_filter(ast)
50+
filtered === nothing || Core.eval(Main, filtered)
51+
return nothing
52+
end
53+
54+
# like above, but tries to avoid parsing the ast if it sees from the name identifier token
55+
# it won't pass the filter
56+
function _eval_from_stream(stream, path, ti_filter, bytes)
57+
if ti_filter.name isa Nothing
58+
parse!(stream; rule=:statement)
59+
ast = build_tree(Expr, stream; filename=path)
60+
filtered = ti_filter(ast)
61+
filtered === nothing || Core.eval(Main, filtered)
62+
return nothing
63+
end
64+
65+
name_t = peek_full_token(stream, 4) # 3 was '\"'
66+
name = @inbounds StringView(@view(bytes[name_t.first_byte:name_t.last_byte]))
67+
parse!(stream; rule=:statement)
68+
if _contains(name, ti_filter.name)
69+
ast = build_tree(Expr, stream; filename=path)
70+
filtered = ti_filter(ast)
71+
filtered === nothing || Core.eval(Main, filtered)
72+
end
73+
return nothing
74+
end

0 commit comments

Comments
 (0)