1 /*
2 * Copyright (c) Facebook, Inc. and its affiliates.
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 <folly/logging/xlog.h>
18
19 #include <chrono>
20 #include <thread>
21
22 #include <folly/logging/LogConfigParser.h>
23 #include <folly/logging/LogHandler.h>
24 #include <folly/logging/LogMessage.h>
25 #include <folly/logging/LoggerDB.h>
26 #include <folly/logging/test/TestLogHandler.h>
27 #include <folly/logging/test/XlogHeader1.h>
28 #include <folly/logging/test/XlogHeader2.h>
29 #include <folly/portability/Constexpr.h>
30 #include <folly/portability/GMock.h>
31 #include <folly/portability/GTest.h>
32 #include <folly/test/TestUtils.h>
33
34 using namespace folly;
35 using std::make_shared;
36 using testing::ElementsAre;
37 using testing::ElementsAreArray;
38 using namespace std::chrono_literals;
39
40 XLOG_SET_CATEGORY_NAME("xlog_test.main_file")
41
42 namespace {
43 class XlogTest : public testing::Test {
44 public:
XlogTest()45 XlogTest() {
46 // Note that the XLOG* macros always use the main LoggerDB singleton.
47 // There is no way to get them to use a test LoggerDB during unit tests.
48 //
49 // In order to ensure that changes to the LoggerDB singleton do not persist
50 // across test functions we reset the configuration to a fixed state before
51 // each test starts.
52 auto config =
53 parseLogConfig(".=WARN:default; default=stream:stream=stderr");
54 LoggerDB::get().resetConfig(config);
55 }
56 };
57 } // namespace
58
TEST_F(XlogTest,xlogName)59 TEST_F(XlogTest, xlogName) {
60 EXPECT_EQ("xlog_test.main_file", XLOG_GET_CATEGORY_NAME());
61 EXPECT_EQ("xlog_test.main_file", XLOG_GET_CATEGORY()->getName());
62 }
63
TEST_F(XlogTest,xlogIf)64 TEST_F(XlogTest, xlogIf) {
65 auto handler = make_shared<TestLogHandler>();
66 LoggerDB::get().getCategory("xlog_test")->addHandler(handler);
67 auto& messages = handler->getMessages();
68
69 // info messages are not enabled initially.
70 EXPECT_FALSE(XLOG_IS_ON(INFO));
71 EXPECT_TRUE(XLOG_IS_ON(ERR));
72 XLOG_IF(INFO, false, "testing 1");
73 EXPECT_EQ(0, messages.size());
74 messages.clear();
75
76 XLOG_IF(INFO, true, "testing 1");
77 EXPECT_EQ(0, messages.size());
78 messages.clear();
79
80 // Increase the log level, then log a message.
81 LoggerDB::get().setLevel("xlog_test.main_file", LogLevel::DBG1);
82 XLOG_IF(DBG1, false, "testing: ", 1, 2, 3);
83 ASSERT_EQ(0, messages.size());
84 messages.clear();
85
86 XLOG_IF(DBG1, true, "testing: ", 1, 2, 3);
87 ASSERT_EQ(1, messages.size());
88 messages.clear();
89
90 // more complex conditional expressions
91 std::array<bool, 2> conds = {{false, true}};
92 for (unsigned i = 0; i < conds.size(); i++) {
93 for (unsigned j = 0; j < conds.size(); j++) {
94 XLOG_IF(DBG1, conds[i] && conds[j], "testing conditional");
95 EXPECT_EQ((conds[i] && conds[j]) ? 1 : 0, messages.size());
96 messages.clear();
97
98 XLOG_IF(DBG1, conds[i] || conds[j], "testing conditional");
99 EXPECT_EQ((conds[i] || conds[j]) ? 1 : 0, messages.size());
100 messages.clear();
101 }
102 }
103
104 XLOG_IF(DBG1, 0x6 & 0x2, "More conditional 1");
105 EXPECT_EQ(1, messages.size());
106 messages.clear();
107
108 XLOG_IF(DBG1, 0x6 | 0x2, "More conditional 2");
109 EXPECT_EQ(1, messages.size());
110 messages.clear();
111
112 XLOG_IF(DBG1, 0x6 | 0x2 ? true : false, "More conditional 3");
113 EXPECT_EQ(1, messages.size());
114 messages.clear();
115
116 XLOG_IF(DBG1, 0x6 | 0x2 ? true : false, "More conditional 3");
117 EXPECT_EQ(1, messages.size());
118 messages.clear();
119
120 XLOG_IF(DBG1, 0x3 & 0x4 ? true : false, "More conditional 4");
121 EXPECT_EQ(0, messages.size());
122 messages.clear();
123
124 XLOG_IF(DBG1, false ? true : false, "More conditional 5");
125 EXPECT_EQ(0, messages.size());
126 messages.clear();
127
128 XLOGF_IF(DBG1, false, "number: {:>3d}; string: {}", 12, "foo");
129 ASSERT_EQ(0, messages.size());
130 messages.clear();
131 XLOGF_IF(DBG1, true, "number: {:>3d}; string: {}", 12, "foo");
132 ASSERT_EQ(1, messages.size());
133 messages.clear();
134 }
135
TEST_F(XlogTest,xlog)136 TEST_F(XlogTest, xlog) {
137 auto handler = make_shared<TestLogHandler>();
138 LoggerDB::get().getCategory("xlog_test")->addHandler(handler);
139 auto& messages = handler->getMessages();
140
141 // info messages are not enabled initially.
142 EXPECT_FALSE(XLOG_IS_ON(INFO));
143 EXPECT_TRUE(XLOG_IS_ON(ERR));
144 XLOG(INFO, "testing 1");
145 EXPECT_EQ(0, messages.size());
146 messages.clear();
147
148 // Increase the log level, then log a message.
149 LoggerDB::get().setLevel("xlog_test.main_file", LogLevel::DBG1);
150
151 XLOG(DBG1, "testing: ", 1, 2, 3);
152 ASSERT_EQ(1, messages.size());
153 EXPECT_EQ("testing: 123", messages[0].first.getMessage());
154 EXPECT_TRUE(messages[0].first.getFileName().endsWith("XlogTest.cpp"))
155 << "unexpected file name: " << messages[0].first.getFileName();
156 EXPECT_EQ(LogLevel::DBG1, messages[0].first.getLevel());
157 EXPECT_EQ("xlog_test.main_file", messages[0].first.getCategory()->getName());
158 EXPECT_EQ("xlog_test", messages[0].second->getName());
159 messages.clear();
160
161 XLOGF(WARN, "number: {:>3d}; string: {}", 12, "foo");
162 ASSERT_EQ(1, messages.size());
163 EXPECT_EQ("number: 12; string: foo", messages[0].first.getMessage());
164 EXPECT_TRUE(messages[0].first.getFileName().endsWith("XlogTest.cpp"))
165 << "unexpected file name: " << messages[0].first.getFileName();
166 EXPECT_EQ(LogLevel::WARN, messages[0].first.getLevel());
167 EXPECT_EQ("xlog_test.main_file", messages[0].first.getCategory()->getName());
168 EXPECT_EQ("xlog_test", messages[0].second->getName());
169 messages.clear();
170
171 XLOG(DBG2, "this log check should not pass");
172 EXPECT_EQ(0, messages.size());
173 messages.clear();
174
175 // Test stream arguments to XLOG()
176 XLOG(INFO) << "stream test: " << 1 << ", two, " << 3;
177 ASSERT_EQ(1, messages.size());
178 EXPECT_EQ("stream test: 1, two, 3", messages[0].first.getMessage());
179 EXPECT_TRUE(messages[0].first.getFileName().endsWith("XlogTest.cpp"))
180 << "unexpected file name: " << messages[0].first.getFileName();
181 EXPECT_EQ(LogLevel::INFO, messages[0].first.getLevel());
182 EXPECT_EQ("xlog_test.main_file", messages[0].first.getCategory()->getName());
183 EXPECT_EQ("xlog_test", messages[0].second->getName());
184 messages.clear();
185 }
186
TEST_F(XlogTest,perFileCategoryHandling)187 TEST_F(XlogTest, perFileCategoryHandling) {
188 using namespace logging_test;
189
190 auto handler = make_shared<TestLogHandler>();
191 LoggerDB::get().getCategory("folly.logging.test")->addHandler(handler);
192 LoggerDB::get().setLevel("folly.logging.test", LogLevel::DBG9);
193 auto& messages = handler->getMessages();
194
195 // Use the simple helper function in XlogHeader2
196 testXlogHdrFunction("factor", 99);
197 ASSERT_EQ(1, messages.size());
198 EXPECT_EQ("test: factor=99", messages[0].first.getMessage());
199 EXPECT_TRUE(messages[0].first.getFileName().endsWith("XlogHeader2.h"))
200 << "unexpected file name: " << messages[0].first.getFileName();
201 EXPECT_EQ(LogLevel::DBG3, messages[0].first.getLevel());
202 EXPECT_EQ(
203 "folly.logging.test.XlogHeader2.h",
204 messages[0].first.getCategory()->getName());
205 EXPECT_EQ("folly.logging.test", messages[0].second->getName());
206 messages.clear();
207
208 // Test the loop function from XlogHeader1
209 testXlogHdrLoop(3, "hello world");
210 ASSERT_EQ(5, messages.size());
211 EXPECT_EQ("starting: hello world", messages[0].first.getMessage());
212 EXPECT_TRUE(messages[0].first.getFileName().endsWith("XlogHeader1.h"))
213 << "unexpected file name: " << messages[0].first.getFileName();
214 EXPECT_EQ(LogLevel::DBG1, messages[0].first.getLevel());
215 EXPECT_EQ(
216 "folly.logging.test.XlogHeader1.h",
217 messages[0].first.getCategory()->getName());
218 EXPECT_EQ("folly.logging.test", messages[0].second->getName());
219
220 EXPECT_EQ("test: hello world", messages[1].first.getMessage());
221 EXPECT_EQ("test: hello world", messages[2].first.getMessage());
222 EXPECT_EQ("test: hello world", messages[3].first.getMessage());
223 EXPECT_EQ("finished: hello world", messages[4].first.getMessage());
224 EXPECT_EQ(LogLevel::DBG5, messages[1].first.getLevel());
225 EXPECT_EQ(LogLevel::DBG5, messages[2].first.getLevel());
226 EXPECT_EQ(LogLevel::DBG5, messages[3].first.getLevel());
227 EXPECT_EQ(LogLevel::DBG1, messages[4].first.getLevel());
228 messages.clear();
229
230 // Reduce the log level so that the messages inside the loop
231 // should not be logged.
232 LoggerDB::get().setLevel("folly.logging.test", LogLevel::DBG2);
233 testXlogHdrLoop(300, "hello world");
234 ASSERT_EQ(2, messages.size());
235 EXPECT_EQ("starting: hello world", messages[0].first.getMessage());
236 EXPECT_EQ("finished: hello world", messages[1].first.getMessage());
237 messages.clear();
238
239 // Call the helpers function in XlogFile1.cpp and XlogFile2.cpp and makes
240 // sure their categories are reported correctly.
241 testXlogFile1Dbg1("foobar 1234");
242 ASSERT_EQ(1, messages.size());
243 EXPECT_EQ("file1: foobar 1234", messages[0].first.getMessage());
244 EXPECT_EQ(
245 "folly.logging.test.XlogFile1.cpp",
246 messages[0].first.getCategory()->getName());
247 messages.clear();
248
249 testXlogFile2Dbg1("hello world");
250 ASSERT_EQ(1, messages.size());
251 EXPECT_EQ("file2: hello world", messages[0].first.getMessage());
252 EXPECT_EQ(
253 "folly.logging.test.XlogFile2.cpp",
254 messages[0].first.getCategory()->getName());
255 messages.clear();
256
257 // Adjust the log level and make sure the changes take effect for the .cpp
258 // file categories
259 LoggerDB::get().setLevel("folly.logging.test", LogLevel::INFO);
260 testXlogFile1Dbg1("log check should fail now");
261 testXlogFile2Dbg1("this should fail too");
262 EXPECT_EQ(0, messages.size());
263 messages.clear();
264
265 LoggerDB::get().setLevel("folly.logging.test.XlogFile1", LogLevel::DBG1);
266 testXlogFile1Dbg1("this log check should pass now");
267 testXlogFile2Dbg1("but this one should still fail");
268 ASSERT_EQ(1, messages.size());
269 EXPECT_EQ(
270 "file1: this log check should pass now", messages[0].first.getMessage());
271 EXPECT_EQ(
272 "folly.logging.test.XlogFile1.cpp",
273 messages[0].first.getCategory()->getName());
274 messages.clear();
275 }
276
TEST_F(XlogTest,rateLimiting)277 TEST_F(XlogTest, rateLimiting) {
278 auto SEVEN = 7;
279 auto handler = make_shared<TestLogHandler>();
280 LoggerDB::get().getCategory("xlog_test")->addHandler(handler);
281 LoggerDB::get().setLevel("xlog_test", LogLevel::DBG1);
282
283 // Test XLOG_EVERY_N
284 for (size_t n = 0; n < 50; ++n) {
285 XLOG_EVERY_N(DBG1, 7, "msg ", n);
286 }
287 EXPECT_THAT(
288 handler->getMessageValues(),
289 ElementsAre(
290 "msg 0",
291 "msg 7",
292 "msg 14",
293 "msg 21",
294 "msg 28",
295 "msg 35",
296 "msg 42",
297 "msg 49"));
298 handler->clearMessages();
299
300 for (size_t n = 0; n < 50; ++n) {
301 XLOG_EVERY_N(DBG1, SEVEN + 1, "msg ", n);
302 }
303 EXPECT_THAT(
304 handler->getMessageValues(),
305 ElementsAre(
306 "msg 0", "msg 8", "msg 16", "msg 24", "msg 32", "msg 40", "msg 48"));
307 handler->clearMessages();
308
309 // Test XLOG_EVERY_N_IF
310 for (size_t n = 0; n < 50; ++n) {
311 bool shouldLog = n % 2 == 0;
312 XLOG_EVERY_N_IF(DBG1, shouldLog, 7, "msg ", n);
313 }
314 EXPECT_THAT(
315 handler->getMessageValues(),
316 ElementsAre("msg 0", "msg 14", "msg 28", "msg 42"));
317 handler->clearMessages();
318
319 for (size_t n = 0; n < 50; ++n) {
320 XLOG_EVERY_N(DBG1, SEVEN + 1, "msg ", n);
321 }
322 EXPECT_THAT(
323 handler->getMessageValues(),
324 ElementsAre(
325 "msg 0", "msg 8", "msg 16", "msg 24", "msg 32", "msg 40", "msg 48"));
326 handler->clearMessages();
327
328 // Test XLOG_EVERY_N_EXACT
329 for (size_t n = 0; n < 50; ++n) {
330 XLOG_EVERY_N_EXACT(DBG1, 7, "msg ", n);
331 }
332 EXPECT_THAT(
333 handler->getMessageValues(),
334 ElementsAre(
335 "msg 0",
336 "msg 7",
337 "msg 14",
338 "msg 21",
339 "msg 28",
340 "msg 35",
341 "msg 42",
342 "msg 49"));
343 handler->clearMessages();
344
345 for (size_t n = 0; n < 50; ++n) {
346 XLOG_EVERY_N_EXACT(DBG1, SEVEN + 1, "msg ", n);
347 }
348 EXPECT_THAT(
349 handler->getMessageValues(),
350 ElementsAre(
351 "msg 0", "msg 8", "msg 16", "msg 24", "msg 32", "msg 40", "msg 48"));
352 handler->clearMessages();
353
354 // Test XLOG_EVERY_N_THREAD
355 for (size_t n = 0; n < 50; ++n) {
356 XLOG_EVERY_N_THREAD(DBG1, 7, "msg ", n);
357 }
358 EXPECT_THAT(
359 handler->getMessageValues(),
360 ElementsAre(
361 "msg 0",
362 "msg 7",
363 "msg 14",
364 "msg 21",
365 "msg 28",
366 "msg 35",
367 "msg 42",
368 "msg 49"));
369 handler->clearMessages();
370
371 for (size_t n = 0; n < 50; ++n) {
372 XLOG_EVERY_N_THREAD(DBG1, SEVEN + 1, "msg ", n);
373 }
374 EXPECT_THAT(
375 handler->getMessageValues(),
376 ElementsAre(
377 "msg 0", "msg 8", "msg 16", "msg 24", "msg 32", "msg 40", "msg 48"));
378 handler->clearMessages();
379
380 // Test XLOG_EVERY_MS, XLOGF_EVERY_MS and XLOG_N_PER_MS
381 // We test these together to minimize the number of sleep operations.
382 for (size_t n = 0; n < 10; ++n) {
383 // Integer arguments are treated as millisecond
384 XLOG_EVERY_MS(DBG1, 100, "int arg ", n);
385 // Other duration arguments also work, as long as they are
386 // coarser than milliseconds
387 XLOG_EVERY_MS(DBG1, 100ms, "ms arg ", n);
388 XLOG_EVERY_MS(DBG1, 1s, "s arg ", n);
389 auto t = 1s;
390 XLOG_EVERY_MS(DBG1, t, "s arg capture ", n);
391
392 // Use XLOGF_EVERY_MS
393 XLOGF_EVERY_MS(DBG1, 100, "fmt arg {}", n);
394 XLOGF_EVERY_MS(DBG1, 100ms, "fmt ms arg {}", n);
395
396 // Use XLOG_N_PER_MS() too
397 XLOG_N_PER_MS(DBG1, 2, 100, "2x int arg ", n);
398 XLOG_N_PER_MS(DBG1, 1, 100ms, "1x ms arg ", n);
399 XLOG_N_PER_MS(DBG1, 3, 1s, "3x s arg ", n);
400
401 // Conditional logging
402 bool shouldLog = n && (n % 2 == 0);
403 XLOG_EVERY_MS_IF(DBG1, shouldLog, 100, "int arg conditional ", n);
404
405 // Sleep for 100ms between iterations 5 and 6
406 if (n == 5) {
407 /* sleep override */ std::this_thread::sleep_for(110ms);
408 }
409 }
410 EXPECT_THAT(
411 handler->getMessageValues(),
412 ElementsAreArray({
413 "int arg 0",
414 "ms arg 0",
415 "s arg 0",
416 "s arg capture 0",
417 "fmt arg 0",
418 "fmt ms arg 0",
419 "2x int arg 0",
420 "1x ms arg 0",
421 "3x s arg 0",
422 "2x int arg 1",
423 "3x s arg 1",
424 "3x s arg 2",
425 "int arg conditional 2",
426 "int arg 6",
427 "ms arg 6",
428 "fmt arg 6",
429 "fmt ms arg 6",
430 "2x int arg 6",
431 "1x ms arg 6",
432 "int arg conditional 6",
433 "2x int arg 7",
434 }));
435 handler->clearMessages();
436
437 // Test XLOG_FIRST_N
438 for (size_t n = 0; n < 10; ++n) {
439 XLOG_FIRST_N(DBG1, 4, "bah ", n);
440 }
441 EXPECT_THAT(
442 handler->getMessageValues(),
443 ElementsAreArray({
444 "bah 0",
445 "bah 1",
446 "bah 2",
447 "bah 3",
448 }));
449 handler->clearMessages();
450 }
451
TEST_F(XlogTest,rateLimitingEndOfThread)452 TEST_F(XlogTest, rateLimitingEndOfThread) {
453 auto handler = make_shared<TestLogHandler>();
454 LoggerDB::get().getCategory("xlog_test")->addHandler(handler);
455 LoggerDB::get().setLevel("xlog_test", LogLevel::DBG1);
456
457 auto th = std::thread([&] {
458 auto enqueue = [](int num) {
459 pthread_key_t key;
460 pthread_key_create(&key, [](void* obj) {
461 auto* i = static_cast<int*>(obj);
462 XLOG_EVERY_N_THREAD(DBG1, 1, "dtor ", *i);
463 delete i;
464 });
465 pthread_setspecific(key, new int(num));
466 };
467
468 enqueue(100);
469 enqueue(101);
470 for (size_t n = 0; n < 50; ++n) {
471 XLOG_EVERY_N_THREAD(DBG1, 7, "msg ", n);
472 }
473 enqueue(102);
474 enqueue(103);
475 });
476 th.join();
477 EXPECT_THAT(
478 handler->getMessageValues(),
479 ElementsAreArray({
480 "msg 0",
481 "msg 7",
482 "msg 14",
483 "msg 21",
484 "msg 28",
485 "msg 35",
486 "msg 42",
487 "msg 49",
488 "dtor 100",
489 "dtor 101",
490 "dtor 102",
491 "dtor 103",
492 }));
493 handler->clearMessages();
494 }
495
TEST_F(XlogTest,getXlogCategoryName)496 TEST_F(XlogTest, getXlogCategoryName) {
497 EXPECT_EQ("foo.cpp", getXlogCategoryNameForFile("foo.cpp"));
498 EXPECT_EQ("foo.h", getXlogCategoryNameForFile("foo.h"));
499
500 // Directory separators should be translated to "." during LogName
501 // canonicalization
502 EXPECT_EQ("src/test/foo.cpp", getXlogCategoryNameForFile("src/test/foo.cpp"));
503 EXPECT_EQ(
504 "src.test.foo.cpp",
505 LogName::canonicalize(getXlogCategoryNameForFile("src/test/foo.cpp")));
506 EXPECT_EQ("src/test/foo.h", getXlogCategoryNameForFile("src/test/foo.h"));
507 EXPECT_EQ(
508 "src.test.foo.h",
509 LogName::canonicalize(getXlogCategoryNameForFile("src/test/foo.h")));
510
511 // Buck's directory prefixes for generated source files
512 // should be stripped out
513 EXPECT_EQ(
514 "myproject.generated_header.h",
515 LogName::canonicalize(getXlogCategoryNameForFile(
516 "buck-out/gen/myproject#headers/myproject/generated_header.h")));
517 EXPECT_EQ(
518 "foo.bar.test.h",
519 LogName::canonicalize(getXlogCategoryNameForFile(
520 "buck-out/gen/foo/bar#header-map,headers/foo/bar/test.h")));
521 }
522
TEST(Xlog,xlogStripFilename)523 TEST(Xlog, xlogStripFilename) {
524 EXPECT_STREQ("c/d.txt", xlogStripFilename("/a/b/c/d.txt", "/a/b"));
525 EXPECT_STREQ("c/d.txt", xlogStripFilename("/a/b/c/d.txt", "/a/b/"));
526 EXPECT_STREQ(
527 "ships/cruiser.cpp",
528 xlogStripFilename(
529 "/home/johndoe/src/spacesim/ships/cruiser.cpp",
530 "/home/johndoe/src/spacesim"));
531 EXPECT_STREQ(
532 "ships/cruiser.cpp",
533 xlogStripFilename("src/spacesim/ships/cruiser.cpp", "src/spacesim"));
534
535 // Test with multiple prefixes
536 EXPECT_STREQ("c/d.txt", xlogStripFilename("/a/b/c/d.txt", "/x/y:1/2:/a/b"));
537 EXPECT_STREQ("c/d.txt", xlogStripFilename("/a/b/c/d.txt", "/x/y:/a/b:/1/2"));
538
539 EXPECT_STREQ(
540 "/foobar/src/test.cpp", xlogStripFilename("/foobar/src/test.cpp", "/foo"))
541 << "should only strip full directory name matches";
542 EXPECT_STREQ(
543 "src/test.cpp",
544 xlogStripFilename("/foobar/src/test.cpp", "/foo:/foobar"));
545
546 EXPECT_STREQ(
547 "/a/b/c/d.txt", xlogStripFilename("/a/b/c/d.txt", "/a/b/c/d.txt"))
548 << "should not strip if the result will be empty";
549 EXPECT_STREQ("c/d.txt", xlogStripFilename("/a/b/c/d.txt", ":/x/y::/a/b:"))
550 << "empty prefixes in the prefix list should be ignored";
551
552 EXPECT_STREQ("d.txt", xlogStripFilename("/a/b/c/d.txt", "/a/b/c:/a"))
553 << "only the first prefix match should be honored";
554 EXPECT_STREQ("b/c/d.txt", xlogStripFilename("/a/b/c/d.txt", "/a:/a/b/c"))
555 << "only the first prefix match should be honored";
556
557 // xlogStripFilename() should ideally be a purely compile-time evaluation.
558 // Use a static_assert() to ensure that it can be evaluated at compile time.
559 // We use EXPECT_STREQ() checks above for most of the testing since it
560 // produces nicer messages on failure.
561 static_assert(
562 constexpr_strcmp(
563 xlogStripFilename("/my/project/src/test.cpp", "/my/project"),
564 "src/test.cpp") == 0,
565 "incorrect xlogStripFilename() behavior");
566
567 if (kIsWindows) {
568 EXPECT_STREQ(
569 "c\\d.txt", xlogStripFilename("Z:\\a\\b\\c\\d.txt", "Z:\\a\\b"));
570 EXPECT_STREQ("c\\d.txt", xlogStripFilename("Z:\\a\\b\\c\\d.txt", "Z:/a/b"));
571
572 EXPECT_STREQ("c/d.txt", xlogStripFilename("Z:/a/b/c/d.txt", "Z:\\a\\b"));
573 EXPECT_STREQ("c/d.txt", xlogStripFilename("Z:/a/b/c/d.txt", "Z:/a/b"));
574
575 EXPECT_STREQ(
576 "c\\d.txt", xlogStripFilename("Z:\\a\\b\\c\\d.txt", "C:/x/y:Z:/a/b"));
577 EXPECT_STREQ(
578 "c\\d.txt", xlogStripFilename("Z:\\a\\b\\c\\d.txt", "Z:/x/y:Z:/a/b"));
579 }
580 }
581
TEST(Xlog,XCheckPrecedence)582 TEST(Xlog, XCheckPrecedence) {
583 // Ensure that XCHECK_XX() and XDCHECK_XX() avoid the common macro pitfall of
584 // not wrapping arguments in parentheses and causing incorrect operator
585 // precedence issues.
586 XCHECK_EQ(0x22 & 0x3, 2);
587 XDCHECK_EQ(2, 0x22 & 0x3);
588 XCHECK_NE(0x62 & 0x22, 2);
589 XDCHECK_NE(0x62 & 0x22, 2);
590 }
591