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