1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 // http://code.google.com/p/protobuf/
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
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
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.
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.
31 // Author: kenton@google.com (Kenton Varda)
32 // Based on original Protocol Buffers design by
33 // Sanjay Ghemawat, Jeff Dean, and others.
35 #include <google/protobuf/stubs/hash.h>
37 #include <google/protobuf/compiler/importer.h>
38 #include <google/protobuf/descriptor.h>
39 #include <google/protobuf/io/zero_copy_stream_impl.h>
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>
55 #define EXPECT_SUBSTRING(needle, haystack) \
56 EXPECT_PRED_FORMAT2(testing::IsSubstring, (needle), (haystack))
58 class MockErrorCollector : public MultiFileErrorCollector {
60 MockErrorCollector() {}
61 ~MockErrorCollector() {}
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);
73 // -------------------------------------------------------------------
75 // A dummy implementation of SourceTree backed by a simple map.
76 class MockSourceTree : public SourceTree {
81 void AddFile(const string& name, const char* contents) {
82 files_[name] = contents;
85 // implements SourceTree -------------------------------------------
86 io::ZeroCopyInputStream* Open(const string& filename) {
87 const char* contents = FindPtrOrNull(files_, filename);
88 if (contents == NULL) {
91 return new io::ArrayInputStream(contents, strlen(contents));
96 hash_map<string, const char*> files_;
99 // ===================================================================
101 class ImporterTest : public testing::Test {
104 : importer_(&source_tree_, &error_collector_) {}
106 void AddFile(const string& filename, const char* text) {
107 source_tree_.AddFile(filename, text);
110 // Return the collected error text
111 string error() const { return error_collector_.text_; }
113 MockErrorCollector error_collector_;
114 MockSourceTree source_tree_;
118 TEST_F(ImporterTest, Import) {
119 // Test normal importing.
121 "syntax = \"proto2\";\n"
124 const FileDescriptor* file = importer_.Import("foo.proto");
125 EXPECT_EQ("", error_collector_.text_);
126 ASSERT_TRUE(file != NULL);
128 ASSERT_EQ(1, file->message_type_count());
129 EXPECT_EQ("Foo", file->message_type(0)->name());
131 // Importing again should return same object.
132 EXPECT_EQ(file, importer_.Import("foo.proto"));
135 TEST_F(ImporterTest, ImportNested) {
136 // Test that importing a file which imports another file works.
138 "syntax = \"proto2\";\n"
139 "import \"bar.proto\";\n"
141 " optional Bar bar = 1;\n"
144 "syntax = \"proto2\";\n"
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);
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));
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());
172 TEST_F(ImporterTest, FileNotFound) {
173 // Error: Parsing a file that doesn't exist.
174 EXPECT_TRUE(importer_.Import("foo.proto") == NULL);
176 "foo.proto:-1:0: File not found.\n",
177 error_collector_.text_);
180 TEST_F(ImporterTest, ImportNotFound) {
181 // Error: Importing a file that doesn't exist.
183 "syntax = \"proto2\";\n"
184 "import \"bar.proto\";\n");
186 EXPECT_TRUE(importer_.Import("foo.proto") == NULL);
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_);
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");
202 EXPECT_TRUE(importer_.Import("recursive1.proto") == NULL);
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 "
208 "recursive1.proto:-1:0: Import \"recursive2.proto\" was not found "
210 error_collector_.text_);
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) {
218 "syntax = \"proto2\";\n"
220 " required string key = 1;\n"
223 " repeated Item items = 1 [experimental_map_key = \"key\"];\n"
226 const FileDescriptor* file = importer_.Import("map.proto");
227 ASSERT_TRUE(file != NULL) << error_collector_.text_;
228 EXPECT_EQ("", error_collector_.text_);
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);
242 TEST_F(ImporterTest, MapFieldNotRepeated) {
245 "syntax = \"proto2\";\n"
247 " required string key = 1;\n"
250 " required Item items = 1 [experimental_map_key = \"key\"];\n"
253 EXPECT_TRUE(importer_.Import("map.proto") == NULL);
254 EXPECT_SUBSTRING("only allowed for repeated fields", error());
257 TEST_F(ImporterTest, MapFieldNotMessageType) {
260 "syntax = \"proto2\";\n"
262 " repeated int32 items = 1 [experimental_map_key = \"key\"];\n"
265 EXPECT_TRUE(importer_.Import("map.proto") == NULL);
266 EXPECT_SUBSTRING("only allowed for fields with a message type", error());
269 TEST_F(ImporterTest, MapFieldTypeNotFound) {
272 "syntax = \"proto2\";\n"
274 " repeated Unknown items = 1 [experimental_map_key = \"key\"];\n"
277 EXPECT_TRUE(importer_.Import("map.proto") == NULL);
278 EXPECT_SUBSTRING("not defined", error());
281 TEST_F(ImporterTest, MapFieldKeyNotFound) {
284 "syntax = \"proto2\";\n"
286 " required string key = 1;\n"
289 " repeated Item items = 1 [experimental_map_key = \"badkey\"];\n"
292 EXPECT_TRUE(importer_.Import("map.proto") == NULL);
293 EXPECT_SUBSTRING("Could not find field", error());
296 TEST_F(ImporterTest, MapFieldKeyRepeated) {
299 "syntax = \"proto2\";\n"
301 " repeated string key = 1;\n"
304 " repeated Item items = 1 [experimental_map_key = \"key\"];\n"
307 EXPECT_TRUE(importer_.Import("map.proto") == NULL);
308 EXPECT_SUBSTRING("must not name a repeated field", error());
311 TEST_F(ImporterTest, MapFieldKeyNotScalar) {
314 "syntax = \"proto2\";\n"
315 "message ItemKey { }\n"
317 " required ItemKey key = 1;\n"
320 " repeated Item items = 1 [experimental_map_key = \"key\"];\n"
323 EXPECT_TRUE(importer_.Import("map.proto") == NULL);
324 EXPECT_SUBSTRING("must name a scalar or string", error());
327 // ===================================================================
329 class DiskSourceTreeTest : public testing::Test {
331 virtual void SetUp() {
332 dirnames_.push_back(TestTempDir() + "/test_proto2_import_path_1");
333 dirnames_.push_back(TestTempDir() + "/test_proto2_import_path_2");
335 for (int i = 0; i < dirnames_.size(); i++) {
336 if (File::Exists(dirnames_[i])) {
337 File::DeleteRecursively(dirnames_[i], NULL, NULL);
339 GOOGLE_CHECK(File::CreateDir(dirnames_[i].c_str(), DEFAULT_FILE_MODE));
343 virtual void TearDown() {
344 for (int i = 0; i < dirnames_.size(); i++) {
345 File::DeleteRecursively(dirnames_[i], NULL, NULL);
349 void AddFile(const string& filename, const char* contents) {
350 File::WriteStringToFileOrDie(contents, filename);
353 void AddSubdir(const string& dirname) {
354 GOOGLE_CHECK(File::CreateDir(dirname.c_str(), DEFAULT_FILE_MODE));
357 void ExpectFileContents(const string& filename,
358 const char* expected_contents) {
359 scoped_ptr<io::ZeroCopyInputStream> input(source_tree_.Open(filename));
361 ASSERT_FALSE(input == NULL);
363 // Read all the data from the file.
364 string file_contents;
367 while (input->Next(&data, &size)) {
368 file_contents.append(reinterpret_cast<const char*>(data), size);
371 EXPECT_EQ(expected_contents, file_contents);
374 void ExpectFileNotFound(const string& filename) {
375 scoped_ptr<io::ZeroCopyInputStream> input(source_tree_.Open(filename));
376 EXPECT_TRUE(input == NULL);
379 DiskSourceTree source_tree_;
381 // Paths of two on-disk directories to use during the test.
382 vector<string> dirnames_;
385 TEST_F(DiskSourceTreeTest, MapRoot) {
386 // Test opening a file in a directory that is mapped to the root of the
388 AddFile(dirnames_[0] + "/foo", "Hello World!");
389 source_tree_.MapPath("", dirnames_[0]);
391 ExpectFileContents("foo", "Hello World!");
392 ExpectFileNotFound("bar");
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.
399 AddFile(dirnames_[0] + "/foo", "Hello World!");
400 source_tree_.MapPath("baz", dirnames_[0]);
402 ExpectFileContents("baz/foo", "Hello World!");
403 ExpectFileNotFound("baz/bar");
404 ExpectFileNotFound("foo");
405 ExpectFileNotFound("bar");
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/");
414 TEST_F(DiskSourceTreeTest, NoParent) {
415 // Test that we cannot open files in a parent of a mapped directory.
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");
422 ExpectFileContents("baz", "Blah.");
423 ExpectFileNotFound("../foo");
424 ExpectFileNotFound("../bar/baz");
427 TEST_F(DiskSourceTreeTest, MapFile) {
428 // Test opening a file that is mapped directly into the source tree.
430 AddFile(dirnames_[0] + "/foo", "Hello World!");
431 source_tree_.MapPath("foo", dirnames_[0] + "/foo");
433 ExpectFileContents("foo", "Hello World!");
434 ExpectFileNotFound("bar");
437 TEST_F(DiskSourceTreeTest, SearchMultipleDirectories) {
438 // Test mapping and searching multiple directories.
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]);
446 ExpectFileContents("foo", "Hello World!");
447 ExpectFileContents("bar", "Goodbye World!");
448 ExpectFileNotFound("baz");
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.
455 // Create the "bar" directory so we can put a file in it.
456 ASSERT_TRUE(File::CreateDir((dirnames_[0] + "/bar").c_str(),
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]);
466 ExpectFileContents("bar/foo", "Hello World!");
469 TEST_F(DiskSourceTreeTest, DiskFileToVirtualFile) {
470 // Test DiskFileToVirtualFile.
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]);
478 string shadowing_disk_file;
480 EXPECT_EQ(DiskSourceTree::NO_MAPPING,
481 source_tree_.DiskFileToVirtualFile(
482 "/foo", &virtual_file, &shadowing_disk_file));
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);
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);
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);
501 TEST_F(DiskSourceTreeTest, DiskFileToVirtualFileCanonicalization) {
502 // Test handling of "..", ".", etc. in DiskFileToVirtualFile().
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/");
512 string shadowing_disk_file;
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));
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));
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));
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);
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);
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);
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);
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);
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);
568 TEST_F(DiskSourceTreeTest, VirtualFileToDiskFile) {
569 // Test VirtualFileToDiskFile.
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]);
577 // Existent files, shadowed and non-shadowed case.
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);
584 // Nonexistent file in existent directory and vice versa.
585 string not_touched = "not touched";
586 EXPECT_FALSE(source_tree_.VirtualFileToDiskFile("bar/baz", ¬_touched));
587 EXPECT_EQ("not touched", not_touched);
588 EXPECT_FALSE(source_tree_.VirtualFileToDiskFile("baz/foo", ¬_touched));
589 EXPECT_EQ("not touched", not_touched);
591 // Accept NULL as output parameter.
592 EXPECT_TRUE(source_tree_.VirtualFileToDiskFile("bar/foo", NULL));
593 EXPECT_FALSE(source_tree_.VirtualFileToDiskFile("baz/foo", NULL));
598 } // namespace compiler
599 } // namespace protobuf
600 } // namespace google