blob: dda007d48f728f2eeb92d7d4a946a16279e93956 [file] [log] [blame]
temporal40ee5512008-07-10 02:12:20 +00001// Protocol Buffers - Google's data interchange format
kenton@google.com24bf56f2008-09-24 20:31:01 +00002// Copyright 2008 Google Inc. All rights reserved.
Feng Xiaoe4288622014-10-01 16:26:23 -07003// https://developers.google.com/protocol-buffers/
temporal40ee5512008-07-10 02:12:20 +00004//
kenton@google.com24bf56f2008-09-24 20:31:01 +00005// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are
7// met:
temporal40ee5512008-07-10 02:12:20 +00008//
kenton@google.com24bf56f2008-09-24 20:31:01 +00009// * 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.
temporal40ee5512008-07-10 02:12:20 +000018//
kenton@google.com24bf56f2008-09-24 20:31:01 +000019// 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.
temporal40ee5512008-07-10 02:12:20 +000030
31// Author: kenton@google.com (Kenton Varda)
32// Based on original Protocol Buffers design by
33// Sanjay Ghemawat, Jeff Dean, and others.
34
temporal779f61c2008-08-13 03:15:00 +000035#include <sys/types.h>
36#include <sys/stat.h>
37#include <fcntl.h>
38#ifdef _MSC_VER
39#include <io.h>
40#else
41#include <unistd.h>
42#endif
jieluo@google.com4de8f552014-07-18 00:47:59 +000043#include <memory>
Feng Xiao6ef984a2014-11-10 17:34:54 -080044#ifndef _SHARED_PTR_H
45#include <google/protobuf/stubs/shared_ptr.h>
46#endif
temporal40ee5512008-07-10 02:12:20 +000047#include <vector>
48
temporal779f61c2008-08-13 03:15:00 +000049#include <google/protobuf/descriptor.pb.h>
temporal40ee5512008-07-10 02:12:20 +000050#include <google/protobuf/descriptor.h>
51#include <google/protobuf/io/zero_copy_stream.h>
52#include <google/protobuf/compiler/command_line_interface.h>
53#include <google/protobuf/compiler/code_generator.h>
Feng Xiao6ef984a2014-11-10 17:34:54 -080054#include <google/protobuf/testing/file.h>
kenton@google.comfccb1462009-12-18 02:11:36 +000055#include <google/protobuf/compiler/mock_code_generator.h>
liujisi@google.com57014ff2010-12-21 05:56:35 +000056#include <google/protobuf/compiler/subprocess.h>
temporal40ee5512008-07-10 02:12:20 +000057#include <google/protobuf/io/printer.h>
temporal779f61c2008-08-13 03:15:00 +000058#include <google/protobuf/unittest.pb.h>
temporal40ee5512008-07-10 02:12:20 +000059#include <google/protobuf/testing/file.h>
60#include <google/protobuf/stubs/strutil.h>
kenton@google.comfccb1462009-12-18 02:11:36 +000061#include <google/protobuf/stubs/substitute.h>
temporal40ee5512008-07-10 02:12:20 +000062
63#include <google/protobuf/testing/googletest.h>
64#include <gtest/gtest.h>
65
Feng Xiaoc5147e32015-12-28 16:40:39 -080066namespace google {
67namespace protobuf {
68namespace compiler {
Feng Xiao6ef984a2014-11-10 17:34:54 -080069
Jisi Liu7a00a1e2015-02-21 17:28:51 -080070// Disable the whole test when we use tcmalloc for "draconian" heap checks, in
71// which case tcmalloc will print warnings that fail the plugin tests.
72#if !GOOGLE_PROTOBUF_HEAP_CHECK_DRACONIAN
temporal40ee5512008-07-10 02:12:20 +000073
temporal779f61c2008-08-13 03:15:00 +000074#if defined(_WIN32)
75#ifndef STDIN_FILENO
76#define STDIN_FILENO 0
77#endif
78#ifndef STDOUT_FILENO
79#define STDOUT_FILENO 1
80#endif
kenton@google.comc0ee4d22009-12-22 02:05:33 +000081#ifndef F_OK
82#define F_OK 00 // not defined by MSVC for whatever reason
83#endif
temporal779f61c2008-08-13 03:15:00 +000084#endif
85
temporal40ee5512008-07-10 02:12:20 +000086namespace {
87
Feng Xiao6ef984a2014-11-10 17:34:54 -080088bool FileExists(const string& path) {
89 return File::Exists(path);
90}
91
temporal40ee5512008-07-10 02:12:20 +000092class CommandLineInterfaceTest : public testing::Test {
93 protected:
94 virtual void SetUp();
95 virtual void TearDown();
96
97 // Runs the CommandLineInterface with the given command line. The
98 // command is automatically split on spaces, and the string "$tmpdir"
99 // is replaced with TestTempDir().
jieluo@google.com8d6f04a2014-08-06 20:49:30 +0000100 void Run(const string& command);
temporal40ee5512008-07-10 02:12:20 +0000101
102 // -----------------------------------------------------------------
103 // Methods to set up the test (called before Run()).
104
temporalcc930432008-07-21 20:28:30 +0000105 class NullCodeGenerator;
temporal40ee5512008-07-10 02:12:20 +0000106
kenton@google.comfccb1462009-12-18 02:11:36 +0000107 // Normally plugins are allowed for all tests. Call this to explicitly
108 // disable them.
109 void DisallowPlugins() { disallow_plugins_ = true; }
temporalcc930432008-07-21 20:28:30 +0000110
temporal40ee5512008-07-10 02:12:20 +0000111 // Create a temp file within temp_directory_ with the given name.
112 // The containing directory is also created if necessary.
113 void CreateTempFile(const string& name, const string& contents);
114
kenton@google.comfccb1462009-12-18 02:11:36 +0000115 // Create a subdirectory within temp_directory_.
116 void CreateTempDir(const string& name);
117
Bo Yang5db21732015-05-21 14:28:59 -0700118#ifdef PROTOBUF_OPENSOURCE
Bo Yanga1b351c2015-02-04 10:20:20 -0800119 // Change working directory to temp directory.
120 void SwitchToTempDirectory() {
121 File::ChangeWorkingDirectory(temp_directory_);
temporal40ee5512008-07-10 02:12:20 +0000122 }
Bo Yang5db21732015-05-21 14:28:59 -0700123#else // !PROTOBUF_OPENSOURCE
124 // TODO(teboring): Figure out how to change and get working directory in
125 // google3.
126#endif // !PROTOBUF_OPENSOURCE
temporal40ee5512008-07-10 02:12:20 +0000127
temporal40ee5512008-07-10 02:12:20 +0000128 void SetInputsAreProtoPathRelative(bool enable) {
129 cli_.SetInputsAreProtoPathRelative(enable);
130 }
131
132 // -----------------------------------------------------------------
133 // Methods to check the test results (called after Run()).
134
135 // Checks that no text was written to stderr during Run(), and Run()
136 // returned 0.
137 void ExpectNoErrors();
138
139 // Checks that Run() returned non-zero and the stderr output is exactly
140 // the text given. expected_test may contain references to "$tmpdir",
141 // which will be replaced by the temporary directory path.
142 void ExpectErrorText(const string& expected_text);
143
144 // Checks that Run() returned non-zero and the stderr contains the given
145 // substring.
146 void ExpectErrorSubstring(const string& expected_substring);
147
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000148 // Like ExpectErrorSubstring, but checks that Run() returned zero.
149 void ExpectErrorSubstringWithZeroReturnCode(
150 const string& expected_substring);
151
jieluo@google.com4de8f552014-07-18 00:47:59 +0000152 // Checks that the captured stdout is the same as the expected_text.
153 void ExpectCapturedStdout(const string& expected_text);
154
temporal40ee5512008-07-10 02:12:20 +0000155 // Returns true if ExpectErrorSubstring(expected_substring) would pass, but
156 // does not fail otherwise.
157 bool HasAlternateErrorSubstring(const string& expected_substring);
158
159 // Checks that MockCodeGenerator::Generate() was called in the given
kenton@google.comfccb1462009-12-18 02:11:36 +0000160 // context (or the generator in test_plugin.cc, which produces the same
161 // output). That is, this tests if the generator with the given name
temporal40ee5512008-07-10 02:12:20 +0000162 // was called with the given parameter and proto file and produced the
163 // given output file. This is checked by reading the output file and
164 // checking that it contains the content that MockCodeGenerator would
165 // generate given these inputs. message_name is the name of the first
166 // message that appeared in the proto file; this is just to make extra
167 // sure that the correct file was parsed.
168 void ExpectGenerated(const string& generator_name,
169 const string& parameter,
170 const string& proto_name,
kenton@google.comfccb1462009-12-18 02:11:36 +0000171 const string& message_name);
172 void ExpectGenerated(const string& generator_name,
173 const string& parameter,
174 const string& proto_name,
temporal40ee5512008-07-10 02:12:20 +0000175 const string& message_name,
kenton@google.comfccb1462009-12-18 02:11:36 +0000176 const string& output_directory);
liujisi@google.com33165fe2010-11-02 13:14:58 +0000177 void ExpectGeneratedWithMultipleInputs(const string& generator_name,
178 const string& all_proto_names,
179 const string& proto_name,
180 const string& message_name);
kenton@google.comfccb1462009-12-18 02:11:36 +0000181 void ExpectGeneratedWithInsertions(const string& generator_name,
182 const string& parameter,
183 const string& insertions,
184 const string& proto_name,
185 const string& message_name);
temporal40ee5512008-07-10 02:12:20 +0000186
kenton@google.com91218af2009-12-18 07:20:43 +0000187 void ExpectNullCodeGeneratorCalled(const string& parameter);
188
temporal779f61c2008-08-13 03:15:00 +0000189 void ReadDescriptorSet(const string& filename,
190 FileDescriptorSet* descriptor_set);
191
Bo Yang5914ce72015-02-03 21:35:50 -0800192 void ExpectFileContent(const string& filename,
193 const string& content);
194
temporal40ee5512008-07-10 02:12:20 +0000195 private:
196 // The object we are testing.
197 CommandLineInterface cli_;
198
kenton@google.comfccb1462009-12-18 02:11:36 +0000199 // Was DisallowPlugins() called?
200 bool disallow_plugins_;
201
temporal40ee5512008-07-10 02:12:20 +0000202 // We create a directory within TestTempDir() in order to add extra
203 // protection against accidentally deleting user files (since we recursively
204 // delete this directory during the test). This is the full path of that
205 // directory.
206 string temp_directory_;
207
208 // The result of Run().
209 int return_code_;
210
211 // The captured stderr output.
212 string error_text_;
213
jieluo@google.com4de8f552014-07-18 00:47:59 +0000214 // The captured stdout.
215 string captured_stdout_;
216
temporal40ee5512008-07-10 02:12:20 +0000217 // Pointers which need to be deleted later.
temporalcc930432008-07-21 20:28:30 +0000218 vector<CodeGenerator*> mock_generators_to_delete_;
kenton@google.com91218af2009-12-18 07:20:43 +0000219
220 NullCodeGenerator* null_generator_;
temporal40ee5512008-07-10 02:12:20 +0000221};
222
temporalcc930432008-07-21 20:28:30 +0000223class CommandLineInterfaceTest::NullCodeGenerator : public CodeGenerator {
224 public:
225 NullCodeGenerator() : called_(false) {}
226 ~NullCodeGenerator() {}
227
228 mutable bool called_;
229 mutable string parameter_;
230
231 // implements CodeGenerator ----------------------------------------
232 bool Generate(const FileDescriptor* file,
233 const string& parameter,
liujisi@google.com33165fe2010-11-02 13:14:58 +0000234 GeneratorContext* context,
temporalcc930432008-07-21 20:28:30 +0000235 string* error) const {
236 called_ = true;
237 parameter_ = parameter;
238 return true;
239 }
240};
241
temporal40ee5512008-07-10 02:12:20 +0000242// ===================================================================
243
244void CommandLineInterfaceTest::SetUp() {
245 // Most of these tests were written before this option was added, so we
246 // run with the option on (which used to be the only way) except in certain
247 // tests where we turn it off.
248 cli_.SetInputsAreProtoPathRelative(true);
249
250 temp_directory_ = TestTempDir() + "/proto2_cli_test_temp";
251
252 // If the temp directory already exists, it must be left over from a
253 // previous run. Delete it.
Feng Xiao6ef984a2014-11-10 17:34:54 -0800254 if (FileExists(temp_directory_)) {
temporal40ee5512008-07-10 02:12:20 +0000255 File::DeleteRecursively(temp_directory_, NULL, NULL);
256 }
257
258 // Create the temp directory.
jieluo@google.com4de8f552014-07-18 00:47:59 +0000259 GOOGLE_CHECK_OK(File::CreateDir(temp_directory_, 0777));
kenton@google.comfccb1462009-12-18 02:11:36 +0000260
261 // Register generators.
262 CodeGenerator* generator = new MockCodeGenerator("test_generator");
263 mock_generators_to_delete_.push_back(generator);
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000264 cli_.RegisterGenerator("--test_out", "--test_opt", generator, "Test output.");
kenton@google.comfccb1462009-12-18 02:11:36 +0000265 cli_.RegisterGenerator("-t", generator, "Test output.");
266
267 generator = new MockCodeGenerator("alt_generator");
268 mock_generators_to_delete_.push_back(generator);
269 cli_.RegisterGenerator("--alt_out", generator, "Alt output.");
270
kenton@google.com91218af2009-12-18 07:20:43 +0000271 generator = null_generator_ = new NullCodeGenerator();
kenton@google.comfccb1462009-12-18 02:11:36 +0000272 mock_generators_to_delete_.push_back(generator);
273 cli_.RegisterGenerator("--null_out", generator, "Null output.");
274
275 disallow_plugins_ = false;
temporal40ee5512008-07-10 02:12:20 +0000276}
277
278void CommandLineInterfaceTest::TearDown() {
279 // Delete the temp directory.
Feng Xiaof157a562014-11-14 11:50:31 -0800280 if (FileExists(temp_directory_)) {
Feng Xiaobaca1a82014-11-07 14:09:18 -0500281 File::DeleteRecursively(temp_directory_, NULL, NULL);
282 }
temporal40ee5512008-07-10 02:12:20 +0000283
284 // Delete all the MockCodeGenerators.
285 for (int i = 0; i < mock_generators_to_delete_.size(); i++) {
286 delete mock_generators_to_delete_[i];
287 }
288 mock_generators_to_delete_.clear();
289}
290
jieluo@google.com8d6f04a2014-08-06 20:49:30 +0000291void CommandLineInterfaceTest::Run(const string& command) {
jieluo@google.com4de8f552014-07-18 00:47:59 +0000292 vector<string> args = Split(command, " ", true);
temporal40ee5512008-07-10 02:12:20 +0000293
kenton@google.comfccb1462009-12-18 02:11:36 +0000294 if (!disallow_plugins_) {
295 cli_.AllowPlugins("prefix-");
Bo Yang46bd60b2015-04-27 17:44:45 -0700296#ifndef GOOGLE_THIRD_PARTY_PROTOBUF
Konstantin Podsvirove3019462015-09-17 12:08:47 +0300297 string plugin_path;
298#ifdef GOOGLE_PROTOBUF_TEST_PLUGIN_PATH
299 plugin_path = GOOGLE_PROTOBUF_TEST_PLUGIN_PATH;
300#else
kenton@google.comc0ee4d22009-12-22 02:05:33 +0000301 const char* possible_paths[] = {
302 // When building with shared libraries, libtool hides the real executable
303 // in .libs and puts a fake wrapper in the current directory.
304 // Unfortunately, due to an apparent bug on Cygwin/MinGW, if one program
305 // wrapped in this way (e.g. protobuf-tests.exe) tries to execute another
306 // program wrapped in this way (e.g. test_plugin.exe), the latter fails
307 // with error code 127 and no explanation message. Presumably the problem
308 // is that the wrapper for protobuf-tests.exe set some environment
309 // variables that confuse the wrapper for test_plugin.exe. Luckily, it
310 // turns out that if we simply invoke the wrapped test_plugin.exe
311 // directly, it works -- I guess the environment variables set by the
312 // protobuf-tests.exe wrapper happen to be correct for it too. So we do
313 // that.
314 ".libs/test_plugin.exe", // Win32 w/autotool (Cygwin / MinGW)
315 "test_plugin.exe", // Other Win32 (MSVC)
316 "test_plugin", // Unix
317 };
kenton@google.comc0ee4d22009-12-22 02:05:33 +0000318 for (int i = 0; i < GOOGLE_ARRAYSIZE(possible_paths); i++) {
319 if (access(possible_paths[i], F_OK) == 0) {
320 plugin_path = possible_paths[i];
321 break;
322 }
323 }
Konstantin Podsvirov2fa04392015-09-15 15:01:05 +0300324#endif
kenton@google.comc0ee4d22009-12-22 02:05:33 +0000325
326 if (plugin_path.empty()) {
Bo Yang46bd60b2015-04-27 17:44:45 -0700327#else
328 string plugin_path = "third_party/protobuf/test_plugin";
329
330 if (access(plugin_path.c_str(), F_OK) != 0) {
331#endif // GOOGLE_THIRD_PARTY_PROTOBUF
kenton@google.comc0ee4d22009-12-22 02:05:33 +0000332 GOOGLE_LOG(ERROR)
333 << "Plugin executable not found. Plugin tests are likely to fail.";
334 } else {
335 args.push_back("--plugin=prefix-gen-plug=" + plugin_path);
336 }
kenton@google.comfccb1462009-12-18 02:11:36 +0000337 }
338
Feng Xiaof157a562014-11-14 11:50:31 -0800339 google::protobuf::scoped_array<const char * > argv(new const char* [args.size()]);
temporal40ee5512008-07-10 02:12:20 +0000340
341 for (int i = 0; i < args.size(); i++) {
342 args[i] = StringReplace(args[i], "$tmpdir", temp_directory_, true);
343 argv[i] = args[i].c_str();
344 }
345
jieluo@google.com8d6f04a2014-08-06 20:49:30 +0000346 // TODO(jieluo): Cygwin doesn't work well if we try to capture stderr and
347 // stdout at the same time. Need to figure out why and add this capture back
348 // for Cygwin.
349#if !defined(__CYGWIN__)
350 CaptureTestStdout();
351#endif
temporal40ee5512008-07-10 02:12:20 +0000352 CaptureTestStderr();
353
354 return_code_ = cli_.Run(args.size(), argv.get());
355
356 error_text_ = GetCapturedTestStderr();
jieluo@google.com8d6f04a2014-08-06 20:49:30 +0000357#if !defined(__CYGWIN__)
358 captured_stdout_ = GetCapturedTestStdout();
359#endif
temporal40ee5512008-07-10 02:12:20 +0000360}
361
362// -------------------------------------------------------------------
363
temporal40ee5512008-07-10 02:12:20 +0000364void CommandLineInterfaceTest::CreateTempFile(
365 const string& name,
366 const string& contents) {
367 // Create parent directory, if necessary.
368 string::size_type slash_pos = name.find_last_of('/');
369 if (slash_pos != string::npos) {
370 string dir = name.substr(0, slash_pos);
Feng Xiao6ef984a2014-11-10 17:34:54 -0800371 if (!FileExists(temp_directory_ + "/" + dir)) {
jieluo@google.com4de8f552014-07-18 00:47:59 +0000372 GOOGLE_CHECK_OK(File::RecursivelyCreateDir(temp_directory_ + "/" + dir,
373 0777));
374 }
temporal40ee5512008-07-10 02:12:20 +0000375 }
376
377 // Write file.
378 string full_name = temp_directory_ + "/" + name;
jieluo@google.com4de8f552014-07-18 00:47:59 +0000379 GOOGLE_CHECK_OK(File::SetContents(full_name, contents, true));
temporal40ee5512008-07-10 02:12:20 +0000380}
381
kenton@google.comfccb1462009-12-18 02:11:36 +0000382void CommandLineInterfaceTest::CreateTempDir(const string& name) {
jieluo@google.com4de8f552014-07-18 00:47:59 +0000383 GOOGLE_CHECK_OK(File::RecursivelyCreateDir(temp_directory_ + "/" + name,
384 0777));
kenton@google.comfccb1462009-12-18 02:11:36 +0000385}
386
temporal40ee5512008-07-10 02:12:20 +0000387// -------------------------------------------------------------------
388
389void CommandLineInterfaceTest::ExpectNoErrors() {
390 EXPECT_EQ(0, return_code_);
391 EXPECT_EQ("", error_text_);
392}
393
394void CommandLineInterfaceTest::ExpectErrorText(const string& expected_text) {
395 EXPECT_NE(0, return_code_);
396 EXPECT_EQ(StringReplace(expected_text, "$tmpdir", temp_directory_, true),
397 error_text_);
398}
399
400void CommandLineInterfaceTest::ExpectErrorSubstring(
401 const string& expected_substring) {
402 EXPECT_NE(0, return_code_);
403 EXPECT_PRED_FORMAT2(testing::IsSubstring, expected_substring, error_text_);
404}
405
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000406void CommandLineInterfaceTest::ExpectErrorSubstringWithZeroReturnCode(
407 const string& expected_substring) {
408 EXPECT_EQ(0, return_code_);
409 EXPECT_PRED_FORMAT2(testing::IsSubstring, expected_substring, error_text_);
410}
411
temporal40ee5512008-07-10 02:12:20 +0000412bool CommandLineInterfaceTest::HasAlternateErrorSubstring(
413 const string& expected_substring) {
414 EXPECT_NE(0, return_code_);
415 return error_text_.find(expected_substring) != string::npos;
416}
417
418void CommandLineInterfaceTest::ExpectGenerated(
419 const string& generator_name,
420 const string& parameter,
421 const string& proto_name,
kenton@google.comfccb1462009-12-18 02:11:36 +0000422 const string& message_name) {
423 MockCodeGenerator::ExpectGenerated(
liujisi@google.com33165fe2010-11-02 13:14:58 +0000424 generator_name, parameter, "", proto_name, message_name, proto_name,
425 temp_directory_);
kenton@google.comfccb1462009-12-18 02:11:36 +0000426}
temporal40ee5512008-07-10 02:12:20 +0000427
kenton@google.comfccb1462009-12-18 02:11:36 +0000428void CommandLineInterfaceTest::ExpectGenerated(
429 const string& generator_name,
430 const string& parameter,
431 const string& proto_name,
432 const string& message_name,
433 const string& output_directory) {
434 MockCodeGenerator::ExpectGenerated(
liujisi@google.com33165fe2010-11-02 13:14:58 +0000435 generator_name, parameter, "", proto_name, message_name, proto_name,
kenton@google.comfccb1462009-12-18 02:11:36 +0000436 temp_directory_ + "/" + output_directory);
437}
438
liujisi@google.com33165fe2010-11-02 13:14:58 +0000439void CommandLineInterfaceTest::ExpectGeneratedWithMultipleInputs(
440 const string& generator_name,
441 const string& all_proto_names,
442 const string& proto_name,
443 const string& message_name) {
444 MockCodeGenerator::ExpectGenerated(
445 generator_name, "", "", proto_name, message_name,
446 all_proto_names,
447 temp_directory_);
448}
449
kenton@google.comfccb1462009-12-18 02:11:36 +0000450void CommandLineInterfaceTest::ExpectGeneratedWithInsertions(
451 const string& generator_name,
452 const string& parameter,
453 const string& insertions,
454 const string& proto_name,
455 const string& message_name) {
456 MockCodeGenerator::ExpectGenerated(
457 generator_name, parameter, insertions, proto_name, message_name,
liujisi@google.com33165fe2010-11-02 13:14:58 +0000458 proto_name, temp_directory_);
temporal40ee5512008-07-10 02:12:20 +0000459}
460
kenton@google.com91218af2009-12-18 07:20:43 +0000461void CommandLineInterfaceTest::ExpectNullCodeGeneratorCalled(
462 const string& parameter) {
463 EXPECT_TRUE(null_generator_->called_);
464 EXPECT_EQ(parameter, null_generator_->parameter_);
465}
466
temporal779f61c2008-08-13 03:15:00 +0000467void CommandLineInterfaceTest::ReadDescriptorSet(
468 const string& filename, FileDescriptorSet* descriptor_set) {
469 string path = temp_directory_ + "/" + filename;
470 string file_contents;
jieluo@google.com4de8f552014-07-18 00:47:59 +0000471 GOOGLE_CHECK_OK(File::GetContents(path, &file_contents, true));
472
temporal779f61c2008-08-13 03:15:00 +0000473 if (!descriptor_set->ParseFromString(file_contents)) {
474 FAIL() << "Could not parse file contents: " << path;
475 }
476}
477
jieluo@google.com4de8f552014-07-18 00:47:59 +0000478void CommandLineInterfaceTest::ExpectCapturedStdout(
479 const string& expected_text) {
480 EXPECT_EQ(expected_text, captured_stdout_);
481}
482
Bo Yang5914ce72015-02-03 21:35:50 -0800483
484void CommandLineInterfaceTest::ExpectFileContent(
485 const string& filename, const string& content) {
486 string path = temp_directory_ + "/" + filename;
487 string file_contents;
488 GOOGLE_CHECK_OK(File::GetContents(path, &file_contents, true));
489
490 EXPECT_EQ(StringReplace(content, "$tmpdir", temp_directory_, true),
491 file_contents);
492}
493
temporal40ee5512008-07-10 02:12:20 +0000494// ===================================================================
495
temporal40ee5512008-07-10 02:12:20 +0000496TEST_F(CommandLineInterfaceTest, BasicOutput) {
497 // Test that the common case works.
498
temporal40ee5512008-07-10 02:12:20 +0000499 CreateTempFile("foo.proto",
500 "syntax = \"proto2\";\n"
501 "message Foo {}\n");
502
503 Run("protocol_compiler --test_out=$tmpdir "
504 "--proto_path=$tmpdir foo.proto");
505
506 ExpectNoErrors();
kenton@google.comfccb1462009-12-18 02:11:36 +0000507 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
508}
509
510TEST_F(CommandLineInterfaceTest, BasicPlugin) {
511 // Test that basic plugins work.
512
513 CreateTempFile("foo.proto",
514 "syntax = \"proto2\";\n"
515 "message Foo {}\n");
516
517 Run("protocol_compiler --plug_out=$tmpdir "
518 "--proto_path=$tmpdir foo.proto");
519
520 ExpectNoErrors();
521 ExpectGenerated("test_plugin", "", "foo.proto", "Foo");
522}
523
524TEST_F(CommandLineInterfaceTest, GeneratorAndPlugin) {
525 // Invoke a generator and a plugin at the same time.
526
527 CreateTempFile("foo.proto",
528 "syntax = \"proto2\";\n"
529 "message Foo {}\n");
530
531 Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
532 "--proto_path=$tmpdir foo.proto");
533
534 ExpectNoErrors();
535 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
536 ExpectGenerated("test_plugin", "", "foo.proto", "Foo");
temporal40ee5512008-07-10 02:12:20 +0000537}
538
539TEST_F(CommandLineInterfaceTest, MultipleInputs) {
540 // Test parsing multiple input files.
541
temporal40ee5512008-07-10 02:12:20 +0000542 CreateTempFile("foo.proto",
543 "syntax = \"proto2\";\n"
544 "message Foo {}\n");
545 CreateTempFile("bar.proto",
546 "syntax = \"proto2\";\n"
547 "message Bar {}\n");
548
kenton@google.comfccb1462009-12-18 02:11:36 +0000549 Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
temporal40ee5512008-07-10 02:12:20 +0000550 "--proto_path=$tmpdir foo.proto bar.proto");
551
552 ExpectNoErrors();
liujisi@google.com33165fe2010-11-02 13:14:58 +0000553 ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
554 "foo.proto", "Foo");
555 ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
556 "bar.proto", "Bar");
557 ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
558 "foo.proto", "Foo");
559 ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
560 "bar.proto", "Bar");
561}
562
563TEST_F(CommandLineInterfaceTest, MultipleInputsWithImport) {
564 // Test parsing multiple input files with an import of a separate file.
565
566 CreateTempFile("foo.proto",
567 "syntax = \"proto2\";\n"
568 "message Foo {}\n");
569 CreateTempFile("bar.proto",
570 "syntax = \"proto2\";\n"
571 "import \"baz.proto\";\n"
572 "message Bar {\n"
573 " optional Baz a = 1;\n"
574 "}\n");
575 CreateTempFile("baz.proto",
576 "syntax = \"proto2\";\n"
577 "message Baz {}\n");
578
579 Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
580 "--proto_path=$tmpdir foo.proto bar.proto");
581
582 ExpectNoErrors();
583 ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
584 "foo.proto", "Foo");
585 ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
586 "bar.proto", "Bar");
587 ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
588 "foo.proto", "Foo");
589 ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
590 "bar.proto", "Bar");
temporal40ee5512008-07-10 02:12:20 +0000591}
592
593TEST_F(CommandLineInterfaceTest, CreateDirectory) {
594 // Test that when we output to a sub-directory, it is created.
595
kenton@google.comfccb1462009-12-18 02:11:36 +0000596 CreateTempFile("bar/baz/foo.proto",
temporal40ee5512008-07-10 02:12:20 +0000597 "syntax = \"proto2\";\n"
598 "message Foo {}\n");
kenton@google.comfccb1462009-12-18 02:11:36 +0000599 CreateTempDir("out");
600 CreateTempDir("plugout");
temporal40ee5512008-07-10 02:12:20 +0000601
kenton@google.comfccb1462009-12-18 02:11:36 +0000602 Run("protocol_compiler --test_out=$tmpdir/out --plug_out=$tmpdir/plugout "
603 "--proto_path=$tmpdir bar/baz/foo.proto");
temporal40ee5512008-07-10 02:12:20 +0000604
605 ExpectNoErrors();
kenton@google.comfccb1462009-12-18 02:11:36 +0000606 ExpectGenerated("test_generator", "", "bar/baz/foo.proto", "Foo", "out");
607 ExpectGenerated("test_plugin", "", "bar/baz/foo.proto", "Foo", "plugout");
temporal40ee5512008-07-10 02:12:20 +0000608}
609
610TEST_F(CommandLineInterfaceTest, GeneratorParameters) {
611 // Test that generator parameters are correctly parsed from the command line.
612
temporal40ee5512008-07-10 02:12:20 +0000613 CreateTempFile("foo.proto",
614 "syntax = \"proto2\";\n"
615 "message Foo {}\n");
616
617 Run("protocol_compiler --test_out=TestParameter:$tmpdir "
kenton@google.comfccb1462009-12-18 02:11:36 +0000618 "--plug_out=TestPluginParameter:$tmpdir "
temporal40ee5512008-07-10 02:12:20 +0000619 "--proto_path=$tmpdir foo.proto");
620
621 ExpectNoErrors();
kenton@google.comfccb1462009-12-18 02:11:36 +0000622 ExpectGenerated("test_generator", "TestParameter", "foo.proto", "Foo");
623 ExpectGenerated("test_plugin", "TestPluginParameter", "foo.proto", "Foo");
624}
625
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000626TEST_F(CommandLineInterfaceTest, ExtraGeneratorParameters) {
627 // Test that generator parameters specified with the option flag are
628 // correctly passed to the code generator.
629
630 CreateTempFile("foo.proto",
631 "syntax = \"proto2\";\n"
632 "message Foo {}\n");
633 // Create the "a" and "b" sub-directories.
634 CreateTempDir("a");
635 CreateTempDir("b");
636
637 Run("protocol_compiler "
638 "--test_opt=foo1 "
639 "--test_out=bar:$tmpdir/a "
640 "--test_opt=foo2 "
641 "--test_out=baz:$tmpdir/b "
642 "--test_opt=foo3 "
643 "--proto_path=$tmpdir foo.proto");
644
645 ExpectNoErrors();
646 ExpectGenerated(
647 "test_generator", "bar,foo1,foo2,foo3", "foo.proto", "Foo", "a");
648 ExpectGenerated(
649 "test_generator", "baz,foo1,foo2,foo3", "foo.proto", "Foo", "b");
650}
651
kenton@google.comfccb1462009-12-18 02:11:36 +0000652TEST_F(CommandLineInterfaceTest, Insert) {
653 // Test running a generator that inserts code into another's output.
654
655 CreateTempFile("foo.proto",
656 "syntax = \"proto2\";\n"
657 "message Foo {}\n");
658
659 Run("protocol_compiler "
660 "--test_out=TestParameter:$tmpdir "
661 "--plug_out=TestPluginParameter:$tmpdir "
662 "--test_out=insert=test_generator,test_plugin:$tmpdir "
663 "--plug_out=insert=test_generator,test_plugin:$tmpdir "
664 "--proto_path=$tmpdir foo.proto");
665
666 ExpectNoErrors();
667 ExpectGeneratedWithInsertions(
668 "test_generator", "TestParameter", "test_generator,test_plugin",
669 "foo.proto", "Foo");
670 ExpectGeneratedWithInsertions(
671 "test_plugin", "TestPluginParameter", "test_generator,test_plugin",
672 "foo.proto", "Foo");
temporal40ee5512008-07-10 02:12:20 +0000673}
674
liujisi@google.com9b7f6c52010-12-08 03:45:27 +0000675#if defined(_WIN32)
temporalcc930432008-07-21 20:28:30 +0000676
677TEST_F(CommandLineInterfaceTest, WindowsOutputPath) {
678 // Test that the output path can be a Windows-style path.
679
temporalcc930432008-07-21 20:28:30 +0000680 CreateTempFile("foo.proto",
681 "syntax = \"proto2\";\n");
682
kenton@google.comfccb1462009-12-18 02:11:36 +0000683 Run("protocol_compiler --null_out=C:\\ "
temporalcc930432008-07-21 20:28:30 +0000684 "--proto_path=$tmpdir foo.proto");
685
686 ExpectNoErrors();
kenton@google.com91218af2009-12-18 07:20:43 +0000687 ExpectNullCodeGeneratorCalled("");
temporalcc930432008-07-21 20:28:30 +0000688}
689
690TEST_F(CommandLineInterfaceTest, WindowsOutputPathAndParameter) {
691 // Test that we can have a windows-style output path and a parameter.
692
temporalcc930432008-07-21 20:28:30 +0000693 CreateTempFile("foo.proto",
694 "syntax = \"proto2\";\n");
695
kenton@google.comfccb1462009-12-18 02:11:36 +0000696 Run("protocol_compiler --null_out=bar:C:\\ "
temporalcc930432008-07-21 20:28:30 +0000697 "--proto_path=$tmpdir foo.proto");
698
699 ExpectNoErrors();
kenton@google.com91218af2009-12-18 07:20:43 +0000700 ExpectNullCodeGeneratorCalled("bar");
temporalcc930432008-07-21 20:28:30 +0000701}
702
kenton@google.comef3730c2009-04-28 00:49:36 +0000703TEST_F(CommandLineInterfaceTest, TrailingBackslash) {
704 // Test that the directories can end in backslashes. Some users claim this
705 // doesn't work on their system.
706
kenton@google.comef3730c2009-04-28 00:49:36 +0000707 CreateTempFile("foo.proto",
708 "syntax = \"proto2\";\n"
709 "message Foo {}\n");
710
711 Run("protocol_compiler --test_out=$tmpdir\\ "
712 "--proto_path=$tmpdir\\ foo.proto");
713
714 ExpectNoErrors();
kenton@google.comfccb1462009-12-18 02:11:36 +0000715 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
kenton@google.comef3730c2009-04-28 00:49:36 +0000716}
717
temporalcc930432008-07-21 20:28:30 +0000718#endif // defined(_WIN32) || defined(__CYGWIN__)
719
temporal40ee5512008-07-10 02:12:20 +0000720TEST_F(CommandLineInterfaceTest, PathLookup) {
721 // Test that specifying multiple directories in the proto search path works.
722
temporal40ee5512008-07-10 02:12:20 +0000723 CreateTempFile("b/bar.proto",
724 "syntax = \"proto2\";\n"
725 "message Bar {}\n");
726 CreateTempFile("a/foo.proto",
727 "syntax = \"proto2\";\n"
728 "import \"bar.proto\";\n"
729 "message Foo {\n"
730 " optional Bar a = 1;\n"
731 "}\n");
732 CreateTempFile("b/foo.proto", "this should not be parsed\n");
733
734 Run("protocol_compiler --test_out=$tmpdir "
735 "--proto_path=$tmpdir/a --proto_path=$tmpdir/b foo.proto");
736
737 ExpectNoErrors();
kenton@google.comfccb1462009-12-18 02:11:36 +0000738 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
temporal40ee5512008-07-10 02:12:20 +0000739}
740
741TEST_F(CommandLineInterfaceTest, ColonDelimitedPath) {
742 // Same as PathLookup, but we provide the proto_path in a single flag.
743
temporal40ee5512008-07-10 02:12:20 +0000744 CreateTempFile("b/bar.proto",
745 "syntax = \"proto2\";\n"
746 "message Bar {}\n");
747 CreateTempFile("a/foo.proto",
748 "syntax = \"proto2\";\n"
749 "import \"bar.proto\";\n"
750 "message Foo {\n"
751 " optional Bar a = 1;\n"
752 "}\n");
753 CreateTempFile("b/foo.proto", "this should not be parsed\n");
754
755#undef PATH_SEPARATOR
756#if defined(_WIN32)
757#define PATH_SEPARATOR ";"
758#else
759#define PATH_SEPARATOR ":"
760#endif
761
762 Run("protocol_compiler --test_out=$tmpdir "
Nobuaki Sukegawa8ba0e352014-11-30 19:42:51 +0900763 "--proto_path=$tmpdir/a" PATH_SEPARATOR "$tmpdir/b foo.proto");
temporal40ee5512008-07-10 02:12:20 +0000764
765#undef PATH_SEPARATOR
766
767 ExpectNoErrors();
kenton@google.comfccb1462009-12-18 02:11:36 +0000768 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
temporal40ee5512008-07-10 02:12:20 +0000769}
770
771TEST_F(CommandLineInterfaceTest, NonRootMapping) {
772 // Test setting up a search path mapping a directory to a non-root location.
773
temporal40ee5512008-07-10 02:12:20 +0000774 CreateTempFile("foo.proto",
775 "syntax = \"proto2\";\n"
776 "message Foo {}\n");
777
778 Run("protocol_compiler --test_out=$tmpdir "
779 "--proto_path=bar=$tmpdir bar/foo.proto");
780
781 ExpectNoErrors();
kenton@google.comfccb1462009-12-18 02:11:36 +0000782 ExpectGenerated("test_generator", "", "bar/foo.proto", "Foo");
temporal40ee5512008-07-10 02:12:20 +0000783}
784
785TEST_F(CommandLineInterfaceTest, MultipleGenerators) {
786 // Test that we can have multiple generators and use both in one invocation,
787 // each with a different output directory.
788
temporal40ee5512008-07-10 02:12:20 +0000789 CreateTempFile("foo.proto",
790 "syntax = \"proto2\";\n"
791 "message Foo {}\n");
792 // Create the "a" and "b" sub-directories.
kenton@google.comfccb1462009-12-18 02:11:36 +0000793 CreateTempDir("a");
794 CreateTempDir("b");
temporal40ee5512008-07-10 02:12:20 +0000795
796 Run("protocol_compiler "
kenton@google.comfccb1462009-12-18 02:11:36 +0000797 "--test_out=$tmpdir/a "
798 "--alt_out=$tmpdir/b "
temporal40ee5512008-07-10 02:12:20 +0000799 "--proto_path=$tmpdir foo.proto");
800
801 ExpectNoErrors();
kenton@google.comfccb1462009-12-18 02:11:36 +0000802 ExpectGenerated("test_generator", "", "foo.proto", "Foo", "a");
803 ExpectGenerated("alt_generator", "", "foo.proto", "Foo", "b");
temporal40ee5512008-07-10 02:12:20 +0000804}
805
806TEST_F(CommandLineInterfaceTest, DisallowServicesNoServices) {
807 // Test that --disallow_services doesn't cause a problem when there are no
808 // services.
809
temporal40ee5512008-07-10 02:12:20 +0000810 CreateTempFile("foo.proto",
811 "syntax = \"proto2\";\n"
812 "message Foo {}\n");
813
814 Run("protocol_compiler --disallow_services --test_out=$tmpdir "
815 "--proto_path=$tmpdir foo.proto");
816
817 ExpectNoErrors();
kenton@google.comfccb1462009-12-18 02:11:36 +0000818 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
temporal40ee5512008-07-10 02:12:20 +0000819}
820
821TEST_F(CommandLineInterfaceTest, DisallowServicesHasService) {
822 // Test that --disallow_services produces an error when there are services.
823
temporal40ee5512008-07-10 02:12:20 +0000824 CreateTempFile("foo.proto",
825 "syntax = \"proto2\";\n"
826 "message Foo {}\n"
827 "service Bar {}\n");
828
829 Run("protocol_compiler --disallow_services --test_out=$tmpdir "
830 "--proto_path=$tmpdir foo.proto");
831
832 ExpectErrorSubstring("foo.proto: This file contains services");
833}
834
835TEST_F(CommandLineInterfaceTest, AllowServicesHasService) {
836 // Test that services work fine as long as --disallow_services is not used.
837
temporal40ee5512008-07-10 02:12:20 +0000838 CreateTempFile("foo.proto",
839 "syntax = \"proto2\";\n"
840 "message Foo {}\n"
841 "service Bar {}\n");
842
843 Run("protocol_compiler --test_out=$tmpdir "
844 "--proto_path=$tmpdir foo.proto");
845
846 ExpectNoErrors();
kenton@google.comfccb1462009-12-18 02:11:36 +0000847 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
temporal40ee5512008-07-10 02:12:20 +0000848}
849
850TEST_F(CommandLineInterfaceTest, CwdRelativeInputs) {
851 // Test that we can accept working-directory-relative input files.
852
853 SetInputsAreProtoPathRelative(false);
854
temporal40ee5512008-07-10 02:12:20 +0000855 CreateTempFile("foo.proto",
856 "syntax = \"proto2\";\n"
857 "message Foo {}\n");
858
859 Run("protocol_compiler --test_out=$tmpdir "
860 "--proto_path=$tmpdir $tmpdir/foo.proto");
861
862 ExpectNoErrors();
kenton@google.comfccb1462009-12-18 02:11:36 +0000863 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
temporal40ee5512008-07-10 02:12:20 +0000864}
865
temporal779f61c2008-08-13 03:15:00 +0000866TEST_F(CommandLineInterfaceTest, WriteDescriptorSet) {
867 CreateTempFile("foo.proto",
868 "syntax = \"proto2\";\n"
869 "message Foo {}\n");
870 CreateTempFile("bar.proto",
871 "syntax = \"proto2\";\n"
872 "import \"foo.proto\";\n"
873 "message Bar {\n"
874 " optional Foo foo = 1;\n"
875 "}\n");
876
877 Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
878 "--proto_path=$tmpdir bar.proto");
879
880 ExpectNoErrors();
881
882 FileDescriptorSet descriptor_set;
883 ReadDescriptorSet("descriptor_set", &descriptor_set);
884 if (HasFatalFailure()) return;
jieluo@google.com4de8f552014-07-18 00:47:59 +0000885 EXPECT_EQ(1, descriptor_set.file_size());
temporal779f61c2008-08-13 03:15:00 +0000886 EXPECT_EQ("bar.proto", descriptor_set.file(0).name());
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000887 // Descriptor set should not have source code info.
888 EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
Feng Xiaoe841bac2015-12-11 17:09:20 -0800889 // Descriptor set should have json_name.
890 EXPECT_EQ("Bar", descriptor_set.file(0).message_type(0).name());
891 EXPECT_EQ("foo", descriptor_set.file(0).message_type(0).field(0).name());
892 EXPECT_TRUE(descriptor_set.file(0).message_type(0).field(0).has_json_name());
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000893}
894
Feng Xiaoeee38b02015-08-22 18:25:48 -0700895TEST_F(CommandLineInterfaceTest, WriteDescriptorSetWithDuplicates) {
896 CreateTempFile("foo.proto",
897 "syntax = \"proto2\";\n"
898 "message Foo {}\n");
899 CreateTempFile("bar.proto",
900 "syntax = \"proto2\";\n"
901 "import \"foo.proto\";\n"
902 "message Bar {\n"
903 " optional Foo foo = 1;\n"
904 "}\n");
905 CreateTempFile("baz.proto",
906 "syntax = \"proto2\";\n"
907 "import \"foo.proto\";\n"
908 "message Baz {\n"
909 " optional Foo foo = 1;\n"
910 "}\n");
911
912 Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
913 "--proto_path=$tmpdir bar.proto foo.proto bar.proto baz.proto");
914
915 ExpectNoErrors();
916
917 FileDescriptorSet descriptor_set;
918 ReadDescriptorSet("descriptor_set", &descriptor_set);
919 if (HasFatalFailure()) return;
920 EXPECT_EQ(3, descriptor_set.file_size());
921 EXPECT_EQ("bar.proto", descriptor_set.file(0).name());
922 EXPECT_EQ("foo.proto", descriptor_set.file(1).name());
923 EXPECT_EQ("baz.proto", descriptor_set.file(2).name());
924 // Descriptor set should not have source code info.
925 EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
Feng Xiaoe841bac2015-12-11 17:09:20 -0800926 // Descriptor set should have json_name.
927 EXPECT_EQ("Bar", descriptor_set.file(0).message_type(0).name());
928 EXPECT_EQ("foo", descriptor_set.file(0).message_type(0).field(0).name());
929 EXPECT_TRUE(descriptor_set.file(0).message_type(0).field(0).has_json_name());
Feng Xiaoeee38b02015-08-22 18:25:48 -0700930}
931
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000932TEST_F(CommandLineInterfaceTest, WriteDescriptorSetWithSourceInfo) {
933 CreateTempFile("foo.proto",
934 "syntax = \"proto2\";\n"
935 "message Foo {}\n");
936 CreateTempFile("bar.proto",
937 "syntax = \"proto2\";\n"
938 "import \"foo.proto\";\n"
939 "message Bar {\n"
940 " optional Foo foo = 1;\n"
941 "}\n");
942
943 Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
944 "--include_source_info --proto_path=$tmpdir bar.proto");
945
946 ExpectNoErrors();
947
948 FileDescriptorSet descriptor_set;
949 ReadDescriptorSet("descriptor_set", &descriptor_set);
950 if (HasFatalFailure()) return;
jieluo@google.com4de8f552014-07-18 00:47:59 +0000951 EXPECT_EQ(1, descriptor_set.file_size());
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000952 EXPECT_EQ("bar.proto", descriptor_set.file(0).name());
953 // Source code info included.
954 EXPECT_TRUE(descriptor_set.file(0).has_source_code_info());
temporal779f61c2008-08-13 03:15:00 +0000955}
956
957TEST_F(CommandLineInterfaceTest, WriteTransitiveDescriptorSet) {
958 CreateTempFile("foo.proto",
959 "syntax = \"proto2\";\n"
960 "message Foo {}\n");
961 CreateTempFile("bar.proto",
962 "syntax = \"proto2\";\n"
963 "import \"foo.proto\";\n"
964 "message Bar {\n"
965 " optional Foo foo = 1;\n"
966 "}\n");
967
968 Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
969 "--include_imports --proto_path=$tmpdir bar.proto");
970
971 ExpectNoErrors();
972
973 FileDescriptorSet descriptor_set;
974 ReadDescriptorSet("descriptor_set", &descriptor_set);
975 if (HasFatalFailure()) return;
jieluo@google.com4de8f552014-07-18 00:47:59 +0000976 EXPECT_EQ(2, descriptor_set.file_size());
temporal779f61c2008-08-13 03:15:00 +0000977 if (descriptor_set.file(0).name() == "bar.proto") {
kenton@google.com7fb9ae92009-09-02 02:42:56 +0000978 std::swap(descriptor_set.mutable_file()->mutable_data()[0],
979 descriptor_set.mutable_file()->mutable_data()[1]);
temporal779f61c2008-08-13 03:15:00 +0000980 }
981 EXPECT_EQ("foo.proto", descriptor_set.file(0).name());
982 EXPECT_EQ("bar.proto", descriptor_set.file(1).name());
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000983 // Descriptor set should not have source code info.
984 EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
985 EXPECT_FALSE(descriptor_set.file(1).has_source_code_info());
986}
987
988TEST_F(CommandLineInterfaceTest, WriteTransitiveDescriptorSetWithSourceInfo) {
989 CreateTempFile("foo.proto",
990 "syntax = \"proto2\";\n"
991 "message Foo {}\n");
992 CreateTempFile("bar.proto",
993 "syntax = \"proto2\";\n"
994 "import \"foo.proto\";\n"
995 "message Bar {\n"
996 " optional Foo foo = 1;\n"
997 "}\n");
998
999 Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
1000 "--include_imports --include_source_info --proto_path=$tmpdir bar.proto");
1001
1002 ExpectNoErrors();
1003
1004 FileDescriptorSet descriptor_set;
1005 ReadDescriptorSet("descriptor_set", &descriptor_set);
1006 if (HasFatalFailure()) return;
jieluo@google.com4de8f552014-07-18 00:47:59 +00001007 EXPECT_EQ(2, descriptor_set.file_size());
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +00001008 if (descriptor_set.file(0).name() == "bar.proto") {
1009 std::swap(descriptor_set.mutable_file()->mutable_data()[0],
1010 descriptor_set.mutable_file()->mutable_data()[1]);
1011 }
1012 EXPECT_EQ("foo.proto", descriptor_set.file(0).name());
1013 EXPECT_EQ("bar.proto", descriptor_set.file(1).name());
1014 // Source code info included.
1015 EXPECT_TRUE(descriptor_set.file(0).has_source_code_info());
1016 EXPECT_TRUE(descriptor_set.file(1).has_source_code_info());
temporal779f61c2008-08-13 03:15:00 +00001017}
1018
Bo Yangcf603a92015-05-24 22:28:04 -07001019#ifdef _WIN32
1020// TODO(teboring): Figure out how to write test on windows.
1021#else
Bo Yange2555e22015-02-07 15:28:54 -08001022TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFileGivenTwoInputs) {
1023 CreateTempFile("foo.proto",
1024 "syntax = \"proto2\";\n"
1025 "message Foo {}\n");
1026 CreateTempFile("bar.proto",
1027 "syntax = \"proto2\";\n"
1028 "import \"foo.proto\";\n"
1029 "message Bar {\n"
1030 " optional Foo foo = 1;\n"
1031 "}\n");
1032
1033 Run("protocol_compiler --dependency_out=$tmpdir/manifest "
1034 "--test_out=$tmpdir --proto_path=$tmpdir bar.proto foo.proto");
1035
1036 ExpectErrorText(
1037 "Can only process one input file when using --dependency_out=FILE.\n");
1038}
1039
Bo Yang5db21732015-05-21 14:28:59 -07001040#ifdef PROTOBUF_OPENSOURCE
Bo Yang5914ce72015-02-03 21:35:50 -08001041TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFile) {
1042 CreateTempFile("foo.proto",
1043 "syntax = \"proto2\";\n"
1044 "message Foo {}\n");
1045 CreateTempFile("bar.proto",
1046 "syntax = \"proto2\";\n"
1047 "import \"foo.proto\";\n"
1048 "message Bar {\n"
1049 " optional Foo foo = 1;\n"
1050 "}\n");
1051
Tamir Dubersteined715042015-03-18 00:43:34 -07001052 string current_working_directory = getcwd(NULL, 0);
Bo Yangeb2ce022015-02-09 11:57:41 -08001053 SwitchToTempDirectory();
1054
1055 Run("protocol_compiler --dependency_out=manifest --test_out=. "
1056 "bar.proto");
Bo Yang5914ce72015-02-03 21:35:50 -08001057
1058 ExpectNoErrors();
1059
1060 ExpectFileContent("manifest",
Bo Yangeb2ce022015-02-09 11:57:41 -08001061 "bar.proto.MockCodeGenerator.test_generator: "
1062 "foo.proto\\\n bar.proto");
1063
1064 File::ChangeWorkingDirectory(current_working_directory);
Bo Yang5914ce72015-02-03 21:35:50 -08001065}
Bo Yang5db21732015-05-21 14:28:59 -07001066#else // !PROTOBUF_OPENSOURCE
1067// TODO(teboring): Figure out how to change and get working directory in
1068// google3.
1069#endif // !PROTOBUF_OPENSOURCE
Bo Yang5914ce72015-02-03 21:35:50 -08001070
Bo Yangeb2ce022015-02-09 11:57:41 -08001071TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFileForAbsolutePath) {
Bo Yang5914ce72015-02-03 21:35:50 -08001072 CreateTempFile("foo.proto",
1073 "syntax = \"proto2\";\n"
1074 "message Foo {}\n");
1075 CreateTempFile("bar.proto",
1076 "syntax = \"proto2\";\n"
1077 "import \"foo.proto\";\n"
1078 "message Bar {\n"
1079 " optional Foo foo = 1;\n"
1080 "}\n");
1081
Bo Yangeb2ce022015-02-09 11:57:41 -08001082 Run("protocol_compiler --dependency_out=$tmpdir/manifest "
Bo Yang5914ce72015-02-03 21:35:50 -08001083 "--test_out=$tmpdir --proto_path=$tmpdir bar.proto");
1084
1085 ExpectNoErrors();
1086
1087 ExpectFileContent("manifest",
Bo Yange2555e22015-02-07 15:28:54 -08001088 "$tmpdir/bar.proto.MockCodeGenerator.test_generator: "
1089 "$tmpdir/foo.proto\\\n $tmpdir/bar.proto");
Bo Yang5914ce72015-02-03 21:35:50 -08001090}
Bo Yangcf603a92015-05-24 22:28:04 -07001091#endif // !_WIN32
Bo Yang5914ce72015-02-03 21:35:50 -08001092
temporal40ee5512008-07-10 02:12:20 +00001093// -------------------------------------------------------------------
1094
1095TEST_F(CommandLineInterfaceTest, ParseErrors) {
1096 // Test that parse errors are reported.
1097
temporal40ee5512008-07-10 02:12:20 +00001098 CreateTempFile("foo.proto",
1099 "syntax = \"proto2\";\n"
1100 "badsyntax\n");
1101
1102 Run("protocol_compiler --test_out=$tmpdir "
1103 "--proto_path=$tmpdir foo.proto");
1104
1105 ExpectErrorText(
1106 "foo.proto:2:1: Expected top-level statement (e.g. \"message\").\n");
1107}
1108
1109TEST_F(CommandLineInterfaceTest, ParseErrorsMultipleFiles) {
1110 // Test that parse errors are reported from multiple files.
1111
temporal40ee5512008-07-10 02:12:20 +00001112 // We set up files such that foo.proto actually depends on bar.proto in
1113 // two ways: Directly and through baz.proto. bar.proto's errors should
1114 // only be reported once.
1115 CreateTempFile("bar.proto",
1116 "syntax = \"proto2\";\n"
1117 "badsyntax\n");
1118 CreateTempFile("baz.proto",
1119 "syntax = \"proto2\";\n"
1120 "import \"bar.proto\";\n");
1121 CreateTempFile("foo.proto",
1122 "syntax = \"proto2\";\n"
1123 "import \"bar.proto\";\n"
1124 "import \"baz.proto\";\n");
1125
1126 Run("protocol_compiler --test_out=$tmpdir "
1127 "--proto_path=$tmpdir foo.proto");
1128
1129 ExpectErrorText(
1130 "bar.proto:2:1: Expected top-level statement (e.g. \"message\").\n"
1131 "baz.proto: Import \"bar.proto\" was not found or had errors.\n"
1132 "foo.proto: Import \"bar.proto\" was not found or had errors.\n"
1133 "foo.proto: Import \"baz.proto\" was not found or had errors.\n");
1134}
1135
1136TEST_F(CommandLineInterfaceTest, InputNotFoundError) {
1137 // Test what happens if the input file is not found.
1138
temporal40ee5512008-07-10 02:12:20 +00001139 Run("protocol_compiler --test_out=$tmpdir "
1140 "--proto_path=$tmpdir foo.proto");
1141
1142 ExpectErrorText(
1143 "foo.proto: File not found.\n");
1144}
1145
1146TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotFoundError) {
1147 // Test what happens when a working-directory-relative input file is not
1148 // found.
1149
1150 SetInputsAreProtoPathRelative(false);
1151
temporal40ee5512008-07-10 02:12:20 +00001152 Run("protocol_compiler --test_out=$tmpdir "
1153 "--proto_path=$tmpdir $tmpdir/foo.proto");
1154
1155 ExpectErrorText(
1156 "$tmpdir/foo.proto: No such file or directory\n");
1157}
1158
1159TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotMappedError) {
1160 // Test what happens when a working-directory-relative input file is not
1161 // mapped to a virtual path.
1162
1163 SetInputsAreProtoPathRelative(false);
1164
temporal40ee5512008-07-10 02:12:20 +00001165 CreateTempFile("foo.proto",
1166 "syntax = \"proto2\";\n"
1167 "message Foo {}\n");
1168
1169 // Create a directory called "bar" so that we can point --proto_path at it.
1170 CreateTempFile("bar/dummy", "");
1171
1172 Run("protocol_compiler --test_out=$tmpdir "
1173 "--proto_path=$tmpdir/bar $tmpdir/foo.proto");
1174
1175 ExpectErrorText(
1176 "$tmpdir/foo.proto: File does not reside within any path "
1177 "specified using --proto_path (or -I). You must specify a "
kenton@google.com477f7992009-10-07 21:38:11 +00001178 "--proto_path which encompasses this file. Note that the "
1179 "proto_path must be an exact prefix of the .proto file "
1180 "names -- protoc is too dumb to figure out when two paths "
1181 "(e.g. absolute and relative) are equivalent (it's harder "
1182 "than you think).\n");
temporal40ee5512008-07-10 02:12:20 +00001183}
1184
1185TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotFoundAndNotMappedError) {
1186 // Check what happens if the input file is not found *and* is not mapped
1187 // in the proto_path.
1188
1189 SetInputsAreProtoPathRelative(false);
1190
temporal40ee5512008-07-10 02:12:20 +00001191 // Create a directory called "bar" so that we can point --proto_path at it.
1192 CreateTempFile("bar/dummy", "");
1193
1194 Run("protocol_compiler --test_out=$tmpdir "
1195 "--proto_path=$tmpdir/bar $tmpdir/foo.proto");
1196
1197 ExpectErrorText(
1198 "$tmpdir/foo.proto: No such file or directory\n");
1199}
1200
1201TEST_F(CommandLineInterfaceTest, CwdRelativeInputShadowedError) {
1202 // Test what happens when a working-directory-relative input file is shadowed
1203 // by another file in the virtual path.
1204
1205 SetInputsAreProtoPathRelative(false);
1206
temporal40ee5512008-07-10 02:12:20 +00001207 CreateTempFile("foo/foo.proto",
1208 "syntax = \"proto2\";\n"
1209 "message Foo {}\n");
1210 CreateTempFile("bar/foo.proto",
1211 "syntax = \"proto2\";\n"
1212 "message Bar {}\n");
1213
1214 Run("protocol_compiler --test_out=$tmpdir "
1215 "--proto_path=$tmpdir/foo --proto_path=$tmpdir/bar "
1216 "$tmpdir/bar/foo.proto");
1217
1218 ExpectErrorText(
1219 "$tmpdir/bar/foo.proto: Input is shadowed in the --proto_path "
1220 "by \"$tmpdir/foo/foo.proto\". Either use the latter "
1221 "file as your input or reorder the --proto_path so that the "
1222 "former file's location comes first.\n");
1223}
1224
1225TEST_F(CommandLineInterfaceTest, ProtoPathNotFoundError) {
1226 // Test what happens if the input file is not found.
1227
temporal40ee5512008-07-10 02:12:20 +00001228 Run("protocol_compiler --test_out=$tmpdir "
1229 "--proto_path=$tmpdir/foo foo.proto");
1230
1231 ExpectErrorText(
1232 "$tmpdir/foo: warning: directory does not exist.\n"
1233 "foo.proto: File not found.\n");
1234}
1235
1236TEST_F(CommandLineInterfaceTest, MissingInputError) {
1237 // Test that we get an error if no inputs are given.
1238
temporal40ee5512008-07-10 02:12:20 +00001239 Run("protocol_compiler --test_out=$tmpdir "
1240 "--proto_path=$tmpdir");
1241
1242 ExpectErrorText("Missing input file.\n");
1243}
1244
1245TEST_F(CommandLineInterfaceTest, MissingOutputError) {
temporal40ee5512008-07-10 02:12:20 +00001246 CreateTempFile("foo.proto",
1247 "syntax = \"proto2\";\n"
1248 "message Foo {}\n");
1249
1250 Run("protocol_compiler --proto_path=$tmpdir foo.proto");
1251
1252 ExpectErrorText("Missing output directives.\n");
1253}
1254
1255TEST_F(CommandLineInterfaceTest, OutputWriteError) {
temporal40ee5512008-07-10 02:12:20 +00001256 CreateTempFile("foo.proto",
1257 "syntax = \"proto2\";\n"
1258 "message Foo {}\n");
1259
kenton@google.comfccb1462009-12-18 02:11:36 +00001260 string output_file =
1261 MockCodeGenerator::GetOutputFileName("test_generator", "foo.proto");
1262
temporal40ee5512008-07-10 02:12:20 +00001263 // Create a directory blocking our output location.
kenton@google.comfccb1462009-12-18 02:11:36 +00001264 CreateTempDir(output_file);
temporal40ee5512008-07-10 02:12:20 +00001265
1266 Run("protocol_compiler --test_out=$tmpdir "
1267 "--proto_path=$tmpdir foo.proto");
1268
kenton@google.com5f121642009-12-23 07:03:06 +00001269 // MockCodeGenerator no longer detects an error because we actually write to
1270 // an in-memory location first, then dump to disk at the end. This is no
1271 // big deal.
1272 // ExpectErrorSubstring("MockCodeGenerator detected write error.");
kenton@google.comfccb1462009-12-18 02:11:36 +00001273
temporal40ee5512008-07-10 02:12:20 +00001274#if defined(_WIN32) && !defined(__CYGWIN__)
1275 // Windows with MSVCRT.dll produces EPERM instead of EISDIR.
kenton@google.comfccb1462009-12-18 02:11:36 +00001276 if (HasAlternateErrorSubstring(output_file + ": Permission denied")) {
temporal40ee5512008-07-10 02:12:20 +00001277 return;
1278 }
1279#endif
1280
kenton@google.comfccb1462009-12-18 02:11:36 +00001281 ExpectErrorSubstring(output_file + ": Is a directory");
1282}
1283
1284TEST_F(CommandLineInterfaceTest, PluginOutputWriteError) {
1285 CreateTempFile("foo.proto",
1286 "syntax = \"proto2\";\n"
1287 "message Foo {}\n");
1288
1289 string output_file =
1290 MockCodeGenerator::GetOutputFileName("test_plugin", "foo.proto");
1291
1292 // Create a directory blocking our output location.
1293 CreateTempDir(output_file);
1294
1295 Run("protocol_compiler --plug_out=$tmpdir "
1296 "--proto_path=$tmpdir foo.proto");
1297
1298#if defined(_WIN32) && !defined(__CYGWIN__)
1299 // Windows with MSVCRT.dll produces EPERM instead of EISDIR.
1300 if (HasAlternateErrorSubstring(output_file + ": Permission denied")) {
1301 return;
1302 }
1303#endif
1304
1305 ExpectErrorSubstring(output_file + ": Is a directory");
temporal40ee5512008-07-10 02:12:20 +00001306}
1307
1308TEST_F(CommandLineInterfaceTest, OutputDirectoryNotFoundError) {
temporal40ee5512008-07-10 02:12:20 +00001309 CreateTempFile("foo.proto",
1310 "syntax = \"proto2\";\n"
1311 "message Foo {}\n");
1312
1313 Run("protocol_compiler --test_out=$tmpdir/nosuchdir "
1314 "--proto_path=$tmpdir foo.proto");
1315
kenton@google.comfccb1462009-12-18 02:11:36 +00001316 ExpectErrorSubstring("nosuchdir/: No such file or directory");
1317}
1318
1319TEST_F(CommandLineInterfaceTest, PluginOutputDirectoryNotFoundError) {
1320 CreateTempFile("foo.proto",
1321 "syntax = \"proto2\";\n"
1322 "message Foo {}\n");
1323
1324 Run("protocol_compiler --plug_out=$tmpdir/nosuchdir "
1325 "--proto_path=$tmpdir foo.proto");
1326
1327 ExpectErrorSubstring("nosuchdir/: No such file or directory");
temporal40ee5512008-07-10 02:12:20 +00001328}
1329
1330TEST_F(CommandLineInterfaceTest, OutputDirectoryIsFileError) {
temporal40ee5512008-07-10 02:12:20 +00001331 CreateTempFile("foo.proto",
1332 "syntax = \"proto2\";\n"
1333 "message Foo {}\n");
1334
1335 Run("protocol_compiler --test_out=$tmpdir/foo.proto "
1336 "--proto_path=$tmpdir foo.proto");
1337
1338#if defined(_WIN32) && !defined(__CYGWIN__)
1339 // Windows with MSVCRT.dll produces EINVAL instead of ENOTDIR.
1340 if (HasAlternateErrorSubstring("foo.proto/: Invalid argument")) {
1341 return;
1342 }
1343#endif
1344
1345 ExpectErrorSubstring("foo.proto/: Not a directory");
1346}
1347
1348TEST_F(CommandLineInterfaceTest, GeneratorError) {
kenton@google.comfccb1462009-12-18 02:11:36 +00001349 CreateTempFile("foo.proto",
1350 "syntax = \"proto2\";\n"
1351 "message MockCodeGenerator_Error {}\n");
1352
1353 Run("protocol_compiler --test_out=$tmpdir "
1354 "--proto_path=$tmpdir foo.proto");
1355
1356 ExpectErrorSubstring(
1357 "--test_out: foo.proto: Saw message type MockCodeGenerator_Error.");
1358}
1359
1360TEST_F(CommandLineInterfaceTest, GeneratorPluginError) {
1361 // Test a generator plugin that returns an error.
temporal40ee5512008-07-10 02:12:20 +00001362
1363 CreateTempFile("foo.proto",
1364 "syntax = \"proto2\";\n"
kenton@google.comfccb1462009-12-18 02:11:36 +00001365 "message MockCodeGenerator_Error {}\n");
temporal40ee5512008-07-10 02:12:20 +00001366
kenton@google.comfccb1462009-12-18 02:11:36 +00001367 Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
temporal40ee5512008-07-10 02:12:20 +00001368 "--proto_path=$tmpdir foo.proto");
1369
kenton@google.comfccb1462009-12-18 02:11:36 +00001370 ExpectErrorSubstring(
1371 "--plug_out: foo.proto: Saw message type MockCodeGenerator_Error.");
1372}
1373
1374TEST_F(CommandLineInterfaceTest, GeneratorPluginFail) {
1375 // Test a generator plugin that exits with an error code.
1376
1377 CreateTempFile("foo.proto",
1378 "syntax = \"proto2\";\n"
1379 "message MockCodeGenerator_Exit {}\n");
1380
1381 Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
1382 "--proto_path=$tmpdir foo.proto");
1383
1384 ExpectErrorSubstring("Saw message type MockCodeGenerator_Exit.");
1385 ExpectErrorSubstring(
1386 "--plug_out: prefix-gen-plug: Plugin failed with status code 123.");
1387}
1388
1389TEST_F(CommandLineInterfaceTest, GeneratorPluginCrash) {
1390 // Test a generator plugin that crashes.
1391
1392 CreateTempFile("foo.proto",
1393 "syntax = \"proto2\";\n"
1394 "message MockCodeGenerator_Abort {}\n");
1395
1396 Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
1397 "--proto_path=$tmpdir foo.proto");
1398
1399 ExpectErrorSubstring("Saw message type MockCodeGenerator_Abort.");
1400
kenton@google.com684d45b2009-12-19 04:50:00 +00001401#ifdef _WIN32
1402 // Windows doesn't have signals. It looks like abort()ing causes the process
1403 // to exit with status code 3, but let's not depend on the exact number here.
1404 ExpectErrorSubstring(
1405 "--plug_out: prefix-gen-plug: Plugin failed with status code");
1406#else
kenton@google.comfccb1462009-12-18 02:11:36 +00001407 // Don't depend on the exact signal number.
1408 ExpectErrorSubstring(
1409 "--plug_out: prefix-gen-plug: Plugin killed by signal");
kenton@google.com684d45b2009-12-19 04:50:00 +00001410#endif
kenton@google.comfccb1462009-12-18 02:11:36 +00001411}
1412
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +00001413TEST_F(CommandLineInterfaceTest, PluginReceivesSourceCodeInfo) {
1414 CreateTempFile("foo.proto",
1415 "syntax = \"proto2\";\n"
1416 "message MockCodeGenerator_HasSourceCodeInfo {}\n");
1417
1418 Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
1419
1420 ExpectErrorSubstring(
1421 "Saw message type MockCodeGenerator_HasSourceCodeInfo: 1.");
1422}
1423
Feng Xiaoe841bac2015-12-11 17:09:20 -08001424TEST_F(CommandLineInterfaceTest, PluginReceivesJsonName) {
1425 CreateTempFile("foo.proto",
1426 "syntax = \"proto2\";\n"
1427 "message MockCodeGenerator_HasJsonName {\n"
1428 " optional int32 value = 1;\n"
1429 "}\n");
1430
1431 Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
1432
1433 ExpectErrorSubstring("Saw json_name: 1");
1434}
1435
kenton@google.comfccb1462009-12-18 02:11:36 +00001436TEST_F(CommandLineInterfaceTest, GeneratorPluginNotFound) {
1437 // Test what happens if the plugin isn't found.
1438
1439 CreateTempFile("error.proto",
1440 "syntax = \"proto2\";\n"
1441 "message Foo {}\n");
1442
1443 Run("protocol_compiler --badplug_out=TestParameter:$tmpdir "
1444 "--plugin=prefix-gen-badplug=no_such_file "
1445 "--proto_path=$tmpdir error.proto");
1446
kenton@google.com684d45b2009-12-19 04:50:00 +00001447#ifdef _WIN32
liujisi@google.com57014ff2010-12-21 05:56:35 +00001448 ExpectErrorSubstring("--badplug_out: prefix-gen-badplug: " +
1449 Subprocess::Win32ErrorMessage(ERROR_FILE_NOT_FOUND));
kenton@google.com684d45b2009-12-19 04:50:00 +00001450#else
1451 // Error written to stdout by child process after exec() fails.
kenton@google.comfccb1462009-12-18 02:11:36 +00001452 ExpectErrorSubstring(
1453 "no_such_file: program not found or is not executable");
1454
kenton@google.com684d45b2009-12-19 04:50:00 +00001455 // Error written by parent process when child fails.
kenton@google.comfccb1462009-12-18 02:11:36 +00001456 ExpectErrorSubstring(
1457 "--badplug_out: prefix-gen-badplug: Plugin failed with status code 1.");
kenton@google.com684d45b2009-12-19 04:50:00 +00001458#endif
kenton@google.comfccb1462009-12-18 02:11:36 +00001459}
1460
1461TEST_F(CommandLineInterfaceTest, GeneratorPluginNotAllowed) {
1462 // Test what happens if plugins aren't allowed.
1463
1464 CreateTempFile("error.proto",
1465 "syntax = \"proto2\";\n"
1466 "message Foo {}\n");
1467
1468 DisallowPlugins();
1469 Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
1470 "--proto_path=$tmpdir error.proto");
1471
1472 ExpectErrorSubstring("Unknown flag: --plug_out");
temporal40ee5512008-07-10 02:12:20 +00001473}
1474
1475TEST_F(CommandLineInterfaceTest, HelpText) {
temporal40ee5512008-07-10 02:12:20 +00001476 Run("test_exec_name --help");
1477
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +00001478 ExpectErrorSubstringWithZeroReturnCode("Usage: test_exec_name ");
1479 ExpectErrorSubstringWithZeroReturnCode("--test_out=OUT_DIR");
1480 ExpectErrorSubstringWithZeroReturnCode("Test output.");
1481 ExpectErrorSubstringWithZeroReturnCode("--alt_out=OUT_DIR");
1482 ExpectErrorSubstringWithZeroReturnCode("Alt output.");
temporal40ee5512008-07-10 02:12:20 +00001483}
1484
kenton@google.comf663b162009-04-15 19:50:54 +00001485TEST_F(CommandLineInterfaceTest, GccFormatErrors) {
1486 // Test --error_format=gcc (which is the default, but we want to verify
1487 // that it can be set explicitly).
1488
kenton@google.comf663b162009-04-15 19:50:54 +00001489 CreateTempFile("foo.proto",
1490 "syntax = \"proto2\";\n"
1491 "badsyntax\n");
1492
1493 Run("protocol_compiler --test_out=$tmpdir "
1494 "--proto_path=$tmpdir --error_format=gcc foo.proto");
1495
1496 ExpectErrorText(
1497 "foo.proto:2:1: Expected top-level statement (e.g. \"message\").\n");
1498}
1499
1500TEST_F(CommandLineInterfaceTest, MsvsFormatErrors) {
1501 // Test --error_format=msvs
1502
kenton@google.comf663b162009-04-15 19:50:54 +00001503 CreateTempFile("foo.proto",
1504 "syntax = \"proto2\";\n"
1505 "badsyntax\n");
1506
1507 Run("protocol_compiler --test_out=$tmpdir "
1508 "--proto_path=$tmpdir --error_format=msvs foo.proto");
1509
1510 ExpectErrorText(
kenton@google.com6793c1a2010-04-05 21:45:45 +00001511 "$tmpdir/foo.proto(2) : error in column=1: Expected top-level statement "
kenton@google.comf663b162009-04-15 19:50:54 +00001512 "(e.g. \"message\").\n");
1513}
1514
1515TEST_F(CommandLineInterfaceTest, InvalidErrorFormat) {
1516 // Test --error_format=msvs
1517
kenton@google.comf663b162009-04-15 19:50:54 +00001518 CreateTempFile("foo.proto",
1519 "syntax = \"proto2\";\n"
1520 "badsyntax\n");
1521
1522 Run("protocol_compiler --test_out=$tmpdir "
1523 "--proto_path=$tmpdir --error_format=invalid foo.proto");
1524
1525 ExpectErrorText(
1526 "Unknown error format: invalid\n");
1527}
1528
temporal40ee5512008-07-10 02:12:20 +00001529// -------------------------------------------------------------------
1530// Flag parsing tests
1531
1532TEST_F(CommandLineInterfaceTest, ParseSingleCharacterFlag) {
1533 // Test that a single-character flag works.
1534
temporal40ee5512008-07-10 02:12:20 +00001535 CreateTempFile("foo.proto",
1536 "syntax = \"proto2\";\n"
1537 "message Foo {}\n");
1538
temporal779f61c2008-08-13 03:15:00 +00001539 Run("protocol_compiler -t$tmpdir "
temporal40ee5512008-07-10 02:12:20 +00001540 "--proto_path=$tmpdir foo.proto");
1541
1542 ExpectNoErrors();
kenton@google.comfccb1462009-12-18 02:11:36 +00001543 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
temporal40ee5512008-07-10 02:12:20 +00001544}
1545
1546TEST_F(CommandLineInterfaceTest, ParseSpaceDelimitedValue) {
1547 // Test that separating the flag value with a space works.
1548
temporal40ee5512008-07-10 02:12:20 +00001549 CreateTempFile("foo.proto",
1550 "syntax = \"proto2\";\n"
1551 "message Foo {}\n");
1552
1553 Run("protocol_compiler --test_out $tmpdir "
1554 "--proto_path=$tmpdir foo.proto");
1555
1556 ExpectNoErrors();
kenton@google.comfccb1462009-12-18 02:11:36 +00001557 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
temporal40ee5512008-07-10 02:12:20 +00001558}
1559
1560TEST_F(CommandLineInterfaceTest, ParseSingleCharacterSpaceDelimitedValue) {
1561 // Test that separating the flag value with a space works for
1562 // single-character flags.
1563
temporal40ee5512008-07-10 02:12:20 +00001564 CreateTempFile("foo.proto",
1565 "syntax = \"proto2\";\n"
1566 "message Foo {}\n");
1567
temporal779f61c2008-08-13 03:15:00 +00001568 Run("protocol_compiler -t $tmpdir "
temporal40ee5512008-07-10 02:12:20 +00001569 "--proto_path=$tmpdir foo.proto");
1570
1571 ExpectNoErrors();
kenton@google.comfccb1462009-12-18 02:11:36 +00001572 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
temporal40ee5512008-07-10 02:12:20 +00001573}
1574
1575TEST_F(CommandLineInterfaceTest, MissingValueError) {
1576 // Test that we get an error if a flag is missing its value.
1577
temporal40ee5512008-07-10 02:12:20 +00001578 Run("protocol_compiler --test_out --proto_path=$tmpdir foo.proto");
1579
1580 ExpectErrorText("Missing value for flag: --test_out\n");
1581}
1582
1583TEST_F(CommandLineInterfaceTest, MissingValueAtEndError) {
1584 // Test that we get an error if the last argument is a flag requiring a
1585 // value.
1586
temporal40ee5512008-07-10 02:12:20 +00001587 Run("protocol_compiler --test_out");
1588
1589 ExpectErrorText("Missing value for flag: --test_out\n");
1590}
1591
jieluo@google.com4de8f552014-07-18 00:47:59 +00001592TEST_F(CommandLineInterfaceTest, PrintFreeFieldNumbers) {
1593 CreateTempFile(
1594 "foo.proto",
1595 "syntax = \"proto2\";\n"
1596 "package foo;\n"
1597 "message Foo {\n"
1598 " optional int32 a = 2;\n"
1599 " optional string b = 4;\n"
1600 " optional string c = 5;\n"
1601 " optional int64 d = 8;\n"
1602 " optional double e = 10;\n"
1603 "}\n");
1604 CreateTempFile(
1605 "bar.proto",
1606 "syntax = \"proto2\";\n"
1607 "message Bar {\n"
1608 " optional int32 a = 2;\n"
1609 " extensions 4 to 5;\n"
1610 " optional int64 d = 8;\n"
1611 " extensions 10;\n"
1612 "}\n");
1613 CreateTempFile(
1614 "baz.proto",
1615 "syntax = \"proto2\";\n"
1616 "message Baz {\n"
1617 " optional int32 a = 2;\n"
1618 " optional int64 d = 8;\n"
1619 " extensions 15 to max;\n" // unordered.
1620 " extensions 13;\n"
1621 " extensions 10 to 12;\n"
1622 " extensions 5;\n"
1623 " extensions 4;\n"
1624 "}\n");
1625 CreateTempFile(
1626 "quz.proto",
1627 "syntax = \"proto2\";\n"
1628 "message Quz {\n"
1629 " message Foo {}\n" // nested message
1630 " optional int32 a = 2;\n"
1631 " optional group C = 4 {\n"
1632 " optional int32 d = 5;\n"
1633 " }\n"
1634 " extensions 8 to 10;\n"
1635 " optional group E = 11 {\n"
1636 " optional int32 f = 9;\n" // explicitly reuse extension range 8-10
1637 " optional group G = 15 {\n" // nested group
1638 " message Foo {}\n" // nested message inside nested group
1639 " }\n"
1640 " }\n"
1641 "}\n");
1642
1643 Run("protocol_compiler --print_free_field_numbers --proto_path=$tmpdir "
jieluo@google.com8d6f04a2014-08-06 20:49:30 +00001644 "foo.proto bar.proto baz.proto quz.proto");
jieluo@google.com4de8f552014-07-18 00:47:59 +00001645
1646 ExpectNoErrors();
jieluo@google.com8d6f04a2014-08-06 20:49:30 +00001647
1648 // TODO(jieluo): Cygwin doesn't work well if we try to capture stderr and
1649 // stdout at the same time. Need to figure out why and add this test back
1650 // for Cygwin.
1651#if !defined(__CYGWIN__)
jieluo@google.com4de8f552014-07-18 00:47:59 +00001652 ExpectCapturedStdout(
1653 "foo.Foo free: 1 3 6-7 9 11-INF\n"
1654 "Bar free: 1 3 6-7 9 11-INF\n"
1655 "Baz free: 1 3 6-7 9 14\n"
1656 "Quz.Foo free: 1-INF\n"
1657 "Quz.E.G.Foo free: 1-INF\n"
1658 "Quz free: 1 3 6-7 12-14 16-INF\n");
jieluo@google.com8d6f04a2014-08-06 20:49:30 +00001659#endif
jieluo@google.com4de8f552014-07-18 00:47:59 +00001660}
1661
temporal779f61c2008-08-13 03:15:00 +00001662// ===================================================================
1663
1664// Test for --encode and --decode. Note that it would be easier to do this
1665// test as a shell script, but we'd like to be able to run the test on
1666// platforms that don't have a Bourne-compatible shell available (especially
1667// Windows/MSVC).
1668class EncodeDecodeTest : public testing::Test {
1669 protected:
1670 virtual void SetUp() {
1671 duped_stdin_ = dup(STDIN_FILENO);
1672 }
1673
1674 virtual void TearDown() {
1675 dup2(duped_stdin_, STDIN_FILENO);
1676 close(duped_stdin_);
1677 }
1678
1679 void RedirectStdinFromText(const string& input) {
1680 string filename = TestTempDir() + "/test_stdin";
jieluo@google.com4de8f552014-07-18 00:47:59 +00001681 GOOGLE_CHECK_OK(File::SetContents(filename, input, true));
temporal779f61c2008-08-13 03:15:00 +00001682 GOOGLE_CHECK(RedirectStdinFromFile(filename));
1683 }
1684
1685 bool RedirectStdinFromFile(const string& filename) {
1686 int fd = open(filename.c_str(), O_RDONLY);
1687 if (fd < 0) return false;
1688 dup2(fd, STDIN_FILENO);
1689 close(fd);
1690 return true;
1691 }
1692
1693 // Remove '\r' characters from text.
1694 string StripCR(const string& text) {
1695 string result;
1696
1697 for (int i = 0; i < text.size(); i++) {
1698 if (text[i] != '\r') {
1699 result.push_back(text[i]);
1700 }
1701 }
1702
1703 return result;
1704 }
1705
1706 enum Type { TEXT, BINARY };
1707 enum ReturnCode { SUCCESS, ERROR };
1708
1709 bool Run(const string& command) {
1710 vector<string> args;
1711 args.push_back("protoc");
1712 SplitStringUsing(command, " ", &args);
1713 args.push_back("--proto_path=" + TestSourceDir());
1714
Feng Xiaof157a562014-11-14 11:50:31 -08001715 google::protobuf::scoped_array<const char * > argv(new const char* [args.size()]);
temporal779f61c2008-08-13 03:15:00 +00001716 for (int i = 0; i < args.size(); i++) {
1717 argv[i] = args[i].c_str();
1718 }
1719
1720 CommandLineInterface cli;
1721 cli.SetInputsAreProtoPathRelative(true);
1722
1723 CaptureTestStdout();
1724 CaptureTestStderr();
1725
1726 int result = cli.Run(args.size(), argv.get());
1727
1728 captured_stdout_ = GetCapturedTestStdout();
1729 captured_stderr_ = GetCapturedTestStderr();
1730
1731 return result == 0;
1732 }
1733
1734 void ExpectStdoutMatchesBinaryFile(const string& filename) {
1735 string expected_output;
jieluo@google.com4de8f552014-07-18 00:47:59 +00001736 GOOGLE_CHECK_OK(File::GetContents(filename, &expected_output, true));
temporal779f61c2008-08-13 03:15:00 +00001737
1738 // Don't use EXPECT_EQ because we don't want to print raw binary data to
1739 // stdout on failure.
1740 EXPECT_TRUE(captured_stdout_ == expected_output);
1741 }
1742
1743 void ExpectStdoutMatchesTextFile(const string& filename) {
1744 string expected_output;
jieluo@google.com4de8f552014-07-18 00:47:59 +00001745 GOOGLE_CHECK_OK(File::GetContents(filename, &expected_output, true));
temporal779f61c2008-08-13 03:15:00 +00001746
1747 ExpectStdoutMatchesText(expected_output);
1748 }
1749
1750 void ExpectStdoutMatchesText(const string& expected_text) {
1751 EXPECT_EQ(StripCR(expected_text), StripCR(captured_stdout_));
1752 }
1753
1754 void ExpectStderrMatchesText(const string& expected_text) {
1755 EXPECT_EQ(StripCR(expected_text), StripCR(captured_stderr_));
1756 }
1757
1758 private:
1759 int duped_stdin_;
1760 string captured_stdout_;
1761 string captured_stderr_;
1762};
1763
1764TEST_F(EncodeDecodeTest, Encode) {
jieluo@google.com4de8f552014-07-18 00:47:59 +00001765 RedirectStdinFromFile(TestSourceDir() + "/google/protobuf/"
1766 "testdata/text_format_unittest_data_oneof_implemented.txt");
temporal779f61c2008-08-13 03:15:00 +00001767 EXPECT_TRUE(Run("google/protobuf/unittest.proto "
1768 "--encode=protobuf_unittest.TestAllTypes"));
1769 ExpectStdoutMatchesBinaryFile(TestSourceDir() +
jieluo@google.com4de8f552014-07-18 00:47:59 +00001770 "/google/protobuf/testdata/golden_message_oneof_implemented");
temporal779f61c2008-08-13 03:15:00 +00001771 ExpectStderrMatchesText("");
1772}
1773
1774TEST_F(EncodeDecodeTest, Decode) {
1775 RedirectStdinFromFile(TestSourceDir() +
jieluo@google.com4de8f552014-07-18 00:47:59 +00001776 "/google/protobuf/testdata/golden_message_oneof_implemented");
temporal779f61c2008-08-13 03:15:00 +00001777 EXPECT_TRUE(Run("google/protobuf/unittest.proto "
1778 "--decode=protobuf_unittest.TestAllTypes"));
1779 ExpectStdoutMatchesTextFile(TestSourceDir() +
jieluo@google.com4de8f552014-07-18 00:47:59 +00001780 "/google/protobuf/"
1781 "testdata/text_format_unittest_data_oneof_implemented.txt");
temporal779f61c2008-08-13 03:15:00 +00001782 ExpectStderrMatchesText("");
1783}
1784
1785TEST_F(EncodeDecodeTest, Partial) {
1786 RedirectStdinFromText("");
1787 EXPECT_TRUE(Run("google/protobuf/unittest.proto "
1788 "--encode=protobuf_unittest.TestRequired"));
1789 ExpectStdoutMatchesText("");
1790 ExpectStderrMatchesText(
1791 "warning: Input message is missing required fields: a, b, c\n");
1792}
1793
1794TEST_F(EncodeDecodeTest, DecodeRaw) {
1795 protobuf_unittest::TestAllTypes message;
1796 message.set_optional_int32(123);
1797 message.set_optional_string("foo");
1798 string data;
1799 message.SerializeToString(&data);
1800
1801 RedirectStdinFromText(data);
1802 EXPECT_TRUE(Run("--decode_raw"));
1803 ExpectStdoutMatchesText("1: 123\n"
1804 "14: \"foo\"\n");
1805 ExpectStderrMatchesText("");
1806}
1807
1808TEST_F(EncodeDecodeTest, UnknownType) {
1809 EXPECT_FALSE(Run("google/protobuf/unittest.proto "
1810 "--encode=NoSuchType"));
1811 ExpectStdoutMatchesText("");
1812 ExpectStderrMatchesText("Type not defined: NoSuchType\n");
1813}
1814
1815TEST_F(EncodeDecodeTest, ProtoParseError) {
1816 EXPECT_FALSE(Run("google/protobuf/no_such_file.proto "
1817 "--encode=NoSuchType"));
1818 ExpectStdoutMatchesText("");
1819 ExpectStderrMatchesText(
1820 "google/protobuf/no_such_file.proto: File not found.\n");
1821}
1822
temporal40ee5512008-07-10 02:12:20 +00001823} // anonymous namespace
1824
Feng Xiaoc5147e32015-12-28 16:40:39 -08001825#endif // !GOOGLE_PROTOBUF_HEAP_CHECK_DRACONIAN
1826
temporal40ee5512008-07-10 02:12:20 +00001827} // namespace compiler
1828} // namespace protobuf
Jisi Liu885b6122015-02-28 14:51:22 -08001829} // namespace google