• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <google/protobuf/compiler/importer.h>
36 
37 #include <google/protobuf/stubs/hash.h>
38 #include <memory>
39 #ifndef _SHARED_PTR_H
40 #include <google/protobuf/stubs/shared_ptr.h>
41 #endif
42 
43 #include <google/protobuf/stubs/logging.h>
44 #include <google/protobuf/stubs/common.h>
45 #include <google/protobuf/testing/file.h>
46 #include <google/protobuf/testing/file.h>
47 #include <google/protobuf/testing/file.h>
48 #include <google/protobuf/io/zero_copy_stream_impl.h>
49 #include <google/protobuf/descriptor.h>
50 #include <google/protobuf/stubs/strutil.h>
51 #include <google/protobuf/stubs/substitute.h>
52 #include <google/protobuf/testing/googletest.h>
53 #include <gtest/gtest.h>
54 #include <google/protobuf/stubs/map_util.h>
55 
56 namespace google {
57 namespace protobuf {
58 namespace compiler {
59 
60 namespace {
61 
FileExists(const string & path)62 bool FileExists(const string& path) {
63   return File::Exists(path);
64 }
65 
66 #define EXPECT_SUBSTRING(needle, haystack) \
67   EXPECT_PRED_FORMAT2(testing::IsSubstring, (needle), (haystack))
68 
69 class MockErrorCollector : public MultiFileErrorCollector {
70  public:
MockErrorCollector()71   MockErrorCollector() {}
~MockErrorCollector()72   ~MockErrorCollector() {}
73 
74   string text_;
75   string warning_text_;
76 
77   // implements ErrorCollector ---------------------------------------
AddError(const string & filename,int line,int column,const string & message)78   void AddError(const string& filename, int line, int column,
79                 const string& message) {
80     strings::SubstituteAndAppend(&text_, "$0:$1:$2: $3\n",
81                                  filename, line, column, message);
82   }
83 
AddWarning(const string & filename,int line,int column,const string & message)84   void AddWarning(const string& filename, int line, int column,
85                   const string& message) {
86     strings::SubstituteAndAppend(&warning_text_, "$0:$1:$2: $3\n",
87                                  filename, line, column, message);
88   }
89 };
90 
91 // -------------------------------------------------------------------
92 
93 // A dummy implementation of SourceTree backed by a simple map.
94 class MockSourceTree : public SourceTree {
95  public:
MockSourceTree()96   MockSourceTree() {}
~MockSourceTree()97   ~MockSourceTree() {}
98 
AddFile(const string & name,const char * contents)99   void AddFile(const string& name, const char* contents) {
100     files_[name] = contents;
101   }
102 
103   // implements SourceTree -------------------------------------------
Open(const string & filename)104   io::ZeroCopyInputStream* Open(const string& filename) {
105     const char* contents = FindPtrOrNull(files_, filename);
106     if (contents == NULL) {
107       return NULL;
108     } else {
109       return new io::ArrayInputStream(contents, strlen(contents));
110     }
111   }
112 
GetLastErrorMessage()113   string GetLastErrorMessage() {
114     return "File not found.";
115   }
116 
117  private:
118   hash_map<string, const char*> files_;
119 };
120 
121 // ===================================================================
122 
123 class ImporterTest : public testing::Test {
124  protected:
ImporterTest()125   ImporterTest()
126     : importer_(&source_tree_, &error_collector_) {}
127 
AddFile(const string & filename,const char * text)128   void AddFile(const string& filename, const char* text) {
129     source_tree_.AddFile(filename, text);
130   }
131 
132   // Return the collected error text
error() const133   string error() const { return error_collector_.text_; }
warning() const134   string warning() const { return error_collector_.warning_text_; }
135 
136   MockErrorCollector error_collector_;
137   MockSourceTree source_tree_;
138   Importer importer_;
139 };
140 
TEST_F(ImporterTest,Import)141 TEST_F(ImporterTest, Import) {
142   // Test normal importing.
143   AddFile("foo.proto",
144     "syntax = \"proto2\";\n"
145     "message Foo {}\n");
146 
147   const FileDescriptor* file = importer_.Import("foo.proto");
148   EXPECT_EQ("", error_collector_.text_);
149   ASSERT_TRUE(file != NULL);
150 
151   ASSERT_EQ(1, file->message_type_count());
152   EXPECT_EQ("Foo", file->message_type(0)->name());
153 
154   // Importing again should return same object.
155   EXPECT_EQ(file, importer_.Import("foo.proto"));
156 }
157 
TEST_F(ImporterTest,ImportNested)158 TEST_F(ImporterTest, ImportNested) {
159   // Test that importing a file which imports another file works.
160   AddFile("foo.proto",
161     "syntax = \"proto2\";\n"
162     "import \"bar.proto\";\n"
163     "message Foo {\n"
164     "  optional Bar bar = 1;\n"
165     "}\n");
166   AddFile("bar.proto",
167     "syntax = \"proto2\";\n"
168     "message Bar {}\n");
169 
170   // Note that both files are actually parsed by the first call to Import()
171   // here, since foo.proto imports bar.proto.  The second call just returns
172   // the same ProtoFile for bar.proto which was constructed while importing
173   // foo.proto.  We test that this is the case below by checking that bar
174   // is among foo's dependencies (by pointer).
175   const FileDescriptor* foo = importer_.Import("foo.proto");
176   const FileDescriptor* bar = importer_.Import("bar.proto");
177   EXPECT_EQ("", error_collector_.text_);
178   ASSERT_TRUE(foo != NULL);
179   ASSERT_TRUE(bar != NULL);
180 
181   // Check that foo's dependency is the same object as bar.
182   ASSERT_EQ(1, foo->dependency_count());
183   EXPECT_EQ(bar, foo->dependency(0));
184 
185   // Check that foo properly cross-links bar.
186   ASSERT_EQ(1, foo->message_type_count());
187   ASSERT_EQ(1, bar->message_type_count());
188   ASSERT_EQ(1, foo->message_type(0)->field_count());
189   ASSERT_EQ(FieldDescriptor::TYPE_MESSAGE,
190             foo->message_type(0)->field(0)->type());
191   EXPECT_EQ(bar->message_type(0),
192             foo->message_type(0)->field(0)->message_type());
193 }
194 
TEST_F(ImporterTest,FileNotFound)195 TEST_F(ImporterTest, FileNotFound) {
196   // Error:  Parsing a file that doesn't exist.
197   EXPECT_TRUE(importer_.Import("foo.proto") == NULL);
198   EXPECT_EQ(
199     "foo.proto:-1:0: File not found.\n",
200     error_collector_.text_);
201 }
202 
TEST_F(ImporterTest,ImportNotFound)203 TEST_F(ImporterTest, ImportNotFound) {
204   // Error:  Importing a file that doesn't exist.
205   AddFile("foo.proto",
206     "syntax = \"proto2\";\n"
207     "import \"bar.proto\";\n");
208 
209   EXPECT_TRUE(importer_.Import("foo.proto") == NULL);
210   EXPECT_EQ(
211     "bar.proto:-1:0: File not found.\n"
212     "foo.proto:-1:0: Import \"bar.proto\" was not found or had errors.\n",
213     error_collector_.text_);
214 }
215 
TEST_F(ImporterTest,RecursiveImport)216 TEST_F(ImporterTest, RecursiveImport) {
217   // Error:  Recursive import.
218   AddFile("recursive1.proto",
219     "syntax = \"proto2\";\n"
220     "import \"recursive2.proto\";\n");
221   AddFile("recursive2.proto",
222     "syntax = \"proto2\";\n"
223     "import \"recursive1.proto\";\n");
224 
225   EXPECT_TRUE(importer_.Import("recursive1.proto") == NULL);
226   EXPECT_EQ(
227     "recursive1.proto:-1:0: File recursively imports itself: recursive1.proto "
228       "-> recursive2.proto -> recursive1.proto\n"
229     "recursive2.proto:-1:0: Import \"recursive1.proto\" was not found "
230       "or had errors.\n"
231     "recursive1.proto:-1:0: Import \"recursive2.proto\" was not found "
232       "or had errors.\n",
233     error_collector_.text_);
234 }
235 
236 
237 // ===================================================================
238 
239 class DiskSourceTreeTest : public testing::Test {
240  protected:
SetUp()241   virtual void SetUp() {
242     dirnames_.push_back(TestTempDir() + "/test_proto2_import_path_1");
243     dirnames_.push_back(TestTempDir() + "/test_proto2_import_path_2");
244 
245     for (int i = 0; i < dirnames_.size(); i++) {
246       if (FileExists(dirnames_[i])) {
247         File::DeleteRecursively(dirnames_[i], NULL, NULL);
248       }
249       GOOGLE_CHECK_OK(File::CreateDir(dirnames_[i], 0777));
250     }
251   }
252 
TearDown()253   virtual void TearDown() {
254     for (int i = 0; i < dirnames_.size(); i++) {
255       if (FileExists(dirnames_[i])) {
256         File::DeleteRecursively(dirnames_[i], NULL, NULL);
257       }
258     }
259   }
260 
AddFile(const string & filename,const char * contents)261   void AddFile(const string& filename, const char* contents) {
262     GOOGLE_CHECK_OK(File::SetContents(filename, contents, true));
263   }
264 
AddSubdir(const string & dirname)265   void AddSubdir(const string& dirname) {
266     GOOGLE_CHECK_OK(File::CreateDir(dirname, 0777));
267   }
268 
ExpectFileContents(const string & filename,const char * expected_contents)269   void ExpectFileContents(const string& filename,
270                           const char* expected_contents) {
271     google::protobuf::scoped_ptr<io::ZeroCopyInputStream> input(source_tree_.Open(filename));
272 
273     ASSERT_FALSE(input == NULL);
274 
275     // Read all the data from the file.
276     string file_contents;
277     const void* data;
278     int size;
279     while (input->Next(&data, &size)) {
280       file_contents.append(reinterpret_cast<const char*>(data), size);
281     }
282 
283     EXPECT_EQ(expected_contents, file_contents);
284   }
285 
ExpectCannotOpenFile(const string & filename,const string & error_message)286   void ExpectCannotOpenFile(const string& filename,
287                             const string& error_message) {
288     google::protobuf::scoped_ptr<io::ZeroCopyInputStream> input(source_tree_.Open(filename));
289     EXPECT_TRUE(input == NULL);
290     EXPECT_EQ(error_message, source_tree_.GetLastErrorMessage());
291   }
292 
293   DiskSourceTree source_tree_;
294 
295   // Paths of two on-disk directories to use during the test.
296   vector<string> dirnames_;
297 };
298 
TEST_F(DiskSourceTreeTest,MapRoot)299 TEST_F(DiskSourceTreeTest, MapRoot) {
300   // Test opening a file in a directory that is mapped to the root of the
301   // source tree.
302   AddFile(dirnames_[0] + "/foo", "Hello World!");
303   source_tree_.MapPath("", dirnames_[0]);
304 
305   ExpectFileContents("foo", "Hello World!");
306   ExpectCannotOpenFile("bar", "File not found.");
307 }
308 
TEST_F(DiskSourceTreeTest,MapDirectory)309 TEST_F(DiskSourceTreeTest, MapDirectory) {
310   // Test opening a file in a directory that is mapped to somewhere other
311   // than the root of the source tree.
312 
313   AddFile(dirnames_[0] + "/foo", "Hello World!");
314   source_tree_.MapPath("baz", dirnames_[0]);
315 
316   ExpectFileContents("baz/foo", "Hello World!");
317   ExpectCannotOpenFile("baz/bar", "File not found.");
318   ExpectCannotOpenFile("foo", "File not found.");
319   ExpectCannotOpenFile("bar", "File not found.");
320 
321   // Non-canonical file names should not work.
322   ExpectCannotOpenFile("baz//foo",
323                        "Backslashes, consecutive slashes, \".\", or \"..\" are "
324                        "not allowed in the virtual path");
325   ExpectCannotOpenFile("baz/../baz/foo",
326                        "Backslashes, consecutive slashes, \".\", or \"..\" are "
327                        "not allowed in the virtual path");
328   ExpectCannotOpenFile("baz/./foo",
329                        "Backslashes, consecutive slashes, \".\", or \"..\" are "
330                        "not allowed in the virtual path");
331   ExpectCannotOpenFile("baz/foo/", "File not found.");
332 }
333 
TEST_F(DiskSourceTreeTest,NoParent)334 TEST_F(DiskSourceTreeTest, NoParent) {
335   // Test that we cannot open files in a parent of a mapped directory.
336 
337   AddFile(dirnames_[0] + "/foo", "Hello World!");
338   AddSubdir(dirnames_[0] + "/bar");
339   AddFile(dirnames_[0] + "/bar/baz", "Blah.");
340   source_tree_.MapPath("", dirnames_[0] + "/bar");
341 
342   ExpectFileContents("baz", "Blah.");
343   ExpectCannotOpenFile("../foo",
344                        "Backslashes, consecutive slashes, \".\", or \"..\" are "
345                        "not allowed in the virtual path");
346   ExpectCannotOpenFile("../bar/baz",
347                        "Backslashes, consecutive slashes, \".\", or \"..\" are "
348                        "not allowed in the virtual path");
349 }
350 
TEST_F(DiskSourceTreeTest,MapFile)351 TEST_F(DiskSourceTreeTest, MapFile) {
352   // Test opening a file that is mapped directly into the source tree.
353 
354   AddFile(dirnames_[0] + "/foo", "Hello World!");
355   source_tree_.MapPath("foo", dirnames_[0] + "/foo");
356 
357   ExpectFileContents("foo", "Hello World!");
358   ExpectCannotOpenFile("bar", "File not found.");
359 }
360 
TEST_F(DiskSourceTreeTest,SearchMultipleDirectories)361 TEST_F(DiskSourceTreeTest, SearchMultipleDirectories) {
362   // Test mapping and searching multiple directories.
363 
364   AddFile(dirnames_[0] + "/foo", "Hello World!");
365   AddFile(dirnames_[1] + "/foo", "This file should be hidden.");
366   AddFile(dirnames_[1] + "/bar", "Goodbye World!");
367   source_tree_.MapPath("", dirnames_[0]);
368   source_tree_.MapPath("", dirnames_[1]);
369 
370   ExpectFileContents("foo", "Hello World!");
371   ExpectFileContents("bar", "Goodbye World!");
372   ExpectCannotOpenFile("baz", "File not found.");
373 }
374 
TEST_F(DiskSourceTreeTest,OrderingTrumpsSpecificity)375 TEST_F(DiskSourceTreeTest, OrderingTrumpsSpecificity) {
376   // Test that directories are always searched in order, even when a latter
377   // directory is more-specific than a former one.
378 
379   // Create the "bar" directory so we can put a file in it.
380   GOOGLE_CHECK_OK(File::CreateDir(dirnames_[0] + "/bar", 0777));
381 
382   // Add files and map paths.
383   AddFile(dirnames_[0] + "/bar/foo", "Hello World!");
384   AddFile(dirnames_[1] + "/foo", "This file should be hidden.");
385   source_tree_.MapPath("", dirnames_[0]);
386   source_tree_.MapPath("bar", dirnames_[1]);
387 
388   // Check.
389   ExpectFileContents("bar/foo", "Hello World!");
390 }
391 
TEST_F(DiskSourceTreeTest,DiskFileToVirtualFile)392 TEST_F(DiskSourceTreeTest, DiskFileToVirtualFile) {
393   // Test DiskFileToVirtualFile.
394 
395   AddFile(dirnames_[0] + "/foo", "Hello World!");
396   AddFile(dirnames_[1] + "/foo", "This file should be hidden.");
397   source_tree_.MapPath("bar", dirnames_[0]);
398   source_tree_.MapPath("bar", dirnames_[1]);
399 
400   string virtual_file;
401   string shadowing_disk_file;
402 
403   EXPECT_EQ(DiskSourceTree::NO_MAPPING,
404     source_tree_.DiskFileToVirtualFile(
405       "/foo", &virtual_file, &shadowing_disk_file));
406 
407   EXPECT_EQ(DiskSourceTree::SHADOWED,
408     source_tree_.DiskFileToVirtualFile(
409       dirnames_[1] + "/foo", &virtual_file, &shadowing_disk_file));
410   EXPECT_EQ("bar/foo", virtual_file);
411   EXPECT_EQ(dirnames_[0] + "/foo", shadowing_disk_file);
412 
413   EXPECT_EQ(DiskSourceTree::CANNOT_OPEN,
414     source_tree_.DiskFileToVirtualFile(
415       dirnames_[1] + "/baz", &virtual_file, &shadowing_disk_file));
416   EXPECT_EQ("bar/baz", virtual_file);
417 
418   EXPECT_EQ(DiskSourceTree::SUCCESS,
419     source_tree_.DiskFileToVirtualFile(
420       dirnames_[0] + "/foo", &virtual_file, &shadowing_disk_file));
421   EXPECT_EQ("bar/foo", virtual_file);
422 }
423 
TEST_F(DiskSourceTreeTest,DiskFileToVirtualFileCanonicalization)424 TEST_F(DiskSourceTreeTest, DiskFileToVirtualFileCanonicalization) {
425   // Test handling of "..", ".", etc. in DiskFileToVirtualFile().
426 
427   source_tree_.MapPath("dir1", "..");
428   source_tree_.MapPath("dir2", "../../foo");
429   source_tree_.MapPath("dir3", "./foo/bar/.");
430   source_tree_.MapPath("dir4", ".");
431   source_tree_.MapPath("", "/qux");
432   source_tree_.MapPath("dir5", "/quux/");
433 
434   string virtual_file;
435   string shadowing_disk_file;
436 
437   // "../.." should not be considered to be under "..".
438   EXPECT_EQ(DiskSourceTree::NO_MAPPING,
439     source_tree_.DiskFileToVirtualFile(
440       "../../baz", &virtual_file, &shadowing_disk_file));
441 
442   // "/foo" is not mapped (it should not be misintepreted as being under ".").
443   EXPECT_EQ(DiskSourceTree::NO_MAPPING,
444     source_tree_.DiskFileToVirtualFile(
445       "/foo", &virtual_file, &shadowing_disk_file));
446 
447 #ifdef WIN32
448   // "C:\foo" is not mapped (it should not be misintepreted as being under ".").
449   EXPECT_EQ(DiskSourceTree::NO_MAPPING,
450     source_tree_.DiskFileToVirtualFile(
451       "C:\\foo", &virtual_file, &shadowing_disk_file));
452 #endif  // WIN32
453 
454   // But "../baz" should be.
455   EXPECT_EQ(DiskSourceTree::CANNOT_OPEN,
456     source_tree_.DiskFileToVirtualFile(
457       "../baz", &virtual_file, &shadowing_disk_file));
458   EXPECT_EQ("dir1/baz", virtual_file);
459 
460   // "../../foo/baz" is under "../../foo".
461   EXPECT_EQ(DiskSourceTree::CANNOT_OPEN,
462     source_tree_.DiskFileToVirtualFile(
463       "../../foo/baz", &virtual_file, &shadowing_disk_file));
464   EXPECT_EQ("dir2/baz", virtual_file);
465 
466   // "foo/./bar/baz" is under "./foo/bar/.".
467   EXPECT_EQ(DiskSourceTree::CANNOT_OPEN,
468     source_tree_.DiskFileToVirtualFile(
469       "foo/bar/baz", &virtual_file, &shadowing_disk_file));
470   EXPECT_EQ("dir3/baz", virtual_file);
471 
472   // "bar" is under ".".
473   EXPECT_EQ(DiskSourceTree::CANNOT_OPEN,
474     source_tree_.DiskFileToVirtualFile(
475       "bar", &virtual_file, &shadowing_disk_file));
476   EXPECT_EQ("dir4/bar", virtual_file);
477 
478   // "/qux/baz" is under "/qux".
479   EXPECT_EQ(DiskSourceTree::CANNOT_OPEN,
480     source_tree_.DiskFileToVirtualFile(
481       "/qux/baz", &virtual_file, &shadowing_disk_file));
482   EXPECT_EQ("baz", virtual_file);
483 
484   // "/quux/bar" is under "/quux".
485   EXPECT_EQ(DiskSourceTree::CANNOT_OPEN,
486     source_tree_.DiskFileToVirtualFile(
487       "/quux/bar", &virtual_file, &shadowing_disk_file));
488   EXPECT_EQ("dir5/bar", virtual_file);
489 }
490 
TEST_F(DiskSourceTreeTest,VirtualFileToDiskFile)491 TEST_F(DiskSourceTreeTest, VirtualFileToDiskFile) {
492   // Test VirtualFileToDiskFile.
493 
494   AddFile(dirnames_[0] + "/foo", "Hello World!");
495   AddFile(dirnames_[1] + "/foo", "This file should be hidden.");
496   AddFile(dirnames_[1] + "/quux", "This file should not be hidden.");
497   source_tree_.MapPath("bar", dirnames_[0]);
498   source_tree_.MapPath("bar", dirnames_[1]);
499 
500   // Existent files, shadowed and non-shadowed case.
501   string disk_file;
502   EXPECT_TRUE(source_tree_.VirtualFileToDiskFile("bar/foo", &disk_file));
503   EXPECT_EQ(dirnames_[0] + "/foo", disk_file);
504   EXPECT_TRUE(source_tree_.VirtualFileToDiskFile("bar/quux", &disk_file));
505   EXPECT_EQ(dirnames_[1] + "/quux", disk_file);
506 
507   // Nonexistent file in existent directory and vice versa.
508   string not_touched = "not touched";
509   EXPECT_FALSE(source_tree_.VirtualFileToDiskFile("bar/baz", &not_touched));
510   EXPECT_EQ("not touched", not_touched);
511   EXPECT_FALSE(source_tree_.VirtualFileToDiskFile("baz/foo", &not_touched));
512   EXPECT_EQ("not touched", not_touched);
513 
514   // Accept NULL as output parameter.
515   EXPECT_TRUE(source_tree_.VirtualFileToDiskFile("bar/foo", NULL));
516   EXPECT_FALSE(source_tree_.VirtualFileToDiskFile("baz/foo", NULL));
517 }
518 
519 }  // namespace
520 
521 }  // namespace compiler
522 }  // namespace protobuf
523 }  // namespace google
524