Skip to content

Commit a18d313

Browse files
committed
Cleanup and output JUnit
1 parent 97e9cd6 commit a18d313

File tree

17 files changed

+183
-132
lines changed

17 files changed

+183
-132
lines changed

.github/workflows/test.yml

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ on:
1010
- main
1111

1212
jobs:
13-
build:
13+
build-and-test:
1414
runs-on: ${{ matrix.os }}
1515

1616
strategy:
@@ -43,3 +43,33 @@ jobs:
4343

4444
- name: Test
4545
run: ctest --test-dir build --build-config Release
46+
47+
- name: Upload Test Results
48+
uses: actions/upload-artifact@v4
49+
if: always()
50+
with:
51+
name: Test Results
52+
path: build/spec/results/*.xml
53+
54+
publish-test-results:
55+
name: "Publish Tests Results"
56+
needs: build-and-test
57+
runs-on: ubuntu-latest
58+
permissions:
59+
checks: write
60+
61+
# only needed unless run with comment_mode: off
62+
pull-requests: write
63+
64+
if: always()
65+
66+
steps:
67+
- name: Download Artifacts
68+
uses: actions/download-artifact@v4
69+
with:
70+
path: artifacts
71+
72+
- name: Publish Test Results
73+
uses: EnricoMi/publish-unit-test-result-action@v2
74+
with:
75+
files: "artifacts/**/*.xml"

CMakeLists.txt

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ endif()
5656
# HELPERS
5757

5858
# Add spec
59-
function(add_spec source_file)
59+
function(add_spec source_file args)
6060
cmake_path(GET source_file STEM spec_name)
6161
add_executable(${spec_name} ${source_file})
6262
target_link_libraries(${spec_name} c++spec)
@@ -65,18 +65,27 @@ function(add_spec source_file)
6565
CXX_STANDARD 23
6666
CXX_STANDARD_REQUIRED YES
6767
)
68-
add_test(NAME ${spec_name} COMMAND ${spec_name} --verbose)
68+
add_test(NAME ${spec_name} COMMAND ${spec_name} --verbose ${args})
6969
endfunction(add_spec)
7070

7171
# Discover Specs
7272
function(discover_specs spec_folder)
7373
file(GLOB_RECURSE specs ${spec_folder}/*_spec.cpp)
7474

7575
foreach(spec IN LISTS specs)
76-
add_spec(${spec})
76+
add_spec(${spec} "")
7777
endforeach()
7878
endfunction(discover_specs)
7979

80+
function(discover_specs_output_junit spec_folder)
81+
file(GLOB_RECURSE specs ${spec_folder}/*_spec.cpp)
82+
83+
foreach(spec IN LISTS specs)
84+
cmake_path(GET spec STEM spec_name)
85+
add_spec(${spec} "--output-junit;${CMAKE_CURRENT_BINARY_DIR}/results/${spec_name}.xml")
86+
endforeach()
87+
endfunction(discover_specs_output_junit)
88+
8089
# OPTIONS
8190
option(CPPSPEC_BUILD_TESTS "Build C++Spec tests")
8291
option(CPPSPEC_BUILD_EXAMPLES "Build C++Spec examples")

include/argparse.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ inline Runner parse(int argc, char** const argv) {
6060

6161
auto junit_output_filepath = program.get<std::string>("--output-junit");
6262
if (!junit_output_filepath.empty()) {
63+
// create directories recursively if they don't exist
64+
std::filesystem::path junit_output_path = junit_output_filepath;
65+
std::filesystem::create_directories(junit_output_path.parent_path());
66+
67+
// open file stream
6368
auto* file_stream = new std::ofstream(junit_output_filepath);
6469
auto junit_output = std::make_shared<Formatters::JUnitXML>(*file_stream, false);
6570
return Runner{formatter, junit_output};

include/class_description.hpp

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,16 @@ class ClassDescription : public Description {
2626
std::string type;
2727

2828
public:
29-
const bool has_subject = true;
3029
T subject; // subject field public for usage in `expect([self.]subject)`
3130

3231
// Constructor
3332
// if there's no explicit subject given, then use
3433
// the default constructor of the given type as the implicit subject.
3534
ClassDescription(Block block, std::source_location location = std::source_location::current())
36-
: block(block), type(" : " + Util::demangle(typeid(T).name())), subject(T()) {
37-
this->description = Pretty::to_word(subject);
38-
this->set_location(location);
39-
}
35+
: Description(location, Pretty::to_word(subject)),
36+
block(block),
37+
type(" : " + Util::demangle(typeid(T).name())),
38+
subject(T()) {}
4039

4140
ClassDescription(const char* description,
4241
Block block,
@@ -67,7 +66,7 @@ class ClassDescription : public Description {
6766
: Description(location, Pretty::to_word(subject)),
6867
block(block),
6968
type(" : " + Util::demangle(typeid(T).name())),
70-
subject(std::move(subject)) {}
69+
subject(std::forward<U>(subject)) {}
7170

7271
template <typename U>
7372
ClassDescription(std::initializer_list<U> init_list,
@@ -85,7 +84,7 @@ class ClassDescription : public Description {
8584
std::source_location location = std::source_location::current())
8685
: Description(location, description), block(block), subject(T(init_list)) {}
8786

88-
ItCD<T>& it(const char* description,
87+
ItCD<T>& it(const char* name,
8988
std::function<void(ItCD<T>&)> block,
9089
std::source_location location = std::source_location::current());
9190
ItCD<T>& it(std::function<void(ItCD<T>&)> block, std::source_location location = std::source_location::current());
@@ -114,7 +113,7 @@ class ClassDescription : public Description {
114113

115114
template <class U, class B>
116115
ClassDescription<U>& context(U&& subject, B block, std::source_location location = std::source_location::current()) {
117-
return this->context("", subject, block, location);
116+
return this->context("", std::forward<U>(subject), block, location);
118117
}
119118
void run() override;
120119

@@ -149,7 +148,7 @@ ClassContext<U>& ClassDescription<T>::context(const char* description,
149148
U&& subject,
150149
B block,
151150
std::source_location location) {
152-
auto* context = this->make_child<ClassContext<U>>(description, subject, block, location);
151+
auto* context = this->make_child<ClassContext<U>>(description, std::forward<U>(subject), block, location);
153152
context->ClassContext<U>::before_eaches = this->before_eaches;
154153
context->ClassContext<U>::after_eaches = this->after_eaches;
155154
context->timed_run();
@@ -186,7 +185,7 @@ ClassContext<T>& Description::context(const char* description, T& subject, B blo
186185

187186
template <Util::not_c_string T, class B>
188187
ClassContext<T>& Description::context(T&& subject, B block, std::source_location location) {
189-
auto* context = this->make_child<ClassContext<T>>(subject, block, location);
188+
auto* context = this->make_child<ClassContext<T>>(std::forward<T>(subject), block, location);
190189
context->before_eaches = this->before_eaches;
191190
context->after_eaches = this->after_eaches;
192191
context->timed_run();
@@ -195,7 +194,7 @@ ClassContext<T>& Description::context(T&& subject, B block, std::source_location
195194

196195
template <class T, class B>
197196
ClassContext<T>& Description::context(const char* description, T&& subject, B block, std::source_location location) {
198-
auto* context = this->make_child<ClassContext<T>>(description, subject, block, location);
197+
auto* context = this->make_child<ClassContext<T>>(description, std::forward<T>(subject), block, location);
199198
context->before_eaches = this->before_eaches;
200199
context->after_eaches = this->after_eaches;
201200
context->timed_run();

include/description.hpp

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,10 @@ class Description : public Runnable {
2323
public:
2424
using Block = std::function<void(Description&)>;
2525

26-
const bool has_subject = false;
27-
std::forward_list<LetBase*> lets{};
28-
std::deque<VoidBlock> after_alls{};
29-
std::deque<VoidBlock> before_eaches{};
30-
std::deque<VoidBlock> after_eaches{};
26+
std::forward_list<LetBase*> lets;
27+
std::deque<VoidBlock> after_alls;
28+
std::deque<VoidBlock> before_eaches;
29+
std::deque<VoidBlock> after_eaches;
3130

3231
private:
3332
Block block;
@@ -43,25 +42,27 @@ class Description : public Runnable {
4342
Description(const char* description,
4443
Block block,
4544
std::source_location location = std::source_location::current()) noexcept
46-
: Runnable(location), block(block), description(description) {
45+
: Runnable(location), block(std::move(block)), description(description) {
4746
this->set_location(location);
4847
}
4948

5049
Description(std::source_location location, std::string&& description) noexcept
5150
: Runnable(location), description(std::move(description)) {}
5251

5352
Description(std::source_location location, const char* description, Block block) noexcept
54-
: Runnable(location), block(block), description(description) {}
53+
: Runnable(location), block(std::move(block)), description(description) {}
5554

5655
/********* Specify/It *********/
5756

58-
ItD& it(const char* description, ItD::Block body, std::source_location location = std::source_location::current());
57+
ItD& it(const char* name, ItD::Block body, std::source_location location = std::source_location::current());
5958
ItD& it(ItD::Block body, std::source_location location = std::source_location::current());
6059

6160
/********* Context ***********/
6261

6362
template <class T = std::nullptr_t>
64-
Description& context(const char* name, Block body, std::source_location location = std::source_location::current());
63+
Description& context(const char* description,
64+
Block body,
65+
std::source_location location = std::source_location::current());
6566

6667
template <Util::not_c_string T, class B>
6768
ClassDescription<T>& context(T& subject, B block, std::source_location location = std::source_location::current());
@@ -119,7 +120,7 @@ using Context = Description;
119120
/*========= Description::it =========*/
120121

121122
inline ItD& Description::it(const char* description, ItD::Block block, std::source_location location) {
122-
auto it = this->make_child<ItD>(location, description, block);
123+
auto* it = this->make_child<ItD>(location, description, block);
123124
it->timed_run();
124125
exec_after_eaches();
125126
exec_before_eaches();
@@ -174,13 +175,15 @@ inline void Description::after_all(VoidBlock b) {
174175
/*----------- private -------------*/
175176

176177
inline void Description::exec_before_eaches() {
177-
for (VoidBlock& b : before_eaches)
178+
for (VoidBlock& b : before_eaches) {
178179
b();
180+
}
179181
}
180182

181183
inline void Description::exec_after_eaches() {
182-
for (VoidBlock& b : after_eaches)
184+
for (VoidBlock& b : after_eaches) {
183185
b();
186+
}
184187
}
185188

186189
/*========= Description::let =========*/
@@ -208,8 +211,9 @@ auto Description::let(T block) -> Let<decltype(block())> {
208211
// TODO: Should this be protected?
209212
inline void Description::reset_lets() noexcept {
210213
// For every let in our list, reset it.
211-
for (auto& let : lets)
214+
for (auto& let : lets) {
212215
let->reset();
216+
}
213217

214218
// Recursively reset all the lets in the family tree
215219
if (this->has_parent()) {
@@ -221,8 +225,9 @@ inline void Description::reset_lets() noexcept {
221225

222226
inline void Description::run() {
223227
block(*this); // Run the block
224-
for (VoidBlock& a : after_alls)
228+
for (VoidBlock& a : after_alls) {
225229
a(); // Run all our after_alls
230+
}
226231
}
227232

228233
/*>>>>>>>>>>>>>>>>>>>> ItD <<<<<<<<<<<<<<<<<<<<<<<<<*/

0 commit comments

Comments
 (0)