command_line_interface_unittest.cc 81 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439
  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 <fcntl.h>
  34. #include <sys/stat.h>
  35. #include <sys/types.h>
  36. #ifndef _MSC_VER
  37. #include <unistd.h>
  38. #endif
  39. #include <memory>
  40. #include <vector>
  41. #include <google/protobuf/stubs/stringprintf.h>
  42. #include <google/protobuf/testing/file.h>
  43. #include <google/protobuf/testing/file.h>
  44. #include <google/protobuf/compiler/mock_code_generator.h>
  45. #include <google/protobuf/compiler/subprocess.h>
  46. #include <google/protobuf/compiler/code_generator.h>
  47. #include <google/protobuf/compiler/command_line_interface.h>
  48. #include <google/protobuf/unittest.pb.h>
  49. #include <google/protobuf/io/printer.h>
  50. #include <google/protobuf/io/zero_copy_stream.h>
  51. #include <google/protobuf/descriptor.pb.h>
  52. #include <google/protobuf/descriptor.h>
  53. #include <google/protobuf/stubs/substitute.h>
  54. #include <google/protobuf/testing/file.h>
  55. #include <google/protobuf/testing/googletest.h>
  56. #include <gtest/gtest.h>
  57. #include <google/protobuf/stubs/strutil.h>
  58. #include <google/protobuf/stubs/io_win32.h>
  59. namespace google {
  60. namespace protobuf {
  61. namespace compiler {
  62. #if defined(_WIN32)
  63. // DO NOT include <io.h>, instead create functions in io_win32.{h,cc} and import
  64. // them like we do below.
  65. using google::protobuf::internal::win32::access;
  66. using google::protobuf::internal::win32::dup;
  67. using google::protobuf::internal::win32::dup2;
  68. using google::protobuf::internal::win32::close;
  69. using google::protobuf::internal::win32::open;
  70. using google::protobuf::internal::win32::write;
  71. #endif
  72. // Disable the whole test when we use tcmalloc for "draconian" heap checks, in
  73. // which case tcmalloc will print warnings that fail the plugin tests.
  74. #if !GOOGLE_PROTOBUF_HEAP_CHECK_DRACONIAN
  75. namespace {
  76. bool FileExists(const string& path) {
  77. return File::Exists(path);
  78. }
  79. class CommandLineInterfaceTest : public testing::Test {
  80. protected:
  81. virtual void SetUp();
  82. virtual void TearDown();
  83. // Runs the CommandLineInterface with the given command line. The
  84. // command is automatically split on spaces, and the string "$tmpdir"
  85. // is replaced with TestTempDir().
  86. void Run(const string& command);
  87. void RunWithArgs(std::vector<string> args);
  88. // -----------------------------------------------------------------
  89. // Methods to set up the test (called before Run()).
  90. class NullCodeGenerator;
  91. // Normally plugins are allowed for all tests. Call this to explicitly
  92. // disable them.
  93. void DisallowPlugins() { disallow_plugins_ = true; }
  94. // Create a temp file within temp_directory_ with the given name.
  95. // The containing directory is also created if necessary.
  96. void CreateTempFile(const string& name, const string& contents);
  97. // Create a subdirectory within temp_directory_.
  98. void CreateTempDir(const string& name);
  99. #ifdef PROTOBUF_OPENSOURCE
  100. // Change working directory to temp directory.
  101. void SwitchToTempDirectory() {
  102. File::ChangeWorkingDirectory(temp_directory_);
  103. }
  104. #else // !PROTOBUF_OPENSOURCE
  105. // TODO(teboring): Figure out how to change and get working directory in
  106. // google3.
  107. #endif // !PROTOBUF_OPENSOURCE
  108. // -----------------------------------------------------------------
  109. // Methods to check the test results (called after Run()).
  110. // Checks that no text was written to stderr during Run(), and Run()
  111. // returned 0.
  112. void ExpectNoErrors();
  113. // Checks that Run() returned non-zero and the stderr output is exactly
  114. // the text given. expected_test may contain references to "$tmpdir",
  115. // which will be replaced by the temporary directory path.
  116. void ExpectErrorText(const string& expected_text);
  117. // Checks that Run() returned non-zero and the stderr contains the given
  118. // substring.
  119. void ExpectErrorSubstring(const string& expected_substring);
  120. // Like ExpectErrorSubstring, but checks that Run() returned zero.
  121. void ExpectErrorSubstringWithZeroReturnCode(
  122. const string& expected_substring);
  123. // Checks that the captured stdout is the same as the expected_text.
  124. void ExpectCapturedStdout(const string& expected_text);
  125. // Checks that Run() returned zero and the stdout contains the given
  126. // substring.
  127. void ExpectCapturedStdoutSubstringWithZeroReturnCode(
  128. const string& expected_substring);
  129. // Returns true if ExpectErrorSubstring(expected_substring) would pass, but
  130. // does not fail otherwise.
  131. bool HasAlternateErrorSubstring(const string& expected_substring);
  132. // Checks that MockCodeGenerator::Generate() was called in the given
  133. // context (or the generator in test_plugin.cc, which produces the same
  134. // output). That is, this tests if the generator with the given name
  135. // was called with the given parameter and proto file and produced the
  136. // given output file. This is checked by reading the output file and
  137. // checking that it contains the content that MockCodeGenerator would
  138. // generate given these inputs. message_name is the name of the first
  139. // message that appeared in the proto file; this is just to make extra
  140. // sure that the correct file was parsed.
  141. void ExpectGenerated(const string& generator_name,
  142. const string& parameter,
  143. const string& proto_name,
  144. const string& message_name);
  145. void ExpectGenerated(const string& generator_name,
  146. const string& parameter,
  147. const string& proto_name,
  148. const string& message_name,
  149. const string& output_directory);
  150. void ExpectGeneratedWithMultipleInputs(const string& generator_name,
  151. const string& all_proto_names,
  152. const string& proto_name,
  153. const string& message_name);
  154. void ExpectGeneratedWithInsertions(const string& generator_name,
  155. const string& parameter,
  156. const string& insertions,
  157. const string& proto_name,
  158. const string& message_name);
  159. void CheckGeneratedAnnotations(const string& name, const string& file);
  160. void ExpectNullCodeGeneratorCalled(const string& parameter);
  161. void ReadDescriptorSet(const string& filename,
  162. FileDescriptorSet* descriptor_set);
  163. void WriteDescriptorSet(const string& filename,
  164. const FileDescriptorSet* descriptor_set);
  165. void ExpectFileContent(const string& filename,
  166. const string& content);
  167. private:
  168. // The object we are testing.
  169. CommandLineInterface cli_;
  170. // Was DisallowPlugins() called?
  171. bool disallow_plugins_;
  172. // We create a directory within TestTempDir() in order to add extra
  173. // protection against accidentally deleting user files (since we recursively
  174. // delete this directory during the test). This is the full path of that
  175. // directory.
  176. string temp_directory_;
  177. // The result of Run().
  178. int return_code_;
  179. // The captured stderr output.
  180. string error_text_;
  181. // The captured stdout.
  182. string captured_stdout_;
  183. // Pointers which need to be deleted later.
  184. std::vector<CodeGenerator*> mock_generators_to_delete_;
  185. NullCodeGenerator* null_generator_;
  186. };
  187. class CommandLineInterfaceTest::NullCodeGenerator : public CodeGenerator {
  188. public:
  189. NullCodeGenerator() : called_(false) {}
  190. ~NullCodeGenerator() {}
  191. mutable bool called_;
  192. mutable string parameter_;
  193. // implements CodeGenerator ----------------------------------------
  194. bool Generate(const FileDescriptor* file,
  195. const string& parameter,
  196. GeneratorContext* context,
  197. string* error) const {
  198. called_ = true;
  199. parameter_ = parameter;
  200. return true;
  201. }
  202. };
  203. // ===================================================================
  204. void CommandLineInterfaceTest::SetUp() {
  205. temp_directory_ = TestTempDir() + "/proto2_cli_test_temp";
  206. // If the temp directory already exists, it must be left over from a
  207. // previous run. Delete it.
  208. if (FileExists(temp_directory_)) {
  209. File::DeleteRecursively(temp_directory_, NULL, NULL);
  210. }
  211. // Create the temp directory.
  212. GOOGLE_CHECK_OK(File::CreateDir(temp_directory_, 0777));
  213. // Register generators.
  214. CodeGenerator* generator = new MockCodeGenerator("test_generator");
  215. mock_generators_to_delete_.push_back(generator);
  216. cli_.RegisterGenerator("--test_out", "--test_opt", generator, "Test output.");
  217. cli_.RegisterGenerator("-t", generator, "Test output.");
  218. generator = new MockCodeGenerator("alt_generator");
  219. mock_generators_to_delete_.push_back(generator);
  220. cli_.RegisterGenerator("--alt_out", generator, "Alt output.");
  221. generator = null_generator_ = new NullCodeGenerator();
  222. mock_generators_to_delete_.push_back(generator);
  223. cli_.RegisterGenerator("--null_out", generator, "Null output.");
  224. disallow_plugins_ = false;
  225. }
  226. void CommandLineInterfaceTest::TearDown() {
  227. // Delete the temp directory.
  228. if (FileExists(temp_directory_)) {
  229. File::DeleteRecursively(temp_directory_, NULL, NULL);
  230. }
  231. // Delete all the MockCodeGenerators.
  232. for (int i = 0; i < mock_generators_to_delete_.size(); i++) {
  233. delete mock_generators_to_delete_[i];
  234. }
  235. mock_generators_to_delete_.clear();
  236. }
  237. void CommandLineInterfaceTest::Run(const string& command) {
  238. RunWithArgs(Split(command, " ", true));
  239. }
  240. void CommandLineInterfaceTest::RunWithArgs(std::vector<string> args) {
  241. if (!disallow_plugins_) {
  242. cli_.AllowPlugins("prefix-");
  243. #ifndef GOOGLE_THIRD_PARTY_PROTOBUF
  244. string plugin_path;
  245. #ifdef GOOGLE_PROTOBUF_TEST_PLUGIN_PATH
  246. plugin_path = GOOGLE_PROTOBUF_TEST_PLUGIN_PATH;
  247. #else
  248. const char* possible_paths[] = {
  249. // When building with shared libraries, libtool hides the real executable
  250. // in .libs and puts a fake wrapper in the current directory.
  251. // Unfortunately, due to an apparent bug on Cygwin/MinGW, if one program
  252. // wrapped in this way (e.g. protobuf-tests.exe) tries to execute another
  253. // program wrapped in this way (e.g. test_plugin.exe), the latter fails
  254. // with error code 127 and no explanation message. Presumably the problem
  255. // is that the wrapper for protobuf-tests.exe set some environment
  256. // variables that confuse the wrapper for test_plugin.exe. Luckily, it
  257. // turns out that if we simply invoke the wrapped test_plugin.exe
  258. // directly, it works -- I guess the environment variables set by the
  259. // protobuf-tests.exe wrapper happen to be correct for it too. So we do
  260. // that.
  261. ".libs/test_plugin.exe", // Win32 w/autotool (Cygwin / MinGW)
  262. "test_plugin.exe", // Other Win32 (MSVC)
  263. "test_plugin", // Unix
  264. };
  265. for (int i = 0; i < GOOGLE_ARRAYSIZE(possible_paths); i++) {
  266. if (access(possible_paths[i], F_OK) == 0) {
  267. plugin_path = possible_paths[i];
  268. break;
  269. }
  270. }
  271. #endif
  272. if (plugin_path.empty()) {
  273. #else
  274. string plugin_path = "third_party/protobuf/test_plugin";
  275. if (access(plugin_path.c_str(), F_OK) != 0) {
  276. #endif // GOOGLE_THIRD_PARTY_PROTOBUF
  277. GOOGLE_LOG(ERROR)
  278. << "Plugin executable not found. Plugin tests are likely to fail.";
  279. } else {
  280. args.push_back("--plugin=prefix-gen-plug=" + plugin_path);
  281. }
  282. }
  283. std::unique_ptr<const char * []> argv(new const char* [args.size()]);
  284. for (int i = 0; i < args.size(); i++) {
  285. args[i] = StringReplace(args[i], "$tmpdir", temp_directory_, true);
  286. argv[i] = args[i].c_str();
  287. }
  288. // TODO(jieluo): Cygwin doesn't work well if we try to capture stderr and
  289. // stdout at the same time. Need to figure out why and add this capture back
  290. // for Cygwin.
  291. #if !defined(__CYGWIN__)
  292. CaptureTestStdout();
  293. #endif
  294. CaptureTestStderr();
  295. return_code_ = cli_.Run(args.size(), argv.get());
  296. error_text_ = GetCapturedTestStderr();
  297. #if !defined(__CYGWIN__)
  298. captured_stdout_ = GetCapturedTestStdout();
  299. #endif
  300. }
  301. // -------------------------------------------------------------------
  302. void CommandLineInterfaceTest::CreateTempFile(
  303. const string& name,
  304. const string& contents) {
  305. // Create parent directory, if necessary.
  306. string::size_type slash_pos = name.find_last_of('/');
  307. if (slash_pos != string::npos) {
  308. string dir = name.substr(0, slash_pos);
  309. if (!FileExists(temp_directory_ + "/" + dir)) {
  310. GOOGLE_CHECK_OK(File::RecursivelyCreateDir(temp_directory_ + "/" + dir,
  311. 0777));
  312. }
  313. }
  314. // Write file.
  315. string full_name = temp_directory_ + "/" + name;
  316. GOOGLE_CHECK_OK(File::SetContents(
  317. full_name, StringReplace(contents, "$tmpdir", temp_directory_, true),
  318. true));
  319. }
  320. void CommandLineInterfaceTest::CreateTempDir(const string& name) {
  321. GOOGLE_CHECK_OK(File::RecursivelyCreateDir(temp_directory_ + "/" + name,
  322. 0777));
  323. }
  324. // -------------------------------------------------------------------
  325. void CommandLineInterfaceTest::ExpectNoErrors() {
  326. EXPECT_EQ(0, return_code_);
  327. EXPECT_EQ("", error_text_);
  328. }
  329. void CommandLineInterfaceTest::ExpectErrorText(const string& expected_text) {
  330. EXPECT_NE(0, return_code_);
  331. EXPECT_EQ(StringReplace(expected_text, "$tmpdir", temp_directory_, true),
  332. error_text_);
  333. }
  334. void CommandLineInterfaceTest::ExpectErrorSubstring(
  335. const string& expected_substring) {
  336. EXPECT_NE(0, return_code_);
  337. EXPECT_PRED_FORMAT2(testing::IsSubstring, expected_substring, error_text_);
  338. }
  339. void CommandLineInterfaceTest::ExpectErrorSubstringWithZeroReturnCode(
  340. const string& expected_substring) {
  341. EXPECT_EQ(0, return_code_);
  342. EXPECT_PRED_FORMAT2(testing::IsSubstring, expected_substring, error_text_);
  343. }
  344. bool CommandLineInterfaceTest::HasAlternateErrorSubstring(
  345. const string& expected_substring) {
  346. EXPECT_NE(0, return_code_);
  347. return error_text_.find(expected_substring) != string::npos;
  348. }
  349. void CommandLineInterfaceTest::ExpectGenerated(
  350. const string& generator_name,
  351. const string& parameter,
  352. const string& proto_name,
  353. const string& message_name) {
  354. MockCodeGenerator::ExpectGenerated(
  355. generator_name, parameter, "", proto_name, message_name, proto_name,
  356. temp_directory_);
  357. }
  358. void CommandLineInterfaceTest::ExpectGenerated(
  359. const string& generator_name,
  360. const string& parameter,
  361. const string& proto_name,
  362. const string& message_name,
  363. const string& output_directory) {
  364. MockCodeGenerator::ExpectGenerated(
  365. generator_name, parameter, "", proto_name, message_name, proto_name,
  366. temp_directory_ + "/" + output_directory);
  367. }
  368. void CommandLineInterfaceTest::ExpectGeneratedWithMultipleInputs(
  369. const string& generator_name,
  370. const string& all_proto_names,
  371. const string& proto_name,
  372. const string& message_name) {
  373. MockCodeGenerator::ExpectGenerated(
  374. generator_name, "", "", proto_name, message_name,
  375. all_proto_names,
  376. temp_directory_);
  377. }
  378. void CommandLineInterfaceTest::ExpectGeneratedWithInsertions(
  379. const string& generator_name,
  380. const string& parameter,
  381. const string& insertions,
  382. const string& proto_name,
  383. const string& message_name) {
  384. MockCodeGenerator::ExpectGenerated(
  385. generator_name, parameter, insertions, proto_name, message_name,
  386. proto_name, temp_directory_);
  387. }
  388. void CommandLineInterfaceTest::CheckGeneratedAnnotations(const string& name,
  389. const string& file) {
  390. MockCodeGenerator::CheckGeneratedAnnotations(name, file, temp_directory_);
  391. }
  392. void CommandLineInterfaceTest::ExpectNullCodeGeneratorCalled(
  393. const string& parameter) {
  394. EXPECT_TRUE(null_generator_->called_);
  395. EXPECT_EQ(parameter, null_generator_->parameter_);
  396. }
  397. void CommandLineInterfaceTest::ReadDescriptorSet(
  398. const string& filename, FileDescriptorSet* descriptor_set) {
  399. string path = temp_directory_ + "/" + filename;
  400. string file_contents;
  401. GOOGLE_CHECK_OK(File::GetContents(path, &file_contents, true));
  402. if (!descriptor_set->ParseFromString(file_contents)) {
  403. FAIL() << "Could not parse file contents: " << path;
  404. }
  405. }
  406. void CommandLineInterfaceTest::WriteDescriptorSet(
  407. const string& filename, const FileDescriptorSet* descriptor_set) {
  408. string binary_proto;
  409. GOOGLE_CHECK(descriptor_set->SerializeToString(&binary_proto));
  410. CreateTempFile(filename, binary_proto);
  411. }
  412. void CommandLineInterfaceTest::ExpectCapturedStdout(
  413. const string& expected_text) {
  414. EXPECT_EQ(expected_text, captured_stdout_);
  415. }
  416. void CommandLineInterfaceTest::ExpectCapturedStdoutSubstringWithZeroReturnCode(
  417. const string& expected_substring) {
  418. EXPECT_EQ(0, return_code_);
  419. EXPECT_PRED_FORMAT2(
  420. testing::IsSubstring, expected_substring, captured_stdout_);
  421. }
  422. void CommandLineInterfaceTest::ExpectFileContent(
  423. const string& filename, const string& content) {
  424. string path = temp_directory_ + "/" + filename;
  425. string file_contents;
  426. GOOGLE_CHECK_OK(File::GetContents(path, &file_contents, true));
  427. EXPECT_EQ(StringReplace(content, "$tmpdir", temp_directory_, true),
  428. file_contents);
  429. }
  430. // ===================================================================
  431. TEST_F(CommandLineInterfaceTest, BasicOutput) {
  432. // Test that the common case works.
  433. CreateTempFile("foo.proto",
  434. "syntax = \"proto2\";\n"
  435. "message Foo {}\n");
  436. Run("protocol_compiler --test_out=$tmpdir "
  437. "--proto_path=$tmpdir foo.proto");
  438. ExpectNoErrors();
  439. ExpectGenerated("test_generator", "", "foo.proto", "Foo");
  440. }
  441. TEST_F(CommandLineInterfaceTest, BasicOutput_DescriptorSetIn) {
  442. // Test that the common case works.
  443. FileDescriptorSet file_descriptor_set;
  444. FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
  445. file_descriptor_proto->set_name("foo.proto");
  446. file_descriptor_proto->add_message_type()->set_name("Foo");
  447. WriteDescriptorSet("foo.bin", &file_descriptor_set);
  448. Run("protocol_compiler --test_out=$tmpdir "
  449. "--descriptor_set_in=$tmpdir/foo.bin foo.proto");
  450. ExpectNoErrors();
  451. ExpectGenerated("test_generator", "", "foo.proto", "Foo");
  452. }
  453. TEST_F(CommandLineInterfaceTest, BasicPlugin) {
  454. // Test that basic plugins work.
  455. CreateTempFile("foo.proto",
  456. "syntax = \"proto2\";\n"
  457. "message Foo {}\n");
  458. Run("protocol_compiler --plug_out=$tmpdir "
  459. "--proto_path=$tmpdir foo.proto");
  460. ExpectNoErrors();
  461. ExpectGenerated("test_plugin", "", "foo.proto", "Foo");
  462. }
  463. TEST_F(CommandLineInterfaceTest, BasicPlugin_DescriptorSetIn) {
  464. // Test that basic plugins work.
  465. FileDescriptorSet file_descriptor_set;
  466. FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
  467. file_descriptor_proto->set_name("foo.proto");
  468. file_descriptor_proto->add_message_type()->set_name("Foo");
  469. WriteDescriptorSet("foo.bin", &file_descriptor_set);
  470. Run("protocol_compiler --plug_out=$tmpdir "
  471. "--descriptor_set_in=$tmpdir/foo.bin foo.proto");
  472. ExpectNoErrors();
  473. ExpectGenerated("test_plugin", "", "foo.proto", "Foo");
  474. }
  475. TEST_F(CommandLineInterfaceTest, GeneratorAndPlugin) {
  476. // Invoke a generator and a plugin at the same time.
  477. CreateTempFile("foo.proto",
  478. "syntax = \"proto2\";\n"
  479. "message Foo {}\n");
  480. Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
  481. "--proto_path=$tmpdir foo.proto");
  482. ExpectNoErrors();
  483. ExpectGenerated("test_generator", "", "foo.proto", "Foo");
  484. ExpectGenerated("test_plugin", "", "foo.proto", "Foo");
  485. }
  486. TEST_F(CommandLineInterfaceTest, GeneratorAndPlugin_DescriptorSetIn) {
  487. // Invoke a generator and a plugin at the same time.
  488. FileDescriptorSet file_descriptor_set;
  489. FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
  490. file_descriptor_proto->set_name("foo.proto");
  491. file_descriptor_proto->add_message_type()->set_name("Foo");
  492. WriteDescriptorSet("foo.bin", &file_descriptor_set);
  493. Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
  494. "--descriptor_set_in=$tmpdir/foo.bin foo.proto");
  495. ExpectNoErrors();
  496. ExpectGenerated("test_generator", "", "foo.proto", "Foo");
  497. ExpectGenerated("test_plugin", "", "foo.proto", "Foo");
  498. }
  499. TEST_F(CommandLineInterfaceTest, MultipleInputs) {
  500. // Test parsing multiple input files.
  501. CreateTempFile("foo.proto",
  502. "syntax = \"proto2\";\n"
  503. "message Foo {}\n");
  504. CreateTempFile("bar.proto",
  505. "syntax = \"proto2\";\n"
  506. "message Bar {}\n");
  507. Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
  508. "--proto_path=$tmpdir foo.proto bar.proto");
  509. ExpectNoErrors();
  510. ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
  511. "foo.proto", "Foo");
  512. ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
  513. "bar.proto", "Bar");
  514. ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
  515. "foo.proto", "Foo");
  516. ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
  517. "bar.proto", "Bar");
  518. }
  519. TEST_F(CommandLineInterfaceTest, MultipleInputs_DescriptorSetIn) {
  520. // Test parsing multiple input files.
  521. FileDescriptorSet file_descriptor_set;
  522. FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
  523. file_descriptor_proto->set_name("foo.proto");
  524. file_descriptor_proto->add_message_type()->set_name("Foo");
  525. file_descriptor_proto = file_descriptor_set.add_file();
  526. file_descriptor_proto->set_name("bar.proto");
  527. file_descriptor_proto->add_message_type()->set_name("Bar");
  528. WriteDescriptorSet("foo.bin", &file_descriptor_set);
  529. Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
  530. "--descriptor_set_in=$tmpdir/foo.bin foo.proto bar.proto");
  531. ExpectNoErrors();
  532. ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
  533. "foo.proto", "Foo");
  534. ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
  535. "bar.proto", "Bar");
  536. ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
  537. "foo.proto", "Foo");
  538. ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
  539. "bar.proto", "Bar");
  540. }
  541. TEST_F(CommandLineInterfaceTest, MultipleInputsWithImport) {
  542. // Test parsing multiple input files with an import of a separate file.
  543. CreateTempFile("foo.proto",
  544. "syntax = \"proto2\";\n"
  545. "message Foo {}\n");
  546. CreateTempFile("bar.proto",
  547. "syntax = \"proto2\";\n"
  548. "import \"baz.proto\";\n"
  549. "message Bar {\n"
  550. " optional Baz a = 1;\n"
  551. "}\n");
  552. CreateTempFile("baz.proto",
  553. "syntax = \"proto2\";\n"
  554. "message Baz {}\n");
  555. Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
  556. "--proto_path=$tmpdir foo.proto bar.proto");
  557. ExpectNoErrors();
  558. ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
  559. "foo.proto", "Foo");
  560. ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
  561. "bar.proto", "Bar");
  562. ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
  563. "foo.proto", "Foo");
  564. ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
  565. "bar.proto", "Bar");
  566. }
  567. TEST_F(CommandLineInterfaceTest, MultipleInputsWithImport_DescriptorSetIn) {
  568. // Test parsing multiple input files with an import of a separate file.
  569. FileDescriptorSet file_descriptor_set;
  570. FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
  571. file_descriptor_proto->set_name("foo.proto");
  572. file_descriptor_proto->add_message_type()->set_name("Foo");
  573. file_descriptor_proto = file_descriptor_set.add_file();
  574. file_descriptor_proto->set_name("bar.proto");
  575. file_descriptor_proto->add_dependency("baz.proto");
  576. DescriptorProto* message = file_descriptor_proto->add_message_type();
  577. message->set_name("Bar");
  578. FieldDescriptorProto* field = message->add_field();
  579. field->set_type_name("Baz");
  580. field->set_name("a");
  581. field->set_number(1);
  582. WriteDescriptorSet("foo_and_bar.bin", &file_descriptor_set);
  583. file_descriptor_set.clear_file();
  584. file_descriptor_proto = file_descriptor_set.add_file();
  585. file_descriptor_proto->set_name("baz.proto");
  586. file_descriptor_proto->add_message_type()->set_name("Baz");
  587. file_descriptor_proto = file_descriptor_set.add_file();
  588. file_descriptor_proto->set_name("bat.proto");
  589. file_descriptor_proto->add_dependency("baz.proto");
  590. message = file_descriptor_proto->add_message_type();
  591. message->set_name("Bat");
  592. field = message->add_field();
  593. field->set_type_name("Baz");
  594. field->set_name("a");
  595. field->set_number(1);
  596. WriteDescriptorSet("baz_and_bat.bin", &file_descriptor_set);
  597. Run(strings::Substitute(
  598. "protocol_compiler --test_out=$$tmpdir --plug_out=$$tmpdir "
  599. "--descriptor_set_in=$0 foo.proto bar.proto",
  600. string("$tmpdir/foo_and_bar.bin") +
  601. CommandLineInterface::kPathSeparator +
  602. "$tmpdir/baz_and_bat.bin"));
  603. ExpectNoErrors();
  604. ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
  605. "foo.proto", "Foo");
  606. ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
  607. "bar.proto", "Bar");
  608. ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
  609. "foo.proto", "Foo");
  610. ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
  611. "bar.proto", "Bar");
  612. Run(strings::Substitute(
  613. "protocol_compiler --test_out=$$tmpdir --plug_out=$$tmpdir "
  614. "--descriptor_set_in=$0 baz.proto bat.proto",
  615. string("$tmpdir/foo_and_bar.bin") +
  616. CommandLineInterface::kPathSeparator +
  617. "$tmpdir/baz_and_bat.bin"));
  618. ExpectNoErrors();
  619. ExpectGeneratedWithMultipleInputs("test_generator", "baz.proto,bat.proto",
  620. "baz.proto", "Baz");
  621. ExpectGeneratedWithMultipleInputs("test_generator", "baz.proto,bat.proto",
  622. "bat.proto", "Bat");
  623. ExpectGeneratedWithMultipleInputs("test_plugin", "baz.proto,bat.proto",
  624. "baz.proto", "Baz");
  625. ExpectGeneratedWithMultipleInputs("test_plugin", "baz.proto,bat.proto",
  626. "bat.proto", "Bat");
  627. }
  628. TEST_F(CommandLineInterfaceTest,
  629. MultipleInputsWithImport_DescriptorSetIn_DuplicateFileDescriptor) {
  630. // Test parsing multiple input files with an import of a separate file.
  631. FileDescriptorSet file_descriptor_set;
  632. FileDescriptorProto foo_file_descriptor_proto;
  633. foo_file_descriptor_proto.set_name("foo.proto");
  634. foo_file_descriptor_proto.add_message_type()->set_name("Foo");
  635. file_descriptor_set.add_file()->CopyFrom(foo_file_descriptor_proto);
  636. FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
  637. file_descriptor_proto->set_name("bar.proto");
  638. file_descriptor_proto->add_dependency("baz.proto");
  639. file_descriptor_proto->add_dependency("foo.proto");
  640. DescriptorProto* message = file_descriptor_proto->add_message_type();
  641. message->set_name("Bar");
  642. FieldDescriptorProto* field = message->add_field();
  643. field->set_type_name("Baz");
  644. field->set_name("a");
  645. field->set_number(1);
  646. field = message->add_field();
  647. field->set_type_name("Foo");
  648. field->set_name("f");
  649. field->set_number(2);
  650. WriteDescriptorSet("foo_and_bar.bin", &file_descriptor_set);
  651. file_descriptor_set.clear_file();
  652. file_descriptor_set.add_file()->CopyFrom(foo_file_descriptor_proto);
  653. file_descriptor_proto = file_descriptor_set.add_file();
  654. file_descriptor_proto->set_name("baz.proto");
  655. file_descriptor_proto->add_dependency("foo.proto");
  656. message = file_descriptor_proto->add_message_type();
  657. message->set_name("Baz");
  658. field = message->add_field();
  659. field->set_type_name("Foo");
  660. field->set_name("f");
  661. field->set_number(1);
  662. WriteDescriptorSet("foo_and_baz.bin", &file_descriptor_set);
  663. Run(strings::Substitute(
  664. "protocol_compiler --test_out=$$tmpdir --plug_out=$$tmpdir "
  665. "--descriptor_set_in=$0 bar.proto",
  666. string("$tmpdir/foo_and_bar.bin") +
  667. CommandLineInterface::kPathSeparator +
  668. "$tmpdir/foo_and_baz.bin"));
  669. ExpectNoErrors();
  670. ExpectGenerated("test_generator", "", "bar.proto", "Bar");
  671. ExpectGenerated("test_plugin", "", "bar.proto", "Bar");
  672. }
  673. TEST_F(CommandLineInterfaceTest,
  674. MultipleInputsWithImport_DescriptorSetIn_MissingImport) {
  675. // Test parsing multiple input files with an import of a separate file.
  676. FileDescriptorSet file_descriptor_set;
  677. FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
  678. file_descriptor_proto->set_name("foo.proto");
  679. file_descriptor_proto->add_message_type()->set_name("Foo");
  680. file_descriptor_proto = file_descriptor_set.add_file();
  681. file_descriptor_proto->set_name("bar.proto");
  682. file_descriptor_proto->add_dependency("baz.proto");
  683. DescriptorProto* message = file_descriptor_proto->add_message_type();
  684. message->set_name("Bar");
  685. FieldDescriptorProto* field = message->add_field();
  686. field->set_type_name("Baz");
  687. field->set_name("a");
  688. field->set_number(1);
  689. WriteDescriptorSet("foo_and_bar.bin", &file_descriptor_set);
  690. file_descriptor_set.clear_file();
  691. file_descriptor_proto = file_descriptor_set.add_file();
  692. file_descriptor_proto->set_name("baz.proto");
  693. file_descriptor_proto->add_message_type()->set_name("Baz");
  694. WriteDescriptorSet("baz.bin", &file_descriptor_set);
  695. Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
  696. "--descriptor_set_in=$tmpdir/foo_and_bar.bin "
  697. "foo.proto bar.proto");
  698. ExpectErrorSubstring(
  699. "bar.proto: Import \"baz.proto\" was not found or had errors.");
  700. ExpectErrorSubstring("bar.proto: \"Baz\" is not defined.");
  701. }
  702. TEST_F(CommandLineInterfaceTest, CreateDirectory) {
  703. // Test that when we output to a sub-directory, it is created.
  704. CreateTempFile("bar/baz/foo.proto",
  705. "syntax = \"proto2\";\n"
  706. "message Foo {}\n");
  707. CreateTempDir("out");
  708. CreateTempDir("plugout");
  709. Run("protocol_compiler --test_out=$tmpdir/out --plug_out=$tmpdir/plugout "
  710. "--proto_path=$tmpdir bar/baz/foo.proto");
  711. ExpectNoErrors();
  712. ExpectGenerated("test_generator", "", "bar/baz/foo.proto", "Foo", "out");
  713. ExpectGenerated("test_plugin", "", "bar/baz/foo.proto", "Foo", "plugout");
  714. }
  715. TEST_F(CommandLineInterfaceTest, GeneratorParameters) {
  716. // Test that generator parameters are correctly parsed from the command line.
  717. CreateTempFile("foo.proto",
  718. "syntax = \"proto2\";\n"
  719. "message Foo {}\n");
  720. Run("protocol_compiler --test_out=TestParameter:$tmpdir "
  721. "--plug_out=TestPluginParameter:$tmpdir "
  722. "--proto_path=$tmpdir foo.proto");
  723. ExpectNoErrors();
  724. ExpectGenerated("test_generator", "TestParameter", "foo.proto", "Foo");
  725. ExpectGenerated("test_plugin", "TestPluginParameter", "foo.proto", "Foo");
  726. }
  727. TEST_F(CommandLineInterfaceTest, ExtraGeneratorParameters) {
  728. // Test that generator parameters specified with the option flag are
  729. // correctly passed to the code generator.
  730. CreateTempFile("foo.proto",
  731. "syntax = \"proto2\";\n"
  732. "message Foo {}\n");
  733. // Create the "a" and "b" sub-directories.
  734. CreateTempDir("a");
  735. CreateTempDir("b");
  736. Run("protocol_compiler "
  737. "--test_opt=foo1 "
  738. "--test_out=bar:$tmpdir/a "
  739. "--test_opt=foo2 "
  740. "--test_out=baz:$tmpdir/b "
  741. "--test_opt=foo3 "
  742. "--proto_path=$tmpdir foo.proto");
  743. ExpectNoErrors();
  744. ExpectGenerated(
  745. "test_generator", "bar,foo1,foo2,foo3", "foo.proto", "Foo", "a");
  746. ExpectGenerated(
  747. "test_generator", "baz,foo1,foo2,foo3", "foo.proto", "Foo", "b");
  748. }
  749. TEST_F(CommandLineInterfaceTest, ExtraPluginParameters) {
  750. // Test that generator parameters specified with the option flag are
  751. // correctly passed to the code generator.
  752. CreateTempFile("foo.proto",
  753. "syntax = \"proto2\";\n"
  754. "message Foo {}\n");
  755. // Create the "a" and "b" sub-directories.
  756. CreateTempDir("a");
  757. CreateTempDir("b");
  758. Run("protocol_compiler "
  759. "--plug_opt=foo1 "
  760. "--plug_out=bar:$tmpdir/a "
  761. "--plug_opt=foo2 "
  762. "--plug_out=baz:$tmpdir/b "
  763. "--plug_opt=foo3 "
  764. "--proto_path=$tmpdir foo.proto");
  765. ExpectNoErrors();
  766. ExpectGenerated(
  767. "test_plugin", "bar,foo1,foo2,foo3", "foo.proto", "Foo", "a");
  768. ExpectGenerated(
  769. "test_plugin", "baz,foo1,foo2,foo3", "foo.proto", "Foo", "b");
  770. }
  771. TEST_F(CommandLineInterfaceTest, UnrecognizedExtraParameters) {
  772. CreateTempFile("foo.proto",
  773. "syntax = \"proto2\";\n"
  774. "message Foo {}\n");
  775. Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
  776. "--unknown_plug_a_opt=Foo "
  777. "--unknown_plug_b_opt=Bar "
  778. "--proto_path=$tmpdir foo.proto");
  779. ExpectErrorSubstring("Unknown flag: --unknown_plug_a_opt");
  780. ExpectErrorSubstring("Unknown flag: --unknown_plug_b_opt");
  781. }
  782. TEST_F(CommandLineInterfaceTest, ExtraPluginParametersForOutParameters) {
  783. // This doesn't rely on the plugin having been registred and instead that
  784. // the existence of --[name]_out is enough to make the --[name]_opt valid.
  785. // However, running out of process plugins found via the search path (i.e. -
  786. // not pre registered with --plugin) isn't support in this test suite, so we
  787. // list the options pre/post the _out directive, and then include _opt that
  788. // will be unknown, and confirm the failure output is about the expected
  789. // unknown directive, which means the other were accepted.
  790. // NOTE: UnrecognizedExtraParameters confirms that if two unknown _opt
  791. // directives appear, they both are reported.
  792. CreateTempFile("foo.proto",
  793. "syntax = \"proto2\";\n"
  794. "message Foo {}\n");
  795. Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
  796. "--xyz_opt=foo=bar --xyz_out=$tmpdir "
  797. "--abc_out=$tmpdir --abc_opt=foo=bar "
  798. "--unknown_plug_opt=Foo "
  799. "--proto_path=$tmpdir foo.proto");
  800. ExpectErrorText("Unknown flag: --unknown_plug_opt\n");
  801. }
  802. TEST_F(CommandLineInterfaceTest, Insert) {
  803. // Test running a generator that inserts code into another's output.
  804. CreateTempFile("foo.proto",
  805. "syntax = \"proto2\";\n"
  806. "message Foo {}\n");
  807. Run("protocol_compiler "
  808. "--test_out=TestParameter:$tmpdir "
  809. "--plug_out=TestPluginParameter:$tmpdir "
  810. "--test_out=insert=test_generator,test_plugin:$tmpdir "
  811. "--plug_out=insert=test_generator,test_plugin:$tmpdir "
  812. "--proto_path=$tmpdir foo.proto");
  813. ExpectNoErrors();
  814. ExpectGeneratedWithInsertions(
  815. "test_generator", "TestParameter", "test_generator,test_plugin",
  816. "foo.proto", "Foo");
  817. ExpectGeneratedWithInsertions(
  818. "test_plugin", "TestPluginParameter", "test_generator,test_plugin",
  819. "foo.proto", "Foo");
  820. }
  821. TEST_F(CommandLineInterfaceTest, InsertWithAnnotationFixup) {
  822. // Check that annotation spans are updated after insertions.
  823. CreateTempFile("foo.proto",
  824. "syntax = \"proto2\";\n"
  825. "message MockCodeGenerator_Annotate {}\n");
  826. Run("protocol_compiler "
  827. "--test_out=TestParameter:$tmpdir "
  828. "--plug_out=TestPluginParameter:$tmpdir "
  829. "--test_out=insert=test_generator,test_plugin:$tmpdir "
  830. "--plug_out=insert=test_generator,test_plugin:$tmpdir "
  831. "--proto_path=$tmpdir foo.proto");
  832. ExpectNoErrors();
  833. CheckGeneratedAnnotations("test_generator", "foo.proto");
  834. CheckGeneratedAnnotations("test_plugin", "foo.proto");
  835. }
  836. #if defined(_WIN32)
  837. TEST_F(CommandLineInterfaceTest, WindowsOutputPath) {
  838. // Test that the output path can be a Windows-style path.
  839. CreateTempFile("foo.proto",
  840. "syntax = \"proto2\";\n");
  841. Run("protocol_compiler --null_out=C:\\ "
  842. "--proto_path=$tmpdir foo.proto");
  843. ExpectNoErrors();
  844. ExpectNullCodeGeneratorCalled("");
  845. }
  846. TEST_F(CommandLineInterfaceTest, WindowsOutputPathAndParameter) {
  847. // Test that we can have a windows-style output path and a parameter.
  848. CreateTempFile("foo.proto",
  849. "syntax = \"proto2\";\n");
  850. Run("protocol_compiler --null_out=bar:C:\\ "
  851. "--proto_path=$tmpdir foo.proto");
  852. ExpectNoErrors();
  853. ExpectNullCodeGeneratorCalled("bar");
  854. }
  855. TEST_F(CommandLineInterfaceTest, TrailingBackslash) {
  856. // Test that the directories can end in backslashes. Some users claim this
  857. // doesn't work on their system.
  858. CreateTempFile("foo.proto",
  859. "syntax = \"proto2\";\n"
  860. "message Foo {}\n");
  861. Run("protocol_compiler --test_out=$tmpdir\\ "
  862. "--proto_path=$tmpdir\\ foo.proto");
  863. ExpectNoErrors();
  864. ExpectGenerated("test_generator", "", "foo.proto", "Foo");
  865. }
  866. TEST_F(CommandLineInterfaceTest, Win32ErrorMessage) {
  867. EXPECT_EQ("The system cannot find the file specified.\r\n",
  868. Subprocess::Win32ErrorMessage(ERROR_FILE_NOT_FOUND));
  869. }
  870. #endif // defined(_WIN32) || defined(__CYGWIN__)
  871. TEST_F(CommandLineInterfaceTest, PathLookup) {
  872. // Test that specifying multiple directories in the proto search path works.
  873. CreateTempFile("b/bar.proto",
  874. "syntax = \"proto2\";\n"
  875. "message Bar {}\n");
  876. CreateTempFile("a/foo.proto",
  877. "syntax = \"proto2\";\n"
  878. "import \"bar.proto\";\n"
  879. "message Foo {\n"
  880. " optional Bar a = 1;\n"
  881. "}\n");
  882. CreateTempFile("b/foo.proto", "this should not be parsed\n");
  883. Run("protocol_compiler --test_out=$tmpdir "
  884. "--proto_path=$tmpdir/a --proto_path=$tmpdir/b foo.proto");
  885. ExpectNoErrors();
  886. ExpectGenerated("test_generator", "", "foo.proto", "Foo");
  887. }
  888. TEST_F(CommandLineInterfaceTest, ColonDelimitedPath) {
  889. // Same as PathLookup, but we provide the proto_path in a single flag.
  890. CreateTempFile("b/bar.proto",
  891. "syntax = \"proto2\";\n"
  892. "message Bar {}\n");
  893. CreateTempFile("a/foo.proto",
  894. "syntax = \"proto2\";\n"
  895. "import \"bar.proto\";\n"
  896. "message Foo {\n"
  897. " optional Bar a = 1;\n"
  898. "}\n");
  899. CreateTempFile("b/foo.proto", "this should not be parsed\n");
  900. Run(strings::Substitute(
  901. "protocol_compiler --test_out=$$tmpdir --proto_path=$0 foo.proto",
  902. string("$tmpdir/a") +
  903. CommandLineInterface::kPathSeparator +
  904. "$tmpdir/b"));
  905. ExpectNoErrors();
  906. ExpectGenerated("test_generator", "", "foo.proto", "Foo");
  907. }
  908. TEST_F(CommandLineInterfaceTest, NonRootMapping) {
  909. // Test setting up a search path mapping a directory to a non-root location.
  910. CreateTempFile("foo.proto",
  911. "syntax = \"proto2\";\n"
  912. "message Foo {}\n");
  913. Run("protocol_compiler --test_out=$tmpdir "
  914. "--proto_path=bar=$tmpdir bar/foo.proto");
  915. ExpectNoErrors();
  916. ExpectGenerated("test_generator", "", "bar/foo.proto", "Foo");
  917. }
  918. TEST_F(CommandLineInterfaceTest, PathWithEqualsSign) {
  919. // Test setting up a search path which happens to have '=' in it.
  920. CreateTempDir("with=sign");
  921. CreateTempFile("with=sign/foo.proto",
  922. "syntax = \"proto2\";\n"
  923. "message Foo {}\n");
  924. Run("protocol_compiler --test_out=$tmpdir "
  925. "--proto_path=$tmpdir/with=sign foo.proto");
  926. ExpectNoErrors();
  927. ExpectGenerated("test_generator", "", "foo.proto", "Foo");
  928. }
  929. TEST_F(CommandLineInterfaceTest, MultipleGenerators) {
  930. // Test that we can have multiple generators and use both in one invocation,
  931. // each with a different output directory.
  932. CreateTempFile("foo.proto",
  933. "syntax = \"proto2\";\n"
  934. "message Foo {}\n");
  935. // Create the "a" and "b" sub-directories.
  936. CreateTempDir("a");
  937. CreateTempDir("b");
  938. Run("protocol_compiler "
  939. "--test_out=$tmpdir/a "
  940. "--alt_out=$tmpdir/b "
  941. "--proto_path=$tmpdir foo.proto");
  942. ExpectNoErrors();
  943. ExpectGenerated("test_generator", "", "foo.proto", "Foo", "a");
  944. ExpectGenerated("alt_generator", "", "foo.proto", "Foo", "b");
  945. }
  946. TEST_F(CommandLineInterfaceTest, DisallowServicesNoServices) {
  947. // Test that --disallow_services doesn't cause a problem when there are no
  948. // services.
  949. CreateTempFile("foo.proto",
  950. "syntax = \"proto2\";\n"
  951. "message Foo {}\n");
  952. Run("protocol_compiler --disallow_services --test_out=$tmpdir "
  953. "--proto_path=$tmpdir foo.proto");
  954. ExpectNoErrors();
  955. ExpectGenerated("test_generator", "", "foo.proto", "Foo");
  956. }
  957. TEST_F(CommandLineInterfaceTest, DisallowServicesHasService) {
  958. // Test that --disallow_services produces an error when there are services.
  959. CreateTempFile("foo.proto",
  960. "syntax = \"proto2\";\n"
  961. "message Foo {}\n"
  962. "service Bar {}\n");
  963. Run("protocol_compiler --disallow_services --test_out=$tmpdir "
  964. "--proto_path=$tmpdir foo.proto");
  965. ExpectErrorSubstring("foo.proto: This file contains services");
  966. }
  967. TEST_F(CommandLineInterfaceTest, AllowServicesHasService) {
  968. // Test that services work fine as long as --disallow_services is not used.
  969. CreateTempFile("foo.proto",
  970. "syntax = \"proto2\";\n"
  971. "message Foo {}\n"
  972. "service Bar {}\n");
  973. Run("protocol_compiler --test_out=$tmpdir "
  974. "--proto_path=$tmpdir foo.proto");
  975. ExpectNoErrors();
  976. ExpectGenerated("test_generator", "", "foo.proto", "Foo");
  977. }
  978. TEST_F(CommandLineInterfaceTest, DirectDependencies_Missing_EmptyList) {
  979. CreateTempFile("foo.proto",
  980. "syntax = \"proto2\";\n"
  981. "import \"bar.proto\";\n"
  982. "message Foo { optional Bar bar = 1; }");
  983. CreateTempFile("bar.proto",
  984. "syntax = \"proto2\";\n"
  985. "message Bar { optional string text = 1; }");
  986. Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir "
  987. "--direct_dependencies= foo.proto");
  988. ExpectErrorText(
  989. "foo.proto: File is imported but not declared in --direct_dependencies: "
  990. "bar.proto\n");
  991. }
  992. TEST_F(CommandLineInterfaceTest, DirectDependencies_Missing) {
  993. CreateTempFile("foo.proto",
  994. "syntax = \"proto2\";\n"
  995. "import \"bar.proto\";\n"
  996. "import \"bla.proto\";\n"
  997. "message Foo { optional Bar bar = 1; optional Bla bla = 2; }");
  998. CreateTempFile("bar.proto",
  999. "syntax = \"proto2\";\n"
  1000. "message Bar { optional string text = 1; }");
  1001. CreateTempFile("bla.proto",
  1002. "syntax = \"proto2\";\n"
  1003. "message Bla { optional int64 number = 1; }");
  1004. Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir "
  1005. "--direct_dependencies=bla.proto foo.proto");
  1006. ExpectErrorText(
  1007. "foo.proto: File is imported but not declared in --direct_dependencies: "
  1008. "bar.proto\n");
  1009. }
  1010. TEST_F(CommandLineInterfaceTest, DirectDependencies_NoViolation) {
  1011. CreateTempFile("foo.proto",
  1012. "syntax = \"proto2\";\n"
  1013. "import \"bar.proto\";\n"
  1014. "message Foo { optional Bar bar = 1; }");
  1015. CreateTempFile("bar.proto",
  1016. "syntax = \"proto2\";\n"
  1017. "message Bar { optional string text = 1; }");
  1018. Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir "
  1019. "--direct_dependencies=bar.proto foo.proto");
  1020. ExpectNoErrors();
  1021. }
  1022. TEST_F(CommandLineInterfaceTest, DirectDependencies_NoViolation_MultiImports) {
  1023. CreateTempFile("foo.proto",
  1024. "syntax = \"proto2\";\n"
  1025. "import \"bar.proto\";\n"
  1026. "import \"bla.proto\";\n"
  1027. "message Foo { optional Bar bar = 1; optional Bla bla = 2; }");
  1028. CreateTempFile("bar.proto",
  1029. "syntax = \"proto2\";\n"
  1030. "message Bar { optional string text = 1; }");
  1031. CreateTempFile("bla.proto",
  1032. "syntax = \"proto2\";\n"
  1033. "message Bla { optional int64 number = 1; }");
  1034. Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir "
  1035. "--direct_dependencies=bar.proto:bla.proto foo.proto");
  1036. ExpectNoErrors();
  1037. }
  1038. TEST_F(CommandLineInterfaceTest, DirectDependencies_ProvidedMultipleTimes) {
  1039. CreateTempFile("foo.proto",
  1040. "syntax = \"proto2\";\n");
  1041. Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir "
  1042. "--direct_dependencies=bar.proto --direct_dependencies=bla.proto "
  1043. "foo.proto");
  1044. ExpectErrorText(
  1045. "--direct_dependencies may only be passed once. To specify multiple "
  1046. "direct dependencies, pass them all as a single parameter separated by "
  1047. "':'.\n");
  1048. }
  1049. TEST_F(CommandLineInterfaceTest, DirectDependencies_CustomErrorMessage) {
  1050. CreateTempFile("foo.proto",
  1051. "syntax = \"proto2\";\n"
  1052. "import \"bar.proto\";\n"
  1053. "message Foo { optional Bar bar = 1; }");
  1054. CreateTempFile("bar.proto",
  1055. "syntax = \"proto2\";\n"
  1056. "message Bar { optional string text = 1; }");
  1057. std::vector<string> commands;
  1058. commands.push_back("protocol_compiler");
  1059. commands.push_back("--test_out=$tmpdir");
  1060. commands.push_back("--proto_path=$tmpdir");
  1061. commands.push_back("--direct_dependencies=");
  1062. commands.push_back("--direct_dependencies_violation_msg=Bla \"%s\" Bla");
  1063. commands.push_back("foo.proto");
  1064. RunWithArgs(commands);
  1065. ExpectErrorText("foo.proto: Bla \"bar.proto\" Bla\n");
  1066. }
  1067. TEST_F(CommandLineInterfaceTest, CwdRelativeInputs) {
  1068. // Test that we can accept working-directory-relative input files.
  1069. CreateTempFile("foo.proto",
  1070. "syntax = \"proto2\";\n"
  1071. "message Foo {}\n");
  1072. Run("protocol_compiler --test_out=$tmpdir "
  1073. "--proto_path=$tmpdir $tmpdir/foo.proto");
  1074. ExpectNoErrors();
  1075. ExpectGenerated("test_generator", "", "foo.proto", "Foo");
  1076. }
  1077. TEST_F(CommandLineInterfaceTest, WriteDescriptorSet) {
  1078. CreateTempFile("foo.proto",
  1079. "syntax = \"proto2\";\n"
  1080. "message Foo {}\n");
  1081. CreateTempFile("bar.proto",
  1082. "syntax = \"proto2\";\n"
  1083. "import \"foo.proto\";\n"
  1084. "message Bar {\n"
  1085. " optional Foo foo = 1;\n"
  1086. "}\n");
  1087. Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
  1088. "--proto_path=$tmpdir bar.proto");
  1089. ExpectNoErrors();
  1090. FileDescriptorSet descriptor_set;
  1091. ReadDescriptorSet("descriptor_set", &descriptor_set);
  1092. if (HasFatalFailure()) return;
  1093. EXPECT_EQ(1, descriptor_set.file_size());
  1094. EXPECT_EQ("bar.proto", descriptor_set.file(0).name());
  1095. // Descriptor set should not have source code info.
  1096. EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
  1097. // Descriptor set should have json_name.
  1098. EXPECT_EQ("Bar", descriptor_set.file(0).message_type(0).name());
  1099. EXPECT_EQ("foo", descriptor_set.file(0).message_type(0).field(0).name());
  1100. EXPECT_TRUE(descriptor_set.file(0).message_type(0).field(0).has_json_name());
  1101. }
  1102. TEST_F(CommandLineInterfaceTest, WriteDescriptorSetWithDuplicates) {
  1103. CreateTempFile("foo.proto",
  1104. "syntax = \"proto2\";\n"
  1105. "message Foo {}\n");
  1106. CreateTempFile("bar.proto",
  1107. "syntax = \"proto2\";\n"
  1108. "import \"foo.proto\";\n"
  1109. "message Bar {\n"
  1110. " optional Foo foo = 1;\n"
  1111. "}\n");
  1112. CreateTempFile("baz.proto",
  1113. "syntax = \"proto2\";\n"
  1114. "import \"foo.proto\";\n"
  1115. "message Baz {\n"
  1116. " optional Foo foo = 1;\n"
  1117. "}\n");
  1118. Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
  1119. "--proto_path=$tmpdir bar.proto foo.proto bar.proto baz.proto");
  1120. ExpectNoErrors();
  1121. FileDescriptorSet descriptor_set;
  1122. ReadDescriptorSet("descriptor_set", &descriptor_set);
  1123. if (HasFatalFailure()) return;
  1124. EXPECT_EQ(3, descriptor_set.file_size());
  1125. // foo should come first since the output is in dependency order.
  1126. // since bar and baz are unordered, they should be in command line order.
  1127. EXPECT_EQ("foo.proto", descriptor_set.file(0).name());
  1128. EXPECT_EQ("bar.proto", descriptor_set.file(1).name());
  1129. EXPECT_EQ("baz.proto", descriptor_set.file(2).name());
  1130. // Descriptor set should not have source code info.
  1131. EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
  1132. // Descriptor set should have json_name.
  1133. EXPECT_EQ("Bar", descriptor_set.file(1).message_type(0).name());
  1134. EXPECT_EQ("foo", descriptor_set.file(1).message_type(0).field(0).name());
  1135. EXPECT_TRUE(descriptor_set.file(1).message_type(0).field(0).has_json_name());
  1136. }
  1137. TEST_F(CommandLineInterfaceTest, WriteDescriptorSetWithSourceInfo) {
  1138. CreateTempFile("foo.proto",
  1139. "syntax = \"proto2\";\n"
  1140. "message Foo {}\n");
  1141. CreateTempFile("bar.proto",
  1142. "syntax = \"proto2\";\n"
  1143. "import \"foo.proto\";\n"
  1144. "message Bar {\n"
  1145. " optional Foo foo = 1;\n"
  1146. "}\n");
  1147. Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
  1148. "--include_source_info --proto_path=$tmpdir bar.proto");
  1149. ExpectNoErrors();
  1150. FileDescriptorSet descriptor_set;
  1151. ReadDescriptorSet("descriptor_set", &descriptor_set);
  1152. if (HasFatalFailure()) return;
  1153. EXPECT_EQ(1, descriptor_set.file_size());
  1154. EXPECT_EQ("bar.proto", descriptor_set.file(0).name());
  1155. // Source code info included.
  1156. EXPECT_TRUE(descriptor_set.file(0).has_source_code_info());
  1157. }
  1158. TEST_F(CommandLineInterfaceTest, WriteTransitiveDescriptorSet) {
  1159. CreateTempFile("foo.proto",
  1160. "syntax = \"proto2\";\n"
  1161. "message Foo {}\n");
  1162. CreateTempFile("bar.proto",
  1163. "syntax = \"proto2\";\n"
  1164. "import \"foo.proto\";\n"
  1165. "message Bar {\n"
  1166. " optional Foo foo = 1;\n"
  1167. "}\n");
  1168. Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
  1169. "--include_imports --proto_path=$tmpdir bar.proto");
  1170. ExpectNoErrors();
  1171. FileDescriptorSet descriptor_set;
  1172. ReadDescriptorSet("descriptor_set", &descriptor_set);
  1173. if (HasFatalFailure()) return;
  1174. EXPECT_EQ(2, descriptor_set.file_size());
  1175. if (descriptor_set.file(0).name() == "bar.proto") {
  1176. std::swap(descriptor_set.mutable_file()->mutable_data()[0],
  1177. descriptor_set.mutable_file()->mutable_data()[1]);
  1178. }
  1179. EXPECT_EQ("foo.proto", descriptor_set.file(0).name());
  1180. EXPECT_EQ("bar.proto", descriptor_set.file(1).name());
  1181. // Descriptor set should not have source code info.
  1182. EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
  1183. EXPECT_FALSE(descriptor_set.file(1).has_source_code_info());
  1184. }
  1185. TEST_F(CommandLineInterfaceTest, WriteTransitiveDescriptorSetWithSourceInfo) {
  1186. CreateTempFile("foo.proto",
  1187. "syntax = \"proto2\";\n"
  1188. "message Foo {}\n");
  1189. CreateTempFile("bar.proto",
  1190. "syntax = \"proto2\";\n"
  1191. "import \"foo.proto\";\n"
  1192. "message Bar {\n"
  1193. " optional Foo foo = 1;\n"
  1194. "}\n");
  1195. Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
  1196. "--include_imports --include_source_info --proto_path=$tmpdir bar.proto");
  1197. ExpectNoErrors();
  1198. FileDescriptorSet descriptor_set;
  1199. ReadDescriptorSet("descriptor_set", &descriptor_set);
  1200. if (HasFatalFailure()) return;
  1201. EXPECT_EQ(2, descriptor_set.file_size());
  1202. if (descriptor_set.file(0).name() == "bar.proto") {
  1203. std::swap(descriptor_set.mutable_file()->mutable_data()[0],
  1204. descriptor_set.mutable_file()->mutable_data()[1]);
  1205. }
  1206. EXPECT_EQ("foo.proto", descriptor_set.file(0).name());
  1207. EXPECT_EQ("bar.proto", descriptor_set.file(1).name());
  1208. // Source code info included.
  1209. EXPECT_TRUE(descriptor_set.file(0).has_source_code_info());
  1210. EXPECT_TRUE(descriptor_set.file(1).has_source_code_info());
  1211. }
  1212. #ifdef _WIN32
  1213. // TODO(teboring): Figure out how to write test on windows.
  1214. #else
  1215. TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFileGivenTwoInputs) {
  1216. CreateTempFile("foo.proto",
  1217. "syntax = \"proto2\";\n"
  1218. "message Foo {}\n");
  1219. CreateTempFile("bar.proto",
  1220. "syntax = \"proto2\";\n"
  1221. "import \"foo.proto\";\n"
  1222. "message Bar {\n"
  1223. " optional Foo foo = 1;\n"
  1224. "}\n");
  1225. Run("protocol_compiler --dependency_out=$tmpdir/manifest "
  1226. "--test_out=$tmpdir --proto_path=$tmpdir bar.proto foo.proto");
  1227. ExpectErrorText(
  1228. "Can only process one input file when using --dependency_out=FILE.\n");
  1229. }
  1230. #ifdef PROTOBUF_OPENSOURCE
  1231. TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFile) {
  1232. CreateTempFile("foo.proto",
  1233. "syntax = \"proto2\";\n"
  1234. "message Foo {}\n");
  1235. CreateTempFile("bar.proto",
  1236. "syntax = \"proto2\";\n"
  1237. "import \"foo.proto\";\n"
  1238. "message Bar {\n"
  1239. " optional Foo foo = 1;\n"
  1240. "}\n");
  1241. string current_working_directory = getcwd(NULL, 0);
  1242. SwitchToTempDirectory();
  1243. Run("protocol_compiler --dependency_out=manifest --test_out=. "
  1244. "bar.proto");
  1245. ExpectNoErrors();
  1246. ExpectFileContent("manifest",
  1247. "bar.proto.MockCodeGenerator.test_generator: "
  1248. "foo.proto\\\n bar.proto");
  1249. File::ChangeWorkingDirectory(current_working_directory);
  1250. }
  1251. #else // !PROTOBUF_OPENSOURCE
  1252. // TODO(teboring): Figure out how to change and get working directory in
  1253. // google3.
  1254. #endif // !PROTOBUF_OPENSOURCE
  1255. TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFileForAbsolutePath) {
  1256. CreateTempFile("foo.proto",
  1257. "syntax = \"proto2\";\n"
  1258. "message Foo {}\n");
  1259. CreateTempFile("bar.proto",
  1260. "syntax = \"proto2\";\n"
  1261. "import \"foo.proto\";\n"
  1262. "message Bar {\n"
  1263. " optional Foo foo = 1;\n"
  1264. "}\n");
  1265. Run("protocol_compiler --dependency_out=$tmpdir/manifest "
  1266. "--test_out=$tmpdir --proto_path=$tmpdir bar.proto");
  1267. ExpectNoErrors();
  1268. ExpectFileContent("manifest",
  1269. "$tmpdir/bar.proto.MockCodeGenerator.test_generator: "
  1270. "$tmpdir/foo.proto\\\n $tmpdir/bar.proto");
  1271. }
  1272. #endif // !_WIN32
  1273. TEST_F(CommandLineInterfaceTest, TestArgumentFile) {
  1274. // Test parsing multiple input files using an argument file.
  1275. CreateTempFile("foo.proto",
  1276. "syntax = \"proto2\";\n"
  1277. "message Foo {}\n");
  1278. CreateTempFile("bar.proto",
  1279. "syntax = \"proto2\";\n"
  1280. "message Bar {}\n");
  1281. CreateTempFile("arguments.txt",
  1282. "--test_out=$tmpdir\n"
  1283. "--plug_out=$tmpdir\n"
  1284. "--proto_path=$tmpdir\n"
  1285. "--direct_dependencies_violation_msg=%s is not imported\n"
  1286. "foo.proto\n"
  1287. "bar.proto");
  1288. Run("protocol_compiler @$tmpdir/arguments.txt");
  1289. ExpectNoErrors();
  1290. ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
  1291. "foo.proto", "Foo");
  1292. ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
  1293. "bar.proto", "Bar");
  1294. ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
  1295. "foo.proto", "Foo");
  1296. ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
  1297. "bar.proto", "Bar");
  1298. }
  1299. // -------------------------------------------------------------------
  1300. TEST_F(CommandLineInterfaceTest, ParseErrors) {
  1301. // Test that parse errors are reported.
  1302. CreateTempFile("foo.proto",
  1303. "syntax = \"proto2\";\n"
  1304. "badsyntax\n");
  1305. Run("protocol_compiler --test_out=$tmpdir "
  1306. "--proto_path=$tmpdir foo.proto");
  1307. ExpectErrorText(
  1308. "foo.proto:2:1: Expected top-level statement (e.g. \"message\").\n");
  1309. }
  1310. TEST_F(CommandLineInterfaceTest, ParseErrors_DescriptorSetIn) {
  1311. // Test that parse errors are reported.
  1312. CreateTempFile("foo.bin", "not a FileDescriptorSet");
  1313. Run("protocol_compiler --test_out=$tmpdir "
  1314. "--descriptor_set_in=$tmpdir/foo.bin foo.proto");
  1315. ExpectErrorText(
  1316. "$tmpdir/foo.bin: Unable to parse.\n");
  1317. }
  1318. TEST_F(CommandLineInterfaceTest, ParseErrorsMultipleFiles) {
  1319. // Test that parse errors are reported from multiple files.
  1320. // We set up files such that foo.proto actually depends on bar.proto in
  1321. // two ways: Directly and through baz.proto. bar.proto's errors should
  1322. // only be reported once.
  1323. CreateTempFile("bar.proto",
  1324. "syntax = \"proto2\";\n"
  1325. "badsyntax\n");
  1326. CreateTempFile("baz.proto",
  1327. "syntax = \"proto2\";\n"
  1328. "import \"bar.proto\";\n");
  1329. CreateTempFile("foo.proto",
  1330. "syntax = \"proto2\";\n"
  1331. "import \"bar.proto\";\n"
  1332. "import \"baz.proto\";\n");
  1333. Run("protocol_compiler --test_out=$tmpdir "
  1334. "--proto_path=$tmpdir foo.proto");
  1335. ExpectErrorText(
  1336. "bar.proto:2:1: Expected top-level statement (e.g. \"message\").\n"
  1337. "baz.proto: Import \"bar.proto\" was not found or had errors.\n"
  1338. "foo.proto: Import \"bar.proto\" was not found or had errors.\n"
  1339. "foo.proto: Import \"baz.proto\" was not found or had errors.\n");
  1340. }
  1341. TEST_F(CommandLineInterfaceTest, RecursiveImportFails) {
  1342. // Create a proto file that imports itself.
  1343. CreateTempFile("foo.proto",
  1344. "syntax = \"proto2\";\n"
  1345. "import \"foo.proto\";\n");
  1346. Run("protocol_compiler --test_out=$tmpdir "
  1347. "--proto_path=$tmpdir foo.proto");
  1348. ExpectErrorSubstring(
  1349. "foo.proto: File recursively imports itself: foo.proto -> foo.proto\n");
  1350. }
  1351. TEST_F(CommandLineInterfaceTest, InputNotFoundError) {
  1352. // Test what happens if the input file is not found.
  1353. Run("protocol_compiler --test_out=$tmpdir "
  1354. "--proto_path=$tmpdir foo.proto");
  1355. ExpectErrorText("foo.proto: No such file or directory\n");
  1356. }
  1357. TEST_F(CommandLineInterfaceTest, InputNotFoundError_DescriptorSetIn) {
  1358. // Test what happens if the input file is not found.
  1359. Run("protocol_compiler --test_out=$tmpdir "
  1360. "--descriptor_set_in=$tmpdir/foo.bin foo.proto");
  1361. ExpectErrorText(
  1362. "$tmpdir/foo.bin: No such file or directory\n");
  1363. }
  1364. TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotFoundError) {
  1365. // Test what happens when a working-directory-relative input file is not
  1366. // found.
  1367. Run("protocol_compiler --test_out=$tmpdir "
  1368. "--proto_path=$tmpdir $tmpdir/foo.proto");
  1369. ExpectErrorText(
  1370. "$tmpdir/foo.proto: No such file or directory\n");
  1371. }
  1372. TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotMappedError) {
  1373. // Test what happens when a working-directory-relative input file is not
  1374. // mapped to a virtual path.
  1375. CreateTempFile("foo.proto",
  1376. "syntax = \"proto2\";\n"
  1377. "message Foo {}\n");
  1378. // Create a directory called "bar" so that we can point --proto_path at it.
  1379. CreateTempFile("bar/dummy", "");
  1380. Run("protocol_compiler --test_out=$tmpdir "
  1381. "--proto_path=$tmpdir/bar $tmpdir/foo.proto");
  1382. ExpectErrorText(
  1383. "$tmpdir/foo.proto: File does not reside within any path "
  1384. "specified using --proto_path (or -I). You must specify a "
  1385. "--proto_path which encompasses this file. Note that the "
  1386. "proto_path must be an exact prefix of the .proto file "
  1387. "names -- protoc is too dumb to figure out when two paths "
  1388. "(e.g. absolute and relative) are equivalent (it's harder "
  1389. "than you think).\n");
  1390. }
  1391. TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotFoundAndNotMappedError) {
  1392. // Check what happens if the input file is not found *and* is not mapped
  1393. // in the proto_path.
  1394. // Create a directory called "bar" so that we can point --proto_path at it.
  1395. CreateTempFile("bar/dummy", "");
  1396. Run("protocol_compiler --test_out=$tmpdir "
  1397. "--proto_path=$tmpdir/bar $tmpdir/foo.proto");
  1398. ExpectErrorText(
  1399. "$tmpdir/foo.proto: No such file or directory\n");
  1400. }
  1401. TEST_F(CommandLineInterfaceTest, CwdRelativeInputShadowedError) {
  1402. // Test what happens when a working-directory-relative input file is shadowed
  1403. // by another file in the virtual path.
  1404. CreateTempFile("foo/foo.proto",
  1405. "syntax = \"proto2\";\n"
  1406. "message Foo {}\n");
  1407. CreateTempFile("bar/foo.proto",
  1408. "syntax = \"proto2\";\n"
  1409. "message Bar {}\n");
  1410. Run("protocol_compiler --test_out=$tmpdir "
  1411. "--proto_path=$tmpdir/foo --proto_path=$tmpdir/bar "
  1412. "$tmpdir/bar/foo.proto");
  1413. ExpectErrorText(
  1414. "$tmpdir/bar/foo.proto: Input is shadowed in the --proto_path "
  1415. "by \"$tmpdir/foo/foo.proto\". Either use the latter "
  1416. "file as your input or reorder the --proto_path so that the "
  1417. "former file's location comes first.\n");
  1418. }
  1419. TEST_F(CommandLineInterfaceTest, ProtoPathNotFoundError) {
  1420. // Test what happens if the input file is not found.
  1421. Run("protocol_compiler --test_out=$tmpdir "
  1422. "--proto_path=$tmpdir/foo foo.proto");
  1423. ExpectErrorText(
  1424. "$tmpdir/foo: warning: directory does not exist.\n"
  1425. "foo.proto: No such file or directory\n");
  1426. }
  1427. TEST_F(CommandLineInterfaceTest, ProtoPathAndDescriptorSetIn) {
  1428. Run("protocol_compiler --test_out=$tmpdir "
  1429. "--proto_path=$tmpdir --descriptor_set_in=$tmpdir/foo.bin foo.proto");
  1430. ExpectErrorText(
  1431. "Only one of --descriptor_set_in and --proto_path can be specified.\n");
  1432. Run("protocol_compiler --test_out=$tmpdir "
  1433. "--descriptor_set_in=$tmpdir/foo.bin --proto_path=$tmpdir foo.proto");
  1434. ExpectErrorText(
  1435. "Only one of --proto_path and --descriptor_set_in can be specified.\n");
  1436. }
  1437. TEST_F(CommandLineInterfaceTest, ProtoPathAndDependencyOut) {
  1438. Run("protocol_compiler --test_out=$tmpdir "
  1439. "--dependency_out=$tmpdir/manifest "
  1440. "--descriptor_set_in=$tmpdir/foo.bin foo.proto");
  1441. ExpectErrorText(
  1442. "--descriptor_set_in cannot be used with --dependency_out.\n");
  1443. Run("protocol_compiler --test_out=$tmpdir "
  1444. "--descriptor_set_in=$tmpdir/foo.bin "
  1445. "--dependency_out=$tmpdir/manifest foo.proto");
  1446. ExpectErrorText(
  1447. "--dependency_out cannot be used with --descriptor_set_in.\n");
  1448. }
  1449. TEST_F(CommandLineInterfaceTest, MissingInputError) {
  1450. // Test that we get an error if no inputs are given.
  1451. Run("protocol_compiler --test_out=$tmpdir "
  1452. "--proto_path=$tmpdir");
  1453. ExpectErrorText("Missing input file.\n");
  1454. }
  1455. TEST_F(CommandLineInterfaceTest, MissingOutputError) {
  1456. CreateTempFile("foo.proto",
  1457. "syntax = \"proto2\";\n"
  1458. "message Foo {}\n");
  1459. Run("protocol_compiler --proto_path=$tmpdir foo.proto");
  1460. ExpectErrorText("Missing output directives.\n");
  1461. }
  1462. TEST_F(CommandLineInterfaceTest, OutputWriteError) {
  1463. CreateTempFile("foo.proto",
  1464. "syntax = \"proto2\";\n"
  1465. "message Foo {}\n");
  1466. string output_file =
  1467. MockCodeGenerator::GetOutputFileName("test_generator", "foo.proto");
  1468. // Create a directory blocking our output location.
  1469. CreateTempDir(output_file);
  1470. Run("protocol_compiler --test_out=$tmpdir "
  1471. "--proto_path=$tmpdir foo.proto");
  1472. // MockCodeGenerator no longer detects an error because we actually write to
  1473. // an in-memory location first, then dump to disk at the end. This is no
  1474. // big deal.
  1475. // ExpectErrorSubstring("MockCodeGenerator detected write error.");
  1476. #if defined(_WIN32) && !defined(__CYGWIN__)
  1477. // Windows with MSVCRT.dll produces EPERM instead of EISDIR.
  1478. if (HasAlternateErrorSubstring(output_file + ": Permission denied")) {
  1479. return;
  1480. }
  1481. #endif
  1482. ExpectErrorSubstring(output_file + ": Is a directory");
  1483. }
  1484. TEST_F(CommandLineInterfaceTest, PluginOutputWriteError) {
  1485. CreateTempFile("foo.proto",
  1486. "syntax = \"proto2\";\n"
  1487. "message Foo {}\n");
  1488. string output_file =
  1489. MockCodeGenerator::GetOutputFileName("test_plugin", "foo.proto");
  1490. // Create a directory blocking our output location.
  1491. CreateTempDir(output_file);
  1492. Run("protocol_compiler --plug_out=$tmpdir "
  1493. "--proto_path=$tmpdir foo.proto");
  1494. #if defined(_WIN32) && !defined(__CYGWIN__)
  1495. // Windows with MSVCRT.dll produces EPERM instead of EISDIR.
  1496. if (HasAlternateErrorSubstring(output_file + ": Permission denied")) {
  1497. return;
  1498. }
  1499. #endif
  1500. ExpectErrorSubstring(output_file + ": Is a directory");
  1501. }
  1502. TEST_F(CommandLineInterfaceTest, OutputDirectoryNotFoundError) {
  1503. CreateTempFile("foo.proto",
  1504. "syntax = \"proto2\";\n"
  1505. "message Foo {}\n");
  1506. Run("protocol_compiler --test_out=$tmpdir/nosuchdir "
  1507. "--proto_path=$tmpdir foo.proto");
  1508. ExpectErrorSubstring("nosuchdir/: No such file or directory");
  1509. }
  1510. TEST_F(CommandLineInterfaceTest, PluginOutputDirectoryNotFoundError) {
  1511. CreateTempFile("foo.proto",
  1512. "syntax = \"proto2\";\n"
  1513. "message Foo {}\n");
  1514. Run("protocol_compiler --plug_out=$tmpdir/nosuchdir "
  1515. "--proto_path=$tmpdir foo.proto");
  1516. ExpectErrorSubstring("nosuchdir/: No such file or directory");
  1517. }
  1518. TEST_F(CommandLineInterfaceTest, OutputDirectoryIsFileError) {
  1519. CreateTempFile("foo.proto",
  1520. "syntax = \"proto2\";\n"
  1521. "message Foo {}\n");
  1522. Run("protocol_compiler --test_out=$tmpdir/foo.proto "
  1523. "--proto_path=$tmpdir foo.proto");
  1524. #if defined(_WIN32) && !defined(__CYGWIN__)
  1525. // Windows with MSVCRT.dll produces EINVAL instead of ENOTDIR.
  1526. if (HasAlternateErrorSubstring("foo.proto/: Invalid argument")) {
  1527. return;
  1528. }
  1529. #endif
  1530. ExpectErrorSubstring("foo.proto/: Not a directory");
  1531. }
  1532. TEST_F(CommandLineInterfaceTest, GeneratorError) {
  1533. CreateTempFile("foo.proto",
  1534. "syntax = \"proto2\";\n"
  1535. "message MockCodeGenerator_Error {}\n");
  1536. Run("protocol_compiler --test_out=$tmpdir "
  1537. "--proto_path=$tmpdir foo.proto");
  1538. ExpectErrorSubstring(
  1539. "--test_out: foo.proto: Saw message type MockCodeGenerator_Error.");
  1540. }
  1541. TEST_F(CommandLineInterfaceTest, GeneratorPluginError) {
  1542. // Test a generator plugin that returns an error.
  1543. CreateTempFile("foo.proto",
  1544. "syntax = \"proto2\";\n"
  1545. "message MockCodeGenerator_Error {}\n");
  1546. Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
  1547. "--proto_path=$tmpdir foo.proto");
  1548. ExpectErrorSubstring(
  1549. "--plug_out: foo.proto: Saw message type MockCodeGenerator_Error.");
  1550. }
  1551. TEST_F(CommandLineInterfaceTest, GeneratorPluginFail) {
  1552. // Test a generator plugin that exits with an error code.
  1553. CreateTempFile("foo.proto",
  1554. "syntax = \"proto2\";\n"
  1555. "message MockCodeGenerator_Exit {}\n");
  1556. Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
  1557. "--proto_path=$tmpdir foo.proto");
  1558. ExpectErrorSubstring("Saw message type MockCodeGenerator_Exit.");
  1559. ExpectErrorSubstring(
  1560. "--plug_out: prefix-gen-plug: Plugin failed with status code 123.");
  1561. }
  1562. TEST_F(CommandLineInterfaceTest, GeneratorPluginCrash) {
  1563. // Test a generator plugin that crashes.
  1564. CreateTempFile("foo.proto",
  1565. "syntax = \"proto2\";\n"
  1566. "message MockCodeGenerator_Abort {}\n");
  1567. Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
  1568. "--proto_path=$tmpdir foo.proto");
  1569. ExpectErrorSubstring("Saw message type MockCodeGenerator_Abort.");
  1570. #ifdef _WIN32
  1571. // Windows doesn't have signals. It looks like abort()ing causes the process
  1572. // to exit with status code 3, but let's not depend on the exact number here.
  1573. ExpectErrorSubstring(
  1574. "--plug_out: prefix-gen-plug: Plugin failed with status code");
  1575. #else
  1576. // Don't depend on the exact signal number.
  1577. ExpectErrorSubstring(
  1578. "--plug_out: prefix-gen-plug: Plugin killed by signal");
  1579. #endif
  1580. }
  1581. TEST_F(CommandLineInterfaceTest, PluginReceivesSourceCodeInfo) {
  1582. CreateTempFile("foo.proto",
  1583. "syntax = \"proto2\";\n"
  1584. "message MockCodeGenerator_HasSourceCodeInfo {}\n");
  1585. Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
  1586. ExpectErrorSubstring(
  1587. "Saw message type MockCodeGenerator_HasSourceCodeInfo: 1.");
  1588. }
  1589. TEST_F(CommandLineInterfaceTest, PluginReceivesJsonName) {
  1590. CreateTempFile("foo.proto",
  1591. "syntax = \"proto2\";\n"
  1592. "message MockCodeGenerator_HasJsonName {\n"
  1593. " optional int32 value = 1;\n"
  1594. "}\n");
  1595. Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
  1596. ExpectErrorSubstring("Saw json_name: 1");
  1597. }
  1598. TEST_F(CommandLineInterfaceTest, PluginReceivesCompilerVersion) {
  1599. CreateTempFile("foo.proto",
  1600. "syntax = \"proto2\";\n"
  1601. "message MockCodeGenerator_ShowVersionNumber {\n"
  1602. " optional int32 value = 1;\n"
  1603. "}\n");
  1604. Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
  1605. ExpectErrorSubstring(
  1606. StringPrintf("Saw compiler_version: %d %s",
  1607. GOOGLE_PROTOBUF_VERSION,
  1608. GOOGLE_PROTOBUF_VERSION_SUFFIX));
  1609. }
  1610. TEST_F(CommandLineInterfaceTest, GeneratorPluginNotFound) {
  1611. // Test what happens if the plugin isn't found.
  1612. CreateTempFile("error.proto",
  1613. "syntax = \"proto2\";\n"
  1614. "message Foo {}\n");
  1615. Run("protocol_compiler --badplug_out=TestParameter:$tmpdir "
  1616. "--plugin=prefix-gen-badplug=no_such_file "
  1617. "--proto_path=$tmpdir error.proto");
  1618. #ifdef _WIN32
  1619. ExpectErrorSubstring("--badplug_out: prefix-gen-badplug: " +
  1620. Subprocess::Win32ErrorMessage(ERROR_FILE_NOT_FOUND));
  1621. #else
  1622. // Error written to stdout by child process after exec() fails.
  1623. ExpectErrorSubstring(
  1624. "no_such_file: program not found or is not executable");
  1625. // Error written by parent process when child fails.
  1626. ExpectErrorSubstring(
  1627. "--badplug_out: prefix-gen-badplug: Plugin failed with status code 1.");
  1628. #endif
  1629. }
  1630. TEST_F(CommandLineInterfaceTest, GeneratorPluginNotAllowed) {
  1631. // Test what happens if plugins aren't allowed.
  1632. CreateTempFile("error.proto",
  1633. "syntax = \"proto2\";\n"
  1634. "message Foo {}\n");
  1635. DisallowPlugins();
  1636. Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
  1637. "--proto_path=$tmpdir error.proto");
  1638. ExpectErrorSubstring("Unknown flag: --plug_out");
  1639. }
  1640. TEST_F(CommandLineInterfaceTest, HelpText) {
  1641. Run("test_exec_name --help");
  1642. ExpectCapturedStdoutSubstringWithZeroReturnCode("Usage: test_exec_name ");
  1643. ExpectCapturedStdoutSubstringWithZeroReturnCode("--test_out=OUT_DIR");
  1644. ExpectCapturedStdoutSubstringWithZeroReturnCode("Test output.");
  1645. ExpectCapturedStdoutSubstringWithZeroReturnCode("--alt_out=OUT_DIR");
  1646. ExpectCapturedStdoutSubstringWithZeroReturnCode("Alt output.");
  1647. }
  1648. TEST_F(CommandLineInterfaceTest, GccFormatErrors) {
  1649. // Test --error_format=gcc (which is the default, but we want to verify
  1650. // that it can be set explicitly).
  1651. CreateTempFile("foo.proto",
  1652. "syntax = \"proto2\";\n"
  1653. "badsyntax\n");
  1654. Run("protocol_compiler --test_out=$tmpdir "
  1655. "--proto_path=$tmpdir --error_format=gcc foo.proto");
  1656. ExpectErrorText(
  1657. "foo.proto:2:1: Expected top-level statement (e.g. \"message\").\n");
  1658. }
  1659. TEST_F(CommandLineInterfaceTest, MsvsFormatErrors) {
  1660. // Test --error_format=msvs
  1661. CreateTempFile("foo.proto",
  1662. "syntax = \"proto2\";\n"
  1663. "badsyntax\n");
  1664. Run("protocol_compiler --test_out=$tmpdir "
  1665. "--proto_path=$tmpdir --error_format=msvs foo.proto");
  1666. ExpectErrorText(
  1667. "$tmpdir/foo.proto(2) : error in column=1: Expected top-level statement "
  1668. "(e.g. \"message\").\n");
  1669. }
  1670. TEST_F(CommandLineInterfaceTest, InvalidErrorFormat) {
  1671. // Test --error_format=msvs
  1672. CreateTempFile("foo.proto",
  1673. "syntax = \"proto2\";\n"
  1674. "badsyntax\n");
  1675. Run("protocol_compiler --test_out=$tmpdir "
  1676. "--proto_path=$tmpdir --error_format=invalid foo.proto");
  1677. ExpectErrorText(
  1678. "Unknown error format: invalid\n");
  1679. }
  1680. // -------------------------------------------------------------------
  1681. // Flag parsing tests
  1682. TEST_F(CommandLineInterfaceTest, ParseSingleCharacterFlag) {
  1683. // Test that a single-character flag works.
  1684. CreateTempFile("foo.proto",
  1685. "syntax = \"proto2\";\n"
  1686. "message Foo {}\n");
  1687. Run("protocol_compiler -t$tmpdir "
  1688. "--proto_path=$tmpdir foo.proto");
  1689. ExpectNoErrors();
  1690. ExpectGenerated("test_generator", "", "foo.proto", "Foo");
  1691. }
  1692. TEST_F(CommandLineInterfaceTest, ParseSpaceDelimitedValue) {
  1693. // Test that separating the flag value with a space works.
  1694. CreateTempFile("foo.proto",
  1695. "syntax = \"proto2\";\n"
  1696. "message Foo {}\n");
  1697. Run("protocol_compiler --test_out $tmpdir "
  1698. "--proto_path=$tmpdir foo.proto");
  1699. ExpectNoErrors();
  1700. ExpectGenerated("test_generator", "", "foo.proto", "Foo");
  1701. }
  1702. TEST_F(CommandLineInterfaceTest, ParseSingleCharacterSpaceDelimitedValue) {
  1703. // Test that separating the flag value with a space works for
  1704. // single-character flags.
  1705. CreateTempFile("foo.proto",
  1706. "syntax = \"proto2\";\n"
  1707. "message Foo {}\n");
  1708. Run("protocol_compiler -t $tmpdir "
  1709. "--proto_path=$tmpdir foo.proto");
  1710. ExpectNoErrors();
  1711. ExpectGenerated("test_generator", "", "foo.proto", "Foo");
  1712. }
  1713. TEST_F(CommandLineInterfaceTest, MissingValueError) {
  1714. // Test that we get an error if a flag is missing its value.
  1715. Run("protocol_compiler --test_out --proto_path=$tmpdir foo.proto");
  1716. ExpectErrorText("Missing value for flag: --test_out\n");
  1717. }
  1718. TEST_F(CommandLineInterfaceTest, MissingValueAtEndError) {
  1719. // Test that we get an error if the last argument is a flag requiring a
  1720. // value.
  1721. Run("protocol_compiler --test_out");
  1722. ExpectErrorText("Missing value for flag: --test_out\n");
  1723. }
  1724. TEST_F(CommandLineInterfaceTest, PrintFreeFieldNumbers) {
  1725. CreateTempFile(
  1726. "foo.proto",
  1727. "syntax = \"proto2\";\n"
  1728. "package foo;\n"
  1729. "message Foo {\n"
  1730. " optional int32 a = 2;\n"
  1731. " optional string b = 4;\n"
  1732. " optional string c = 5;\n"
  1733. " optional int64 d = 8;\n"
  1734. " optional double e = 10;\n"
  1735. "}\n");
  1736. CreateTempFile(
  1737. "bar.proto",
  1738. "syntax = \"proto2\";\n"
  1739. "message Bar {\n"
  1740. " optional int32 a = 2;\n"
  1741. " extensions 4 to 5;\n"
  1742. " optional int64 d = 8;\n"
  1743. " extensions 10;\n"
  1744. "}\n");
  1745. CreateTempFile(
  1746. "baz.proto",
  1747. "syntax = \"proto2\";\n"
  1748. "message Baz {\n"
  1749. " optional int32 a = 2;\n"
  1750. " optional int64 d = 8;\n"
  1751. " extensions 15 to max;\n" // unordered.
  1752. " extensions 13;\n"
  1753. " extensions 10 to 12;\n"
  1754. " extensions 5;\n"
  1755. " extensions 4;\n"
  1756. "}\n");
  1757. CreateTempFile(
  1758. "quz.proto",
  1759. "syntax = \"proto2\";\n"
  1760. "message Quz {\n"
  1761. " message Foo {}\n" // nested message
  1762. " optional int32 a = 2;\n"
  1763. " optional group C = 4 {\n"
  1764. " optional int32 d = 5;\n"
  1765. " }\n"
  1766. " extensions 8 to 10;\n"
  1767. " optional group E = 11 {\n"
  1768. " optional int32 f = 9;\n" // explicitly reuse extension range 8-10
  1769. " optional group G = 15 {\n" // nested group
  1770. " message Foo {}\n" // nested message inside nested group
  1771. " }\n"
  1772. " }\n"
  1773. "}\n");
  1774. Run("protocol_compiler --print_free_field_numbers --proto_path=$tmpdir "
  1775. "foo.proto bar.proto baz.proto quz.proto");
  1776. ExpectNoErrors();
  1777. // TODO(jieluo): Cygwin doesn't work well if we try to capture stderr and
  1778. // stdout at the same time. Need to figure out why and add this test back
  1779. // for Cygwin.
  1780. #if !defined(__CYGWIN__)
  1781. ExpectCapturedStdout(
  1782. "foo.Foo free: 1 3 6-7 9 11-INF\n"
  1783. "Bar free: 1 3 6-7 9 11-INF\n"
  1784. "Baz free: 1 3 6-7 9 14\n"
  1785. "Quz.Foo free: 1-INF\n"
  1786. "Quz.E.G.Foo free: 1-INF\n"
  1787. "Quz free: 1 3 6-7 12-14 16-INF\n");
  1788. #endif
  1789. }
  1790. // ===================================================================
  1791. // Test for --encode and --decode. Note that it would be easier to do this
  1792. // test as a shell script, but we'd like to be able to run the test on
  1793. // platforms that don't have a Bourne-compatible shell available (especially
  1794. // Windows/MSVC).
  1795. enum EncodeDecodeTestMode {
  1796. PROTO_PATH,
  1797. DESCRIPTOR_SET_IN
  1798. };
  1799. class EncodeDecodeTest : public testing::TestWithParam<EncodeDecodeTestMode> {
  1800. protected:
  1801. virtual void SetUp() {
  1802. WriteUnittestProtoDescriptorSet();
  1803. duped_stdin_ = dup(STDIN_FILENO);
  1804. }
  1805. virtual void TearDown() {
  1806. dup2(duped_stdin_, STDIN_FILENO);
  1807. close(duped_stdin_);
  1808. }
  1809. void RedirectStdinFromText(const string& input) {
  1810. string filename = TestTempDir() + "/test_stdin";
  1811. GOOGLE_CHECK_OK(File::SetContents(filename, input, true));
  1812. GOOGLE_CHECK(RedirectStdinFromFile(filename));
  1813. }
  1814. bool RedirectStdinFromFile(const string& filename) {
  1815. int fd = open(filename.c_str(), O_RDONLY);
  1816. if (fd < 0) return false;
  1817. dup2(fd, STDIN_FILENO);
  1818. close(fd);
  1819. return true;
  1820. }
  1821. // Remove '\r' characters from text.
  1822. string StripCR(const string& text) {
  1823. string result;
  1824. for (int i = 0; i < text.size(); i++) {
  1825. if (text[i] != '\r') {
  1826. result.push_back(text[i]);
  1827. }
  1828. }
  1829. return result;
  1830. }
  1831. enum Type { TEXT, BINARY };
  1832. enum ReturnCode { SUCCESS, ERROR };
  1833. bool Run(const string& command) {
  1834. std::vector<string> args;
  1835. args.push_back("protoc");
  1836. SplitStringUsing(command, " ", &args);
  1837. switch (GetParam()) {
  1838. case PROTO_PATH:
  1839. args.push_back("--proto_path=" + TestSourceDir());
  1840. break;
  1841. case DESCRIPTOR_SET_IN:
  1842. args.push_back(StrCat(
  1843. "--descriptor_set_in=",
  1844. unittest_proto_descriptor_set_filename_));
  1845. break;
  1846. default:
  1847. ADD_FAILURE() << "unexpected EncodeDecodeTestMode: " << GetParam();
  1848. }
  1849. std::unique_ptr<const char * []> argv(new const char* [args.size()]);
  1850. for (int i = 0; i < args.size(); i++) {
  1851. argv[i] = args[i].c_str();
  1852. }
  1853. CommandLineInterface cli;
  1854. CaptureTestStdout();
  1855. CaptureTestStderr();
  1856. int result = cli.Run(args.size(), argv.get());
  1857. captured_stdout_ = GetCapturedTestStdout();
  1858. captured_stderr_ = GetCapturedTestStderr();
  1859. return result == 0;
  1860. }
  1861. void ExpectStdoutMatchesBinaryFile(const string& filename) {
  1862. string expected_output;
  1863. GOOGLE_CHECK_OK(File::GetContents(filename, &expected_output, true));
  1864. // Don't use EXPECT_EQ because we don't want to print raw binary data to
  1865. // stdout on failure.
  1866. EXPECT_TRUE(captured_stdout_ == expected_output);
  1867. }
  1868. void ExpectStdoutMatchesTextFile(const string& filename) {
  1869. string expected_output;
  1870. GOOGLE_CHECK_OK(File::GetContents(filename, &expected_output, true));
  1871. ExpectStdoutMatchesText(expected_output);
  1872. }
  1873. void ExpectStdoutMatchesText(const string& expected_text) {
  1874. EXPECT_EQ(StripCR(expected_text), StripCR(captured_stdout_));
  1875. }
  1876. void ExpectStderrMatchesText(const string& expected_text) {
  1877. EXPECT_EQ(StripCR(expected_text), StripCR(captured_stderr_));
  1878. }
  1879. private:
  1880. void WriteUnittestProtoDescriptorSet() {
  1881. unittest_proto_descriptor_set_filename_ =
  1882. TestTempDir() + "/unittest_proto_descriptor_set.bin";
  1883. FileDescriptorSet file_descriptor_set;
  1884. protobuf_unittest::TestAllTypes test_all_types;
  1885. test_all_types.descriptor()->file()->CopyTo(file_descriptor_set.add_file());
  1886. protobuf_unittest_import::ImportMessage import_message;
  1887. import_message.descriptor()->file()->CopyTo(file_descriptor_set.add_file());
  1888. protobuf_unittest_import::PublicImportMessage public_import_message;
  1889. public_import_message.descriptor()->file()->CopyTo(
  1890. file_descriptor_set.add_file());
  1891. GOOGLE_DCHECK(file_descriptor_set.IsInitialized());
  1892. string binary_proto;
  1893. GOOGLE_CHECK(file_descriptor_set.SerializeToString(&binary_proto));
  1894. GOOGLE_CHECK_OK(File::SetContents(
  1895. unittest_proto_descriptor_set_filename_,
  1896. binary_proto,
  1897. true));
  1898. }
  1899. int duped_stdin_;
  1900. string captured_stdout_;
  1901. string captured_stderr_;
  1902. string unittest_proto_descriptor_set_filename_;
  1903. };
  1904. TEST_P(EncodeDecodeTest, Encode) {
  1905. RedirectStdinFromFile(TestSourceDir() + "/google/protobuf/"
  1906. "testdata/text_format_unittest_data_oneof_implemented.txt");
  1907. EXPECT_TRUE(Run("google/protobuf/unittest.proto "
  1908. "--encode=protobuf_unittest.TestAllTypes"));
  1909. ExpectStdoutMatchesBinaryFile(TestSourceDir() +
  1910. "/google/protobuf/testdata/golden_message_oneof_implemented");
  1911. ExpectStderrMatchesText("");
  1912. }
  1913. TEST_P(EncodeDecodeTest, Decode) {
  1914. RedirectStdinFromFile(TestSourceDir() +
  1915. "/google/protobuf/testdata/golden_message_oneof_implemented");
  1916. EXPECT_TRUE(Run("google/protobuf/unittest.proto "
  1917. "--decode=protobuf_unittest.TestAllTypes"));
  1918. ExpectStdoutMatchesTextFile(TestSourceDir() +
  1919. "/google/protobuf/"
  1920. "testdata/text_format_unittest_data_oneof_implemented.txt");
  1921. ExpectStderrMatchesText("");
  1922. }
  1923. TEST_P(EncodeDecodeTest, Partial) {
  1924. RedirectStdinFromText("");
  1925. EXPECT_TRUE(Run("google/protobuf/unittest.proto "
  1926. "--encode=protobuf_unittest.TestRequired"));
  1927. ExpectStdoutMatchesText("");
  1928. ExpectStderrMatchesText(
  1929. "warning: Input message is missing required fields: a, b, c\n");
  1930. }
  1931. TEST_P(EncodeDecodeTest, DecodeRaw) {
  1932. protobuf_unittest::TestAllTypes message;
  1933. message.set_optional_int32(123);
  1934. message.set_optional_string("foo");
  1935. string data;
  1936. message.SerializeToString(&data);
  1937. RedirectStdinFromText(data);
  1938. EXPECT_TRUE(Run("--decode_raw"));
  1939. ExpectStdoutMatchesText("1: 123\n"
  1940. "14: \"foo\"\n");
  1941. ExpectStderrMatchesText("");
  1942. }
  1943. TEST_P(EncodeDecodeTest, UnknownType) {
  1944. EXPECT_FALSE(Run("google/protobuf/unittest.proto "
  1945. "--encode=NoSuchType"));
  1946. ExpectStdoutMatchesText("");
  1947. ExpectStderrMatchesText("Type not defined: NoSuchType\n");
  1948. }
  1949. TEST_P(EncodeDecodeTest, ProtoParseError) {
  1950. EXPECT_FALSE(Run("google/protobuf/no_such_file.proto "
  1951. "--encode=NoSuchType"));
  1952. ExpectStdoutMatchesText("");
  1953. ExpectStderrMatchesText(
  1954. "google/protobuf/no_such_file.proto: No such file or directory\n");
  1955. }
  1956. INSTANTIATE_TEST_CASE_P(FileDescriptorSetSource,
  1957. EncodeDecodeTest,
  1958. testing::Values(PROTO_PATH, DESCRIPTOR_SET_IN));
  1959. } // anonymous namespace
  1960. #endif // !GOOGLE_PROTOBUF_HEAP_CHECK_DRACONIAN
  1961. } // namespace compiler
  1962. } // namespace protobuf
  1963. } // namespace google