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 <algorithm>
16 #include <cstring>
17
18 #include "light_public_overrides/pw_unit_test/framework_backend.h"
19 #include "pw_assert/check.h"
20
21 namespace pw::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 // Find the right place in the test list to insert new test case.
44 TestInfo* info = tests_;
45 for (; info->next() != nullptr; info = info->next()) {
46 // Stop if this is the last test case from new test's suite.
47 if (strcmp(info->test_case().suite_name,
48 new_test->test_case().suite_name) == 0 &&
49 strcmp(info->next()->test_case().suite_name,
50 new_test->test_case().suite_name) != 0) {
51 break;
52 }
53 }
54
55 new_test->set_next(info->next());
56 info->set_next(new_test);
57 }
58
RunAllTests()59 int Framework::RunAllTests() {
60 exit_status_ = 0;
61 run_tests_summary_.passed_tests = 0;
62 run_tests_summary_.failed_tests = 0;
63 run_tests_summary_.skipped_tests = 0;
64 run_tests_summary_.disabled_tests = 0;
65
66 if (event_handler_ != nullptr) {
67 event_handler_->RunAllTestsStart();
68 }
69 for (const TestInfo* test = tests_; test != nullptr; test = test->next()) {
70 if (ShouldRunTest(*test)) {
71 test->run();
72 } else if (!test->enabled()) {
73 run_tests_summary_.disabled_tests++;
74
75 if (event_handler_ != nullptr) {
76 event_handler_->TestCaseDisabled(test->test_case());
77 }
78 } else {
79 run_tests_summary_.skipped_tests++;
80 }
81 }
82 if (event_handler_ != nullptr) {
83 event_handler_->RunAllTestsEnd(run_tests_summary_);
84 }
85 return exit_status_;
86 }
87
SetUpTestSuiteIfNeeded(SetUpTestSuiteFunc set_up_ts) const88 void Framework::SetUpTestSuiteIfNeeded(SetUpTestSuiteFunc set_up_ts) const {
89 if (set_up_ts == Test::SetUpTestSuite) {
90 return;
91 }
92
93 for (TestInfo* info = tests_; info != current_test_; info = info->next()) {
94 if (info->test_case().suite_name == current_test_->test_case().suite_name) {
95 return;
96 }
97 }
98
99 set_up_ts();
100 }
101
TearDownTestSuiteIfNeeded(TearDownTestSuiteFunc tear_down_ts) const102 void Framework::TearDownTestSuiteIfNeeded(
103 TearDownTestSuiteFunc tear_down_ts) const {
104 if (tear_down_ts == Test::TearDownTestSuite) {
105 return;
106 }
107
108 for (TestInfo* info = current_test_->next(); info != nullptr;
109 info = info->next()) {
110 if (info->test_case().suite_name == current_test_->test_case().suite_name) {
111 return;
112 }
113 }
114
115 tear_down_ts();
116 }
117
StartTest(const TestInfo & test)118 void Framework::StartTest(const TestInfo& test) {
119 current_test_ = &test;
120 current_result_ = TestResult::kSuccess;
121
122 if (event_handler_ != nullptr) {
123 event_handler_->TestCaseStart(test.test_case());
124 }
125 }
126
EndCurrentTest()127 void Framework::EndCurrentTest() {
128 switch (current_result_) {
129 case TestResult::kSuccess:
130 run_tests_summary_.passed_tests++;
131 break;
132 case TestResult::kFailure:
133 run_tests_summary_.failed_tests++;
134 break;
135 case TestResult::kSkipped:
136 run_tests_summary_.skipped_tests++;
137 break;
138 }
139
140 if (event_handler_ != nullptr) {
141 event_handler_->TestCaseEnd(current_test_->test_case(), current_result_);
142 }
143
144 current_test_ = nullptr;
145 }
146
CurrentTestSkip(int line)147 FailureMessageAdapter Framework::CurrentTestSkip(int line) {
148 if (current_result_ == TestResult::kSuccess) {
149 current_result_ = TestResult::kSkipped;
150 }
151 return CurrentTestExpectSimple(
152 "(test skipped)", "(test skipped)", line, true);
153 }
154
CurrentTestExpectSimple(const char * expression,const char * evaluated_expression,int line,bool success)155 FailureMessageAdapter Framework::CurrentTestExpectSimple(
156 const char* expression,
157 const char* evaluated_expression,
158 int line,
159 bool success) {
160 PW_CHECK_NOTNULL(
161 current_test_,
162 "EXPECT/ASSERT was called when no test was running! EXPECT/ASSERT cannot "
163 "be used from static constructors/destructors or before or after "
164 "RUN_ALL_TESTS().");
165
166 if (!success) {
167 current_result_ = TestResult::kFailure;
168 exit_status_ = 1;
169 }
170
171 if (event_handler_ == nullptr) {
172 return {};
173 }
174
175 TestExpectation expectation = {
176 .expression = expression,
177 .evaluated_expression = evaluated_expression,
178 .line_number = line,
179 .success = success,
180 };
181
182 event_handler_->TestCaseExpect(current_test_->test_case(), expectation);
183 return {};
184 }
185
ShouldRunTest(const TestInfo & test_info) const186 bool Framework::ShouldRunTest(const TestInfo& test_info) const {
187 if (!test_suites_to_run_.empty()) {
188 std::string_view test_suite(test_info.test_case().suite_name);
189
190 bool suite_matches =
191 std::any_of(test_suites_to_run_.begin(),
192 test_suites_to_run_.end(),
193 [&](auto& name) { return test_suite == name; });
194
195 if (!suite_matches) {
196 return false;
197 }
198 }
199
200 return test_info.enabled();
201 }
202
enabled() const203 bool TestInfo::enabled() const {
204 constexpr size_t kStringSize = sizeof("DISABLED_") - 1;
205 return std::strncmp("DISABLED_", test_case().test_name, kStringSize) != 0 &&
206 std::strncmp("DISABLED_", test_case().suite_name, kStringSize) != 0;
207 }
208
209 } // namespace internal
210 } // namespace pw::unit_test
211