1- using JuliaSyntax: ParseError, ParseStream, @K_str , any_error, build_tree, bump_trivia, kind, last_byte, parse!, peek_full_token, peek_token
1+ using JuliaSyntax: JuliaSyntax, ParseError, ParseStream, @K_str
2+ using JuliaSyntax: any_error, build_tree, first_byte, kind, parse!, peek_full_token, peek_token
23using StringViews
34
45function include_test_file (ti_filter:: TestItemFilter , path:: String )
@@ -8,23 +9,23 @@ function include_test_file(ti_filter::TestItemFilter, path::String)
89 tls = task_local_storage ()
910 tls[:SOURCE_PATH ] = path # This is also done by Base.include
1011 try
11- while true
12- bump_trivia (stream, skip_newlines= true )
12+ @inbounds while true
13+ JuliaSyntax . bump_trivia (stream, skip_newlines= true )
1314 t = peek_token (stream, 1 )
1415 k = kind (t)
1516 k == K " EndMarker" && break
17+ line = _source_line_index (line_starts, first_byte (stream))
1618 if k == K " @"
17- line = searchsortedfirst (line_starts, last_byte (stream))
1819 tf = peek_full_token (stream, 2 )
19- v = @inbounds @ view (bytes[tf. first_byte: tf. last_byte])
20- if v == b " testitem" ; _eval_from_stream (stream, path, line, ti_filter, bytes)
21- elseif v == b " testsetup" ; _eval_from_stream (stream, path, line)
22- elseif v == b " test_rel" ; _eval_from_stream (stream, path, line, ti_filter)
20+ v = @view (bytes[tf. first_byte: tf. last_byte])
21+ if v == b " testitem" ; _eval_test_item_stream (stream, path, line, ti_filter, bytes)
22+ elseif v == b " testsetup" ; _eval_test_setup_stream (stream, path, line)
23+ elseif v == b " test_rel" ; _eval_test_rel_stream (stream, path, line, ti_filter, bytes )
2324 else
24- error (" Test files must only include `@testitem` and `@testsetup` calls, got an `@$(StringView (v)) ` at $(path) " ) # TODO
25+ error (" Test files must only include `@testitem` and `@testsetup` calls, got an `@$(StringView (v)) ` at $(path) : $(line) " ) # TODO
2526 end
2627 else
27- error (" Test files must only include `@testitem` and `@testsetup` calls, got a $t at $(path) " ) # TODO
28+ error (" Test files must only include `@testitem` and `@testsetup` calls, got a $t at $(path) : $(line) " ) # TODO
2829 end
2930 empty! (stream) # TODO : SourceFile created on a reset stream always start line number at 1
3031 end
@@ -33,20 +34,37 @@ function include_test_file(ti_filter::TestItemFilter, path::String)
3334 end
3435end
3536
36- _contains (s:: AbstractString , pattern:: Regex ) = occursin (pattern, s)
37- _contains (s:: AbstractString , pattern:: AbstractString ) = s == pattern
37+ @inline function _source_line_index (line_starts, bytes_pos)
38+ lineidx = searchsortedfirst (line_starts, bytes_pos)
39+ return (lineidx < lastindex (line_starts)) ? lineidx : lineidx- 1
40+ end
3841
3942# unconditionally eval
40- function _eval_from_stream (stream, path, line)
43+ function _eval_test_setup_stream (stream, path, line)
4144 parse! (stream; rule= :statement )
4245 ast = build_tree (Expr, stream; filename= path, first_line= line)
4346 Core. eval (Main, ast)
4447 return nothing
4548end
4649
4750# test_rel -> apply ti_filter on the parsed ast
48- function _eval_from_stream (stream, path, line, ti_filter:: TestItemFilter )
51+ function _eval_test_rel_stream (stream, path, line, ti_filter:: TestItemFilter , bytes )
4952 parse! (stream; rule= :statement )
53+ if ! (ti_filter. name isa Nothing)
54+ @inbounds for (i, token) in enumerate (stream. tokens)
55+ if kind (token) == K " Identifier"
56+ fbyte = JuliaSyntax. token_first_byte (stream, i)
57+ lbyte = JuliaSyntax. token_last_byte (stream, i)
58+ if @view (bytes[fbyte: lbyte]) == b " name"
59+ fbyte = JuliaSyntax. token_first_byte (stream, i + 3 )
60+ lbyte = JuliaSyntax. token_last_byte (stream, i + 3 )
61+ name = StringView (@view (bytes[fbyte: lbyte]))
62+ _contains (name, ti_filter. name) && break
63+ return nothing
64+ end
65+ end
66+ end
67+ end
5068 ast = build_tree (Expr, stream; filename= path, first_line= line)
5169 any_error (stream) && throw (ParseError (stream, filename= path))
5270 filtered = __filter_rai (ti_filter, ast):: Union{Nothing, Expr}
5674
5775# like above, but tries to avoid parsing the ast if it sees from the name identifier token
5876# it won't pass the filter
59- function _eval_from_stream (stream, path, line, ti_filter:: TestItemFilter , bytes)
60- if ti_filter. name isa Nothing
77+ function _eval_test_item_stream (stream, path, line, ti_filter:: TestItemFilter , bytes)
78+ if ! (ti_filter. name isa Nothing)
79+ name_t = peek_full_token (stream, 4 ) # 3 was '\"'
80+ name = @inbounds StringView (@view (bytes[name_t. first_byte: name_t. last_byte]))
81+ parse! (stream; rule= :statement )
82+ _contains (name, ti_filter. name) || return nothing
83+ else
6184 parse! (stream; rule= :statement )
62- ast = build_tree (Expr, stream; filename= path, first_line= line)
63- any_error (stream) && throw (ParseError (stream, filename= path))
64- filtered = __filter_ti (ti_filter, ast):: Union{Nothing, Expr}
65- filtered === nothing || Core. eval (Main, filtered:: Expr )
66- return nothing
6785 end
6886
69- name_t = peek_full_token (stream, 4 ) # 3 was '\"'
70- name = @inbounds StringView (@view (bytes[name_t. first_byte: name_t. last_byte]))
71- parse! (stream; rule= :statement )
72- if _contains (name, ti_filter. name)
73- ast = build_tree (Expr, stream; filename= path, first_line= line)
74- any_error (stream) && throw (ParseError (stream, filename= path))
75- filtered = __filter_ti (ti_filter, ast):: Union{Nothing, Expr}
76- filtered === nothing || Core. eval (Main, filtered:: Expr )
77- end
87+ ast = build_tree (Expr, stream; filename= path, first_line= line)
88+ any_error (stream) && throw (ParseError (stream, filename= path))
89+ filtered = __filter_ti (ti_filter, ast):: Union{Nothing, Expr}
90+ filtered === nothing || Core. eval (Main, filtered:: Expr )
7891 return nothing
7992end
93+
94+ @inline _contains (s:: AbstractString , pattern:: Regex ) = occursin (pattern, s)
95+ @inline _contains (s:: AbstractString , pattern:: AbstractString ) = s == pattern
0 commit comments