1 // Copyright 2019 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_unit_test/framework.h"
16
17 #include <algorithm>
18 #include <cstring>
19
20 namespace pw {
21 namespace unit_test {
22
RegisterEventHandler(EventHandler * event_handler)23 void RegisterEventHandler(EventHandler* event_handler) {
24 internal::Framework::Get().RegisterEventHandler(event_handler);
25 }
26
27 namespace internal {
28
29 // Singleton instance of the unit test framework class.
30 Framework Framework::framework_;
31
32 // Linked list of all test cases in the test executable. This is static as it is
33 // populated using static initialization.
34 TestInfo* Framework::tests_ = nullptr;
35
RegisterTest(TestInfo * new_test) const36 void Framework::RegisterTest(TestInfo* new_test) const {
37 // If the test list is empty, set new_test as the first test.
38 if (tests_ == nullptr) {
39 tests_ = new_test;
40 return;
41 }
42
43 // Append the test case to the end of the test list.
44 TestInfo* info = tests_;
45 for (; info->next() != nullptr; info = info->next()) {
46 }
47 info->set_next(new_test);
48 }
49
RunAllTests()50 int Framework::RunAllTests() {
51 run_tests_summary_.passed_tests = 0;
52 run_tests_summary_.failed_tests = 0;
53 run_tests_summary_.skipped_tests = 0;
54 run_tests_summary_.disabled_tests = 0;
55
56 if (event_handler_ != nullptr) {
57 event_handler_->RunAllTestsStart();
58 }
59 for (const TestInfo* test = tests_; test != nullptr; test = test->next()) {
60 if (ShouldRunTest(*test)) {
61 test->run();
62 } else if (!test->enabled()) {
63 run_tests_summary_.disabled_tests++;
64
65 if (event_handler_ != nullptr) {
66 event_handler_->TestCaseDisabled(test->test_case());
67 }
68 } else {
69 run_tests_summary_.skipped_tests++;
70 }
71 }
72 if (event_handler_ != nullptr) {
73 event_handler_->RunAllTestsEnd(run_tests_summary_);
74 }
75 return exit_status_;
76 }
77
StartTest(const TestInfo & test)78 void Framework::StartTest(const TestInfo& test) {
79 current_test_ = &test;
80 current_result_ = TestResult::kSuccess;
81
82 if (event_handler_ != nullptr) {
83 event_handler_->TestCaseStart(test.test_case());
84 }
85 }
86
EndCurrentTest()87 void Framework::EndCurrentTest() {
88 switch (current_result_) {
89 case TestResult::kSuccess:
90 run_tests_summary_.passed_tests++;
91 break;
92 case TestResult::kFailure:
93 run_tests_summary_.failed_tests++;
94 break;
95 case TestResult::kSkipped:
96 run_tests_summary_.skipped_tests++;
97 break;
98 }
99
100 if (event_handler_ != nullptr) {
101 event_handler_->TestCaseEnd(current_test_->test_case(), current_result_);
102 }
103
104 current_test_ = nullptr;
105 }
106
CurrentTestSkip(int line)107 void Framework::CurrentTestSkip(int line) {
108 if (current_result_ == TestResult::kSuccess) {
109 current_result_ = TestResult::kSkipped;
110 }
111 return CurrentTestExpectSimple(
112 "(test skipped)", "(test skipped)", line, true);
113 }
114
CurrentTestExpectSimple(const char * expression,const char * evaluated_expression,int line,bool success)115 void Framework::CurrentTestExpectSimple(const char* expression,
116 const char* evaluated_expression,
117 int line,
118 bool success) {
119 if (!success) {
120 current_result_ = TestResult::kFailure;
121 exit_status_ = 1;
122 }
123
124 if (event_handler_ == nullptr) {
125 return;
126 }
127
128 TestExpectation expectation = {
129 .expression = expression,
130 .evaluated_expression = evaluated_expression,
131 .line_number = line,
132 .success = success,
133 };
134
135 event_handler_->TestCaseExpect(current_test_->test_case(), expectation);
136 }
137
ShouldRunTest(const TestInfo & test_info) const138 bool Framework::ShouldRunTest(const TestInfo& test_info) const {
139 #if PW_CXX_STANDARD_IS_SUPPORTED(17)
140 // Test suite filtering is only supported if using C++17.
141 if (!test_suites_to_run_.empty()) {
142 std::string_view test_suite(test_info.test_case().suite_name);
143
144 bool suite_matches =
145 std::any_of(test_suites_to_run_.begin(),
146 test_suites_to_run_.end(),
147 [&](auto& name) { return test_suite == name; });
148
149 if (!suite_matches) {
150 return false;
151 }
152 }
153 #endif // PW_CXX_STANDARD_IS_SUPPORTED(17)
154
155 return test_info.enabled();
156 }
157
enabled() const158 bool TestInfo::enabled() const {
159 constexpr size_t kStringSize = sizeof("DISABLED_") - 1;
160 return std::strncmp("DISABLED_", test_case().test_name, kStringSize) != 0 &&
161 std::strncmp("DISABLED_", test_case().suite_name, kStringSize) != 0;
162 }
163
164 } // namespace internal
165 } // namespace unit_test
166 } // namespace pw
167