proxygen
ConfigParserTest.cpp File Reference

Go to the source code of this file.

Functions

 TEST (LogConfig, parseBasic)
 
 TEST (LogConfig, parseBasicErrors)
 
 TEST (LogConfig, parseJson)
 
 TEST (LogConfig, parseJsonErrors)
 
 TEST (LogConfig, toJson)
 
 TEST (LogConfig, mergeConfigs)
 

Function Documentation

TEST ( LogConfig  ,
parseBasic   
)

Definition at line 32 of file ConfigParserTest.cpp.

References config, folly::DBG2, folly::DBG7, folly::ERR, EXPECT_THAT, folly::FATAL, folly::INFO, folly::none, testing::Pair(), folly::parseLogConfig(), testing::UnorderedElementsAre(), and folly::WARN.

32  {
33  auto config = parseLogConfig("");
34  EXPECT_THAT(config.getCategoryConfigs(), UnorderedElementsAre());
35  EXPECT_THAT(config.getHandlerConfigs(), UnorderedElementsAre());
36 
37  config = parseLogConfig(" ");
38  EXPECT_THAT(config.getCategoryConfigs(), UnorderedElementsAre());
39  EXPECT_THAT(config.getHandlerConfigs(), UnorderedElementsAre());
40 
41  config = parseLogConfig(".=ERROR,folly=DBG2");
43  config.getCategoryConfigs(),
45  Pair("", LogCategoryConfig{LogLevel::ERR, true}),
46  Pair("folly", LogCategoryConfig{LogLevel::DBG2, true})));
47  EXPECT_THAT(config.getHandlerConfigs(), UnorderedElementsAre());
48 
49  config = parseLogConfig(" INFO , folly := FATAL ");
51  config.getCategoryConfigs(),
53  Pair("", LogCategoryConfig{LogLevel::INFO, true}),
54  Pair("folly", LogCategoryConfig{LogLevel::FATAL, false})));
55  EXPECT_THAT(config.getHandlerConfigs(), UnorderedElementsAre());
56 
57  config =
58  parseLogConfig("my.category:=INFO , my.other.stuff := 19,foo.bar=DBG7");
60  config.getCategoryConfigs(),
62  Pair("my.category", LogCategoryConfig{LogLevel::INFO, false}),
63  Pair(
64  "my.other.stuff",
65  LogCategoryConfig{static_cast<LogLevel>(19), false}),
66  Pair("foo.bar", LogCategoryConfig{LogLevel::DBG7, true})));
67  EXPECT_THAT(config.getHandlerConfigs(), UnorderedElementsAre());
68 
69  config = parseLogConfig(" ERR ");
71  config.getCategoryConfigs(),
72  UnorderedElementsAre(Pair("", LogCategoryConfig{LogLevel::ERR, true})));
73  EXPECT_THAT(config.getHandlerConfigs(), UnorderedElementsAre());
74 
75  config = parseLogConfig(" ERR: ");
77  config.getCategoryConfigs(),
79  Pair("", LogCategoryConfig{LogLevel::ERR, true, {}})));
80  EXPECT_THAT(config.getHandlerConfigs(), UnorderedElementsAre());
81 
82  config = parseLogConfig(" ERR:stderr; stderr=stream:stream=stderr ");
84  config.getCategoryConfigs(),
86  Pair("", LogCategoryConfig{LogLevel::ERR, true, {"stderr"}})));
88  config.getHandlerConfigs(),
90  Pair("stderr", LogHandlerConfig{"stream", {{"stream", "stderr"}}})));
91 
92  config = parseLogConfig(
93  "ERR:myfile:custom, folly=DBG2, folly.io:=WARN:other;"
94  "myfile=file:path=/tmp/x.log; "
95  "custom=custom:foo=bar,hello=world,a = b = c; "
96  "other=custom2");
98  config.getCategoryConfigs(),
100  Pair(
101  "", LogCategoryConfig{LogLevel::ERR, true, {"myfile", "custom"}}),
102  Pair("folly", LogCategoryConfig{LogLevel::DBG2, true}),
103  Pair(
104  "folly.io",
105  LogCategoryConfig{LogLevel::WARN, false, {"other"}})));
106  EXPECT_THAT(
107  config.getHandlerConfigs(),
109  Pair("myfile", LogHandlerConfig{"file", {{"path", "/tmp/x.log"}}}),
110  Pair(
111  "custom",
113  "custom",
114  {{"foo", "bar"}, {"hello", "world"}, {"a", "b = c"}}}),
115  Pair("other", LogHandlerConfig{"custom2"})));
116 
117  // Test updating existing handler configs, with no handler type
118  config = parseLogConfig("ERR;foo");
119  EXPECT_THAT(
120  config.getCategoryConfigs(),
121  UnorderedElementsAre(Pair("", LogCategoryConfig{LogLevel::ERR, true})));
122  EXPECT_THAT(
123  config.getHandlerConfigs(),
125 
126  config = parseLogConfig("ERR;foo:a=b,c=d");
127  EXPECT_THAT(
128  config.getCategoryConfigs(),
129  UnorderedElementsAre(Pair("", LogCategoryConfig{LogLevel::ERR, true})));
130  EXPECT_THAT(
131  config.getHandlerConfigs(),
133  "foo", LogHandlerConfig{folly::none, {{"a", "b"}, {"c", "d"}}})));
134 
135  config = parseLogConfig("ERR;test=file:path=/tmp/test.log;foo:a=b,c=d");
136  EXPECT_THAT(
137  config.getCategoryConfigs(),
138  UnorderedElementsAre(Pair("", LogCategoryConfig{LogLevel::ERR, true})));
139  EXPECT_THAT(
140  config.getHandlerConfigs(),
142  Pair("foo", LogHandlerConfig{folly::none, {{"a", "b"}, {"c", "d"}}}),
143  Pair("test", LogHandlerConfig{"file", {{"path", "/tmp/test.log"}}})));
144 
145  // Log handler changes with no category changes
146  config = parseLogConfig("; myhandler=custom:foo=bar");
147  EXPECT_THAT(config.getCategoryConfigs(), UnorderedElementsAre());
148  EXPECT_THAT(
149  config.getHandlerConfigs(),
151  Pair("myhandler", LogHandlerConfig{"custom", {{"foo", "bar"}}})));
152 }
LogConfig parseLogConfig(StringPiece value)
AHArrayT::Config config
internal::UnorderedElementsAreMatcher< ::testing::tuple<> > UnorderedElementsAre()
internal::PairMatcher< FirstMatcher, SecondMatcher > Pair(FirstMatcher first_matcher, SecondMatcher second_matcher)
LogLevel
Definition: LogLevel.h:38
#define EXPECT_THAT(value, matcher)
constexpr None none
Definition: Optional.h:87
TEST ( LogConfig  ,
parseBasicErrors   
)

Definition at line 154 of file ConfigParserTest.cpp.

References EXPECT_THROW_RE, and folly::parseLogConfig().

154  {
155  // Errors in the log category settings
157  parseLogConfig("=="),
159  R"(invalid log level "=" for category "")");
161  parseLogConfig("bogus_level"),
163  R"(invalid log level "bogus_level" for category ".")");
165  parseLogConfig("foo=bogus_level"),
167  R"(invalid log level "bogus_level" for category "foo")");
169  parseLogConfig("foo=WARN,bar=invalid"),
171  R"(invalid log level "invalid" for category "bar")");
173  parseLogConfig("foo=WARN,bar="),
175  R"(invalid log level "" for category "bar")");
177  parseLogConfig("foo=WARN,bar:="),
179  R"(invalid log level "" for category "bar")");
181  parseLogConfig("foo:=,bar:=WARN"),
183  R"(invalid log level "" for category "foo")");
185  parseLogConfig("x"),
187  R"(invalid log level "x" for category ".")");
189  parseLogConfig("x,y,z"),
191  R"(invalid log level "x" for category ".")");
193  parseLogConfig("foo=WARN,"),
195  R"(invalid log level "" for category ".")");
197  parseLogConfig("="),
199  R"(invalid log level "" for category "")");
201  parseLogConfig(":="),
203  R"(invalid log level "" for category "")");
205  parseLogConfig("foo=bar=ERR"),
207  R"(invalid log level "bar=ERR" for category "foo")");
209  parseLogConfig("foo.bar=ERR,foo..bar=INFO"),
211  R"(category "foo\.bar" listed multiple times under different names: )"
212  R"("foo\.+bar" and "foo\.+bar")");
214  parseLogConfig("=ERR,.=INFO"),
216  R"(category "" listed multiple times under different names: )"
217  R"("\.?" and "\.?")");
218 
219  // Errors in the log handler settings
221  parseLogConfig("ERR;"),
223  "error parsing log handler configuration: empty log handler name");
225  parseLogConfig("ERR;foo="),
227  R"(error parsing configuration for log handler "foo": )"
228  "empty log handler type");
230  parseLogConfig("ERR;=file"),
232  "error parsing log handler configuration: empty log handler name");
234  parseLogConfig("ERR;handler1=file;"),
236  "error parsing log handler configuration: empty log handler name");
238  parseLogConfig("ERR;test=file,path=/tmp/test.log;foo:a=b,c=d"),
240  R"(error parsing configuration for log handler "test": )"
241  R"(invalid type "file,path=/tmp/test.log": type name cannot contain )"
242  "a comma when using the basic config format");
244  parseLogConfig("ERR;test,path=/tmp/test.log;foo:a=b,c=d"),
246  R"(error parsing configuration for log handler "test,path": )"
247  "name cannot contain a comma when using the basic config format");
248 }
#define EXPECT_THROW_RE(statement, exceptionType, pattern)
Definition: TestUtils.h:119
LogConfig parseLogConfig(StringPiece value)
TEST ( LogConfig  ,
parseJson   
)

Definition at line 250 of file ConfigParserTest.cpp.

References config, folly::DBG1, folly::DBG2, folly::DBG7, folly::ERR, EXPECT_THAT, folly::FATAL, folly::INFO, testing::Pair(), folly::parseLogConfig(), and testing::UnorderedElementsAre().

250  {
251  auto config = parseLogConfig("{}");
252  EXPECT_THAT(config.getCategoryConfigs(), UnorderedElementsAre());
253  config = parseLogConfig(" {} ");
254  EXPECT_THAT(config.getCategoryConfigs(), UnorderedElementsAre());
255 
256  config = parseLogConfig(R"JSON({
257  "categories": {
258  ".": "ERROR",
259  "folly": "DBG2",
260  }
261  })JSON");
262  EXPECT_THAT(
263  config.getCategoryConfigs(),
265  Pair("", LogCategoryConfig{LogLevel::ERR, true}),
266  Pair("folly", LogCategoryConfig{LogLevel::DBG2, true})));
267  EXPECT_THAT(config.getHandlerConfigs(), UnorderedElementsAre());
268 
269  config = parseLogConfig(R"JSON({
270  "categories": {
271  "": "ERROR",
272  "folly": "DBG2",
273  }
274  })JSON");
275  EXPECT_THAT(
276  config.getCategoryConfigs(),
278  Pair("", LogCategoryConfig{LogLevel::ERR, true}),
279  Pair("folly", LogCategoryConfig{LogLevel::DBG2, true})));
280  EXPECT_THAT(config.getHandlerConfigs(), UnorderedElementsAre());
281 
282  config = parseLogConfig(R"JSON({
283  "categories": {
284  ".": { "level": "INFO" },
285  "folly": { "level": "FATAL", "inherit": false },
286  }
287  })JSON");
288  EXPECT_THAT(
289  config.getCategoryConfigs(),
291  Pair("", LogCategoryConfig{LogLevel::INFO, true}),
292  Pair("folly", LogCategoryConfig{LogLevel::FATAL, false})));
293  EXPECT_THAT(config.getHandlerConfigs(), UnorderedElementsAre());
294 
295  config = parseLogConfig(R"JSON({
296  "categories": {
297  "my.category": { "level": "INFO", "inherit": true },
298  // comments are allowed
299  "my.other.stuff": { "level": 19, "inherit": false },
300  "foo.bar": { "level": "DBG7" },
301  },
302  "handlers": {
303  "h1": { "type": "custom", "options": {"foo": "bar", "a": "z"} }
304  }
305  })JSON");
306  EXPECT_THAT(
307  config.getCategoryConfigs(),
309  Pair("my.category", LogCategoryConfig{LogLevel::INFO, true}),
310  Pair(
311  "my.other.stuff",
312  LogCategoryConfig{static_cast<LogLevel>(19), false}),
313  Pair("foo.bar", LogCategoryConfig{LogLevel::DBG7, true})));
314  EXPECT_THAT(
315  config.getHandlerConfigs(),
317  "h1", LogHandlerConfig{"custom", {{"foo", "bar"}, {"a", "z"}}})));
318 
319  // The JSON config parsing should allow unusual log category names
320  // containing whitespace, equal signs, and other characters not allowed in
321  // the basic config style.
322  config = parseLogConfig(R"JSON({
323  "categories": {
324  " my.category ": { "level": "INFO" },
325  " foo; bar=asdf, test": { "level": "DBG1" },
326  },
327  "handlers": {
328  "h1;h2,h3= ": { "type": " x;y " }
329  }
330  })JSON");
331  EXPECT_THAT(
332  config.getCategoryConfigs(),
334  Pair(" my.category ", LogCategoryConfig{LogLevel::INFO, true}),
335  Pair(
336  " foo; bar=asdf, test",
337  LogCategoryConfig{LogLevel::DBG1, true})));
338  EXPECT_THAT(
339  config.getHandlerConfigs(),
340  UnorderedElementsAre(Pair("h1;h2,h3= ", LogHandlerConfig{" x;y "})));
341 }
LogConfig parseLogConfig(StringPiece value)
AHArrayT::Config config
internal::UnorderedElementsAreMatcher< ::testing::tuple<> > UnorderedElementsAre()
internal::PairMatcher< FirstMatcher, SecondMatcher > Pair(FirstMatcher first_matcher, SecondMatcher second_matcher)
LogLevel
Definition: LogLevel.h:38
#define EXPECT_THAT(value, matcher)
TEST ( LogConfig  ,
parseJsonErrors   
)

Definition at line 343 of file ConfigParserTest.cpp.

References EXPECT_THROW_RE, folly::parseLogConfig(), and folly::parseLogConfigJson().

343  {
345  parseLogConfigJson("5"),
347  "JSON config input must be an object");
349  parseLogConfigJson("true"),
351  "JSON config input must be an object");
353  parseLogConfigJson(R"("hello")"),
355  "JSON config input must be an object");
357  parseLogConfigJson("[1, 2, 3]"),
359  "JSON config input must be an object");
361  parseLogConfigJson(""), std::runtime_error, "json parse error");
363  parseLogConfigJson("{"), std::runtime_error, "json parse error");
364  EXPECT_THROW_RE(parseLogConfig("{"), std::runtime_error, "json parse error");
366  parseLogConfig("{}}"), std::runtime_error, "json parse error");
367 
368  StringPiece input = R"JSON({
369  "categories": 5
370  })JSON";
372  parseLogConfig(input),
374  "unexpected data type for log categories config: "
375  "got integer, expected an object");
376  input = R"JSON({
377  "categories": {
378  "foo": true,
379  }
380  })JSON";
382  parseLogConfig(input),
384  R"(unexpected data type for configuration of category "foo": )"
385  "got boolean, expected an object, string, or integer");
386 
387  input = R"JSON({
388  "categories": {
389  "foo": [1, 2, 3],
390  }
391  })JSON";
393  parseLogConfig(input),
395  R"(unexpected data type for configuration of category "foo": )"
396  "got array, expected an object, string, or integer");
397 
398  input = R"JSON({
399  "categories": {
400  ".": { "level": "INFO" },
401  "folly": { "level": "FATAL", "inherit": 19 },
402  }
403  })JSON";
405  parseLogConfig(input),
407  R"(unexpected data type for inherit field of category "folly": )"
408  "got integer, expected a boolean");
409  input = R"JSON({
410  "categories": {
411  "folly": { "level": [], },
412  }
413  })JSON";
415  parseLogConfig(input),
417  R"(unexpected data type for level field of category "folly": )"
418  "got array, expected a string or integer");
419  input = R"JSON({
420  "categories": {
421  5: {}
422  }
423  })JSON";
425  parseLogConfig(input), std::runtime_error, "json parse error");
426 
427  input = R"JSON({
428  "categories": {
429  "foo...bar": { "level": "INFO", },
430  "foo..bar": { "level": "INFO", },
431  }
432  })JSON";
434  parseLogConfig(input),
436  R"(category "foo\.bar" listed multiple times under different names: )"
437  R"("foo\.\.+bar" and "foo\.+bar")");
438  input = R"JSON({
439  "categories": {
440  "...": { "level": "ERR", },
441  "": { "level": "INFO", },
442  }
443  })JSON";
445  parseLogConfig(input),
447  R"(category "" listed multiple times under different names: )"
448  R"X("(\.\.\.|)" and "(\.\.\.|)")X");
449 
450  input = R"JSON({
451  "categories": { "folly": { "level": "ERR" } },
452  "handlers": 9.8
453  })JSON";
455  parseLogConfig(input),
457  "unexpected data type for log handlers config: "
458  "got double, expected an object");
459 
460  input = R"JSON({
461  "categories": { "folly": { "level": "ERR" } },
462  "handlers": {
463  "foo": "test"
464  }
465  })JSON";
467  parseLogConfig(input),
469  R"(unexpected data type for configuration of handler "foo": )"
470  "got string, expected an object");
471 
472  input = R"JSON({
473  "categories": { "folly": { "level": "ERR" } },
474  "handlers": {
475  "foo": {}
476  }
477  })JSON";
479  parseLogConfig(input),
481  R"(no handler type specified for log handler "foo")");
482 
483  input = R"JSON({
484  "categories": { "folly": { "level": "ERR" } },
485  "handlers": {
486  "foo": {
487  "type": 19
488  }
489  }
490  })JSON";
492  parseLogConfig(input),
494  R"(unexpected data type for "type" field of handler "foo": )"
495  "got integer, expected a string");
496 
497  input = R"JSON({
498  "categories": { "folly": { "level": "ERR" } },
499  "handlers": {
500  "foo": {
501  "type": "custom",
502  "options": true
503  }
504  }
505  })JSON";
507  parseLogConfig(input),
509  R"(unexpected data type for "options" field of handler "foo": )"
510  "got boolean, expected an object");
511 
512  input = R"JSON({
513  "categories": { "folly": { "level": "ERR" } },
514  "handlers": {
515  "foo": {
516  "type": "custom",
517  "options": ["foo", "bar"]
518  }
519  }
520  })JSON";
522  parseLogConfig(input),
524  R"(unexpected data type for "options" field of handler "foo": )"
525  "got array, expected an object");
526 
527  input = R"JSON({
528  "categories": { "folly": { "level": "ERR" } },
529  "handlers": {
530  "foo": {
531  "type": "custom",
532  "options": {"bar": 5}
533  }
534  }
535  })JSON";
537  parseLogConfig(input),
539  R"(unexpected data type for option "bar" of handler "foo": )"
540  "got integer, expected a string");
541 }
#define EXPECT_THROW_RE(statement, exceptionType, pattern)
Definition: TestUtils.h:119
LogConfig parseLogConfig(StringPiece value)
LogConfig parseLogConfigJson(StringPiece value)
TEST ( LogConfig  ,
toJson   
)

Definition at line 543 of file ConfigParserTest.cpp.

References config, EXPECT_EQ, folly::logConfigToDynamic(), folly::parseJson(), and folly::parseLogConfig().

543  {
544  auto config = parseLogConfig("");
545  auto expectedJson = folly::parseJson(R"JSON({
546  "categories": {},
547  "handlers": {}
548 })JSON");
549  EXPECT_EQ(expectedJson, logConfigToDynamic(config));
550 
552  "ERROR:h1,foo.bar:=FATAL,folly=INFO:; "
553  "h1=custom:foo=bar");
554  expectedJson = folly::parseJson(R"JSON({
555  "categories" : {
556  "" : {
557  "inherit" : true,
558  "level" : "ERR",
559  "handlers" : ["h1"]
560  },
561  "folly" : {
562  "inherit" : true,
563  "level" : "INFO",
564  "handlers" : []
565  },
566  "foo.bar" : {
567  "inherit" : false,
568  "level" : "FATAL"
569  }
570  },
571  "handlers" : {
572  "h1": {
573  "type": "custom",
574  "options": { "foo": "bar" }
575  }
576  }
577 })JSON");
578  EXPECT_EQ(expectedJson, logConfigToDynamic(config));
579 }
dynamic parseJson(StringPiece range)
Definition: json.cpp:900
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
LogConfig parseLogConfig(StringPiece value)
AHArrayT::Config config
dynamic logConfigToDynamic(const LogConfig &config)
TEST ( LogConfig  ,
mergeConfigs   
)

Definition at line 581 of file ConfigParserTest.cpp.

References config, folly::DBG2, folly::ERR, EXPECT_THAT, EXPECT_THROW_RE, folly::FATAL, folly::INFO, testing::Pair(), folly::parseLogConfig(), testing::UnorderedElementsAre(), and folly::WARN.

581  {
582  auto config = parseLogConfig("bar=ERR:");
583  config.update(parseLogConfig("foo:=INFO"));
584  EXPECT_THAT(
585  config.getCategoryConfigs(),
587  Pair("foo", LogCategoryConfig{LogLevel::INFO, false}),
588  Pair("bar", LogCategoryConfig{LogLevel::ERR, true, {}})));
589  EXPECT_THAT(config.getHandlerConfigs(), UnorderedElementsAre());
590 
591  config =
592  parseLogConfig("WARN:default; default=custom:opt1=value1,opt2=value2");
593  config.update(parseLogConfig("folly.io=DBG2,foo=INFO"));
594  EXPECT_THAT(
595  config.getCategoryConfigs(),
597  Pair("", LogCategoryConfig{LogLevel::WARN, true, {"default"}}),
598  Pair("foo", LogCategoryConfig{LogLevel::INFO, true}),
599  Pair("folly.io", LogCategoryConfig{LogLevel::DBG2, true})));
600  EXPECT_THAT(
601  config.getHandlerConfigs(),
603  "default",
605  "custom", {{"opt1", "value1"}, {"opt2", "value2"}}))));
606 
607  // Updating the root category's log level without specifying
608  // handlers should leave its current handler list intact
609  config =
610  parseLogConfig("WARN:default; default=custom:opt1=value1,opt2=value2");
611  config.update(parseLogConfig("ERR"));
612  EXPECT_THAT(
613  config.getCategoryConfigs(),
615  Pair("", LogCategoryConfig{LogLevel::ERR, true, {"default"}})));
616  EXPECT_THAT(
617  config.getHandlerConfigs(),
619  "default",
621  "custom", {{"opt1", "value1"}, {"opt2", "value2"}}))));
622 
623  config =
624  parseLogConfig("WARN:default; default=custom:opt1=value1,opt2=value2");
625  config.update(parseLogConfig(".:=ERR"));
626  EXPECT_THAT(
627  config.getCategoryConfigs(),
629  Pair("", LogCategoryConfig{LogLevel::ERR, false, {"default"}})));
630  EXPECT_THAT(
631  config.getHandlerConfigs(),
633  "default",
635  "custom", {{"opt1", "value1"}, {"opt2", "value2"}}))));
636 
637  // Test clearing the root category's log handlers
638  config =
639  parseLogConfig("WARN:default; default=custom:opt1=value1,opt2=value2");
640  config.update(parseLogConfig("FATAL:"));
641  EXPECT_THAT(
642  config.getCategoryConfigs(),
644  Pair("", LogCategoryConfig{LogLevel::FATAL, true, {}})));
645  EXPECT_THAT(
646  config.getHandlerConfigs(),
648  "default",
650  "custom", {{"opt1", "value1"}, {"opt2", "value2"}}))));
651 
652  // Test updating the settings on a log handler
653  config =
654  parseLogConfig("WARN:default; default=stream:stream=stderr,async=false");
655  config.update(parseLogConfig("INFO; default:async=true"));
656  EXPECT_THAT(
657  config.getCategoryConfigs(),
659  Pair("", LogCategoryConfig{LogLevel::INFO, true, {"default"}})));
660  EXPECT_THAT(
661  config.getHandlerConfigs(),
663  "default",
665  "stream", {{"stream", "stderr"}, {"async", "true"}}))));
666 
667  // Updating the settings for a non-existent log handler should fail
668  config =
669  parseLogConfig("WARN:default; default=stream:stream=stderr,async=false");
671  config.update(parseLogConfig("INFO; other:async=true")),
672  std::invalid_argument,
673  "cannot update configuration for "
674  R"(unknown log handler "other")");
675 }
#define EXPECT_THROW_RE(statement, exceptionType, pattern)
Definition: TestUtils.h:119
LogConfig parseLogConfig(StringPiece value)
AHArrayT::Config config
internal::UnorderedElementsAreMatcher< ::testing::tuple<> > UnorderedElementsAre()
internal::PairMatcher< FirstMatcher, SecondMatcher > Pair(FirstMatcher first_matcher, SecondMatcher second_matcher)
#define EXPECT_THAT(value, matcher)