xref: /aosp_15_r20/external/cronet/net/dns/nsswitch_reader_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2021 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/dns/nsswitch_reader.h"
6 
7 #include <string>
8 #include <utility>
9 #include <vector>
10 
11 #include "base/check.h"
12 #include "base/functional/bind.h"
13 #include "testing/gmock/include/gmock/gmock.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 
16 namespace net {
17 namespace {
18 
19 class TestFileReader {
20  public:
TestFileReader(std::string text)21   explicit TestFileReader(std::string text) : text_(std::move(text)) {}
22   TestFileReader(const TestFileReader&) = delete;
23   TestFileReader& operator=(const TestFileReader&) = delete;
24 
GetFileReadCall()25   NsswitchReader::FileReadCall GetFileReadCall() {
26     return base::BindRepeating(&TestFileReader::ReadFile,
27                                base::Unretained(this));
28   }
29 
ReadFile()30   std::string ReadFile() {
31     CHECK(!already_read_);
32 
33     already_read_ = true;
34     return text_;
35   }
36 
37  private:
38   std::string text_;
39   bool already_read_ = false;
40 };
41 
42 class NsswitchReaderTest : public testing::Test {
43  public:
44   NsswitchReaderTest() = default;
45   NsswitchReaderTest(const NsswitchReaderTest&) = delete;
46   NsswitchReaderTest& operator=(const NsswitchReaderTest&) = delete;
47 
48  protected:
49   NsswitchReader reader_;
50 };
51 
52 // Attempt to load the actual nsswitch.conf for the test machine and run
53 // rationality checks for the result.
TEST_F(NsswitchReaderTest,ActualReadAndParseHosts)54 TEST_F(NsswitchReaderTest, ActualReadAndParseHosts) {
55   std::vector<NsswitchReader::ServiceSpecification> services =
56       reader_.ReadAndParseHosts();
57 
58   // Assume nobody will ever run this on a machine with more than 1000
59   // configured services.
60   EXPECT_THAT(services, testing::SizeIs(testing::Le(1000u)));
61 
62   // Assume no service will ever have more than 10 configured actions per
63   // service.
64   for (const NsswitchReader::ServiceSpecification& service : services) {
65     EXPECT_THAT(service.actions, testing::SizeIs(testing::Le(10u)));
66   }
67 }
68 
TEST_F(NsswitchReaderTest,FileReadErrorResultsInDefault)69 TEST_F(NsswitchReaderTest, FileReadErrorResultsInDefault) {
70   TestFileReader file_reader("");
71   reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
72 
73   std::vector<NsswitchReader::ServiceSpecification> services =
74       reader_.ReadAndParseHosts();
75 
76   // Expect "files dns".
77   EXPECT_THAT(
78       services,
79       testing::ElementsAre(
80           NsswitchReader::ServiceSpecification(NsswitchReader::Service::kFiles),
81           NsswitchReader::ServiceSpecification(NsswitchReader::Service::kDns)));
82 }
83 
TEST_F(NsswitchReaderTest,MissingHostsResultsInDefault)84 TEST_F(NsswitchReaderTest, MissingHostsResultsInDefault) {
85   const std::string kFile =
86       "passwd: files ldap\nshadow: files\ngroup: files ldap\n";
87   TestFileReader file_reader(kFile);
88   reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
89 
90   std::vector<NsswitchReader::ServiceSpecification> services =
91       reader_.ReadAndParseHosts();
92 
93   // Expect "files dns".
94   EXPECT_THAT(
95       services,
96       testing::ElementsAre(
97           NsswitchReader::ServiceSpecification(NsswitchReader::Service::kFiles),
98           NsswitchReader::ServiceSpecification(NsswitchReader::Service::kDns)));
99 }
100 
TEST_F(NsswitchReaderTest,ParsesAllKnownServices)101 TEST_F(NsswitchReaderTest, ParsesAllKnownServices) {
102   const std::string kFile =
103       "hosts: files dns mdns mdns4 mdns6 mdns_minimal mdns4_minimal "
104       "mdns6_minimal myhostname resolve nis";
105   TestFileReader file_reader(kFile);
106   reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
107 
108   std::vector<NsswitchReader::ServiceSpecification> services =
109       reader_.ReadAndParseHosts();
110 
111   EXPECT_THAT(
112       services,
113       testing::ElementsAre(
114           NsswitchReader::ServiceSpecification(NsswitchReader::Service::kFiles),
115           NsswitchReader::ServiceSpecification(NsswitchReader::Service::kDns),
116           NsswitchReader::ServiceSpecification(NsswitchReader::Service::kMdns),
117           NsswitchReader::ServiceSpecification(NsswitchReader::Service::kMdns4),
118           NsswitchReader::ServiceSpecification(NsswitchReader::Service::kMdns6),
119           NsswitchReader::ServiceSpecification(
120               NsswitchReader::Service::kMdnsMinimal),
121           NsswitchReader::ServiceSpecification(
122               NsswitchReader::Service::kMdns4Minimal),
123           NsswitchReader::ServiceSpecification(
124               NsswitchReader::Service::kMdns6Minimal),
125           NsswitchReader::ServiceSpecification(
126               NsswitchReader::Service::kMyHostname),
127           NsswitchReader::ServiceSpecification(
128               NsswitchReader::Service::kResolve),
129           NsswitchReader::ServiceSpecification(NsswitchReader::Service::kNis)));
130 }
131 
TEST_F(NsswitchReaderTest,ParsesRepeatedServices)132 TEST_F(NsswitchReaderTest, ParsesRepeatedServices) {
133   const std::string kFile = "hosts: mdns4 mdns6 mdns6 myhostname";
134   TestFileReader file_reader(kFile);
135   reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
136 
137   std::vector<NsswitchReader::ServiceSpecification> services =
138       reader_.ReadAndParseHosts();
139 
140   EXPECT_THAT(
141       services,
142       testing::ElementsAre(
143           NsswitchReader::ServiceSpecification(NsswitchReader::Service::kMdns4),
144           NsswitchReader::ServiceSpecification(NsswitchReader::Service::kMdns6),
145           NsswitchReader::ServiceSpecification(NsswitchReader::Service::kMdns6),
146           NsswitchReader::ServiceSpecification(
147               NsswitchReader::Service::kMyHostname)));
148 }
149 
TEST_F(NsswitchReaderTest,ParsesAllKnownActions)150 TEST_F(NsswitchReaderTest, ParsesAllKnownActions) {
151   const std::string kFile =
152       "hosts: files [UNAVAIL=RETURN] [UNAVAIL=CONTINUE] [UNAVAIL=MERGE]";
153   TestFileReader file_reader(kFile);
154   reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
155 
156   std::vector<NsswitchReader::ServiceSpecification> services =
157       reader_.ReadAndParseHosts();
158 
159   EXPECT_THAT(services,
160               testing::ElementsAre(NsswitchReader::ServiceSpecification(
161                   NsswitchReader::Service::kFiles,
162                   {{/*negated=*/false, NsswitchReader::Status::kUnavailable,
163                     NsswitchReader::Action::kReturn},
164                    {/*negated=*/false, NsswitchReader::Status::kUnavailable,
165                     NsswitchReader::Action::kContinue},
166                    {/*negated=*/false, NsswitchReader::Status::kUnavailable,
167                     NsswitchReader::Action::kMerge}})));
168 }
169 
TEST_F(NsswitchReaderTest,ParsesAllKnownStatuses)170 TEST_F(NsswitchReaderTest, ParsesAllKnownStatuses) {
171   const std::string kFile =
172       "hosts: dns [SUCCESS=RETURN] [NOTFOUND=RETURN] [UNAVAIL=RETURN] "
173       "[TRYAGAIN=RETURN]";
174   TestFileReader file_reader(kFile);
175   reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
176 
177   std::vector<NsswitchReader::ServiceSpecification> services =
178       reader_.ReadAndParseHosts();
179 
180   EXPECT_THAT(services,
181               testing::ElementsAre(NsswitchReader::ServiceSpecification(
182                   NsswitchReader::Service::kDns,
183                   {{/*negated=*/false, NsswitchReader::Status::kSuccess,
184                     NsswitchReader::Action::kReturn},
185                    {/*negated=*/false, NsswitchReader::Status::kNotFound,
186                     NsswitchReader::Action::kReturn},
187                    {/*negated=*/false, NsswitchReader::Status::kUnavailable,
188                     NsswitchReader::Action::kReturn},
189                    {/*negated=*/false, NsswitchReader::Status::kTryAgain,
190                     NsswitchReader::Action::kReturn}})));
191 }
192 
TEST_F(NsswitchReaderTest,ParsesRepeatedActions)193 TEST_F(NsswitchReaderTest, ParsesRepeatedActions) {
194   const std::string kFile =
195       "hosts: nis [!SUCCESS=RETURN] [NOTFOUND=RETURN] [NOTFOUND=RETURN] "
196       "[!UNAVAIL=RETURN]";
197   TestFileReader file_reader(kFile);
198   reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
199 
200   std::vector<NsswitchReader::ServiceSpecification> services =
201       reader_.ReadAndParseHosts();
202 
203   EXPECT_THAT(services,
204               testing::ElementsAre(NsswitchReader::ServiceSpecification(
205                   NsswitchReader::Service::kNis,
206                   {{/*negated=*/true, NsswitchReader::Status::kSuccess,
207                     NsswitchReader::Action::kReturn},
208                    {/*negated=*/false, NsswitchReader::Status::kNotFound,
209                     NsswitchReader::Action::kReturn},
210                    {/*negated=*/false, NsswitchReader::Status::kNotFound,
211                     NsswitchReader::Action::kReturn},
212                    {/*negated=*/true, NsswitchReader::Status::kUnavailable,
213                     NsswitchReader::Action::kReturn}})));
214 }
215 
TEST_F(NsswitchReaderTest,ParsesCombinedActionLists)216 TEST_F(NsswitchReaderTest, ParsesCombinedActionLists) {
217   const std::string kFile =
218       "hosts: dns [SUCCESS=RETURN !NOTFOUND=RETURN UNAVAIL=RETURN] files";
219   TestFileReader file_reader(kFile);
220   reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
221 
222   std::vector<NsswitchReader::ServiceSpecification> services =
223       reader_.ReadAndParseHosts();
224 
225   EXPECT_THAT(services,
226               testing::ElementsAre(
227                   NsswitchReader::ServiceSpecification(
228                       NsswitchReader::Service::kDns,
229                       {{/*negated=*/false, NsswitchReader::Status::kSuccess,
230                         NsswitchReader::Action::kReturn},
231                        {/*negated=*/true, NsswitchReader::Status::kNotFound,
232                         NsswitchReader::Action::kReturn},
233                        {/*negated=*/false, NsswitchReader::Status::kUnavailable,
234                         NsswitchReader::Action::kReturn}}),
235                   NsswitchReader::ServiceSpecification(
236                       NsswitchReader::Service::kFiles)));
237 }
238 
TEST_F(NsswitchReaderTest,HandlesAtypicalWhitespace)239 TEST_F(NsswitchReaderTest, HandlesAtypicalWhitespace) {
240   const std::string kFile =
241       " database:  service   \n\n   hosts: files\tdns   mdns4 \t mdns6    \t  "
242       "\t\n\t\n";
243   TestFileReader file_reader(kFile);
244   reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
245 
246   std::vector<NsswitchReader::ServiceSpecification> services =
247       reader_.ReadAndParseHosts();
248 
249   EXPECT_THAT(
250       services,
251       testing::ElementsAre(
252           NsswitchReader::ServiceSpecification(NsswitchReader::Service::kFiles),
253           NsswitchReader::ServiceSpecification(NsswitchReader::Service::kDns),
254           NsswitchReader::ServiceSpecification(NsswitchReader::Service::kMdns4),
255           NsswitchReader::ServiceSpecification(
256               NsswitchReader::Service::kMdns6)));
257 }
258 
TEST_F(NsswitchReaderTest,HandlesAtypicalWhitespaceInActions)259 TEST_F(NsswitchReaderTest, HandlesAtypicalWhitespaceInActions) {
260   const std::string kFile =
261       "hosts: dns [ !UNAVAIL=MERGE \t NOTFOUND=RETURN\t][ UNAVAIL=CONTINUE]";
262   TestFileReader file_reader(kFile);
263   reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
264 
265   std::vector<NsswitchReader::ServiceSpecification> services =
266       reader_.ReadAndParseHosts();
267 
268   EXPECT_THAT(services,
269               testing::ElementsAre(NsswitchReader::ServiceSpecification(
270                   NsswitchReader::Service::kDns,
271                   {{/*negated=*/true, NsswitchReader::Status::kUnavailable,
272                     NsswitchReader::Action::kMerge},
273                    {/*negated=*/false, NsswitchReader::Status::kNotFound,
274                     NsswitchReader::Action::kReturn},
275                    {/*negated=*/false, NsswitchReader::Status::kUnavailable,
276                     NsswitchReader::Action::kContinue}})));
277 }
278 
TEST_F(NsswitchReaderTest,ParsesActionsWithoutService)279 TEST_F(NsswitchReaderTest, ParsesActionsWithoutService) {
280   const std::string kFile = "hosts: [SUCCESS=RETURN]";
281   TestFileReader file_reader(kFile);
282   reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
283 
284   std::vector<NsswitchReader::ServiceSpecification> services =
285       reader_.ReadAndParseHosts();
286 
287   EXPECT_THAT(services,
288               testing::ElementsAre(NsswitchReader::ServiceSpecification(
289                   NsswitchReader::Service::kUnknown,
290                   {{/*negated=*/false, NsswitchReader::Status::kSuccess,
291                     NsswitchReader::Action::kReturn}})));
292 }
293 
TEST_F(NsswitchReaderTest,ParsesNegatedActions)294 TEST_F(NsswitchReaderTest, ParsesNegatedActions) {
295   const std::string kFile =
296       "hosts: mdns_minimal [!UNAVAIL=RETURN] [NOTFOUND=CONTINUE] "
297       "[!TRYAGAIN=CONTINUE]";
298   TestFileReader file_reader(kFile);
299   reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
300 
301   std::vector<NsswitchReader::ServiceSpecification> services =
302       reader_.ReadAndParseHosts();
303 
304   EXPECT_THAT(services,
305               testing::ElementsAre(NsswitchReader::ServiceSpecification(
306                   NsswitchReader::Service::kMdnsMinimal,
307                   {{/*negated=*/true, NsswitchReader::Status::kUnavailable,
308                     NsswitchReader::Action::kReturn},
309                    {/*negated=*/false, NsswitchReader::Status::kNotFound,
310                     NsswitchReader::Action::kContinue},
311                    {/*negated=*/true, NsswitchReader::Status::kTryAgain,
312                     NsswitchReader::Action::kContinue}})));
313 }
314 
TEST_F(NsswitchReaderTest,ParsesUnrecognizedServiceAsUnknown)315 TEST_F(NsswitchReaderTest, ParsesUnrecognizedServiceAsUnknown) {
316   const std::string kFile =
317       "passwd: files\nhosts: files super_awesome_service myhostname";
318   TestFileReader file_reader(kFile);
319   reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
320 
321   std::vector<NsswitchReader::ServiceSpecification> services =
322       reader_.ReadAndParseHosts();
323 
324   EXPECT_THAT(services,
325               testing::ElementsAre(NsswitchReader::ServiceSpecification(
326                                        NsswitchReader::Service::kFiles),
327                                    NsswitchReader::ServiceSpecification(
328                                        NsswitchReader::Service::kUnknown),
329                                    NsswitchReader::ServiceSpecification(
330                                        NsswitchReader::Service::kMyHostname)));
331 }
332 
TEST_F(NsswitchReaderTest,ParsesUnrecognizedStatusAsUnknown)333 TEST_F(NsswitchReaderTest, ParsesUnrecognizedStatusAsUnknown) {
334   const std::string kFile =
335       "hosts: nis [HELLO=CONTINUE]\nshadow: service\ndatabase: cheese";
336   TestFileReader file_reader(kFile);
337   reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
338 
339   std::vector<NsswitchReader::ServiceSpecification> services =
340       reader_.ReadAndParseHosts();
341 
342   EXPECT_THAT(services,
343               testing::ElementsAre(NsswitchReader::ServiceSpecification(
344                   NsswitchReader::Service::kNis,
345                   {{/*negated=*/false, NsswitchReader::Status::kUnknown,
346                     NsswitchReader::Action::kContinue}})));
347 }
348 
TEST_F(NsswitchReaderTest,ParsesUnrecognizedActionAsUnknown)349 TEST_F(NsswitchReaderTest, ParsesUnrecognizedActionAsUnknown) {
350   const std::string kFile =
351       "more: service\nhosts: mdns6 [!UNAVAIL=HI]\nshadow: service";
352   TestFileReader file_reader(kFile);
353   reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
354 
355   std::vector<NsswitchReader::ServiceSpecification> services =
356       reader_.ReadAndParseHosts();
357 
358   EXPECT_THAT(services,
359               testing::ElementsAre(NsswitchReader::ServiceSpecification(
360                   NsswitchReader::Service::kMdns6,
361                   {{/*negated=*/true, NsswitchReader::Status::kUnavailable,
362                     NsswitchReader::Action::kUnknown}})));
363 }
364 
TEST_F(NsswitchReaderTest,ParsesInvalidActionsAsUnknown)365 TEST_F(NsswitchReaderTest, ParsesInvalidActionsAsUnknown) {
366   const std::string kFile = "hosts: mdns_minimal [a=b=c] nis";
367   TestFileReader file_reader(kFile);
368   reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
369 
370   std::vector<NsswitchReader::ServiceSpecification> services =
371       reader_.ReadAndParseHosts();
372 
373   EXPECT_THAT(
374       services,
375       testing::ElementsAre(
376           NsswitchReader::ServiceSpecification(
377               NsswitchReader::Service::kMdnsMinimal,
378               {{/*negated=*/false, NsswitchReader::Status::kUnknown,
379                 NsswitchReader::Action::kUnknown}}),
380           NsswitchReader::ServiceSpecification(NsswitchReader::Service::kNis)));
381 }
382 
TEST_F(NsswitchReaderTest,IgnoresInvalidlyClosedActions)383 TEST_F(NsswitchReaderTest, IgnoresInvalidlyClosedActions) {
384   const std::string kFile = "hosts: myhostname [SUCCESS=MERGE";
385   TestFileReader file_reader(kFile);
386   reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
387 
388   std::vector<NsswitchReader::ServiceSpecification> services =
389       reader_.ReadAndParseHosts();
390 
391   EXPECT_THAT(services,
392               testing::ElementsAre(NsswitchReader::ServiceSpecification(
393                   NsswitchReader::Service::kMyHostname,
394                   {{/*negated=*/false, NsswitchReader::Status::kSuccess,
395                     NsswitchReader::Action::kMerge}})));
396 }
397 
TEST_F(NsswitchReaderTest,ParsesServicesAfterInvalidlyClosedActionsAsUnknown)398 TEST_F(NsswitchReaderTest, ParsesServicesAfterInvalidlyClosedActionsAsUnknown) {
399   const std::string kFile = "hosts: resolve [SUCCESS=CONTINUE dns";
400   TestFileReader file_reader(kFile);
401   reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
402 
403   std::vector<NsswitchReader::ServiceSpecification> services =
404       reader_.ReadAndParseHosts();
405 
406   EXPECT_THAT(services,
407               testing::ElementsAre(NsswitchReader::ServiceSpecification(
408                   NsswitchReader::Service::kResolve,
409                   {{/*negated=*/false, NsswitchReader::Status::kSuccess,
410                     NsswitchReader::Action::kContinue},
411                    {/*negated=*/false, NsswitchReader::Status::kUnknown,
412                     NsswitchReader::Action::kUnknown}})));
413 }
414 
TEST_F(NsswitchReaderTest,IgnoresComments)415 TEST_F(NsswitchReaderTest, IgnoresComments) {
416   const std::string kFile =
417       "#hosts: files super_awesome_service myhostname\nnetmask: service";
418   TestFileReader file_reader(kFile);
419   reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
420 
421   std::vector<NsswitchReader::ServiceSpecification> services =
422       reader_.ReadAndParseHosts();
423 
424   // Expect "files dns" due to not finding an uncommented "hosts:" row.
425   EXPECT_THAT(
426       services,
427       testing::ElementsAre(
428           NsswitchReader::ServiceSpecification(NsswitchReader::Service::kFiles),
429           NsswitchReader::ServiceSpecification(NsswitchReader::Service::kDns)));
430 }
431 
TEST_F(NsswitchReaderTest,IgnoresEndOfLineComments)432 TEST_F(NsswitchReaderTest, IgnoresEndOfLineComments) {
433   const std::string kFile =
434       "hosts: files super_awesome_service myhostname # dns";
435   TestFileReader file_reader(kFile);
436   reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
437 
438   std::vector<NsswitchReader::ServiceSpecification> services =
439       reader_.ReadAndParseHosts();
440 
441   EXPECT_THAT(services,
442               testing::ElementsAre(NsswitchReader::ServiceSpecification(
443                                        NsswitchReader::Service::kFiles),
444                                    NsswitchReader::ServiceSpecification(
445                                        NsswitchReader::Service::kUnknown),
446                                    NsswitchReader::ServiceSpecification(
447                                        NsswitchReader::Service::kMyHostname)));
448 }
449 
TEST_F(NsswitchReaderTest,IgnoresCapitalization)450 TEST_F(NsswitchReaderTest, IgnoresCapitalization) {
451   const std::string kFile = "HoStS: mDNS6 [!uNaVaIl=MeRgE]";
452   TestFileReader file_reader(kFile);
453   reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
454 
455   std::vector<NsswitchReader::ServiceSpecification> services =
456       reader_.ReadAndParseHosts();
457 
458   EXPECT_THAT(services,
459               testing::ElementsAre(NsswitchReader::ServiceSpecification(
460                   NsswitchReader::Service::kMdns6,
461                   {{/*negated=*/true, NsswitchReader::Status::kUnavailable,
462                     NsswitchReader::Action::kMerge}})));
463 }
464 
TEST_F(NsswitchReaderTest,IgnoresEmptyActions)465 TEST_F(NsswitchReaderTest, IgnoresEmptyActions) {
466   const std::string kFile = "hosts: mdns_minimal [ \t ][] [ ]";
467   TestFileReader file_reader(kFile);
468   reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
469 
470   std::vector<NsswitchReader::ServiceSpecification> services =
471       reader_.ReadAndParseHosts();
472 
473   EXPECT_THAT(services,
474               testing::ElementsAre(NsswitchReader::ServiceSpecification(
475                   NsswitchReader::Service::kMdnsMinimal)));
476 }
477 
TEST_F(NsswitchReaderTest,IgnoresRepeatedActionBrackets)478 TEST_F(NsswitchReaderTest, IgnoresRepeatedActionBrackets) {
479   const std::string kFile = "hosts: mdns [[SUCCESS=RETURN]]]dns";
480   TestFileReader file_reader(kFile);
481   reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
482 
483   std::vector<NsswitchReader::ServiceSpecification> services =
484       reader_.ReadAndParseHosts();
485 
486   EXPECT_THAT(
487       services,
488       testing::ElementsAre(
489           NsswitchReader::ServiceSpecification(
490               NsswitchReader::Service::kMdns,
491               {{/*negated=*/false, NsswitchReader::Status::kSuccess,
492                 NsswitchReader::Action::kReturn}}),
493           NsswitchReader::ServiceSpecification(NsswitchReader::Service::kDns)));
494 }
495 
TEST_F(NsswitchReaderTest,IgnoresRepeatedActionBracketsWithWhitespace)496 TEST_F(NsswitchReaderTest, IgnoresRepeatedActionBracketsWithWhitespace) {
497   const std::string kFile = "hosts: mdns [ [ SUCCESS=RETURN ]\t] ]\t  mdns6";
498   TestFileReader file_reader(kFile);
499   reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
500 
501   std::vector<NsswitchReader::ServiceSpecification> services =
502       reader_.ReadAndParseHosts();
503 
504   EXPECT_THAT(services,
505               testing::ElementsAre(
506                   NsswitchReader::ServiceSpecification(
507                       NsswitchReader::Service::kMdns,
508                       {{/*negated=*/false, NsswitchReader::Status::kSuccess,
509                         NsswitchReader::Action::kReturn}}),
510                   NsswitchReader::ServiceSpecification(
511                       NsswitchReader::Service::kMdns6)));
512 }
513 
TEST_F(NsswitchReaderTest,RejectsNonSensicalActionBrackets)514 TEST_F(NsswitchReaderTest, RejectsNonSensicalActionBrackets) {
515   const std::string kFile = "hosts: mdns4 [UNAVAIL[=MERGE]]";
516   TestFileReader file_reader(kFile);
517   reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
518 
519   std::vector<NsswitchReader::ServiceSpecification> services =
520       reader_.ReadAndParseHosts();
521 
522   EXPECT_THAT(services,
523               testing::ElementsAre(NsswitchReader::ServiceSpecification(
524                   NsswitchReader::Service::kMdns4,
525                   {{/*negated=*/false, NsswitchReader::Status::kUnknown,
526                     NsswitchReader::Action::kMerge}})));
527 }
528 
TEST_F(NsswitchReaderTest,RejectsServicesWithBrackets)529 TEST_F(NsswitchReaderTest, RejectsServicesWithBrackets) {
530   const std::string kFile = "hosts: se]r[vice[name";
531   TestFileReader file_reader(kFile);
532   reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
533 
534   std::vector<NsswitchReader::ServiceSpecification> services =
535       reader_.ReadAndParseHosts();
536 
537   EXPECT_THAT(services,
538               testing::ElementsAre(NsswitchReader::ServiceSpecification(
539                   NsswitchReader::Service::kUnknown)));
540 }
541 
542 // Other than the case of repeating opening brackets, nested brackets are not
543 // valid and should just get treated as part of an action label.
TEST_F(NsswitchReaderTest,RejectsNestedActionBrackets)544 TEST_F(NsswitchReaderTest, RejectsNestedActionBrackets) {
545   const std::string kFile =
546       "hosts: nis [SUCCESS=RETURN [NOTFOUND=CONTINUE] UNAVAIL=MERGE]";
547   TestFileReader file_reader(kFile);
548   reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
549 
550   std::vector<NsswitchReader::ServiceSpecification> services =
551       reader_.ReadAndParseHosts();
552 
553   EXPECT_THAT(services,
554               testing::ElementsAre(
555                   NsswitchReader::ServiceSpecification(
556                       NsswitchReader::Service::kNis,
557                       {{/*negated=*/false, NsswitchReader::Status::kSuccess,
558                         NsswitchReader::Action::kReturn},
559                        {/*negated=*/false, NsswitchReader::Status::kUnknown,
560                         NsswitchReader::Action::kContinue}}),
561                   NsswitchReader::ServiceSpecification(
562                       NsswitchReader::Service::kUnknown)));
563 }
564 
TEST_F(NsswitchReaderTest,IgnoresEmptyActionWithRepeatedBrackets)565 TEST_F(NsswitchReaderTest, IgnoresEmptyActionWithRepeatedBrackets) {
566   const std::string kFile = "hosts: files [[[]]]] mdns";
567   TestFileReader file_reader(kFile);
568   reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
569 
570   std::vector<NsswitchReader::ServiceSpecification> services =
571       reader_.ReadAndParseHosts();
572 
573   EXPECT_THAT(services,
574               testing::ElementsAre(NsswitchReader::ServiceSpecification(
575                                        NsswitchReader::Service::kFiles),
576                                    NsswitchReader::ServiceSpecification(
577                                        NsswitchReader::Service::kMdns)));
578 }
579 
TEST_F(NsswitchReaderTest,IgnoresEmptyActionAtEndOfString)580 TEST_F(NsswitchReaderTest, IgnoresEmptyActionAtEndOfString) {
581   const std::string kFile = "hosts: dns [[";
582   TestFileReader file_reader(kFile);
583   reader_.set_file_read_call_for_testing(file_reader.GetFileReadCall());
584 
585   std::vector<NsswitchReader::ServiceSpecification> services =
586       reader_.ReadAndParseHosts();
587 
588   EXPECT_THAT(services,
589               testing::ElementsAre(NsswitchReader::ServiceSpecification(
590                   NsswitchReader::Service::kDns)));
591 }
592 
593 }  // namespace
594 }  // namespace net
595