blob: 3d27829ef9a3e8a5d404479417b77f9197cc9ad5 [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 Xiao6ef984a2014-11-10 17:34:54 -080066
Jisi Liu7a00a1e2015-02-21 17:28:51 -080067// Disable the whole test when we use tcmalloc for "draconian" heap checks, in
68// which case tcmalloc will print warnings that fail the plugin tests.
69#if !GOOGLE_PROTOBUF_HEAP_CHECK_DRACONIAN
temporal40ee5512008-07-10 02:12:20 +000070namespace google {
71namespace protobuf {
72namespace compiler {
73
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
temporal40ee5512008-07-10 02:12:20 +0000118 void SetInputsAreProtoPathRelative(bool enable) {
119 cli_.SetInputsAreProtoPathRelative(enable);
120 }
121
122 // -----------------------------------------------------------------
123 // Methods to check the test results (called after Run()).
124
125 // Checks that no text was written to stderr during Run(), and Run()
126 // returned 0.
127 void ExpectNoErrors();
128
129 // Checks that Run() returned non-zero and the stderr output is exactly
130 // the text given. expected_test may contain references to "$tmpdir",
131 // which will be replaced by the temporary directory path.
132 void ExpectErrorText(const string& expected_text);
133
134 // Checks that Run() returned non-zero and the stderr contains the given
135 // substring.
136 void ExpectErrorSubstring(const string& expected_substring);
137
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000138 // Like ExpectErrorSubstring, but checks that Run() returned zero.
139 void ExpectErrorSubstringWithZeroReturnCode(
140 const string& expected_substring);
141
jieluo@google.com4de8f552014-07-18 00:47:59 +0000142 // Checks that the captured stdout is the same as the expected_text.
143 void ExpectCapturedStdout(const string& expected_text);
144
temporal40ee5512008-07-10 02:12:20 +0000145 // Returns true if ExpectErrorSubstring(expected_substring) would pass, but
146 // does not fail otherwise.
147 bool HasAlternateErrorSubstring(const string& expected_substring);
148
149 // Checks that MockCodeGenerator::Generate() was called in the given
kenton@google.comfccb1462009-12-18 02:11:36 +0000150 // context (or the generator in test_plugin.cc, which produces the same
151 // output). That is, this tests if the generator with the given name
temporal40ee5512008-07-10 02:12:20 +0000152 // was called with the given parameter and proto file and produced the
153 // given output file. This is checked by reading the output file and
154 // checking that it contains the content that MockCodeGenerator would
155 // generate given these inputs. message_name is the name of the first
156 // message that appeared in the proto file; this is just to make extra
157 // sure that the correct file was parsed.
158 void ExpectGenerated(const string& generator_name,
159 const string& parameter,
160 const string& proto_name,
kenton@google.comfccb1462009-12-18 02:11:36 +0000161 const string& message_name);
162 void ExpectGenerated(const string& generator_name,
163 const string& parameter,
164 const string& proto_name,
temporal40ee5512008-07-10 02:12:20 +0000165 const string& message_name,
kenton@google.comfccb1462009-12-18 02:11:36 +0000166 const string& output_directory);
liujisi@google.com33165fe2010-11-02 13:14:58 +0000167 void ExpectGeneratedWithMultipleInputs(const string& generator_name,
168 const string& all_proto_names,
169 const string& proto_name,
170 const string& message_name);
kenton@google.comfccb1462009-12-18 02:11:36 +0000171 void ExpectGeneratedWithInsertions(const string& generator_name,
172 const string& parameter,
173 const string& insertions,
174 const string& proto_name,
175 const string& message_name);
temporal40ee5512008-07-10 02:12:20 +0000176
kenton@google.com91218af2009-12-18 07:20:43 +0000177 void ExpectNullCodeGeneratorCalled(const string& parameter);
178
temporal779f61c2008-08-13 03:15:00 +0000179 void ReadDescriptorSet(const string& filename,
180 FileDescriptorSet* descriptor_set);
181
temporal40ee5512008-07-10 02:12:20 +0000182 private:
183 // The object we are testing.
184 CommandLineInterface cli_;
185
kenton@google.comfccb1462009-12-18 02:11:36 +0000186 // Was DisallowPlugins() called?
187 bool disallow_plugins_;
188
temporal40ee5512008-07-10 02:12:20 +0000189 // We create a directory within TestTempDir() in order to add extra
190 // protection against accidentally deleting user files (since we recursively
191 // delete this directory during the test). This is the full path of that
192 // directory.
193 string temp_directory_;
194
195 // The result of Run().
196 int return_code_;
197
198 // The captured stderr output.
199 string error_text_;
200
jieluo@google.com4de8f552014-07-18 00:47:59 +0000201 // The captured stdout.
202 string captured_stdout_;
203
temporal40ee5512008-07-10 02:12:20 +0000204 // Pointers which need to be deleted later.
temporalcc930432008-07-21 20:28:30 +0000205 vector<CodeGenerator*> mock_generators_to_delete_;
kenton@google.com91218af2009-12-18 07:20:43 +0000206
207 NullCodeGenerator* null_generator_;
temporal40ee5512008-07-10 02:12:20 +0000208};
209
temporalcc930432008-07-21 20:28:30 +0000210class CommandLineInterfaceTest::NullCodeGenerator : public CodeGenerator {
211 public:
212 NullCodeGenerator() : called_(false) {}
213 ~NullCodeGenerator() {}
214
215 mutable bool called_;
216 mutable string parameter_;
217
218 // implements CodeGenerator ----------------------------------------
219 bool Generate(const FileDescriptor* file,
220 const string& parameter,
liujisi@google.com33165fe2010-11-02 13:14:58 +0000221 GeneratorContext* context,
temporalcc930432008-07-21 20:28:30 +0000222 string* error) const {
223 called_ = true;
224 parameter_ = parameter;
225 return true;
226 }
227};
228
temporal40ee5512008-07-10 02:12:20 +0000229// ===================================================================
230
231void CommandLineInterfaceTest::SetUp() {
232 // Most of these tests were written before this option was added, so we
233 // run with the option on (which used to be the only way) except in certain
234 // tests where we turn it off.
235 cli_.SetInputsAreProtoPathRelative(true);
236
237 temp_directory_ = TestTempDir() + "/proto2_cli_test_temp";
238
239 // If the temp directory already exists, it must be left over from a
240 // previous run. Delete it.
Feng Xiao6ef984a2014-11-10 17:34:54 -0800241 if (FileExists(temp_directory_)) {
temporal40ee5512008-07-10 02:12:20 +0000242 File::DeleteRecursively(temp_directory_, NULL, NULL);
243 }
244
245 // Create the temp directory.
jieluo@google.com4de8f552014-07-18 00:47:59 +0000246 GOOGLE_CHECK_OK(File::CreateDir(temp_directory_, 0777));
kenton@google.comfccb1462009-12-18 02:11:36 +0000247
248 // Register generators.
249 CodeGenerator* generator = new MockCodeGenerator("test_generator");
250 mock_generators_to_delete_.push_back(generator);
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000251 cli_.RegisterGenerator("--test_out", "--test_opt", generator, "Test output.");
kenton@google.comfccb1462009-12-18 02:11:36 +0000252 cli_.RegisterGenerator("-t", generator, "Test output.");
253
254 generator = new MockCodeGenerator("alt_generator");
255 mock_generators_to_delete_.push_back(generator);
256 cli_.RegisterGenerator("--alt_out", generator, "Alt output.");
257
kenton@google.com91218af2009-12-18 07:20:43 +0000258 generator = null_generator_ = new NullCodeGenerator();
kenton@google.comfccb1462009-12-18 02:11:36 +0000259 mock_generators_to_delete_.push_back(generator);
260 cli_.RegisterGenerator("--null_out", generator, "Null output.");
261
262 disallow_plugins_ = false;
temporal40ee5512008-07-10 02:12:20 +0000263}
264
265void CommandLineInterfaceTest::TearDown() {
266 // Delete the temp directory.
Feng Xiaof157a562014-11-14 11:50:31 -0800267 if (FileExists(temp_directory_)) {
Feng Xiaobaca1a82014-11-07 14:09:18 -0500268 File::DeleteRecursively(temp_directory_, NULL, NULL);
269 }
temporal40ee5512008-07-10 02:12:20 +0000270
271 // Delete all the MockCodeGenerators.
272 for (int i = 0; i < mock_generators_to_delete_.size(); i++) {
273 delete mock_generators_to_delete_[i];
274 }
275 mock_generators_to_delete_.clear();
276}
277
jieluo@google.com8d6f04a2014-08-06 20:49:30 +0000278void CommandLineInterfaceTest::Run(const string& command) {
jieluo@google.com4de8f552014-07-18 00:47:59 +0000279 vector<string> args = Split(command, " ", true);
temporal40ee5512008-07-10 02:12:20 +0000280
kenton@google.comfccb1462009-12-18 02:11:36 +0000281 if (!disallow_plugins_) {
282 cli_.AllowPlugins("prefix-");
kenton@google.comc0ee4d22009-12-22 02:05:33 +0000283 const char* possible_paths[] = {
284 // When building with shared libraries, libtool hides the real executable
285 // in .libs and puts a fake wrapper in the current directory.
286 // Unfortunately, due to an apparent bug on Cygwin/MinGW, if one program
287 // wrapped in this way (e.g. protobuf-tests.exe) tries to execute another
288 // program wrapped in this way (e.g. test_plugin.exe), the latter fails
289 // with error code 127 and no explanation message. Presumably the problem
290 // is that the wrapper for protobuf-tests.exe set some environment
291 // variables that confuse the wrapper for test_plugin.exe. Luckily, it
292 // turns out that if we simply invoke the wrapped test_plugin.exe
293 // directly, it works -- I guess the environment variables set by the
294 // protobuf-tests.exe wrapper happen to be correct for it too. So we do
295 // that.
296 ".libs/test_plugin.exe", // Win32 w/autotool (Cygwin / MinGW)
297 "test_plugin.exe", // Other Win32 (MSVC)
298 "test_plugin", // Unix
299 };
300
301 string plugin_path;
302
303 for (int i = 0; i < GOOGLE_ARRAYSIZE(possible_paths); i++) {
304 if (access(possible_paths[i], F_OK) == 0) {
305 plugin_path = possible_paths[i];
306 break;
307 }
308 }
309
310 if (plugin_path.empty()) {
311 GOOGLE_LOG(ERROR)
312 << "Plugin executable not found. Plugin tests are likely to fail.";
313 } else {
314 args.push_back("--plugin=prefix-gen-plug=" + plugin_path);
315 }
kenton@google.comfccb1462009-12-18 02:11:36 +0000316 }
317
Feng Xiaof157a562014-11-14 11:50:31 -0800318 google::protobuf::scoped_array<const char * > argv(new const char* [args.size()]);
temporal40ee5512008-07-10 02:12:20 +0000319
320 for (int i = 0; i < args.size(); i++) {
321 args[i] = StringReplace(args[i], "$tmpdir", temp_directory_, true);
322 argv[i] = args[i].c_str();
323 }
324
jieluo@google.com8d6f04a2014-08-06 20:49:30 +0000325 // TODO(jieluo): Cygwin doesn't work well if we try to capture stderr and
326 // stdout at the same time. Need to figure out why and add this capture back
327 // for Cygwin.
328#if !defined(__CYGWIN__)
329 CaptureTestStdout();
330#endif
temporal40ee5512008-07-10 02:12:20 +0000331 CaptureTestStderr();
332
333 return_code_ = cli_.Run(args.size(), argv.get());
334
335 error_text_ = GetCapturedTestStderr();
jieluo@google.com8d6f04a2014-08-06 20:49:30 +0000336#if !defined(__CYGWIN__)
337 captured_stdout_ = GetCapturedTestStdout();
338#endif
temporal40ee5512008-07-10 02:12:20 +0000339}
340
341// -------------------------------------------------------------------
342
temporal40ee5512008-07-10 02:12:20 +0000343void CommandLineInterfaceTest::CreateTempFile(
344 const string& name,
345 const string& contents) {
346 // Create parent directory, if necessary.
347 string::size_type slash_pos = name.find_last_of('/');
348 if (slash_pos != string::npos) {
349 string dir = name.substr(0, slash_pos);
Feng Xiao6ef984a2014-11-10 17:34:54 -0800350 if (!FileExists(temp_directory_ + "/" + dir)) {
jieluo@google.com4de8f552014-07-18 00:47:59 +0000351 GOOGLE_CHECK_OK(File::RecursivelyCreateDir(temp_directory_ + "/" + dir,
352 0777));
353 }
temporal40ee5512008-07-10 02:12:20 +0000354 }
355
356 // Write file.
357 string full_name = temp_directory_ + "/" + name;
jieluo@google.com4de8f552014-07-18 00:47:59 +0000358 GOOGLE_CHECK_OK(File::SetContents(full_name, contents, true));
temporal40ee5512008-07-10 02:12:20 +0000359}
360
kenton@google.comfccb1462009-12-18 02:11:36 +0000361void CommandLineInterfaceTest::CreateTempDir(const string& name) {
jieluo@google.com4de8f552014-07-18 00:47:59 +0000362 GOOGLE_CHECK_OK(File::RecursivelyCreateDir(temp_directory_ + "/" + name,
363 0777));
kenton@google.comfccb1462009-12-18 02:11:36 +0000364}
365
temporal40ee5512008-07-10 02:12:20 +0000366// -------------------------------------------------------------------
367
368void CommandLineInterfaceTest::ExpectNoErrors() {
369 EXPECT_EQ(0, return_code_);
370 EXPECT_EQ("", error_text_);
371}
372
373void CommandLineInterfaceTest::ExpectErrorText(const string& expected_text) {
374 EXPECT_NE(0, return_code_);
375 EXPECT_EQ(StringReplace(expected_text, "$tmpdir", temp_directory_, true),
376 error_text_);
377}
378
379void CommandLineInterfaceTest::ExpectErrorSubstring(
380 const string& expected_substring) {
381 EXPECT_NE(0, return_code_);
382 EXPECT_PRED_FORMAT2(testing::IsSubstring, expected_substring, error_text_);
383}
384
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000385void CommandLineInterfaceTest::ExpectErrorSubstringWithZeroReturnCode(
386 const string& expected_substring) {
387 EXPECT_EQ(0, return_code_);
388 EXPECT_PRED_FORMAT2(testing::IsSubstring, expected_substring, error_text_);
389}
390
temporal40ee5512008-07-10 02:12:20 +0000391bool CommandLineInterfaceTest::HasAlternateErrorSubstring(
392 const string& expected_substring) {
393 EXPECT_NE(0, return_code_);
394 return error_text_.find(expected_substring) != string::npos;
395}
396
397void CommandLineInterfaceTest::ExpectGenerated(
398 const string& generator_name,
399 const string& parameter,
400 const string& proto_name,
kenton@google.comfccb1462009-12-18 02:11:36 +0000401 const string& message_name) {
402 MockCodeGenerator::ExpectGenerated(
liujisi@google.com33165fe2010-11-02 13:14:58 +0000403 generator_name, parameter, "", proto_name, message_name, proto_name,
404 temp_directory_);
kenton@google.comfccb1462009-12-18 02:11:36 +0000405}
temporal40ee5512008-07-10 02:12:20 +0000406
kenton@google.comfccb1462009-12-18 02:11:36 +0000407void CommandLineInterfaceTest::ExpectGenerated(
408 const string& generator_name,
409 const string& parameter,
410 const string& proto_name,
411 const string& message_name,
412 const string& output_directory) {
413 MockCodeGenerator::ExpectGenerated(
liujisi@google.com33165fe2010-11-02 13:14:58 +0000414 generator_name, parameter, "", proto_name, message_name, proto_name,
kenton@google.comfccb1462009-12-18 02:11:36 +0000415 temp_directory_ + "/" + output_directory);
416}
417
liujisi@google.com33165fe2010-11-02 13:14:58 +0000418void CommandLineInterfaceTest::ExpectGeneratedWithMultipleInputs(
419 const string& generator_name,
420 const string& all_proto_names,
421 const string& proto_name,
422 const string& message_name) {
423 MockCodeGenerator::ExpectGenerated(
424 generator_name, "", "", proto_name, message_name,
425 all_proto_names,
426 temp_directory_);
427}
428
kenton@google.comfccb1462009-12-18 02:11:36 +0000429void CommandLineInterfaceTest::ExpectGeneratedWithInsertions(
430 const string& generator_name,
431 const string& parameter,
432 const string& insertions,
433 const string& proto_name,
434 const string& message_name) {
435 MockCodeGenerator::ExpectGenerated(
436 generator_name, parameter, insertions, proto_name, message_name,
liujisi@google.com33165fe2010-11-02 13:14:58 +0000437 proto_name, temp_directory_);
temporal40ee5512008-07-10 02:12:20 +0000438}
439
kenton@google.com91218af2009-12-18 07:20:43 +0000440void CommandLineInterfaceTest::ExpectNullCodeGeneratorCalled(
441 const string& parameter) {
442 EXPECT_TRUE(null_generator_->called_);
443 EXPECT_EQ(parameter, null_generator_->parameter_);
444}
445
temporal779f61c2008-08-13 03:15:00 +0000446void CommandLineInterfaceTest::ReadDescriptorSet(
447 const string& filename, FileDescriptorSet* descriptor_set) {
448 string path = temp_directory_ + "/" + filename;
449 string file_contents;
jieluo@google.com4de8f552014-07-18 00:47:59 +0000450 GOOGLE_CHECK_OK(File::GetContents(path, &file_contents, true));
451
temporal779f61c2008-08-13 03:15:00 +0000452 if (!descriptor_set->ParseFromString(file_contents)) {
453 FAIL() << "Could not parse file contents: " << path;
454 }
455}
456
jieluo@google.com4de8f552014-07-18 00:47:59 +0000457void CommandLineInterfaceTest::ExpectCapturedStdout(
458 const string& expected_text) {
459 EXPECT_EQ(expected_text, captured_stdout_);
460}
461
temporal40ee5512008-07-10 02:12:20 +0000462// ===================================================================
463
temporal40ee5512008-07-10 02:12:20 +0000464TEST_F(CommandLineInterfaceTest, BasicOutput) {
465 // Test that the common case works.
466
temporal40ee5512008-07-10 02:12:20 +0000467 CreateTempFile("foo.proto",
468 "syntax = \"proto2\";\n"
469 "message Foo {}\n");
470
471 Run("protocol_compiler --test_out=$tmpdir "
472 "--proto_path=$tmpdir foo.proto");
473
474 ExpectNoErrors();
kenton@google.comfccb1462009-12-18 02:11:36 +0000475 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
476}
477
478TEST_F(CommandLineInterfaceTest, BasicPlugin) {
479 // Test that basic plugins work.
480
481 CreateTempFile("foo.proto",
482 "syntax = \"proto2\";\n"
483 "message Foo {}\n");
484
485 Run("protocol_compiler --plug_out=$tmpdir "
486 "--proto_path=$tmpdir foo.proto");
487
488 ExpectNoErrors();
489 ExpectGenerated("test_plugin", "", "foo.proto", "Foo");
490}
491
492TEST_F(CommandLineInterfaceTest, GeneratorAndPlugin) {
493 // Invoke a generator and a plugin at the same time.
494
495 CreateTempFile("foo.proto",
496 "syntax = \"proto2\";\n"
497 "message Foo {}\n");
498
499 Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
500 "--proto_path=$tmpdir foo.proto");
501
502 ExpectNoErrors();
503 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
504 ExpectGenerated("test_plugin", "", "foo.proto", "Foo");
temporal40ee5512008-07-10 02:12:20 +0000505}
506
507TEST_F(CommandLineInterfaceTest, MultipleInputs) {
508 // Test parsing multiple input files.
509
temporal40ee5512008-07-10 02:12:20 +0000510 CreateTempFile("foo.proto",
511 "syntax = \"proto2\";\n"
512 "message Foo {}\n");
513 CreateTempFile("bar.proto",
514 "syntax = \"proto2\";\n"
515 "message Bar {}\n");
516
kenton@google.comfccb1462009-12-18 02:11:36 +0000517 Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
temporal40ee5512008-07-10 02:12:20 +0000518 "--proto_path=$tmpdir foo.proto bar.proto");
519
520 ExpectNoErrors();
liujisi@google.com33165fe2010-11-02 13:14:58 +0000521 ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
522 "foo.proto", "Foo");
523 ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
524 "bar.proto", "Bar");
525 ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
526 "foo.proto", "Foo");
527 ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
528 "bar.proto", "Bar");
529}
530
531TEST_F(CommandLineInterfaceTest, MultipleInputsWithImport) {
532 // Test parsing multiple input files with an import of a separate file.
533
534 CreateTempFile("foo.proto",
535 "syntax = \"proto2\";\n"
536 "message Foo {}\n");
537 CreateTempFile("bar.proto",
538 "syntax = \"proto2\";\n"
539 "import \"baz.proto\";\n"
540 "message Bar {\n"
541 " optional Baz a = 1;\n"
542 "}\n");
543 CreateTempFile("baz.proto",
544 "syntax = \"proto2\";\n"
545 "message Baz {}\n");
546
547 Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
548 "--proto_path=$tmpdir foo.proto bar.proto");
549
550 ExpectNoErrors();
551 ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
552 "foo.proto", "Foo");
553 ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
554 "bar.proto", "Bar");
555 ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
556 "foo.proto", "Foo");
557 ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
558 "bar.proto", "Bar");
temporal40ee5512008-07-10 02:12:20 +0000559}
560
561TEST_F(CommandLineInterfaceTest, CreateDirectory) {
562 // Test that when we output to a sub-directory, it is created.
563
kenton@google.comfccb1462009-12-18 02:11:36 +0000564 CreateTempFile("bar/baz/foo.proto",
temporal40ee5512008-07-10 02:12:20 +0000565 "syntax = \"proto2\";\n"
566 "message Foo {}\n");
kenton@google.comfccb1462009-12-18 02:11:36 +0000567 CreateTempDir("out");
568 CreateTempDir("plugout");
temporal40ee5512008-07-10 02:12:20 +0000569
kenton@google.comfccb1462009-12-18 02:11:36 +0000570 Run("protocol_compiler --test_out=$tmpdir/out --plug_out=$tmpdir/plugout "
571 "--proto_path=$tmpdir bar/baz/foo.proto");
temporal40ee5512008-07-10 02:12:20 +0000572
573 ExpectNoErrors();
kenton@google.comfccb1462009-12-18 02:11:36 +0000574 ExpectGenerated("test_generator", "", "bar/baz/foo.proto", "Foo", "out");
575 ExpectGenerated("test_plugin", "", "bar/baz/foo.proto", "Foo", "plugout");
temporal40ee5512008-07-10 02:12:20 +0000576}
577
578TEST_F(CommandLineInterfaceTest, GeneratorParameters) {
579 // Test that generator parameters are correctly parsed from the command line.
580
temporal40ee5512008-07-10 02:12:20 +0000581 CreateTempFile("foo.proto",
582 "syntax = \"proto2\";\n"
583 "message Foo {}\n");
584
585 Run("protocol_compiler --test_out=TestParameter:$tmpdir "
kenton@google.comfccb1462009-12-18 02:11:36 +0000586 "--plug_out=TestPluginParameter:$tmpdir "
temporal40ee5512008-07-10 02:12:20 +0000587 "--proto_path=$tmpdir foo.proto");
588
589 ExpectNoErrors();
kenton@google.comfccb1462009-12-18 02:11:36 +0000590 ExpectGenerated("test_generator", "TestParameter", "foo.proto", "Foo");
591 ExpectGenerated("test_plugin", "TestPluginParameter", "foo.proto", "Foo");
592}
593
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000594TEST_F(CommandLineInterfaceTest, ExtraGeneratorParameters) {
595 // Test that generator parameters specified with the option flag are
596 // correctly passed to the code generator.
597
598 CreateTempFile("foo.proto",
599 "syntax = \"proto2\";\n"
600 "message Foo {}\n");
601 // Create the "a" and "b" sub-directories.
602 CreateTempDir("a");
603 CreateTempDir("b");
604
605 Run("protocol_compiler "
606 "--test_opt=foo1 "
607 "--test_out=bar:$tmpdir/a "
608 "--test_opt=foo2 "
609 "--test_out=baz:$tmpdir/b "
610 "--test_opt=foo3 "
611 "--proto_path=$tmpdir foo.proto");
612
613 ExpectNoErrors();
614 ExpectGenerated(
615 "test_generator", "bar,foo1,foo2,foo3", "foo.proto", "Foo", "a");
616 ExpectGenerated(
617 "test_generator", "baz,foo1,foo2,foo3", "foo.proto", "Foo", "b");
618}
619
kenton@google.comfccb1462009-12-18 02:11:36 +0000620TEST_F(CommandLineInterfaceTest, Insert) {
621 // Test running a generator that inserts code into another's output.
622
623 CreateTempFile("foo.proto",
624 "syntax = \"proto2\";\n"
625 "message Foo {}\n");
626
627 Run("protocol_compiler "
628 "--test_out=TestParameter:$tmpdir "
629 "--plug_out=TestPluginParameter:$tmpdir "
630 "--test_out=insert=test_generator,test_plugin:$tmpdir "
631 "--plug_out=insert=test_generator,test_plugin:$tmpdir "
632 "--proto_path=$tmpdir foo.proto");
633
634 ExpectNoErrors();
635 ExpectGeneratedWithInsertions(
636 "test_generator", "TestParameter", "test_generator,test_plugin",
637 "foo.proto", "Foo");
638 ExpectGeneratedWithInsertions(
639 "test_plugin", "TestPluginParameter", "test_generator,test_plugin",
640 "foo.proto", "Foo");
temporal40ee5512008-07-10 02:12:20 +0000641}
642
liujisi@google.com9b7f6c52010-12-08 03:45:27 +0000643#if defined(_WIN32)
temporalcc930432008-07-21 20:28:30 +0000644
645TEST_F(CommandLineInterfaceTest, WindowsOutputPath) {
646 // Test that the output path can be a Windows-style path.
647
temporalcc930432008-07-21 20:28:30 +0000648 CreateTempFile("foo.proto",
649 "syntax = \"proto2\";\n");
650
kenton@google.comfccb1462009-12-18 02:11:36 +0000651 Run("protocol_compiler --null_out=C:\\ "
temporalcc930432008-07-21 20:28:30 +0000652 "--proto_path=$tmpdir foo.proto");
653
654 ExpectNoErrors();
kenton@google.com91218af2009-12-18 07:20:43 +0000655 ExpectNullCodeGeneratorCalled("");
temporalcc930432008-07-21 20:28:30 +0000656}
657
658TEST_F(CommandLineInterfaceTest, WindowsOutputPathAndParameter) {
659 // Test that we can have a windows-style output path and a parameter.
660
temporalcc930432008-07-21 20:28:30 +0000661 CreateTempFile("foo.proto",
662 "syntax = \"proto2\";\n");
663
kenton@google.comfccb1462009-12-18 02:11:36 +0000664 Run("protocol_compiler --null_out=bar:C:\\ "
temporalcc930432008-07-21 20:28:30 +0000665 "--proto_path=$tmpdir foo.proto");
666
667 ExpectNoErrors();
kenton@google.com91218af2009-12-18 07:20:43 +0000668 ExpectNullCodeGeneratorCalled("bar");
temporalcc930432008-07-21 20:28:30 +0000669}
670
kenton@google.comef3730c2009-04-28 00:49:36 +0000671TEST_F(CommandLineInterfaceTest, TrailingBackslash) {
672 // Test that the directories can end in backslashes. Some users claim this
673 // doesn't work on their system.
674
kenton@google.comef3730c2009-04-28 00:49:36 +0000675 CreateTempFile("foo.proto",
676 "syntax = \"proto2\";\n"
677 "message Foo {}\n");
678
679 Run("protocol_compiler --test_out=$tmpdir\\ "
680 "--proto_path=$tmpdir\\ foo.proto");
681
682 ExpectNoErrors();
kenton@google.comfccb1462009-12-18 02:11:36 +0000683 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
kenton@google.comef3730c2009-04-28 00:49:36 +0000684}
685
temporalcc930432008-07-21 20:28:30 +0000686#endif // defined(_WIN32) || defined(__CYGWIN__)
687
temporal40ee5512008-07-10 02:12:20 +0000688TEST_F(CommandLineInterfaceTest, PathLookup) {
689 // Test that specifying multiple directories in the proto search path works.
690
temporal40ee5512008-07-10 02:12:20 +0000691 CreateTempFile("b/bar.proto",
692 "syntax = \"proto2\";\n"
693 "message Bar {}\n");
694 CreateTempFile("a/foo.proto",
695 "syntax = \"proto2\";\n"
696 "import \"bar.proto\";\n"
697 "message Foo {\n"
698 " optional Bar a = 1;\n"
699 "}\n");
700 CreateTempFile("b/foo.proto", "this should not be parsed\n");
701
702 Run("protocol_compiler --test_out=$tmpdir "
703 "--proto_path=$tmpdir/a --proto_path=$tmpdir/b foo.proto");
704
705 ExpectNoErrors();
kenton@google.comfccb1462009-12-18 02:11:36 +0000706 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
temporal40ee5512008-07-10 02:12:20 +0000707}
708
709TEST_F(CommandLineInterfaceTest, ColonDelimitedPath) {
710 // Same as PathLookup, but we provide the proto_path in a single flag.
711
temporal40ee5512008-07-10 02:12:20 +0000712 CreateTempFile("b/bar.proto",
713 "syntax = \"proto2\";\n"
714 "message Bar {}\n");
715 CreateTempFile("a/foo.proto",
716 "syntax = \"proto2\";\n"
717 "import \"bar.proto\";\n"
718 "message Foo {\n"
719 " optional Bar a = 1;\n"
720 "}\n");
721 CreateTempFile("b/foo.proto", "this should not be parsed\n");
722
723#undef PATH_SEPARATOR
724#if defined(_WIN32)
725#define PATH_SEPARATOR ";"
726#else
727#define PATH_SEPARATOR ":"
728#endif
729
730 Run("protocol_compiler --test_out=$tmpdir "
Nobuaki Sukegawa8ba0e352014-11-30 19:42:51 +0900731 "--proto_path=$tmpdir/a" PATH_SEPARATOR "$tmpdir/b foo.proto");
temporal40ee5512008-07-10 02:12:20 +0000732
733#undef PATH_SEPARATOR
734
735 ExpectNoErrors();
kenton@google.comfccb1462009-12-18 02:11:36 +0000736 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
temporal40ee5512008-07-10 02:12:20 +0000737}
738
739TEST_F(CommandLineInterfaceTest, NonRootMapping) {
740 // Test setting up a search path mapping a directory to a non-root location.
741
temporal40ee5512008-07-10 02:12:20 +0000742 CreateTempFile("foo.proto",
743 "syntax = \"proto2\";\n"
744 "message Foo {}\n");
745
746 Run("protocol_compiler --test_out=$tmpdir "
747 "--proto_path=bar=$tmpdir bar/foo.proto");
748
749 ExpectNoErrors();
kenton@google.comfccb1462009-12-18 02:11:36 +0000750 ExpectGenerated("test_generator", "", "bar/foo.proto", "Foo");
temporal40ee5512008-07-10 02:12:20 +0000751}
752
753TEST_F(CommandLineInterfaceTest, MultipleGenerators) {
754 // Test that we can have multiple generators and use both in one invocation,
755 // each with a different output directory.
756
temporal40ee5512008-07-10 02:12:20 +0000757 CreateTempFile("foo.proto",
758 "syntax = \"proto2\";\n"
759 "message Foo {}\n");
760 // Create the "a" and "b" sub-directories.
kenton@google.comfccb1462009-12-18 02:11:36 +0000761 CreateTempDir("a");
762 CreateTempDir("b");
temporal40ee5512008-07-10 02:12:20 +0000763
764 Run("protocol_compiler "
kenton@google.comfccb1462009-12-18 02:11:36 +0000765 "--test_out=$tmpdir/a "
766 "--alt_out=$tmpdir/b "
temporal40ee5512008-07-10 02:12:20 +0000767 "--proto_path=$tmpdir foo.proto");
768
769 ExpectNoErrors();
kenton@google.comfccb1462009-12-18 02:11:36 +0000770 ExpectGenerated("test_generator", "", "foo.proto", "Foo", "a");
771 ExpectGenerated("alt_generator", "", "foo.proto", "Foo", "b");
temporal40ee5512008-07-10 02:12:20 +0000772}
773
774TEST_F(CommandLineInterfaceTest, DisallowServicesNoServices) {
775 // Test that --disallow_services doesn't cause a problem when there are no
776 // services.
777
temporal40ee5512008-07-10 02:12:20 +0000778 CreateTempFile("foo.proto",
779 "syntax = \"proto2\";\n"
780 "message Foo {}\n");
781
782 Run("protocol_compiler --disallow_services --test_out=$tmpdir "
783 "--proto_path=$tmpdir foo.proto");
784
785 ExpectNoErrors();
kenton@google.comfccb1462009-12-18 02:11:36 +0000786 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
temporal40ee5512008-07-10 02:12:20 +0000787}
788
789TEST_F(CommandLineInterfaceTest, DisallowServicesHasService) {
790 // Test that --disallow_services produces an error when there are services.
791
temporal40ee5512008-07-10 02:12:20 +0000792 CreateTempFile("foo.proto",
793 "syntax = \"proto2\";\n"
794 "message Foo {}\n"
795 "service Bar {}\n");
796
797 Run("protocol_compiler --disallow_services --test_out=$tmpdir "
798 "--proto_path=$tmpdir foo.proto");
799
800 ExpectErrorSubstring("foo.proto: This file contains services");
801}
802
803TEST_F(CommandLineInterfaceTest, AllowServicesHasService) {
804 // Test that services work fine as long as --disallow_services is not used.
805
temporal40ee5512008-07-10 02:12:20 +0000806 CreateTempFile("foo.proto",
807 "syntax = \"proto2\";\n"
808 "message Foo {}\n"
809 "service Bar {}\n");
810
811 Run("protocol_compiler --test_out=$tmpdir "
812 "--proto_path=$tmpdir foo.proto");
813
814 ExpectNoErrors();
kenton@google.comfccb1462009-12-18 02:11:36 +0000815 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
temporal40ee5512008-07-10 02:12:20 +0000816}
817
818TEST_F(CommandLineInterfaceTest, CwdRelativeInputs) {
819 // Test that we can accept working-directory-relative input files.
820
821 SetInputsAreProtoPathRelative(false);
822
temporal40ee5512008-07-10 02:12:20 +0000823 CreateTempFile("foo.proto",
824 "syntax = \"proto2\";\n"
825 "message Foo {}\n");
826
827 Run("protocol_compiler --test_out=$tmpdir "
828 "--proto_path=$tmpdir $tmpdir/foo.proto");
829
830 ExpectNoErrors();
kenton@google.comfccb1462009-12-18 02:11:36 +0000831 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
temporal40ee5512008-07-10 02:12:20 +0000832}
833
temporal779f61c2008-08-13 03:15:00 +0000834TEST_F(CommandLineInterfaceTest, WriteDescriptorSet) {
835 CreateTempFile("foo.proto",
836 "syntax = \"proto2\";\n"
837 "message Foo {}\n");
838 CreateTempFile("bar.proto",
839 "syntax = \"proto2\";\n"
840 "import \"foo.proto\";\n"
841 "message Bar {\n"
842 " optional Foo foo = 1;\n"
843 "}\n");
844
845 Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
846 "--proto_path=$tmpdir bar.proto");
847
848 ExpectNoErrors();
849
850 FileDescriptorSet descriptor_set;
851 ReadDescriptorSet("descriptor_set", &descriptor_set);
852 if (HasFatalFailure()) return;
jieluo@google.com4de8f552014-07-18 00:47:59 +0000853 EXPECT_EQ(1, descriptor_set.file_size());
temporal779f61c2008-08-13 03:15:00 +0000854 EXPECT_EQ("bar.proto", descriptor_set.file(0).name());
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000855 // Descriptor set should not have source code info.
856 EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
857}
858
859TEST_F(CommandLineInterfaceTest, WriteDescriptorSetWithSourceInfo) {
860 CreateTempFile("foo.proto",
861 "syntax = \"proto2\";\n"
862 "message Foo {}\n");
863 CreateTempFile("bar.proto",
864 "syntax = \"proto2\";\n"
865 "import \"foo.proto\";\n"
866 "message Bar {\n"
867 " optional Foo foo = 1;\n"
868 "}\n");
869
870 Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
871 "--include_source_info --proto_path=$tmpdir bar.proto");
872
873 ExpectNoErrors();
874
875 FileDescriptorSet descriptor_set;
876 ReadDescriptorSet("descriptor_set", &descriptor_set);
877 if (HasFatalFailure()) return;
jieluo@google.com4de8f552014-07-18 00:47:59 +0000878 EXPECT_EQ(1, descriptor_set.file_size());
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000879 EXPECT_EQ("bar.proto", descriptor_set.file(0).name());
880 // Source code info included.
881 EXPECT_TRUE(descriptor_set.file(0).has_source_code_info());
temporal779f61c2008-08-13 03:15:00 +0000882}
883
884TEST_F(CommandLineInterfaceTest, WriteTransitiveDescriptorSet) {
885 CreateTempFile("foo.proto",
886 "syntax = \"proto2\";\n"
887 "message Foo {}\n");
888 CreateTempFile("bar.proto",
889 "syntax = \"proto2\";\n"
890 "import \"foo.proto\";\n"
891 "message Bar {\n"
892 " optional Foo foo = 1;\n"
893 "}\n");
894
895 Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
896 "--include_imports --proto_path=$tmpdir bar.proto");
897
898 ExpectNoErrors();
899
900 FileDescriptorSet descriptor_set;
901 ReadDescriptorSet("descriptor_set", &descriptor_set);
902 if (HasFatalFailure()) return;
jieluo@google.com4de8f552014-07-18 00:47:59 +0000903 EXPECT_EQ(2, descriptor_set.file_size());
temporal779f61c2008-08-13 03:15:00 +0000904 if (descriptor_set.file(0).name() == "bar.proto") {
kenton@google.com7fb9ae92009-09-02 02:42:56 +0000905 std::swap(descriptor_set.mutable_file()->mutable_data()[0],
906 descriptor_set.mutable_file()->mutable_data()[1]);
temporal779f61c2008-08-13 03:15:00 +0000907 }
908 EXPECT_EQ("foo.proto", descriptor_set.file(0).name());
909 EXPECT_EQ("bar.proto", descriptor_set.file(1).name());
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000910 // Descriptor set should not have source code info.
911 EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
912 EXPECT_FALSE(descriptor_set.file(1).has_source_code_info());
913}
914
915TEST_F(CommandLineInterfaceTest, WriteTransitiveDescriptorSetWithSourceInfo) {
916 CreateTempFile("foo.proto",
917 "syntax = \"proto2\";\n"
918 "message Foo {}\n");
919 CreateTempFile("bar.proto",
920 "syntax = \"proto2\";\n"
921 "import \"foo.proto\";\n"
922 "message Bar {\n"
923 " optional Foo foo = 1;\n"
924 "}\n");
925
926 Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
927 "--include_imports --include_source_info --proto_path=$tmpdir bar.proto");
928
929 ExpectNoErrors();
930
931 FileDescriptorSet descriptor_set;
932 ReadDescriptorSet("descriptor_set", &descriptor_set);
933 if (HasFatalFailure()) return;
jieluo@google.com4de8f552014-07-18 00:47:59 +0000934 EXPECT_EQ(2, descriptor_set.file_size());
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +0000935 if (descriptor_set.file(0).name() == "bar.proto") {
936 std::swap(descriptor_set.mutable_file()->mutable_data()[0],
937 descriptor_set.mutable_file()->mutable_data()[1]);
938 }
939 EXPECT_EQ("foo.proto", descriptor_set.file(0).name());
940 EXPECT_EQ("bar.proto", descriptor_set.file(1).name());
941 // Source code info included.
942 EXPECT_TRUE(descriptor_set.file(0).has_source_code_info());
943 EXPECT_TRUE(descriptor_set.file(1).has_source_code_info());
temporal779f61c2008-08-13 03:15:00 +0000944}
945
temporal40ee5512008-07-10 02:12:20 +0000946// -------------------------------------------------------------------
947
948TEST_F(CommandLineInterfaceTest, ParseErrors) {
949 // Test that parse errors are reported.
950
temporal40ee5512008-07-10 02:12:20 +0000951 CreateTempFile("foo.proto",
952 "syntax = \"proto2\";\n"
953 "badsyntax\n");
954
955 Run("protocol_compiler --test_out=$tmpdir "
956 "--proto_path=$tmpdir foo.proto");
957
958 ExpectErrorText(
959 "foo.proto:2:1: Expected top-level statement (e.g. \"message\").\n");
960}
961
962TEST_F(CommandLineInterfaceTest, ParseErrorsMultipleFiles) {
963 // Test that parse errors are reported from multiple files.
964
temporal40ee5512008-07-10 02:12:20 +0000965 // We set up files such that foo.proto actually depends on bar.proto in
966 // two ways: Directly and through baz.proto. bar.proto's errors should
967 // only be reported once.
968 CreateTempFile("bar.proto",
969 "syntax = \"proto2\";\n"
970 "badsyntax\n");
971 CreateTempFile("baz.proto",
972 "syntax = \"proto2\";\n"
973 "import \"bar.proto\";\n");
974 CreateTempFile("foo.proto",
975 "syntax = \"proto2\";\n"
976 "import \"bar.proto\";\n"
977 "import \"baz.proto\";\n");
978
979 Run("protocol_compiler --test_out=$tmpdir "
980 "--proto_path=$tmpdir foo.proto");
981
982 ExpectErrorText(
983 "bar.proto:2:1: Expected top-level statement (e.g. \"message\").\n"
984 "baz.proto: Import \"bar.proto\" was not found or had errors.\n"
985 "foo.proto: Import \"bar.proto\" was not found or had errors.\n"
986 "foo.proto: Import \"baz.proto\" was not found or had errors.\n");
987}
988
989TEST_F(CommandLineInterfaceTest, InputNotFoundError) {
990 // Test what happens if the input file is not found.
991
temporal40ee5512008-07-10 02:12:20 +0000992 Run("protocol_compiler --test_out=$tmpdir "
993 "--proto_path=$tmpdir foo.proto");
994
995 ExpectErrorText(
996 "foo.proto: File not found.\n");
997}
998
999TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotFoundError) {
1000 // Test what happens when a working-directory-relative input file is not
1001 // found.
1002
1003 SetInputsAreProtoPathRelative(false);
1004
temporal40ee5512008-07-10 02:12:20 +00001005 Run("protocol_compiler --test_out=$tmpdir "
1006 "--proto_path=$tmpdir $tmpdir/foo.proto");
1007
1008 ExpectErrorText(
1009 "$tmpdir/foo.proto: No such file or directory\n");
1010}
1011
1012TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotMappedError) {
1013 // Test what happens when a working-directory-relative input file is not
1014 // mapped to a virtual path.
1015
1016 SetInputsAreProtoPathRelative(false);
1017
temporal40ee5512008-07-10 02:12:20 +00001018 CreateTempFile("foo.proto",
1019 "syntax = \"proto2\";\n"
1020 "message Foo {}\n");
1021
1022 // Create a directory called "bar" so that we can point --proto_path at it.
1023 CreateTempFile("bar/dummy", "");
1024
1025 Run("protocol_compiler --test_out=$tmpdir "
1026 "--proto_path=$tmpdir/bar $tmpdir/foo.proto");
1027
1028 ExpectErrorText(
1029 "$tmpdir/foo.proto: File does not reside within any path "
1030 "specified using --proto_path (or -I). You must specify a "
kenton@google.com477f7992009-10-07 21:38:11 +00001031 "--proto_path which encompasses this file. Note that the "
1032 "proto_path must be an exact prefix of the .proto file "
1033 "names -- protoc is too dumb to figure out when two paths "
1034 "(e.g. absolute and relative) are equivalent (it's harder "
1035 "than you think).\n");
temporal40ee5512008-07-10 02:12:20 +00001036}
1037
1038TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotFoundAndNotMappedError) {
1039 // Check what happens if the input file is not found *and* is not mapped
1040 // in the proto_path.
1041
1042 SetInputsAreProtoPathRelative(false);
1043
temporal40ee5512008-07-10 02:12:20 +00001044 // Create a directory called "bar" so that we can point --proto_path at it.
1045 CreateTempFile("bar/dummy", "");
1046
1047 Run("protocol_compiler --test_out=$tmpdir "
1048 "--proto_path=$tmpdir/bar $tmpdir/foo.proto");
1049
1050 ExpectErrorText(
1051 "$tmpdir/foo.proto: No such file or directory\n");
1052}
1053
1054TEST_F(CommandLineInterfaceTest, CwdRelativeInputShadowedError) {
1055 // Test what happens when a working-directory-relative input file is shadowed
1056 // by another file in the virtual path.
1057
1058 SetInputsAreProtoPathRelative(false);
1059
temporal40ee5512008-07-10 02:12:20 +00001060 CreateTempFile("foo/foo.proto",
1061 "syntax = \"proto2\";\n"
1062 "message Foo {}\n");
1063 CreateTempFile("bar/foo.proto",
1064 "syntax = \"proto2\";\n"
1065 "message Bar {}\n");
1066
1067 Run("protocol_compiler --test_out=$tmpdir "
1068 "--proto_path=$tmpdir/foo --proto_path=$tmpdir/bar "
1069 "$tmpdir/bar/foo.proto");
1070
1071 ExpectErrorText(
1072 "$tmpdir/bar/foo.proto: Input is shadowed in the --proto_path "
1073 "by \"$tmpdir/foo/foo.proto\". Either use the latter "
1074 "file as your input or reorder the --proto_path so that the "
1075 "former file's location comes first.\n");
1076}
1077
1078TEST_F(CommandLineInterfaceTest, ProtoPathNotFoundError) {
1079 // Test what happens if the input file is not found.
1080
temporal40ee5512008-07-10 02:12:20 +00001081 Run("protocol_compiler --test_out=$tmpdir "
1082 "--proto_path=$tmpdir/foo foo.proto");
1083
1084 ExpectErrorText(
1085 "$tmpdir/foo: warning: directory does not exist.\n"
1086 "foo.proto: File not found.\n");
1087}
1088
1089TEST_F(CommandLineInterfaceTest, MissingInputError) {
1090 // Test that we get an error if no inputs are given.
1091
temporal40ee5512008-07-10 02:12:20 +00001092 Run("protocol_compiler --test_out=$tmpdir "
1093 "--proto_path=$tmpdir");
1094
1095 ExpectErrorText("Missing input file.\n");
1096}
1097
1098TEST_F(CommandLineInterfaceTest, MissingOutputError) {
temporal40ee5512008-07-10 02:12:20 +00001099 CreateTempFile("foo.proto",
1100 "syntax = \"proto2\";\n"
1101 "message Foo {}\n");
1102
1103 Run("protocol_compiler --proto_path=$tmpdir foo.proto");
1104
1105 ExpectErrorText("Missing output directives.\n");
1106}
1107
1108TEST_F(CommandLineInterfaceTest, OutputWriteError) {
temporal40ee5512008-07-10 02:12:20 +00001109 CreateTempFile("foo.proto",
1110 "syntax = \"proto2\";\n"
1111 "message Foo {}\n");
1112
kenton@google.comfccb1462009-12-18 02:11:36 +00001113 string output_file =
1114 MockCodeGenerator::GetOutputFileName("test_generator", "foo.proto");
1115
temporal40ee5512008-07-10 02:12:20 +00001116 // Create a directory blocking our output location.
kenton@google.comfccb1462009-12-18 02:11:36 +00001117 CreateTempDir(output_file);
temporal40ee5512008-07-10 02:12:20 +00001118
1119 Run("protocol_compiler --test_out=$tmpdir "
1120 "--proto_path=$tmpdir foo.proto");
1121
kenton@google.com5f121642009-12-23 07:03:06 +00001122 // MockCodeGenerator no longer detects an error because we actually write to
1123 // an in-memory location first, then dump to disk at the end. This is no
1124 // big deal.
1125 // ExpectErrorSubstring("MockCodeGenerator detected write error.");
kenton@google.comfccb1462009-12-18 02:11:36 +00001126
temporal40ee5512008-07-10 02:12:20 +00001127#if defined(_WIN32) && !defined(__CYGWIN__)
1128 // Windows with MSVCRT.dll produces EPERM instead of EISDIR.
kenton@google.comfccb1462009-12-18 02:11:36 +00001129 if (HasAlternateErrorSubstring(output_file + ": Permission denied")) {
temporal40ee5512008-07-10 02:12:20 +00001130 return;
1131 }
1132#endif
1133
kenton@google.comfccb1462009-12-18 02:11:36 +00001134 ExpectErrorSubstring(output_file + ": Is a directory");
1135}
1136
1137TEST_F(CommandLineInterfaceTest, PluginOutputWriteError) {
1138 CreateTempFile("foo.proto",
1139 "syntax = \"proto2\";\n"
1140 "message Foo {}\n");
1141
1142 string output_file =
1143 MockCodeGenerator::GetOutputFileName("test_plugin", "foo.proto");
1144
1145 // Create a directory blocking our output location.
1146 CreateTempDir(output_file);
1147
1148 Run("protocol_compiler --plug_out=$tmpdir "
1149 "--proto_path=$tmpdir foo.proto");
1150
1151#if defined(_WIN32) && !defined(__CYGWIN__)
1152 // Windows with MSVCRT.dll produces EPERM instead of EISDIR.
1153 if (HasAlternateErrorSubstring(output_file + ": Permission denied")) {
1154 return;
1155 }
1156#endif
1157
1158 ExpectErrorSubstring(output_file + ": Is a directory");
temporal40ee5512008-07-10 02:12:20 +00001159}
1160
1161TEST_F(CommandLineInterfaceTest, OutputDirectoryNotFoundError) {
temporal40ee5512008-07-10 02:12:20 +00001162 CreateTempFile("foo.proto",
1163 "syntax = \"proto2\";\n"
1164 "message Foo {}\n");
1165
1166 Run("protocol_compiler --test_out=$tmpdir/nosuchdir "
1167 "--proto_path=$tmpdir foo.proto");
1168
kenton@google.comfccb1462009-12-18 02:11:36 +00001169 ExpectErrorSubstring("nosuchdir/: No such file or directory");
1170}
1171
1172TEST_F(CommandLineInterfaceTest, PluginOutputDirectoryNotFoundError) {
1173 CreateTempFile("foo.proto",
1174 "syntax = \"proto2\";\n"
1175 "message Foo {}\n");
1176
1177 Run("protocol_compiler --plug_out=$tmpdir/nosuchdir "
1178 "--proto_path=$tmpdir foo.proto");
1179
1180 ExpectErrorSubstring("nosuchdir/: No such file or directory");
temporal40ee5512008-07-10 02:12:20 +00001181}
1182
1183TEST_F(CommandLineInterfaceTest, OutputDirectoryIsFileError) {
temporal40ee5512008-07-10 02:12:20 +00001184 CreateTempFile("foo.proto",
1185 "syntax = \"proto2\";\n"
1186 "message Foo {}\n");
1187
1188 Run("protocol_compiler --test_out=$tmpdir/foo.proto "
1189 "--proto_path=$tmpdir foo.proto");
1190
1191#if defined(_WIN32) && !defined(__CYGWIN__)
1192 // Windows with MSVCRT.dll produces EINVAL instead of ENOTDIR.
1193 if (HasAlternateErrorSubstring("foo.proto/: Invalid argument")) {
1194 return;
1195 }
1196#endif
1197
1198 ExpectErrorSubstring("foo.proto/: Not a directory");
1199}
1200
1201TEST_F(CommandLineInterfaceTest, GeneratorError) {
kenton@google.comfccb1462009-12-18 02:11:36 +00001202 CreateTempFile("foo.proto",
1203 "syntax = \"proto2\";\n"
1204 "message MockCodeGenerator_Error {}\n");
1205
1206 Run("protocol_compiler --test_out=$tmpdir "
1207 "--proto_path=$tmpdir foo.proto");
1208
1209 ExpectErrorSubstring(
1210 "--test_out: foo.proto: Saw message type MockCodeGenerator_Error.");
1211}
1212
1213TEST_F(CommandLineInterfaceTest, GeneratorPluginError) {
1214 // Test a generator plugin that returns an error.
temporal40ee5512008-07-10 02:12:20 +00001215
1216 CreateTempFile("foo.proto",
1217 "syntax = \"proto2\";\n"
kenton@google.comfccb1462009-12-18 02:11:36 +00001218 "message MockCodeGenerator_Error {}\n");
temporal40ee5512008-07-10 02:12:20 +00001219
kenton@google.comfccb1462009-12-18 02:11:36 +00001220 Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
temporal40ee5512008-07-10 02:12:20 +00001221 "--proto_path=$tmpdir foo.proto");
1222
kenton@google.comfccb1462009-12-18 02:11:36 +00001223 ExpectErrorSubstring(
1224 "--plug_out: foo.proto: Saw message type MockCodeGenerator_Error.");
1225}
1226
1227TEST_F(CommandLineInterfaceTest, GeneratorPluginFail) {
1228 // Test a generator plugin that exits with an error code.
1229
1230 CreateTempFile("foo.proto",
1231 "syntax = \"proto2\";\n"
1232 "message MockCodeGenerator_Exit {}\n");
1233
1234 Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
1235 "--proto_path=$tmpdir foo.proto");
1236
1237 ExpectErrorSubstring("Saw message type MockCodeGenerator_Exit.");
1238 ExpectErrorSubstring(
1239 "--plug_out: prefix-gen-plug: Plugin failed with status code 123.");
1240}
1241
1242TEST_F(CommandLineInterfaceTest, GeneratorPluginCrash) {
1243 // Test a generator plugin that crashes.
1244
1245 CreateTempFile("foo.proto",
1246 "syntax = \"proto2\";\n"
1247 "message MockCodeGenerator_Abort {}\n");
1248
1249 Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
1250 "--proto_path=$tmpdir foo.proto");
1251
1252 ExpectErrorSubstring("Saw message type MockCodeGenerator_Abort.");
1253
kenton@google.com684d45b2009-12-19 04:50:00 +00001254#ifdef _WIN32
1255 // Windows doesn't have signals. It looks like abort()ing causes the process
1256 // to exit with status code 3, but let's not depend on the exact number here.
1257 ExpectErrorSubstring(
1258 "--plug_out: prefix-gen-plug: Plugin failed with status code");
1259#else
kenton@google.comfccb1462009-12-18 02:11:36 +00001260 // Don't depend on the exact signal number.
1261 ExpectErrorSubstring(
1262 "--plug_out: prefix-gen-plug: Plugin killed by signal");
kenton@google.com684d45b2009-12-19 04:50:00 +00001263#endif
kenton@google.comfccb1462009-12-18 02:11:36 +00001264}
1265
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +00001266TEST_F(CommandLineInterfaceTest, PluginReceivesSourceCodeInfo) {
1267 CreateTempFile("foo.proto",
1268 "syntax = \"proto2\";\n"
1269 "message MockCodeGenerator_HasSourceCodeInfo {}\n");
1270
1271 Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
1272
1273 ExpectErrorSubstring(
1274 "Saw message type MockCodeGenerator_HasSourceCodeInfo: 1.");
1275}
1276
kenton@google.comfccb1462009-12-18 02:11:36 +00001277TEST_F(CommandLineInterfaceTest, GeneratorPluginNotFound) {
1278 // Test what happens if the plugin isn't found.
1279
1280 CreateTempFile("error.proto",
1281 "syntax = \"proto2\";\n"
1282 "message Foo {}\n");
1283
1284 Run("protocol_compiler --badplug_out=TestParameter:$tmpdir "
1285 "--plugin=prefix-gen-badplug=no_such_file "
1286 "--proto_path=$tmpdir error.proto");
1287
kenton@google.com684d45b2009-12-19 04:50:00 +00001288#ifdef _WIN32
liujisi@google.com57014ff2010-12-21 05:56:35 +00001289 ExpectErrorSubstring("--badplug_out: prefix-gen-badplug: " +
1290 Subprocess::Win32ErrorMessage(ERROR_FILE_NOT_FOUND));
kenton@google.com684d45b2009-12-19 04:50:00 +00001291#else
1292 // Error written to stdout by child process after exec() fails.
kenton@google.comfccb1462009-12-18 02:11:36 +00001293 ExpectErrorSubstring(
1294 "no_such_file: program not found or is not executable");
1295
kenton@google.com684d45b2009-12-19 04:50:00 +00001296 // Error written by parent process when child fails.
kenton@google.comfccb1462009-12-18 02:11:36 +00001297 ExpectErrorSubstring(
1298 "--badplug_out: prefix-gen-badplug: Plugin failed with status code 1.");
kenton@google.com684d45b2009-12-19 04:50:00 +00001299#endif
kenton@google.comfccb1462009-12-18 02:11:36 +00001300}
1301
1302TEST_F(CommandLineInterfaceTest, GeneratorPluginNotAllowed) {
1303 // Test what happens if plugins aren't allowed.
1304
1305 CreateTempFile("error.proto",
1306 "syntax = \"proto2\";\n"
1307 "message Foo {}\n");
1308
1309 DisallowPlugins();
1310 Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
1311 "--proto_path=$tmpdir error.proto");
1312
1313 ExpectErrorSubstring("Unknown flag: --plug_out");
temporal40ee5512008-07-10 02:12:20 +00001314}
1315
1316TEST_F(CommandLineInterfaceTest, HelpText) {
temporal40ee5512008-07-10 02:12:20 +00001317 Run("test_exec_name --help");
1318
xiaofeng@google.comb55a20f2012-09-22 02:40:50 +00001319 ExpectErrorSubstringWithZeroReturnCode("Usage: test_exec_name ");
1320 ExpectErrorSubstringWithZeroReturnCode("--test_out=OUT_DIR");
1321 ExpectErrorSubstringWithZeroReturnCode("Test output.");
1322 ExpectErrorSubstringWithZeroReturnCode("--alt_out=OUT_DIR");
1323 ExpectErrorSubstringWithZeroReturnCode("Alt output.");
temporal40ee5512008-07-10 02:12:20 +00001324}
1325
kenton@google.comf663b162009-04-15 19:50:54 +00001326TEST_F(CommandLineInterfaceTest, GccFormatErrors) {
1327 // Test --error_format=gcc (which is the default, but we want to verify
1328 // that it can be set explicitly).
1329
kenton@google.comf663b162009-04-15 19:50:54 +00001330 CreateTempFile("foo.proto",
1331 "syntax = \"proto2\";\n"
1332 "badsyntax\n");
1333
1334 Run("protocol_compiler --test_out=$tmpdir "
1335 "--proto_path=$tmpdir --error_format=gcc foo.proto");
1336
1337 ExpectErrorText(
1338 "foo.proto:2:1: Expected top-level statement (e.g. \"message\").\n");
1339}
1340
1341TEST_F(CommandLineInterfaceTest, MsvsFormatErrors) {
1342 // Test --error_format=msvs
1343
kenton@google.comf663b162009-04-15 19:50:54 +00001344 CreateTempFile("foo.proto",
1345 "syntax = \"proto2\";\n"
1346 "badsyntax\n");
1347
1348 Run("protocol_compiler --test_out=$tmpdir "
1349 "--proto_path=$tmpdir --error_format=msvs foo.proto");
1350
1351 ExpectErrorText(
kenton@google.com6793c1a2010-04-05 21:45:45 +00001352 "$tmpdir/foo.proto(2) : error in column=1: Expected top-level statement "
kenton@google.comf663b162009-04-15 19:50:54 +00001353 "(e.g. \"message\").\n");
1354}
1355
1356TEST_F(CommandLineInterfaceTest, InvalidErrorFormat) {
1357 // Test --error_format=msvs
1358
kenton@google.comf663b162009-04-15 19:50:54 +00001359 CreateTempFile("foo.proto",
1360 "syntax = \"proto2\";\n"
1361 "badsyntax\n");
1362
1363 Run("protocol_compiler --test_out=$tmpdir "
1364 "--proto_path=$tmpdir --error_format=invalid foo.proto");
1365
1366 ExpectErrorText(
1367 "Unknown error format: invalid\n");
1368}
1369
temporal40ee5512008-07-10 02:12:20 +00001370// -------------------------------------------------------------------
1371// Flag parsing tests
1372
1373TEST_F(CommandLineInterfaceTest, ParseSingleCharacterFlag) {
1374 // Test that a single-character flag works.
1375
temporal40ee5512008-07-10 02:12:20 +00001376 CreateTempFile("foo.proto",
1377 "syntax = \"proto2\";\n"
1378 "message Foo {}\n");
1379
temporal779f61c2008-08-13 03:15:00 +00001380 Run("protocol_compiler -t$tmpdir "
temporal40ee5512008-07-10 02:12:20 +00001381 "--proto_path=$tmpdir foo.proto");
1382
1383 ExpectNoErrors();
kenton@google.comfccb1462009-12-18 02:11:36 +00001384 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
temporal40ee5512008-07-10 02:12:20 +00001385}
1386
1387TEST_F(CommandLineInterfaceTest, ParseSpaceDelimitedValue) {
1388 // Test that separating the flag value with a space works.
1389
temporal40ee5512008-07-10 02:12:20 +00001390 CreateTempFile("foo.proto",
1391 "syntax = \"proto2\";\n"
1392 "message Foo {}\n");
1393
1394 Run("protocol_compiler --test_out $tmpdir "
1395 "--proto_path=$tmpdir foo.proto");
1396
1397 ExpectNoErrors();
kenton@google.comfccb1462009-12-18 02:11:36 +00001398 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
temporal40ee5512008-07-10 02:12:20 +00001399}
1400
1401TEST_F(CommandLineInterfaceTest, ParseSingleCharacterSpaceDelimitedValue) {
1402 // Test that separating the flag value with a space works for
1403 // single-character flags.
1404
temporal40ee5512008-07-10 02:12:20 +00001405 CreateTempFile("foo.proto",
1406 "syntax = \"proto2\";\n"
1407 "message Foo {}\n");
1408
temporal779f61c2008-08-13 03:15:00 +00001409 Run("protocol_compiler -t $tmpdir "
temporal40ee5512008-07-10 02:12:20 +00001410 "--proto_path=$tmpdir foo.proto");
1411
1412 ExpectNoErrors();
kenton@google.comfccb1462009-12-18 02:11:36 +00001413 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
temporal40ee5512008-07-10 02:12:20 +00001414}
1415
1416TEST_F(CommandLineInterfaceTest, MissingValueError) {
1417 // Test that we get an error if a flag is missing its value.
1418
temporal40ee5512008-07-10 02:12:20 +00001419 Run("protocol_compiler --test_out --proto_path=$tmpdir foo.proto");
1420
1421 ExpectErrorText("Missing value for flag: --test_out\n");
1422}
1423
1424TEST_F(CommandLineInterfaceTest, MissingValueAtEndError) {
1425 // Test that we get an error if the last argument is a flag requiring a
1426 // value.
1427
temporal40ee5512008-07-10 02:12:20 +00001428 Run("protocol_compiler --test_out");
1429
1430 ExpectErrorText("Missing value for flag: --test_out\n");
1431}
1432
jieluo@google.com4de8f552014-07-18 00:47:59 +00001433TEST_F(CommandLineInterfaceTest, PrintFreeFieldNumbers) {
1434 CreateTempFile(
1435 "foo.proto",
1436 "syntax = \"proto2\";\n"
1437 "package foo;\n"
1438 "message Foo {\n"
1439 " optional int32 a = 2;\n"
1440 " optional string b = 4;\n"
1441 " optional string c = 5;\n"
1442 " optional int64 d = 8;\n"
1443 " optional double e = 10;\n"
1444 "}\n");
1445 CreateTempFile(
1446 "bar.proto",
1447 "syntax = \"proto2\";\n"
1448 "message Bar {\n"
1449 " optional int32 a = 2;\n"
1450 " extensions 4 to 5;\n"
1451 " optional int64 d = 8;\n"
1452 " extensions 10;\n"
1453 "}\n");
1454 CreateTempFile(
1455 "baz.proto",
1456 "syntax = \"proto2\";\n"
1457 "message Baz {\n"
1458 " optional int32 a = 2;\n"
1459 " optional int64 d = 8;\n"
1460 " extensions 15 to max;\n" // unordered.
1461 " extensions 13;\n"
1462 " extensions 10 to 12;\n"
1463 " extensions 5;\n"
1464 " extensions 4;\n"
1465 "}\n");
1466 CreateTempFile(
1467 "quz.proto",
1468 "syntax = \"proto2\";\n"
1469 "message Quz {\n"
1470 " message Foo {}\n" // nested message
1471 " optional int32 a = 2;\n"
1472 " optional group C = 4 {\n"
1473 " optional int32 d = 5;\n"
1474 " }\n"
1475 " extensions 8 to 10;\n"
1476 " optional group E = 11 {\n"
1477 " optional int32 f = 9;\n" // explicitly reuse extension range 8-10
1478 " optional group G = 15 {\n" // nested group
1479 " message Foo {}\n" // nested message inside nested group
1480 " }\n"
1481 " }\n"
1482 "}\n");
1483
1484 Run("protocol_compiler --print_free_field_numbers --proto_path=$tmpdir "
jieluo@google.com8d6f04a2014-08-06 20:49:30 +00001485 "foo.proto bar.proto baz.proto quz.proto");
jieluo@google.com4de8f552014-07-18 00:47:59 +00001486
1487 ExpectNoErrors();
jieluo@google.com8d6f04a2014-08-06 20:49:30 +00001488
1489 // TODO(jieluo): Cygwin doesn't work well if we try to capture stderr and
1490 // stdout at the same time. Need to figure out why and add this test back
1491 // for Cygwin.
1492#if !defined(__CYGWIN__)
jieluo@google.com4de8f552014-07-18 00:47:59 +00001493 ExpectCapturedStdout(
1494 "foo.Foo free: 1 3 6-7 9 11-INF\n"
1495 "Bar free: 1 3 6-7 9 11-INF\n"
1496 "Baz free: 1 3 6-7 9 14\n"
1497 "Quz.Foo free: 1-INF\n"
1498 "Quz.E.G.Foo free: 1-INF\n"
1499 "Quz free: 1 3 6-7 12-14 16-INF\n");
jieluo@google.com8d6f04a2014-08-06 20:49:30 +00001500#endif
jieluo@google.com4de8f552014-07-18 00:47:59 +00001501}
1502
temporal779f61c2008-08-13 03:15:00 +00001503// ===================================================================
1504
1505// Test for --encode and --decode. Note that it would be easier to do this
1506// test as a shell script, but we'd like to be able to run the test on
1507// platforms that don't have a Bourne-compatible shell available (especially
1508// Windows/MSVC).
1509class EncodeDecodeTest : public testing::Test {
1510 protected:
1511 virtual void SetUp() {
1512 duped_stdin_ = dup(STDIN_FILENO);
1513 }
1514
1515 virtual void TearDown() {
1516 dup2(duped_stdin_, STDIN_FILENO);
1517 close(duped_stdin_);
1518 }
1519
1520 void RedirectStdinFromText(const string& input) {
1521 string filename = TestTempDir() + "/test_stdin";
jieluo@google.com4de8f552014-07-18 00:47:59 +00001522 GOOGLE_CHECK_OK(File::SetContents(filename, input, true));
temporal779f61c2008-08-13 03:15:00 +00001523 GOOGLE_CHECK(RedirectStdinFromFile(filename));
1524 }
1525
1526 bool RedirectStdinFromFile(const string& filename) {
1527 int fd = open(filename.c_str(), O_RDONLY);
1528 if (fd < 0) return false;
1529 dup2(fd, STDIN_FILENO);
1530 close(fd);
1531 return true;
1532 }
1533
1534 // Remove '\r' characters from text.
1535 string StripCR(const string& text) {
1536 string result;
1537
1538 for (int i = 0; i < text.size(); i++) {
1539 if (text[i] != '\r') {
1540 result.push_back(text[i]);
1541 }
1542 }
1543
1544 return result;
1545 }
1546
1547 enum Type { TEXT, BINARY };
1548 enum ReturnCode { SUCCESS, ERROR };
1549
1550 bool Run(const string& command) {
1551 vector<string> args;
1552 args.push_back("protoc");
1553 SplitStringUsing(command, " ", &args);
1554 args.push_back("--proto_path=" + TestSourceDir());
1555
Feng Xiaof157a562014-11-14 11:50:31 -08001556 google::protobuf::scoped_array<const char * > argv(new const char* [args.size()]);
temporal779f61c2008-08-13 03:15:00 +00001557 for (int i = 0; i < args.size(); i++) {
1558 argv[i] = args[i].c_str();
1559 }
1560
1561 CommandLineInterface cli;
1562 cli.SetInputsAreProtoPathRelative(true);
1563
1564 CaptureTestStdout();
1565 CaptureTestStderr();
1566
1567 int result = cli.Run(args.size(), argv.get());
1568
1569 captured_stdout_ = GetCapturedTestStdout();
1570 captured_stderr_ = GetCapturedTestStderr();
1571
1572 return result == 0;
1573 }
1574
1575 void ExpectStdoutMatchesBinaryFile(const string& filename) {
1576 string expected_output;
jieluo@google.com4de8f552014-07-18 00:47:59 +00001577 GOOGLE_CHECK_OK(File::GetContents(filename, &expected_output, true));
temporal779f61c2008-08-13 03:15:00 +00001578
1579 // Don't use EXPECT_EQ because we don't want to print raw binary data to
1580 // stdout on failure.
1581 EXPECT_TRUE(captured_stdout_ == expected_output);
1582 }
1583
1584 void ExpectStdoutMatchesTextFile(const string& filename) {
1585 string expected_output;
jieluo@google.com4de8f552014-07-18 00:47:59 +00001586 GOOGLE_CHECK_OK(File::GetContents(filename, &expected_output, true));
temporal779f61c2008-08-13 03:15:00 +00001587
1588 ExpectStdoutMatchesText(expected_output);
1589 }
1590
1591 void ExpectStdoutMatchesText(const string& expected_text) {
1592 EXPECT_EQ(StripCR(expected_text), StripCR(captured_stdout_));
1593 }
1594
1595 void ExpectStderrMatchesText(const string& expected_text) {
1596 EXPECT_EQ(StripCR(expected_text), StripCR(captured_stderr_));
1597 }
1598
1599 private:
1600 int duped_stdin_;
1601 string captured_stdout_;
1602 string captured_stderr_;
1603};
1604
1605TEST_F(EncodeDecodeTest, Encode) {
jieluo@google.com4de8f552014-07-18 00:47:59 +00001606 RedirectStdinFromFile(TestSourceDir() + "/google/protobuf/"
1607 "testdata/text_format_unittest_data_oneof_implemented.txt");
temporal779f61c2008-08-13 03:15:00 +00001608 EXPECT_TRUE(Run("google/protobuf/unittest.proto "
1609 "--encode=protobuf_unittest.TestAllTypes"));
1610 ExpectStdoutMatchesBinaryFile(TestSourceDir() +
jieluo@google.com4de8f552014-07-18 00:47:59 +00001611 "/google/protobuf/testdata/golden_message_oneof_implemented");
temporal779f61c2008-08-13 03:15:00 +00001612 ExpectStderrMatchesText("");
1613}
1614
1615TEST_F(EncodeDecodeTest, Decode) {
1616 RedirectStdinFromFile(TestSourceDir() +
jieluo@google.com4de8f552014-07-18 00:47:59 +00001617 "/google/protobuf/testdata/golden_message_oneof_implemented");
temporal779f61c2008-08-13 03:15:00 +00001618 EXPECT_TRUE(Run("google/protobuf/unittest.proto "
1619 "--decode=protobuf_unittest.TestAllTypes"));
1620 ExpectStdoutMatchesTextFile(TestSourceDir() +
jieluo@google.com4de8f552014-07-18 00:47:59 +00001621 "/google/protobuf/"
1622 "testdata/text_format_unittest_data_oneof_implemented.txt");
temporal779f61c2008-08-13 03:15:00 +00001623 ExpectStderrMatchesText("");
1624}
1625
1626TEST_F(EncodeDecodeTest, Partial) {
1627 RedirectStdinFromText("");
1628 EXPECT_TRUE(Run("google/protobuf/unittest.proto "
1629 "--encode=protobuf_unittest.TestRequired"));
1630 ExpectStdoutMatchesText("");
1631 ExpectStderrMatchesText(
1632 "warning: Input message is missing required fields: a, b, c\n");
1633}
1634
1635TEST_F(EncodeDecodeTest, DecodeRaw) {
1636 protobuf_unittest::TestAllTypes message;
1637 message.set_optional_int32(123);
1638 message.set_optional_string("foo");
1639 string data;
1640 message.SerializeToString(&data);
1641
1642 RedirectStdinFromText(data);
1643 EXPECT_TRUE(Run("--decode_raw"));
1644 ExpectStdoutMatchesText("1: 123\n"
1645 "14: \"foo\"\n");
1646 ExpectStderrMatchesText("");
1647}
1648
1649TEST_F(EncodeDecodeTest, UnknownType) {
1650 EXPECT_FALSE(Run("google/protobuf/unittest.proto "
1651 "--encode=NoSuchType"));
1652 ExpectStdoutMatchesText("");
1653 ExpectStderrMatchesText("Type not defined: NoSuchType\n");
1654}
1655
1656TEST_F(EncodeDecodeTest, ProtoParseError) {
1657 EXPECT_FALSE(Run("google/protobuf/no_such_file.proto "
1658 "--encode=NoSuchType"));
1659 ExpectStdoutMatchesText("");
1660 ExpectStderrMatchesText(
1661 "google/protobuf/no_such_file.proto: File not found.\n");
1662}
1663
temporal40ee5512008-07-10 02:12:20 +00001664} // anonymous namespace
1665
1666} // namespace compiler
1667} // namespace protobuf
Jisi Liu7a00a1e2015-02-21 17:28:51 -08001668
Jisi Liu885b6122015-02-28 14:51:22 -08001669#endif // !GOOGLE_PROTOBUF_HEAP_CHECK_DRACONIAN
1670} // namespace google