Skip to content

Commit d624548

Browse files
Replace relpath with cheaper function (#174)
* Replace `relpath` with cheaper function Since we call `relpath` for every directory and file in the directory tree before any tests start, we want this to be as cheap as possible (to minimise latency). Hopefully this new function is valid on Windows, but I've no machine to test that :/ * Make type-stable * Change fields to SubString to save an alloc
1 parent e8295b0 commit d624548

File tree

4 files changed

+47
-8
lines changed

4 files changed

+47
-8
lines changed

src/ReTestItems.jl

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,22 @@ function is_testsetup_file(filepath)
633633
)
634634
end
635635

636+
# Like `relpath` but assumes `path` is nested under `startdir`, else just returns `path`.
637+
# Always returns a `SubString` to be type-stable.
638+
function nestedrelpath(path::T, startdir::AbstractString) where {T <: AbstractString}
639+
path == startdir && return SubString{T}(".")
640+
relp = chopprefix(path, startdir)
641+
relp == path && return relp
642+
sep = Base.Filesystem.path_separator
643+
if endswith(startdir, sep)
644+
return relp
645+
elseif startswith(relp, sep)
646+
return chopprefix(relp, sep)
647+
else # `startdir` was a prefix of `path` but not a directory
648+
return SubString{T}(path)
649+
end
650+
end
651+
636652
# is `dir` the root of a subproject inside the current project?
637653
function _is_subproject(dir, current_projectfile)
638654
projectfile = _project_file(dir)
@@ -642,7 +658,7 @@ function _is_subproject(dir, current_projectfile)
642658
projectfile == current_projectfile && return false
643659
# a `test/Project.toml` is special and doesn't indicate a subproject
644660
current_project_dir = dirname(current_projectfile)
645-
rel_projectfile = relpath(projectfile, current_project_dir)
661+
rel_projectfile = nestedrelpath(projectfile, current_project_dir)
646662
rel_projectfile == joinpath("test", "Project.toml") && return false
647663
return true
648664
end
@@ -675,7 +691,7 @@ function include_testfiles!(project_name, projectfile, paths, ti_filter::TestIte
675691
subproject_root = root
676692
continue
677693
end
678-
rpath = relpath(root, project_root)
694+
rpath = nestedrelpath(root, project_root)
679695
startswith(rpath, hidden_re) && continue # skip hidden directories
680696
dir_node = DirNode(rpath; report, verbose=verbose_results)
681697
dir_nodes[rpath] = dir_node
@@ -693,7 +709,7 @@ function include_testfiles!(project_name, projectfile, paths, ti_filter::TestIte
693709
if !(is_testsetup_file(filepath) || (is_test_file(filepath) && is_requested(filepath, paths)))
694710
continue
695711
end
696-
fpath = relpath(filepath, project_root)
712+
fpath = nestedrelpath(filepath, project_root)
697713
file_node = FileNode(fpath, ti_filter; report, verbose=verbose_results)
698714
testitem_names = Set{String}() # to enforce that names in the same file are unique
699715
push!(dir_node, file_node)
@@ -745,7 +761,7 @@ function _throw_duplicate_ids(testitems)
745761
seen = Dict{String,String}()
746762
for ti in testitems
747763
id = ti.id
748-
source = string(relpath(ti.file, ti.project_root), ":", ti.line)
764+
source = string(nestedrelpath(ti.file, ti.project_root), ":", ti.line)
749765
name = string(repr(ti.name), " at ", source)
750766
if haskey(seen, id)
751767
name1 = seen[id]

src/junit_xml.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,15 +98,15 @@ mutable struct JUnitTestSuite # File
9898
counts::JUnitCounts
9999
testcases::Vector{JUnitTestCase}
100100
end
101-
JUnitTestSuite(name::String) = JUnitTestSuite(name, JUnitCounts(), JUnitTestCase[])
101+
JUnitTestSuite(name::AbstractString) = JUnitTestSuite(name, JUnitCounts(), JUnitTestCase[])
102102

103103
mutable struct JUnitTestSuites
104104
const name::String
105105
counts::JUnitCounts
106106
testsuites::Vector{JUnitTestSuite}
107107
end
108108

109-
JUnitTestSuites(name::String) = JUnitTestSuites(name, JUnitCounts(), JUnitTestSuite[])
109+
JUnitTestSuites(name::AbstractString) = JUnitTestSuites(name, JUnitCounts(), JUnitTestSuite[])
110110

111111
function junit_record!(suites1::JUnitTestSuites, suites2::JUnitTestSuites)
112112
update!(suites1.counts, suites2.counts)

src/testcontext.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ mutable struct TestContext
3333
end
3434

3535
struct FileNode
36-
path::String
36+
path::SubString{String}
3737
testset::DefaultTestSet
3838
junit::Union{JUnitTestSuite,Nothing}
3939
testitems::Vector{TestItem} # sorted by line number within file
@@ -49,7 +49,7 @@ Base.push!(f::FileNode, ti::TestItem) = push!(f.testitems, ti)
4949
walk(f, fn::FileNode) = foreach(f, fn.testitems)
5050

5151
struct DirNode
52-
path::String
52+
path::SubString{String}
5353
testset::DefaultTestSet
5454
junit::Union{JUnitTestSuites,Nothing}
5555
children::Vector{Union{FileNode, DirNode}} # sorted lexically by path

test/internals.jl

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,4 +375,27 @@ end
375375
end
376376
end
377377

378+
@testset "nestedrelpath" begin
379+
using ReTestItems: nestedrelpath
380+
@assert Base.Filesystem.path_separator == "/"
381+
path = "test/dir/foo_test.jl"
382+
@test nestedrelpath(path, "test") == relpath(path, "test") == "dir/foo_test.jl"
383+
@test nestedrelpath(path, "test/") == relpath(path, "test/") == "dir/foo_test.jl"
384+
@test nestedrelpath(path, "test/dir") == relpath(path, "test/dir") == "foo_test.jl"
385+
@test nestedrelpath(path, "test/dir/") == relpath(path, "test/dir/") == "foo_test.jl"
386+
@test nestedrelpath(path, "test/dir/foo_test.jl") == relpath(path, "test/dir/foo_test.jl") == "."
387+
388+
# unlike `relpath`: if `startdir` is not a prefix of `path`, the assumption is violated,
389+
# and `path` is just returned as-is
390+
@test nestedrelpath(path, "test/dir/foo_") == "test/dir/foo_test.jl"
391+
@test nestedrelpath(path, "test/dir/other") == "test/dir/foo_test.jl"
392+
@test nestedrelpath(path, "test/dir/other/bar_test.jl") == "test/dir/foo_test.jl"
393+
394+
# leading '/' doesn't get ignored or stripped
395+
@test nestedrelpath("/a/b/c", "/a/b") == "c"
396+
@test nestedrelpath("/a/b/c", "a/b") == "/a/b/c"
397+
@test nestedrelpath("/a/b", "/a/b/c") == "/a/b"
398+
@test nestedrelpath("/a/b", "c") == "/a/b"
399+
end
400+
378401
end # internals.jl testset

0 commit comments

Comments
 (0)