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