1 // Copyright 2020 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/unit_test_service.h"
16
17 #include "pw_containers/vector.h"
18 #include "pw_log/log.h"
19 #include "pw_protobuf/decoder.h"
20 #include "pw_unit_test/framework.h"
21
22 namespace pw::unit_test {
23
Run(ServerContext &,ConstByteSpan request,RawServerWriter & writer)24 void UnitTestService::Run(ServerContext&,
25 ConstByteSpan request,
26 RawServerWriter& writer) {
27 writer_ = std::move(writer);
28 verbose_ = false;
29
30 // List of test suite names to run. The string views in this vector point to
31 // data in the raw protobuf request message, so it is only valid for the
32 // duration of this function.
33 pw::Vector<std::string_view, 16> suites_to_run;
34
35 protobuf::Decoder decoder(request);
36
37 Status status;
38 while ((status = decoder.Next()).ok()) {
39 switch (static_cast<TestRunRequest::Fields>(decoder.FieldNumber())) {
40 case TestRunRequest::Fields::REPORT_PASSED_EXPECTATIONS:
41 decoder.ReadBool(&verbose_);
42 break;
43
44 case TestRunRequest::Fields::TEST_SUITE: {
45 std::string_view suite_name;
46 if (!decoder.ReadString(&suite_name).ok()) {
47 break;
48 }
49
50 if (!suites_to_run.full()) {
51 suites_to_run.push_back(suite_name);
52 } else {
53 PW_LOG_ERROR("Maximum of %d test suite filters supported",
54 suites_to_run.max_size());
55 writer_.Finish(Status::InvalidArgument());
56 return;
57 }
58
59 break;
60 }
61 }
62 }
63
64 if (status != Status::OutOfRange()) {
65 writer_.Finish(status);
66 return;
67 }
68
69 PW_LOG_INFO("Starting unit test run");
70
71 RegisterEventHandler(&handler_);
72 SetTestSuitesToRun(suites_to_run);
73 PW_LOG_DEBUG("%u test suite filters applied",
74 static_cast<unsigned>(suites_to_run.size()));
75
76 RUN_ALL_TESTS();
77
78 RegisterEventHandler(nullptr);
79 SetTestSuitesToRun({});
80
81 PW_LOG_INFO("Unit test run complete");
82
83 writer_.Finish();
84 }
85
WriteTestRunStart()86 void UnitTestService::WriteTestRunStart() {
87 // Write out the key for the start field (even though the message is empty).
88 WriteEvent([&](Event::Encoder& event) { event.GetTestRunStartEncoder(); });
89 }
90
WriteTestRunEnd(const RunTestsSummary & summary)91 void UnitTestService::WriteTestRunEnd(const RunTestsSummary& summary) {
92 WriteEvent([&](Event::Encoder& event) {
93 TestRunEnd::Encoder test_run_end = event.GetTestRunEndEncoder();
94 test_run_end.WritePassed(summary.passed_tests);
95 test_run_end.WriteFailed(summary.failed_tests);
96 test_run_end.WriteSkipped(summary.skipped_tests);
97 test_run_end.WriteDisabled(summary.disabled_tests);
98 });
99 }
100
WriteTestCaseStart(const TestCase & test_case)101 void UnitTestService::WriteTestCaseStart(const TestCase& test_case) {
102 WriteEvent([&](Event::Encoder& event) {
103 TestCaseDescriptor::Encoder descriptor = event.GetTestCaseStartEncoder();
104 descriptor.WriteSuiteName(test_case.suite_name);
105 descriptor.WriteTestName(test_case.test_name);
106 descriptor.WriteFileName(test_case.file_name);
107 });
108 }
109
WriteTestCaseEnd(TestResult result)110 void UnitTestService::WriteTestCaseEnd(TestResult result) {
111 WriteEvent([&](Event::Encoder& event) {
112 event.WriteTestCaseEnd(static_cast<TestCaseResult>(result));
113 });
114 }
115
WriteTestCaseDisabled(const TestCase & test_case)116 void UnitTestService::WriteTestCaseDisabled(const TestCase& test_case) {
117 WriteEvent([&](Event::Encoder& event) {
118 TestCaseDescriptor::Encoder descriptor = event.GetTestCaseDisabledEncoder();
119 descriptor.WriteSuiteName(test_case.suite_name);
120 descriptor.WriteTestName(test_case.test_name);
121 descriptor.WriteFileName(test_case.file_name);
122 });
123 }
124
WriteTestCaseExpectation(const TestExpectation & expectation)125 void UnitTestService::WriteTestCaseExpectation(
126 const TestExpectation& expectation) {
127 if (!verbose_ && expectation.success) {
128 return;
129 }
130
131 WriteEvent([&](Event::Encoder& event) {
132 TestCaseExpectation::Encoder test_case_expectation =
133 event.GetTestCaseExpectationEncoder();
134 test_case_expectation.WriteExpression(expectation.expression);
135 test_case_expectation.WriteEvaluatedExpression(
136 expectation.evaluated_expression);
137 test_case_expectation.WriteLineNumber(expectation.line_number);
138 test_case_expectation.WriteSuccess(expectation.success);
139 });
140 }
141
142 } // namespace pw::unit_test
143