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