1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 //     * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 //     * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 // Author: kenton@google.com (Kenton Varda)
32 //  Based on original Protocol Buffers design by
33 //  Sanjay Ghemawat, Jeff Dean, and others.
34 //
35 // This file makes extensive use of RFC 3092.  :)
36 
37 #include <algorithm>
38 
39 #include <google/protobuf/descriptor_database.h>
40 #include <google/protobuf/descriptor.h>
41 #include <google/protobuf/descriptor.pb.h>
42 #include <google/protobuf/text_format.h>
43 #include <google/protobuf/stubs/strutil.h>
44 
45 #include <google/protobuf/stubs/common.h>
46 #include <google/protobuf/testing/googletest.h>
47 #include <gtest/gtest.h>
48 
49 namespace google {
50 namespace protobuf {
51 namespace {
52 
AddToDatabase(SimpleDescriptorDatabase * database,const char * file_text)53 static void AddToDatabase(SimpleDescriptorDatabase* database,
54                           const char* file_text) {
55   FileDescriptorProto file_proto;
56   EXPECT_TRUE(TextFormat::ParseFromString(file_text, &file_proto));
57   database->Add(file_proto);
58 }
59 
ExpectContainsType(const FileDescriptorProto & proto,const string & type_name)60 static void ExpectContainsType(const FileDescriptorProto& proto,
61                                const string& type_name) {
62   for (int i = 0; i < proto.message_type_size(); i++) {
63     if (proto.message_type(i).name() == type_name) return;
64   }
65   ADD_FAILURE() << "\"" << proto.name()
66                 << "\" did not contain expected type \""
67                 << type_name << "\".";
68 }
69 
70 // ===================================================================
71 
72 #if GTEST_HAS_PARAM_TEST
73 
74 // SimpleDescriptorDatabase, EncodedDescriptorDatabase, and
75 // DescriptorPoolDatabase call for very similar tests.  Instead of writing
76 // three nearly-identical sets of tests, we use parameterized tests to apply
77 // the same code to all three.
78 
79 // The parameterized test runs against a DescriptarDatabaseTestCase.  We have
80 // implementations for each of the three classes we want to test.
81 class DescriptorDatabaseTestCase {
82  public:
~DescriptorDatabaseTestCase()83   virtual ~DescriptorDatabaseTestCase() {}
84 
85   virtual DescriptorDatabase* GetDatabase() = 0;
86   virtual bool AddToDatabase(const FileDescriptorProto& file) = 0;
87 };
88 
89 // Factory function type.
90 typedef DescriptorDatabaseTestCase* DescriptorDatabaseTestCaseFactory();
91 
92 // Specialization for SimpleDescriptorDatabase.
93 class SimpleDescriptorDatabaseTestCase : public DescriptorDatabaseTestCase {
94  public:
New()95   static DescriptorDatabaseTestCase* New() {
96     return new SimpleDescriptorDatabaseTestCase;
97   }
98 
~SimpleDescriptorDatabaseTestCase()99   virtual ~SimpleDescriptorDatabaseTestCase() {}
100 
GetDatabase()101   virtual DescriptorDatabase* GetDatabase() {
102     return &database_;
103   }
AddToDatabase(const FileDescriptorProto & file)104   virtual bool AddToDatabase(const FileDescriptorProto& file) {
105     return database_.Add(file);
106   }
107 
108  private:
109   SimpleDescriptorDatabase database_;
110 };
111 
112 // Specialization for EncodedDescriptorDatabase.
113 class EncodedDescriptorDatabaseTestCase : public DescriptorDatabaseTestCase {
114  public:
New()115   static DescriptorDatabaseTestCase* New() {
116     return new EncodedDescriptorDatabaseTestCase;
117   }
118 
~EncodedDescriptorDatabaseTestCase()119   virtual ~EncodedDescriptorDatabaseTestCase() {}
120 
GetDatabase()121   virtual DescriptorDatabase* GetDatabase() {
122     return &database_;
123   }
AddToDatabase(const FileDescriptorProto & file)124   virtual bool AddToDatabase(const FileDescriptorProto& file) {
125     string data;
126     file.SerializeToString(&data);
127     return database_.AddCopy(data.data(), data.size());
128   }
129 
130  private:
131   EncodedDescriptorDatabase database_;
132 };
133 
134 // Specialization for DescriptorPoolDatabase.
135 class DescriptorPoolDatabaseTestCase : public DescriptorDatabaseTestCase {
136  public:
New()137   static DescriptorDatabaseTestCase* New() {
138     return new EncodedDescriptorDatabaseTestCase;
139   }
140 
DescriptorPoolDatabaseTestCase()141   DescriptorPoolDatabaseTestCase() : database_(pool_) {}
~DescriptorPoolDatabaseTestCase()142   virtual ~DescriptorPoolDatabaseTestCase() {}
143 
GetDatabase()144   virtual DescriptorDatabase* GetDatabase() {
145     return &database_;
146   }
AddToDatabase(const FileDescriptorProto & file)147   virtual bool AddToDatabase(const FileDescriptorProto& file) {
148     return pool_.BuildFile(file);
149   }
150 
151  private:
152   DescriptorPool pool_;
153   DescriptorPoolDatabase database_;
154 };
155 
156 // -------------------------------------------------------------------
157 
158 class DescriptorDatabaseTest
159     : public testing::TestWithParam<DescriptorDatabaseTestCaseFactory*> {
160  protected:
SetUp()161   virtual void SetUp() {
162     test_case_.reset(GetParam()());
163     database_ = test_case_->GetDatabase();
164   }
165 
AddToDatabase(const char * file_descriptor_text)166   void AddToDatabase(const char* file_descriptor_text) {
167     FileDescriptorProto file_proto;
168     EXPECT_TRUE(TextFormat::ParseFromString(file_descriptor_text, &file_proto));
169     EXPECT_TRUE(test_case_->AddToDatabase(file_proto));
170   }
171 
AddToDatabaseWithError(const char * file_descriptor_text)172   void AddToDatabaseWithError(const char* file_descriptor_text) {
173     FileDescriptorProto file_proto;
174     EXPECT_TRUE(TextFormat::ParseFromString(file_descriptor_text, &file_proto));
175     EXPECT_FALSE(test_case_->AddToDatabase(file_proto));
176   }
177 
178   scoped_ptr<DescriptorDatabaseTestCase> test_case_;
179   DescriptorDatabase* database_;
180 };
181 
TEST_P(DescriptorDatabaseTest,FindFileByName)182 TEST_P(DescriptorDatabaseTest, FindFileByName) {
183   AddToDatabase(
184     "name: \"foo.proto\" "
185     "message_type { name:\"Foo\" }");
186   AddToDatabase(
187     "name: \"bar.proto\" "
188     "message_type { name:\"Bar\" }");
189 
190   {
191     FileDescriptorProto file;
192     EXPECT_TRUE(database_->FindFileByName("foo.proto", &file));
193     EXPECT_EQ("foo.proto", file.name());
194     ExpectContainsType(file, "Foo");
195   }
196 
197   {
198     FileDescriptorProto file;
199     EXPECT_TRUE(database_->FindFileByName("bar.proto", &file));
200     EXPECT_EQ("bar.proto", file.name());
201     ExpectContainsType(file, "Bar");
202   }
203 
204   {
205     // Fails to find undefined files.
206     FileDescriptorProto file;
207     EXPECT_FALSE(database_->FindFileByName("baz.proto", &file));
208   }
209 }
210 
TEST_P(DescriptorDatabaseTest,FindFileContainingSymbol)211 TEST_P(DescriptorDatabaseTest, FindFileContainingSymbol) {
212   AddToDatabase(
213     "name: \"foo.proto\" "
214     "message_type { "
215     "  name: \"Foo\" "
216     "  field { name:\"qux\" }"
217     "  nested_type { name: \"Grault\" } "
218     "  enum_type { name: \"Garply\" } "
219     "} "
220     "enum_type { "
221     "  name: \"Waldo\" "
222     "  value { name:\"FRED\" } "
223     "} "
224     "extension { name: \"plugh\" } "
225     "service { "
226     "  name: \"Xyzzy\" "
227     "  method { name: \"Thud\" } "
228     "}"
229     );
230   AddToDatabase(
231     "name: \"bar.proto\" "
232     "package: \"corge\" "
233     "message_type { name: \"Bar\" }");
234 
235   {
236     FileDescriptorProto file;
237     EXPECT_TRUE(database_->FindFileContainingSymbol("Foo", &file));
238     EXPECT_EQ("foo.proto", file.name());
239   }
240 
241   {
242     // Can find fields.
243     FileDescriptorProto file;
244     EXPECT_TRUE(database_->FindFileContainingSymbol("Foo.qux", &file));
245     EXPECT_EQ("foo.proto", file.name());
246   }
247 
248   {
249     // Can find nested types.
250     FileDescriptorProto file;
251     EXPECT_TRUE(database_->FindFileContainingSymbol("Foo.Grault", &file));
252     EXPECT_EQ("foo.proto", file.name());
253   }
254 
255   {
256     // Can find nested enums.
257     FileDescriptorProto file;
258     EXPECT_TRUE(database_->FindFileContainingSymbol("Foo.Garply", &file));
259     EXPECT_EQ("foo.proto", file.name());
260   }
261 
262   {
263     // Can find enum types.
264     FileDescriptorProto file;
265     EXPECT_TRUE(database_->FindFileContainingSymbol("Waldo", &file));
266     EXPECT_EQ("foo.proto", file.name());
267   }
268 
269   {
270     // Can find enum values.
271     FileDescriptorProto file;
272     EXPECT_TRUE(database_->FindFileContainingSymbol("Waldo.FRED", &file));
273     EXPECT_EQ("foo.proto", file.name());
274   }
275 
276   {
277     // Can find extensions.
278     FileDescriptorProto file;
279     EXPECT_TRUE(database_->FindFileContainingSymbol("plugh", &file));
280     EXPECT_EQ("foo.proto", file.name());
281   }
282 
283   {
284     // Can find services.
285     FileDescriptorProto file;
286     EXPECT_TRUE(database_->FindFileContainingSymbol("Xyzzy", &file));
287     EXPECT_EQ("foo.proto", file.name());
288   }
289 
290   {
291     // Can find methods.
292     FileDescriptorProto file;
293     EXPECT_TRUE(database_->FindFileContainingSymbol("Xyzzy.Thud", &file));
294     EXPECT_EQ("foo.proto", file.name());
295   }
296 
297   {
298     // Can find things in packages.
299     FileDescriptorProto file;
300     EXPECT_TRUE(database_->FindFileContainingSymbol("corge.Bar", &file));
301     EXPECT_EQ("bar.proto", file.name());
302   }
303 
304   {
305     // Fails to find undefined symbols.
306     FileDescriptorProto file;
307     EXPECT_FALSE(database_->FindFileContainingSymbol("Baz", &file));
308   }
309 
310   {
311     // Names must be fully-qualified.
312     FileDescriptorProto file;
313     EXPECT_FALSE(database_->FindFileContainingSymbol("Bar", &file));
314   }
315 }
316 
TEST_P(DescriptorDatabaseTest,FindFileContainingExtension)317 TEST_P(DescriptorDatabaseTest, FindFileContainingExtension) {
318   AddToDatabase(
319     "name: \"foo.proto\" "
320     "message_type { "
321     "  name: \"Foo\" "
322     "  extension_range { start: 1 end: 1000 } "
323     "  extension { name:\"qux\" label:LABEL_OPTIONAL type:TYPE_INT32 number:5 "
324     "              extendee: \".Foo\" }"
325     "}");
326   AddToDatabase(
327     "name: \"bar.proto\" "
328     "package: \"corge\" "
329     "dependency: \"foo.proto\" "
330     "message_type { "
331     "  name: \"Bar\" "
332     "  extension_range { start: 1 end: 1000 } "
333     "} "
334     "extension { name:\"grault\" extendee: \".Foo\"       number:32 } "
335     "extension { name:\"garply\" extendee: \".corge.Bar\" number:70 } "
336     "extension { name:\"waldo\"  extendee: \"Bar\"        number:56 } ");
337 
338   {
339     FileDescriptorProto file;
340     EXPECT_TRUE(database_->FindFileContainingExtension("Foo", 5, &file));
341     EXPECT_EQ("foo.proto", file.name());
342   }
343 
344   {
345     FileDescriptorProto file;
346     EXPECT_TRUE(database_->FindFileContainingExtension("Foo", 32, &file));
347     EXPECT_EQ("bar.proto", file.name());
348   }
349 
350   {
351     // Can find extensions for qualified type names.
352     FileDescriptorProto file;
353     EXPECT_TRUE(database_->FindFileContainingExtension("corge.Bar", 70, &file));
354     EXPECT_EQ("bar.proto", file.name());
355   }
356 
357   {
358     // Can't find extensions whose extendee was not fully-qualified in the
359     // FileDescriptorProto.
360     FileDescriptorProto file;
361     EXPECT_FALSE(database_->FindFileContainingExtension("Bar", 56, &file));
362     EXPECT_FALSE(
363         database_->FindFileContainingExtension("corge.Bar", 56, &file));
364   }
365 
366   {
367     // Can't find non-existent extension numbers.
368     FileDescriptorProto file;
369     EXPECT_FALSE(database_->FindFileContainingExtension("Foo", 12, &file));
370   }
371 
372   {
373     // Can't find extensions for non-existent types.
374     FileDescriptorProto file;
375     EXPECT_FALSE(
376         database_->FindFileContainingExtension("NoSuchType", 5, &file));
377   }
378 
379   {
380     // Can't find extensions for unqualified type names.
381     FileDescriptorProto file;
382     EXPECT_FALSE(database_->FindFileContainingExtension("Bar", 70, &file));
383   }
384 }
385 
TEST_P(DescriptorDatabaseTest,FindAllExtensionNumbers)386 TEST_P(DescriptorDatabaseTest, FindAllExtensionNumbers) {
387   AddToDatabase(
388     "name: \"foo.proto\" "
389     "message_type { "
390     "  name: \"Foo\" "
391     "  extension_range { start: 1 end: 1000 } "
392     "  extension { name:\"qux\" label:LABEL_OPTIONAL type:TYPE_INT32 number:5 "
393     "              extendee: \".Foo\" }"
394     "}");
395   AddToDatabase(
396     "name: \"bar.proto\" "
397     "package: \"corge\" "
398     "dependency: \"foo.proto\" "
399     "message_type { "
400     "  name: \"Bar\" "
401     "  extension_range { start: 1 end: 1000 } "
402     "} "
403     "extension { name:\"grault\" extendee: \".Foo\"       number:32 } "
404     "extension { name:\"garply\" extendee: \".corge.Bar\" number:70 } "
405     "extension { name:\"waldo\"  extendee: \"Bar\"        number:56 } ");
406 
407   {
408     vector<int> numbers;
409     EXPECT_TRUE(database_->FindAllExtensionNumbers("Foo", &numbers));
410     ASSERT_EQ(2, numbers.size());
411     sort(numbers.begin(), numbers.end());
412     EXPECT_EQ(5, numbers[0]);
413     EXPECT_EQ(32, numbers[1]);
414   }
415 
416   {
417     vector<int> numbers;
418     EXPECT_TRUE(database_->FindAllExtensionNumbers("corge.Bar", &numbers));
419     // Note: won't find extension 56 due to the name not being fully qualified.
420     ASSERT_EQ(1, numbers.size());
421     EXPECT_EQ(70, numbers[0]);
422   }
423 
424   {
425     // Can't find extensions for non-existent types.
426     vector<int> numbers;
427     EXPECT_FALSE(database_->FindAllExtensionNumbers("NoSuchType", &numbers));
428   }
429 
430   {
431     // Can't find extensions for unqualified types.
432     vector<int> numbers;
433     EXPECT_FALSE(database_->FindAllExtensionNumbers("Bar", &numbers));
434   }
435 }
436 
TEST_P(DescriptorDatabaseTest,ConflictingFileError)437 TEST_P(DescriptorDatabaseTest, ConflictingFileError) {
438   AddToDatabase(
439     "name: \"foo.proto\" "
440     "message_type { "
441     "  name: \"Foo\" "
442     "}");
443   AddToDatabaseWithError(
444     "name: \"foo.proto\" "
445     "message_type { "
446     "  name: \"Bar\" "
447     "}");
448 }
449 
TEST_P(DescriptorDatabaseTest,ConflictingTypeError)450 TEST_P(DescriptorDatabaseTest, ConflictingTypeError) {
451   AddToDatabase(
452     "name: \"foo.proto\" "
453     "message_type { "
454     "  name: \"Foo\" "
455     "}");
456   AddToDatabaseWithError(
457     "name: \"bar.proto\" "
458     "message_type { "
459     "  name: \"Foo\" "
460     "}");
461 }
462 
TEST_P(DescriptorDatabaseTest,ConflictingExtensionError)463 TEST_P(DescriptorDatabaseTest, ConflictingExtensionError) {
464   AddToDatabase(
465     "name: \"foo.proto\" "
466     "extension { name:\"foo\" label:LABEL_OPTIONAL type:TYPE_INT32 number:5 "
467     "            extendee: \".Foo\" }");
468   AddToDatabaseWithError(
469     "name: \"bar.proto\" "
470     "extension { name:\"bar\" label:LABEL_OPTIONAL type:TYPE_INT32 number:5 "
471     "            extendee: \".Foo\" }");
472 }
473 
474 INSTANTIATE_TEST_CASE_P(Simple, DescriptorDatabaseTest,
475     testing::Values(&SimpleDescriptorDatabaseTestCase::New));
476 INSTANTIATE_TEST_CASE_P(MemoryConserving, DescriptorDatabaseTest,
477     testing::Values(&EncodedDescriptorDatabaseTestCase::New));
478 INSTANTIATE_TEST_CASE_P(Pool, DescriptorDatabaseTest,
479     testing::Values(&DescriptorPoolDatabaseTestCase::New));
480 
481 #endif  // GTEST_HAS_PARAM_TEST
482 
TEST(EncodedDescriptorDatabaseExtraTest,FindNameOfFileContainingSymbol)483 TEST(EncodedDescriptorDatabaseExtraTest, FindNameOfFileContainingSymbol) {
484   // Create two files, one of which is in two parts.
485   FileDescriptorProto file1, file2a, file2b;
486   file1.set_name("foo.proto");
487   file1.set_package("foo");
488   file1.add_message_type()->set_name("Foo");
489   file2a.set_name("bar.proto");
490   file2b.set_package("bar");
491   file2b.add_message_type()->set_name("Bar");
492 
493   // Normal serialization allows our optimization to kick in.
494   string data1 = file1.SerializeAsString();
495 
496   // Force out-of-order serialization to test slow path.
497   string data2 = file2b.SerializeAsString() + file2a.SerializeAsString();
498 
499   // Create EncodedDescriptorDatabase containing both files.
500   EncodedDescriptorDatabase db;
501   db.Add(data1.data(), data1.size());
502   db.Add(data2.data(), data2.size());
503 
504   // Test!
505   string filename;
506   EXPECT_TRUE(db.FindNameOfFileContainingSymbol("foo.Foo", &filename));
507   EXPECT_EQ("foo.proto", filename);
508   EXPECT_TRUE(db.FindNameOfFileContainingSymbol("foo.Foo.Blah", &filename));
509   EXPECT_EQ("foo.proto", filename);
510   EXPECT_TRUE(db.FindNameOfFileContainingSymbol("bar.Bar", &filename));
511   EXPECT_EQ("bar.proto", filename);
512   EXPECT_FALSE(db.FindNameOfFileContainingSymbol("foo", &filename));
513   EXPECT_FALSE(db.FindNameOfFileContainingSymbol("bar", &filename));
514   EXPECT_FALSE(db.FindNameOfFileContainingSymbol("baz.Baz", &filename));
515 }
516 
517 // ===================================================================
518 
519 class MergedDescriptorDatabaseTest : public testing::Test {
520  protected:
MergedDescriptorDatabaseTest()521   MergedDescriptorDatabaseTest()
522     : forward_merged_(&database1_, &database2_),
523       reverse_merged_(&database2_, &database1_) {}
524 
SetUp()525   virtual void SetUp() {
526     AddToDatabase(&database1_,
527       "name: \"foo.proto\" "
528       "message_type { name:\"Foo\" extension_range { start: 1 end: 100 } } "
529       "extension { name:\"foo_ext\" extendee: \".Foo\" number:3 "
530       "            label:LABEL_OPTIONAL type:TYPE_INT32 } ");
531     AddToDatabase(&database2_,
532       "name: \"bar.proto\" "
533       "message_type { name:\"Bar\" extension_range { start: 1 end: 100 } } "
534       "extension { name:\"bar_ext\" extendee: \".Bar\" number:5 "
535       "            label:LABEL_OPTIONAL type:TYPE_INT32 } ");
536 
537     // baz.proto exists in both pools, with different definitions.
538     AddToDatabase(&database1_,
539       "name: \"baz.proto\" "
540       "message_type { name:\"Baz\" extension_range { start: 1 end: 100 } } "
541       "message_type { name:\"FromPool1\" } "
542       "extension { name:\"baz_ext\" extendee: \".Baz\" number:12 "
543       "            label:LABEL_OPTIONAL type:TYPE_INT32 } "
544       "extension { name:\"database1_only_ext\" extendee: \".Baz\" number:13 "
545       "            label:LABEL_OPTIONAL type:TYPE_INT32 } ");
546     AddToDatabase(&database2_,
547       "name: \"baz.proto\" "
548       "message_type { name:\"Baz\" extension_range { start: 1 end: 100 } } "
549       "message_type { name:\"FromPool2\" } "
550       "extension { name:\"baz_ext\" extendee: \".Baz\" number:12 "
551       "            label:LABEL_OPTIONAL type:TYPE_INT32 } ");
552   }
553 
554   SimpleDescriptorDatabase database1_;
555   SimpleDescriptorDatabase database2_;
556 
557   MergedDescriptorDatabase forward_merged_;
558   MergedDescriptorDatabase reverse_merged_;
559 };
560 
TEST_F(MergedDescriptorDatabaseTest,FindFileByName)561 TEST_F(MergedDescriptorDatabaseTest, FindFileByName) {
562   {
563     // Can find file that is only in database1_.
564     FileDescriptorProto file;
565     EXPECT_TRUE(forward_merged_.FindFileByName("foo.proto", &file));
566     EXPECT_EQ("foo.proto", file.name());
567     ExpectContainsType(file, "Foo");
568   }
569 
570   {
571     // Can find file that is only in database2_.
572     FileDescriptorProto file;
573     EXPECT_TRUE(forward_merged_.FindFileByName("bar.proto", &file));
574     EXPECT_EQ("bar.proto", file.name());
575     ExpectContainsType(file, "Bar");
576   }
577 
578   {
579     // In forward_merged_, database1_'s baz.proto takes precedence.
580     FileDescriptorProto file;
581     EXPECT_TRUE(forward_merged_.FindFileByName("baz.proto", &file));
582     EXPECT_EQ("baz.proto", file.name());
583     ExpectContainsType(file, "FromPool1");
584   }
585 
586   {
587     // In reverse_merged_, database2_'s baz.proto takes precedence.
588     FileDescriptorProto file;
589     EXPECT_TRUE(reverse_merged_.FindFileByName("baz.proto", &file));
590     EXPECT_EQ("baz.proto", file.name());
591     ExpectContainsType(file, "FromPool2");
592   }
593 
594   {
595     // Can't find non-existent file.
596     FileDescriptorProto file;
597     EXPECT_FALSE(forward_merged_.FindFileByName("no_such.proto", &file));
598   }
599 }
600 
TEST_F(MergedDescriptorDatabaseTest,FindFileContainingSymbol)601 TEST_F(MergedDescriptorDatabaseTest, FindFileContainingSymbol) {
602   {
603     // Can find file that is only in database1_.
604     FileDescriptorProto file;
605     EXPECT_TRUE(forward_merged_.FindFileContainingSymbol("Foo", &file));
606     EXPECT_EQ("foo.proto", file.name());
607     ExpectContainsType(file, "Foo");
608   }
609 
610   {
611     // Can find file that is only in database2_.
612     FileDescriptorProto file;
613     EXPECT_TRUE(forward_merged_.FindFileContainingSymbol("Bar", &file));
614     EXPECT_EQ("bar.proto", file.name());
615     ExpectContainsType(file, "Bar");
616   }
617 
618   {
619     // In forward_merged_, database1_'s baz.proto takes precedence.
620     FileDescriptorProto file;
621     EXPECT_TRUE(forward_merged_.FindFileContainingSymbol("Baz", &file));
622     EXPECT_EQ("baz.proto", file.name());
623     ExpectContainsType(file, "FromPool1");
624   }
625 
626   {
627     // In reverse_merged_, database2_'s baz.proto takes precedence.
628     FileDescriptorProto file;
629     EXPECT_TRUE(reverse_merged_.FindFileContainingSymbol("Baz", &file));
630     EXPECT_EQ("baz.proto", file.name());
631     ExpectContainsType(file, "FromPool2");
632   }
633 
634   {
635     // FromPool1 only shows up in forward_merged_ because it is masked by
636     // database2_'s baz.proto in reverse_merged_.
637     FileDescriptorProto file;
638     EXPECT_TRUE(forward_merged_.FindFileContainingSymbol("FromPool1", &file));
639     EXPECT_FALSE(reverse_merged_.FindFileContainingSymbol("FromPool1", &file));
640   }
641 
642   {
643     // Can't find non-existent symbol.
644     FileDescriptorProto file;
645     EXPECT_FALSE(
646       forward_merged_.FindFileContainingSymbol("NoSuchType", &file));
647   }
648 }
649 
TEST_F(MergedDescriptorDatabaseTest,FindFileContainingExtension)650 TEST_F(MergedDescriptorDatabaseTest, FindFileContainingExtension) {
651   {
652     // Can find file that is only in database1_.
653     FileDescriptorProto file;
654     EXPECT_TRUE(
655       forward_merged_.FindFileContainingExtension("Foo", 3, &file));
656     EXPECT_EQ("foo.proto", file.name());
657     ExpectContainsType(file, "Foo");
658   }
659 
660   {
661     // Can find file that is only in database2_.
662     FileDescriptorProto file;
663     EXPECT_TRUE(
664       forward_merged_.FindFileContainingExtension("Bar", 5, &file));
665     EXPECT_EQ("bar.proto", file.name());
666     ExpectContainsType(file, "Bar");
667   }
668 
669   {
670     // In forward_merged_, database1_'s baz.proto takes precedence.
671     FileDescriptorProto file;
672     EXPECT_TRUE(
673       forward_merged_.FindFileContainingExtension("Baz", 12, &file));
674     EXPECT_EQ("baz.proto", file.name());
675     ExpectContainsType(file, "FromPool1");
676   }
677 
678   {
679     // In reverse_merged_, database2_'s baz.proto takes precedence.
680     FileDescriptorProto file;
681     EXPECT_TRUE(
682       reverse_merged_.FindFileContainingExtension("Baz", 12, &file));
683     EXPECT_EQ("baz.proto", file.name());
684     ExpectContainsType(file, "FromPool2");
685   }
686 
687   {
688     // Baz's extension 13 only shows up in forward_merged_ because it is
689     // masked by database2_'s baz.proto in reverse_merged_.
690     FileDescriptorProto file;
691     EXPECT_TRUE(forward_merged_.FindFileContainingExtension("Baz", 13, &file));
692     EXPECT_FALSE(reverse_merged_.FindFileContainingExtension("Baz", 13, &file));
693   }
694 
695   {
696     // Can't find non-existent extension.
697     FileDescriptorProto file;
698     EXPECT_FALSE(
699       forward_merged_.FindFileContainingExtension("Foo", 6, &file));
700   }
701 }
702 
TEST_F(MergedDescriptorDatabaseTest,FindAllExtensionNumbers)703 TEST_F(MergedDescriptorDatabaseTest, FindAllExtensionNumbers) {
704   {
705     // Message only has extension in database1_
706     vector<int> numbers;
707     EXPECT_TRUE(forward_merged_.FindAllExtensionNumbers("Foo", &numbers));
708     ASSERT_EQ(1, numbers.size());
709     EXPECT_EQ(3, numbers[0]);
710   }
711 
712   {
713     // Message only has extension in database2_
714     vector<int> numbers;
715     EXPECT_TRUE(forward_merged_.FindAllExtensionNumbers("Bar", &numbers));
716     ASSERT_EQ(1, numbers.size());
717     EXPECT_EQ(5, numbers[0]);
718   }
719 
720   {
721     // Merge results from the two databases.
722     vector<int> numbers;
723     EXPECT_TRUE(forward_merged_.FindAllExtensionNumbers("Baz", &numbers));
724     ASSERT_EQ(2, numbers.size());
725     sort(numbers.begin(), numbers.end());
726     EXPECT_EQ(12, numbers[0]);
727     EXPECT_EQ(13, numbers[1]);
728   }
729 
730   {
731     vector<int> numbers;
732     EXPECT_TRUE(reverse_merged_.FindAllExtensionNumbers("Baz", &numbers));
733     ASSERT_EQ(2, numbers.size());
734     sort(numbers.begin(), numbers.end());
735     EXPECT_EQ(12, numbers[0]);
736     EXPECT_EQ(13, numbers[1]);
737   }
738 
739   {
740     // Can't find extensions for a non-existent message.
741     vector<int> numbers;
742     EXPECT_FALSE(reverse_merged_.FindAllExtensionNumbers("Blah", &numbers));
743   }
744 }
745 
746 }  // anonymous namespace
747 }  // namespace protobuf
748 }  // namespace google
749