xref: /aosp_15_r20/external/pigweed/pw_unit_test/framework_light.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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