-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Flatten testsets during report generation #104
Changes from all commits
f52e372
a7aff31
ab9eabb
308cc72
bcec677
56a170b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,6 @@ | ||
name: CI | ||
on: | ||
pull_request: | ||
branches: | ||
- master | ||
push: | ||
branches: | ||
- master | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -105,8 +105,7 @@ function finish(ts::ReportingTestSet) | |
# Display before flattening to match Pkg.test output | ||
display_reporting_testset(ts) | ||
|
||
# We are the top level, lets do this | ||
flatten_results!(ts) | ||
return ts | ||
end | ||
|
||
################################# | ||
|
@@ -205,31 +204,28 @@ any_problems(::Error) = true | |
##################### | ||
|
||
""" | ||
flatten_results!(ts::AbstractTestSet) | ||
has_description(ts::AbstractTestSet) -> Bool | ||
|
||
Returns a flat structure 3 deep, of `TestSet` -> `TestSet` -> `Result`. This is necessary | ||
for writing a report, as a JUnit XML does not allow one testsuite to be nested in another. | ||
The top level `TestSet` becomes the testsuites element, and the middle level `TestSet`s | ||
become individual testsuite elements, and the `Result`s become the testcase elements. | ||
Determine if the testset has been provided a description. | ||
""" | ||
function has_description(ts::AbstractTestSet) | ||
!isempty(ts.description) && ts.description != "test set" | ||
end | ||
|
||
If `ts.results` contains any `Result`s, these are added into a new `TestSet` with the | ||
description "Top level tests", which then replaces them in `ts.results`. | ||
""" | ||
function flatten_results!(ts::AbstractTestSet) | ||
# Add any top level Results to their own TestSet | ||
handle_top_level_results!(ts) | ||
flatten_results!(ts::AbstractTestSet) | ||
|
||
# Flatten all results of top level testset, which should all be testsets now | ||
ts.results = vcat(_flatten_results!.(ts.results)...) | ||
return ts | ||
end | ||
Returns a flat vector of `TestSet`s which only contain `Result`s. This is necessary for | ||
writing a JUnit XML report the schema does not allow nested XML `testsuite` elements. | ||
""" | ||
flatten_results!(ts::AbstractTestSet) = _flatten_results!(ts, 0) | ||
|
||
""" | ||
_flatten_results!(ts::AbstractTestSet)::Vector{<:AbstractTestSet} | ||
|
||
Recursively flatten `ts` to a vector of `TestSet`s. | ||
""" | ||
function _flatten_results!(ts::AbstractTestSet)::Vector{<:AbstractTestSet} | ||
function _flatten_results!(ts::AbstractTestSet, depth::Int)::Vector{AbstractTestSet} | ||
original_results = ts.results | ||
has_new_properties = !isempty(something(properties(ts), tuple())) | ||
flattened_results = AbstractTestSet[] | ||
|
@@ -245,13 +241,15 @@ function _flatten_results!(ts::AbstractTestSet)::Vector{<:AbstractTestSet} | |
function inner!(childts::AbstractTestSet) | ||
# Make it a sibling | ||
update_testset_properties!(childts, ts) | ||
childts.description = ts.description * "/" * childts.description | ||
if depth > 0 || has_description(ts) | ||
childts.description = ts.description * "/" * childts.description | ||
end | ||
Comment on lines
+244
to
+246
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently we only skip appending unnamed top-level test sets from the nested testset description. I think it would be reasonable to skip any unnamed testset at any level but didn't want to work through all of the repercussions of such a change at this time. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree i would want to think about that a little more |
||
push!(flattened_results, childts) | ||
end | ||
|
||
# Iterate through original_results | ||
for res in original_results | ||
childs = _flatten_results!(res) | ||
childs = _flatten_results!(res, depth + 1) | ||
for child in childs | ||
inner!(child) | ||
end | ||
|
@@ -272,7 +270,7 @@ end | |
Return vector containing `rs` so that when iterated through, | ||
`rs` is added to the results vector. | ||
""" | ||
_flatten_results!(rs::Result) = [rs] | ||
_flatten_results!(rs::Result, depth::Int) = [rs] | ||
|
||
""" | ||
update_testset_properties!(childts::AbstractTestSet, ts::AbstractTestSet) | ||
|
@@ -290,51 +288,28 @@ this is handled as follows: | |
See also: [`properties`](@ref) | ||
""" | ||
function update_testset_properties!(childts::AbstractTestSet, ts::AbstractTestSet) | ||
if isnothing(properties(childts)) && !isnothing(properties(ts)) && !isempty(properties(ts)) | ||
child_props = properties(childts) | ||
parent_props = properties(ts) | ||
|
||
if isnothing(child_props) && !isnothing(parent_props) && !isempty(parent_props) | ||
@warn "Properties of testset $(ts.description) can not be added to child testset $(childts.description) as it does not have a TestReports.properties method defined." | ||
# No need to check if childts is has properties defined and ts doesn't as if this is the case | ||
# ts has no properties to add to that of childts. | ||
elseif !isnothing(properties(ts)) | ||
parent_keys = keys(properties(ts)) | ||
child_keys = keys(properties(childts)) | ||
elseif !isnothing(child_props) && !isnothing(parent_props) | ||
parent_keys = keys(parent_props) | ||
child_keys = keys(child_props) | ||
# Loop through keys so that warnings can be issued for any duplicates | ||
for key in parent_keys | ||
if key in child_keys | ||
@warn "Property $key in testest $(ts.description) overwritten by child testset $(childts.description)" | ||
else | ||
properties(childts)[key] = properties(ts)[key] | ||
child_props[key] = parent_props[key] | ||
end | ||
end | ||
end | ||
return childts | ||
end | ||
|
||
""" | ||
handle_top_level_results!(ts::AbstractTestSet) | ||
|
||
If `ts.results` contains any `Result`s, these are removed from `ts.results` and | ||
added to a new `ReportingTestSet`, which in turn is added to `ts.results`. This | ||
leaves `ts.results` only containing `AbstractTestSet`s. | ||
|
||
The `time_taken` field of the new `ReportingTestSet` is calculated by summing | ||
the time taken by the individual results, and the `start_time` field is set to | ||
the `start_time` field of `ts`. | ||
""" | ||
function handle_top_level_results!(ts::AbstractTestSet) | ||
isa_Result = isa.(ts.results, Result) | ||
if any(isa_Result) | ||
original_results = ts.results | ||
ts.results = AbstractTestSet[] | ||
ts_nested = ReportingTestSet("Top level tests") | ||
ts_nested.results = original_results[isa_Result] | ||
set_time_taken!(ts_nested, sum(x -> time_taken(x)::Millisecond, ts_nested.results)) | ||
set_start_time!(ts_nested, start_time(ts)::DateTime) | ||
push!(ts.results, ts_nested) | ||
append!(ts.results, original_results[.!isa_Result]) | ||
end | ||
return ts | ||
end | ||
|
||
""" | ||
display_reporting_testset(ts::ReportingTestSet) | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
using Test | ||
using TestReports | ||
|
||
(@testset ReportingTestSet "Example" begin | ||
(@testset ReportingTestSet "" begin | ||
include("example_normaltestsets.jl") | ||
end) |> report |> println |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for my education can you explain this chagne?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was changed to address this: #104 (comment)
The
branches
filter for thepull_request
event restricts this workflow such that it only runs when a PR targetsmaster
(in this case) as the base branch. This PR originally didn't targetmaster
but another PR so the CI wouldn't run automatically. I don't see a good reason to restrict the CI workflow in these cases so I removed this.