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