1 // Copyright 2024 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 #pragma once 15 16 #include <string_view> 17 18 #include "pw_json/builder.h" 19 #include "pw_unit_test/event_handler.h" 20 #include "pw_unit_test/internal/test_record_trie.h" 21 22 namespace pw::unit_test { 23 namespace json_impl { 24 25 inline constexpr const char* kSkipMacroIndicator = "(test skipped)"; 26 27 } // namespace json_impl 28 29 /// Predefined event handler implementation that outputs a test record (or 30 /// summary) in Chromium JSON Test Results Format. To use it, register the event 31 /// handler, call the ``RUN_ALL_TESTS`` macro, then extract the test record json 32 /// as a string using the ``GetTestRecordJsonString`` method. If you only want 33 /// to extract the failing tests, set the `failing_results_only` parameter to 34 /// true. See ``pw::unit_test::EventHandler`` for explanations of emitted 35 /// events. 36 /// @see 37 /// https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/testing/json_test_results_format.md 38 /// @warning This event handler uses dynamic allocation 39 /// (`new`/`delete`/`std::string`) to generate the test record json. 40 class TestRecordEventHandler : public EventHandler { 41 public: 42 /// Constructor for the event handler. Have to seconds_since_epoch since 43 /// calling std::time(nullptr) in pigweed is not supported. 44 /// 45 /// @param[in] seconds_since_epoch Seconds since epoch. Used in the test 46 /// record as the seconds since epoch for the start of the test run. TestRecordEventHandler(int64_t seconds_since_epoch)47 TestRecordEventHandler(int64_t seconds_since_epoch) 48 : seconds_since_epoch_(seconds_since_epoch) {} 49 50 /// Called when a test case completes. Record the test case result in the test 51 /// record trie. 52 /// 53 /// @param[in] test_case Test case that ended. 54 /// 55 /// @param[in] result Result of the test case. TestCaseEnd(const TestCase & test_case,TestResult result)56 void TestCaseEnd(const TestCase& test_case, TestResult result) override { 57 test_record_trie_.AddTestResult(test_case, result); 58 } 59 60 /// Called after all tests are run. Save the run tests summary for later use. 61 /// 62 /// @param[in] summary A test run summary. Contains counts of each test result 63 /// type. RunAllTestsEnd(const RunTestsSummary & summary)64 void RunAllTestsEnd(const RunTestsSummary& summary) override { 65 run_tests_summary_ = summary; 66 } 67 68 /// Called after each expect/assert statement within a test case with the 69 /// result of the expectation. 70 /// 71 /// We usually expect all tests to PASS. However, if the 72 /// GTEST_SKIP macro is used, the test is expected to be skipped and the 73 /// expectation expression is replaced with "(test skipped)" 74 /// 75 /// @param[in] test_case Test case that the expect statement lies in. 76 /// 77 /// @param[in] expectation Current expectation being checked for the test 78 /// case. TestCaseExpect(const TestCase & test_case,const TestExpectation & expectation)79 void TestCaseExpect(const TestCase& test_case, 80 const TestExpectation& expectation) override { 81 // TODO: b/329688428 - Check for test skips directly rather than doing a 82 // string comparison 83 if (std::string_view(json_impl::kSkipMacroIndicator) == 84 expectation.expression) { 85 test_record_trie_.AddTestResultExpectation(test_case, 86 TestResult::kSkipped); 87 } 88 } 89 90 /// Converts the test record trie into a json string and returns it. 91 /// 92 /// @param[in] max_json_buffer_size The max size (in bytes) of the buffer to 93 /// allocate for the json string. 94 /// 95 /// @param[in] failing_results_only If true, the outputted test record will 96 /// only contain the failing tests. 97 /// 98 /// @returns The test record json as a string. 99 std::string GetTestRecordJsonString(size_t max_json_buffer_size, 100 bool failing_results_only = false) { 101 return test_record_trie_.GetTestRecordJsonString(run_tests_summary_, 102 seconds_since_epoch_, 103 max_json_buffer_size, 104 failing_results_only); 105 } 106 RunAllTestsStart()107 void RunAllTestsStart() override {} TestProgramStart(const ProgramSummary &)108 void TestProgramStart(const ProgramSummary&) override {} EnvironmentsSetUpEnd()109 void EnvironmentsSetUpEnd() override {} TestSuiteStart(const TestSuite &)110 void TestSuiteStart(const TestSuite&) override {} TestSuiteEnd(const TestSuite &)111 void TestSuiteEnd(const TestSuite&) override {} EnvironmentsTearDownEnd()112 void EnvironmentsTearDownEnd() override {} TestProgramEnd(const ProgramSummary &)113 void TestProgramEnd(const ProgramSummary&) override {} TestCaseStart(const TestCase &)114 void TestCaseStart(const TestCase&) override {} TestCaseDisabled(const TestCase &)115 void TestCaseDisabled(const TestCase&) override {} 116 117 private: 118 // Seconds since epoch from the start of the test run. 119 int64_t seconds_since_epoch_; 120 121 // A summary of the test run. Set once RunAllTestsEnd is called and used when 122 // the consumer of this event handler wants to generate the test record json 123 // string. 124 RunTestsSummary run_tests_summary_; 125 126 // The entrypoint for interacting with the test record trie. 127 json_impl::TestRecordTrie test_record_trie_; 128 }; 129 130 } // namespace pw::unit_test 131