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", ¬_touched));
604 EXPECT_EQ("not touched", not_touched);
605 EXPECT_FALSE(source_tree_.VirtualFileToDiskFile("baz/foo", ¬_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