importer_unittest.cc 18 KB

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