command_line_interface_unittest.cc 88 KB

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