1 // Copyright 2022 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_log_rpc_private/test_utils.h"
16
17 #include <cstdint>
18
19 #include "gtest/gtest.h"
20 #include "pw_bytes/span.h"
21 #include "pw_containers/vector.h"
22 #include "pw_log/log.h"
23 #include "pw_log/proto/log.pwpb.h"
24 #include "pw_log_tokenized/metadata.h"
25 #include "pw_protobuf/bytes_utils.h"
26 #include "pw_protobuf/decoder.h"
27
28 namespace pw::log_rpc {
29 namespace {
VerifyOptionallyTokenizedField(protobuf::Decoder & entry_decoder,log::LogEntry::Fields field_number,ConstByteSpan expected_data)30 void VerifyOptionallyTokenizedField(protobuf::Decoder& entry_decoder,
31 log::LogEntry::Fields field_number,
32 ConstByteSpan expected_data) {
33 if (expected_data.empty()) {
34 return;
35 }
36 ConstByteSpan tokenized_data;
37 ASSERT_EQ(entry_decoder.Next(), OkStatus());
38 ASSERT_EQ(entry_decoder.FieldNumber(), static_cast<uint32_t>(field_number));
39 ASSERT_EQ(entry_decoder.ReadBytes(&tokenized_data), OkStatus());
40 std::string_view data_as_string(
41 reinterpret_cast<const char*>(tokenized_data.begin()),
42 tokenized_data.size());
43 std::string_view expected_data_as_string(
44 reinterpret_cast<const char*>(expected_data.begin()),
45 expected_data.size());
46 EXPECT_EQ(data_as_string, expected_data_as_string);
47 }
48 } // namespace
49
50 // Unpacks a `LogEntry` proto buffer to compare it with the expected data and
51 // updates the total drop count found.
VerifyLogEntry(protobuf::Decoder & entry_decoder,const TestLogEntry & expected_entry,uint32_t & drop_count_out)52 void VerifyLogEntry(protobuf::Decoder& entry_decoder,
53 const TestLogEntry& expected_entry,
54 uint32_t& drop_count_out) {
55 VerifyOptionallyTokenizedField(entry_decoder,
56 log::LogEntry::Fields::MESSAGE,
57 expected_entry.tokenized_data);
58 if (expected_entry.metadata.level()) {
59 ASSERT_EQ(entry_decoder.Next(), OkStatus());
60 ASSERT_EQ(entry_decoder.FieldNumber(),
61 static_cast<uint32_t>(log::LogEntry::Fields::LINE_LEVEL));
62 uint32_t line_level;
63 ASSERT_TRUE(entry_decoder.ReadUint32(&line_level).ok());
64 EXPECT_EQ(expected_entry.metadata.level(),
65 line_level & PW_LOG_LEVEL_BITMASK);
66 EXPECT_EQ(expected_entry.metadata.line_number(),
67 (line_level & ~PW_LOG_LEVEL_BITMASK) >> PW_LOG_LEVEL_BITS);
68 }
69 if (expected_entry.metadata.flags()) {
70 ASSERT_EQ(entry_decoder.Next(), OkStatus());
71 ASSERT_EQ(entry_decoder.FieldNumber(),
72 static_cast<uint32_t>(log::LogEntry::Fields::FLAGS));
73 uint32_t flags;
74 ASSERT_TRUE(entry_decoder.ReadUint32(&flags).ok());
75 EXPECT_EQ(expected_entry.metadata.flags(), flags);
76 }
77 if (expected_entry.timestamp) {
78 ASSERT_EQ(entry_decoder.Next(), OkStatus());
79 ASSERT_TRUE(entry_decoder.FieldNumber() ==
80 static_cast<uint32_t>(log::LogEntry::Fields::TIMESTAMP) ||
81 entry_decoder.FieldNumber() ==
82 static_cast<uint32_t>(
83 log::LogEntry::Fields::TIME_SINCE_LAST_ENTRY));
84 int64_t timestamp;
85 ASSERT_TRUE(entry_decoder.ReadInt64(×tamp).ok());
86 EXPECT_EQ(expected_entry.timestamp, timestamp);
87 }
88 if (expected_entry.dropped) {
89 ASSERT_EQ(entry_decoder.Next(), OkStatus());
90 ASSERT_EQ(entry_decoder.FieldNumber(),
91 static_cast<uint32_t>(log::LogEntry::Fields::DROPPED));
92 uint32_t dropped = 0;
93 ASSERT_TRUE(entry_decoder.ReadUint32(&dropped).ok());
94 EXPECT_EQ(expected_entry.dropped, dropped);
95 drop_count_out += dropped;
96 }
97 if (expected_entry.metadata.module()) {
98 ASSERT_EQ(entry_decoder.Next(), OkStatus());
99 ASSERT_EQ(entry_decoder.FieldNumber(),
100 static_cast<uint32_t>(log::LogEntry::Fields::MODULE));
101 const Result<uint32_t> module =
102 protobuf::DecodeBytesToUint32(entry_decoder);
103 ASSERT_EQ(module.status(), OkStatus());
104 EXPECT_EQ(expected_entry.metadata.module(), module.value());
105 }
106 VerifyOptionallyTokenizedField(
107 entry_decoder, log::LogEntry::Fields::FILE, expected_entry.file);
108 VerifyOptionallyTokenizedField(
109 entry_decoder, log::LogEntry::Fields::THREAD, expected_entry.thread);
110 }
111
112 // Compares an encoded LogEntry's fields against the expected sequence ID and
113 // LogEntries, and updates the total entry and drop counts. Starts comparing at
114 // `expected_entries[entries_count_out]`. `expected_entries` must be in the same
115 // order that messages were added to the MultiSink.
VerifyLogEntries(protobuf::Decoder & entries_decoder,const Vector<TestLogEntry> & expected_entries,uint32_t expected_first_entry_sequence_id,size_t & entries_count_out,uint32_t & drop_count_out)116 void VerifyLogEntries(protobuf::Decoder& entries_decoder,
117 const Vector<TestLogEntry>& expected_entries,
118 uint32_t expected_first_entry_sequence_id,
119 size_t& entries_count_out,
120 uint32_t& drop_count_out) {
121 size_t entry_index = entries_count_out;
122 while (entries_decoder.Next().ok()) {
123 if (static_cast<pw::log::LogEntries::Fields>(
124 entries_decoder.FieldNumber()) ==
125 log::LogEntries::Fields::ENTRIES) {
126 ConstByteSpan entry;
127 EXPECT_EQ(entries_decoder.ReadBytes(&entry), OkStatus());
128 protobuf::Decoder entry_decoder(entry);
129 if (expected_entries.empty()) {
130 break;
131 }
132
133 ASSERT_LT(entry_index, expected_entries.size());
134
135 // Keep track of entries and drops respective counts.
136 uint32_t current_drop_count = 0;
137 VerifyLogEntry(
138 entry_decoder, expected_entries[entry_index], current_drop_count);
139 ++entry_index;
140 drop_count_out += current_drop_count;
141 if (current_drop_count == 0) {
142 ++entries_count_out;
143 }
144 } else if (static_cast<pw::log::LogEntries::Fields>(
145 entries_decoder.FieldNumber()) ==
146 log::LogEntries::Fields::FIRST_ENTRY_SEQUENCE_ID) {
147 uint32_t first_entry_sequence_id = 0;
148 EXPECT_EQ(entries_decoder.ReadUint32(&first_entry_sequence_id),
149 OkStatus());
150 EXPECT_EQ(expected_first_entry_sequence_id, first_entry_sequence_id);
151 }
152 }
153 }
154
CountLogEntries(protobuf::Decoder & entries_decoder)155 size_t CountLogEntries(protobuf::Decoder& entries_decoder) {
156 size_t entries_found = 0;
157 while (entries_decoder.Next().ok()) {
158 if (static_cast<pw::log::LogEntries::Fields>(
159 entries_decoder.FieldNumber()) ==
160 log::LogEntries::Fields::ENTRIES) {
161 ++entries_found;
162 }
163 }
164 return entries_found;
165 }
166
167 } // namespace pw::log_rpc
168