xref: /aosp_15_r20/system/libbase/logging_splitters_test.cpp (revision 8f0ba417480079999ba552f1087ae592091b9d02)
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "logging_splitters.h"
18 
19 #include <string>
20 #include <vector>
21 
22 #include <android-base/silent_death_test.h>
23 #include <android-base/strings.h>
24 #include <gtest/gtest.h>
25 
26 using logging_splitters_DeathTest = SilentDeathTest;
27 
28 namespace android {
29 namespace base {
30 
TestNewlineSplitter(const std::string & input,const std::vector<std::string> & expected_output)31 void TestNewlineSplitter(const std::string& input,
32                          const std::vector<std::string>& expected_output) {
33   std::vector<std::string> output;
34   auto logger_function = [&](const char* msg, int length) {
35     if (length == -1) {
36       output.push_back(msg);
37     } else {
38       output.push_back(std::string(msg, length));
39     }
40   };
41   SplitByLines(input.c_str(), logger_function);
42 
43   EXPECT_EQ(expected_output, output);
44 }
45 
TEST(logging_splitters,NewlineSplitter_EmptyString)46 TEST(logging_splitters, NewlineSplitter_EmptyString) {
47   TestNewlineSplitter("", std::vector<std::string>{""});
48 }
49 
TEST(logging_splitters,NewlineSplitter_BasicString)50 TEST(logging_splitters, NewlineSplitter_BasicString) {
51   TestNewlineSplitter("normal string", std::vector<std::string>{"normal string"});
52 }
53 
TEST(logging_splitters,NewlineSplitter_ormalBasicStringTrailingNewline)54 TEST(logging_splitters, NewlineSplitter_ormalBasicStringTrailingNewline) {
55   TestNewlineSplitter("normal string\n", std::vector<std::string>{"normal string", ""});
56 }
57 
TEST(logging_splitters,NewlineSplitter_MultilineTrailing)58 TEST(logging_splitters, NewlineSplitter_MultilineTrailing) {
59   TestNewlineSplitter("normal string\nsecond string\nthirdstring",
60                       std::vector<std::string>{"normal string", "second string", "thirdstring"});
61 }
62 
TEST(logging_splitters,NewlineSplitter_MultilineTrailingNewline)63 TEST(logging_splitters, NewlineSplitter_MultilineTrailingNewline) {
64   TestNewlineSplitter(
65       "normal string\nsecond string\nthirdstring\n",
66       std::vector<std::string>{"normal string", "second string", "thirdstring", ""});
67 }
68 
TEST(logging_splitters,NewlineSplitter_MultilineEmbeddedNewlines)69 TEST(logging_splitters, NewlineSplitter_MultilineEmbeddedNewlines) {
70   TestNewlineSplitter(
71       "normal string\n\n\nsecond string\n\nthirdstring\n",
72       std::vector<std::string>{"normal string", "", "", "second string", "", "thirdstring", ""});
73 }
74 
TestLogdChunkSplitter(const std::string & tag,const std::string & file,const std::string & input,const std::vector<std::string> & expected_output)75 void TestLogdChunkSplitter(const std::string& tag, const std::string& file,
76                            const std::string& input,
77                            const std::vector<std::string>& expected_output) {
78   std::vector<std::string> output;
79   auto logger_function = [&](LogId, LogSeverity, const char*, const char* msg) {
80     output.push_back(msg);
81   };
82 
83   SplitByLogdChunks(MAIN, FATAL, tag.c_str(), file.empty() ? nullptr : file.c_str(), 1000,
84                     input.c_str(), logger_function);
85 
86   auto return_lengths = [&] {
87     std::string sizes;
88     sizes += "expected_output sizes:";
89     for (const auto& string : expected_output) {
90       sizes += " " + std::to_string(string.size());
91     }
92     sizes += "\noutput sizes:";
93     for (const auto& string : output) {
94       sizes += " " + std::to_string(string.size());
95     }
96     return sizes;
97   };
98 
99   EXPECT_EQ(expected_output, output) << return_lengths();
100 }
101 
TEST(logging_splitters,LogdChunkSplitter_EmptyString)102 TEST(logging_splitters, LogdChunkSplitter_EmptyString) {
103   TestLogdChunkSplitter("tag", "", "", std::vector<std::string>{""});
104 }
105 
TEST(logging_splitters,LogdChunkSplitter_BasicString)106 TEST(logging_splitters, LogdChunkSplitter_BasicString) {
107   TestLogdChunkSplitter("tag", "", "normal string", std::vector<std::string>{"normal string"});
108 }
109 
TEST(logging_splitters,LogdChunkSplitter_NormalBasicStringTrailingNewline)110 TEST(logging_splitters, LogdChunkSplitter_NormalBasicStringTrailingNewline) {
111   TestLogdChunkSplitter("tag", "", "normal string\n", std::vector<std::string>{"normal string\n"});
112 }
113 
TEST(logging_splitters,LogdChunkSplitter_MultilineTrailing)114 TEST(logging_splitters, LogdChunkSplitter_MultilineTrailing) {
115   TestLogdChunkSplitter("tag", "", "normal string\nsecond string\nthirdstring",
116                         std::vector<std::string>{"normal string\nsecond string\nthirdstring"});
117 }
118 
TEST(logging_splitters,LogdChunkSplitter_MultilineTrailingNewline)119 TEST(logging_splitters, LogdChunkSplitter_MultilineTrailingNewline) {
120   TestLogdChunkSplitter("tag", "", "normal string\nsecond string\nthirdstring\n",
121                         std::vector<std::string>{"normal string\nsecond string\nthirdstring\n"});
122 }
123 
TEST(logging_splitters,LogdChunkSplitter_MultilineEmbeddedNewlines)124 TEST(logging_splitters, LogdChunkSplitter_MultilineEmbeddedNewlines) {
125   TestLogdChunkSplitter(
126       "tag", "", "normal string\n\n\nsecond string\n\nthirdstring\n",
127       std::vector<std::string>{"normal string\n\n\nsecond string\n\nthirdstring\n"});
128 }
129 
130 // This test should return the same string, the logd logger itself will truncate down to size.
131 // This has historically been the behavior both in libbase and liblog.
TEST(logging_splitters,LogdChunkSplitter_HugeLineNoNewline)132 TEST(logging_splitters, LogdChunkSplitter_HugeLineNoNewline) {
133   auto long_string = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'x');
134   ASSERT_EQ(LOGGER_ENTRY_MAX_PAYLOAD, static_cast<int>(long_string.size()));
135 
136   TestLogdChunkSplitter("tag", "", long_string, std::vector{long_string});
137 }
138 
ReduceToMaxSize(const std::string & tag,const std::string & string)139 std::string ReduceToMaxSize(const std::string& tag, const std::string& string) {
140   return string.substr(0, LOGGER_ENTRY_MAX_PAYLOAD - tag.size() - 35);
141 }
142 
TEST(logging_splitters,LogdChunkSplitter_MultipleHugeLineNoNewline)143 TEST(logging_splitters, LogdChunkSplitter_MultipleHugeLineNoNewline) {
144   auto long_string_x = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'x');
145   auto long_string_y = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'y');
146   auto long_string_z = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'z');
147 
148   auto long_strings = long_string_x + '\n' + long_string_y + '\n' + long_string_z;
149 
150   std::string tag = "tag";
151   std::vector expected = {ReduceToMaxSize(tag, long_string_x), ReduceToMaxSize(tag, long_string_y),
152                           long_string_z};
153 
154   TestLogdChunkSplitter(tag, "", long_strings, expected);
155 }
156 
157 // With a ~4k buffer, we should print 2 long strings per logger call.
TEST(logging_splitters,LogdChunkSplitter_Multiple2kLines)158 TEST(logging_splitters, LogdChunkSplitter_Multiple2kLines) {
159   std::vector expected = {
160       std::string(2000, 'a') + '\n' + std::string(2000, 'b'),
161       std::string(2000, 'c') + '\n' + std::string(2000, 'd'),
162       std::string(2000, 'e') + '\n' + std::string(2000, 'f'),
163   };
164 
165   auto long_strings = Join(expected, '\n');
166 
167   TestLogdChunkSplitter("tag", "", long_strings, expected);
168 }
169 
TEST(logging_splitters,LogdChunkSplitter_ExactSizedLines)170 TEST(logging_splitters, LogdChunkSplitter_ExactSizedLines) {
171   const char* tag = "tag";
172   ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - strlen(tag) - 35;
173   auto long_string_a = std::string(max_size, 'a');
174   auto long_string_b = std::string(max_size, 'b');
175   auto long_string_c = std::string(max_size, 'c');
176 
177   auto long_strings = long_string_a + '\n' + long_string_b + '\n' + long_string_c;
178 
179   TestLogdChunkSplitter(tag, "", long_strings,
180                         std::vector{long_string_a, long_string_b, long_string_c});
181 }
182 
TEST(logging_splitters,LogdChunkSplitter_UnderEqualOver)183 TEST(logging_splitters, LogdChunkSplitter_UnderEqualOver) {
184   std::string tag = "tag";
185   ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - tag.size() - 35;
186 
187   auto first_string_size = 1000;
188   auto first_string = std::string(first_string_size, 'a');
189   auto second_string_size = max_size - first_string_size - 1;
190   auto second_string = std::string(second_string_size, 'b');
191 
192   auto exact_string = std::string(max_size, 'c');
193 
194   auto large_string = std::string(max_size + 50, 'd');
195 
196   auto final_string = std::string("final string!\n\nfinal \n \n final \n");
197 
198   std::vector expected = {first_string + '\n' + second_string, exact_string,
199                           ReduceToMaxSize(tag, large_string), final_string};
200 
201   std::vector input_strings = {first_string + '\n' + second_string, exact_string, large_string,
202                                final_string};
203   auto long_strings = Join(input_strings, '\n');
204 
205   TestLogdChunkSplitter(tag, "", long_strings, expected);
206 }
207 
TEST(logging_splitters,LogdChunkSplitter_WithFile)208 TEST(logging_splitters, LogdChunkSplitter_WithFile) {
209   std::string tag = "tag";
210   std::string file = "/path/to/myfile.cpp";
211   int line = 1000;
212   auto file_header = StringPrintf("%s:%d] ", file.c_str(), line);
213   ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - tag.size() - 35;
214 
215   auto first_string_size = 1000;
216   auto first_string = std::string(first_string_size, 'a');
217   auto second_string_size = max_size - first_string_size - 1 - 2 * file_header.size();
218   auto second_string = std::string(second_string_size, 'b');
219 
220   auto exact_string = std::string(max_size - file_header.size(), 'c');
221 
222   auto large_string = std::string(max_size + 50, 'd');
223 
224   auto final_string = std::string("final string!");
225 
226   std::vector expected = {
227       file_header + first_string + '\n' + file_header + second_string, file_header + exact_string,
228       file_header + ReduceToMaxSize(file_header + tag, large_string), file_header + final_string};
229 
230   std::vector input_strings = {first_string + '\n' + second_string, exact_string, large_string,
231                                final_string};
232   auto long_strings = Join(input_strings, '\n');
233 
234   TestLogdChunkSplitter(tag, file, long_strings, expected);
235 }
236 
237 // We set max_size based off of tag, so if it's too large, the buffer will be sized wrong.
238 // We could recover from this, but it's certainly an error for someone to attempt to use a tag this
239 // large, so we abort instead.
TEST_F(logging_splitters_DeathTest,LogdChunkSplitter_TooLongTag)240 TEST_F(logging_splitters_DeathTest, LogdChunkSplitter_TooLongTag) {
241   auto long_tag = std::string(5000, 'x');
242   auto logger_function = [](LogId, LogSeverity, const char*, const char*) {};
243   ASSERT_DEATH(
244       SplitByLogdChunks(MAIN, ERROR, long_tag.c_str(), nullptr, 0, "message", logger_function), "");
245 }
246 
247 // We do handle excessively large file names correctly however.
TEST(logging_splitters,LogdChunkSplitter_TooLongFile)248 TEST(logging_splitters, LogdChunkSplitter_TooLongFile) {
249   auto long_file = std::string(5000, 'x');
250   std::string tag = "tag";
251 
252   std::vector expected = {ReduceToMaxSize(tag, long_file), ReduceToMaxSize(tag, long_file)};
253 
254   TestLogdChunkSplitter(tag, long_file, "can't see me\nor me", expected);
255 }
256 
TestStderrOutputGenerator(const char * tag,const char * file,int line,const char * message,const std::string & expected)257 void TestStderrOutputGenerator(const char* tag, const char* file, int line, const char* message,
258                                const std::string& expected) {
259   // All log messages will show "01-01 00:00:00.000"...
260   struct timespec ts = {.tv_sec = 0, .tv_nsec = 0};
261   // ...provided we're in the right time zone.
262   putenv(const_cast<char*>("TZ=UTC"));
263   tzset();
264 
265   int pid = 1234;       // All log messages will have 1234 for their PID.
266   uint64_t tid = 4321;  // All log messages will have 4321 for their TID.
267 
268   auto result = StderrOutputGenerator(ts, pid, tid, ERROR, tag, file, line, message);
269   EXPECT_EQ(expected, result);
270 }
271 
TEST(logging_splitters,StderrOutputGenerator_Basic)272 TEST(logging_splitters, StderrOutputGenerator_Basic) {
273   TestStderrOutputGenerator(nullptr, nullptr, 0, "simple message",
274                             "01-01 00:00:00.000  1234  4321 E nullptr : simple message\n");
275   TestStderrOutputGenerator("tag", nullptr, 0, "simple message",
276                             "01-01 00:00:00.000  1234  4321 E tag     : simple message\n");
277   TestStderrOutputGenerator(
278       "tag", "/path/to/some/file", 0, "simple message",
279       "01-01 00:00:00.000  1234  4321 E tag     : /path/to/some/file:0 simple message\n");
280 }
281 
TEST(logging_splitters,StderrOutputGenerator_NewlineTagAndFile)282 TEST(logging_splitters, StderrOutputGenerator_NewlineTagAndFile) {
283   TestStderrOutputGenerator("tag\n\n", nullptr, 0, "simple message",
284                             "01-01 00:00:00.000  1234  4321 E tag\n\n   : simple message\n");
285   TestStderrOutputGenerator(
286       "tag", "/path/to/some/file\n\n", 0, "simple message",
287       "01-01 00:00:00.000  1234  4321 E tag     : /path/to/some/file\n\n:0 simple message\n");
288 }
289 
TEST(logging_splitters,StderrOutputGenerator_TrailingNewLine)290 TEST(logging_splitters, StderrOutputGenerator_TrailingNewLine) {
291   TestStderrOutputGenerator("tag", nullptr, 0, "simple message\n",
292                             "01-01 00:00:00.000  1234  4321 E tag     : simple message\n"
293                             "01-01 00:00:00.000  1234  4321 E tag     : \n");
294 }
295 
TEST(logging_splitters,StderrOutputGenerator_MultiLine)296 TEST(logging_splitters, StderrOutputGenerator_MultiLine) {
297   const char* expected_result =
298       "01-01 00:00:00.000  1234  4321 E tag     : simple message\n"
299       "01-01 00:00:00.000  1234  4321 E tag     : \n"
300       "01-01 00:00:00.000  1234  4321 E tag     : \n"
301       "01-01 00:00:00.000  1234  4321 E tag     : another message \n"
302       "01-01 00:00:00.000  1234  4321 E tag     : \n"
303       "01-01 00:00:00.000  1234  4321 E tag     :  final message \n"
304       "01-01 00:00:00.000  1234  4321 E tag     : \n"
305       "01-01 00:00:00.000  1234  4321 E tag     : \n"
306       "01-01 00:00:00.000  1234  4321 E tag     : \n";
307 
308   TestStderrOutputGenerator("tag", nullptr, 0,
309                             "simple message\n\n\nanother message \n\n final message \n\n\n",
310                             expected_result);
311 }
312 
TEST(logging_splitters,StderrOutputGenerator_MultiLineLong)313 TEST(logging_splitters, StderrOutputGenerator_MultiLineLong) {
314   auto long_string_a = std::string(4000, 'a');
315   auto long_string_b = std::string(4000, 'b');
316 
317   auto message = long_string_a + '\n' + long_string_b;
318   auto expected_result = "01-01 00:00:00.000  1234  4321 E tag     : " + long_string_a + '\n' +
319                          "01-01 00:00:00.000  1234  4321 E tag     : " + long_string_b + '\n';
320   TestStderrOutputGenerator("tag", nullptr, 0, message.c_str(), expected_result);
321 }
322 
323 }  // namespace base
324 }  // namespace android
325