temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 1 | // Protocol Buffers - Google's data interchange format |
kenton@google.com | 24bf56f | 2008-09-24 20:31:01 +0000 | [diff] [blame] | 2 | // Copyright 2008 Google Inc. All rights reserved. |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 3 | // http://code.google.com/p/protobuf/ |
| 4 | // |
kenton@google.com | 24bf56f | 2008-09-24 20:31:01 +0000 | [diff] [blame] | 5 | // Redistribution and use in source and binary forms, with or without |
| 6 | // modification, are permitted provided that the following conditions are |
| 7 | // met: |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 8 | // |
kenton@google.com | 24bf56f | 2008-09-24 20:31:01 +0000 | [diff] [blame] | 9 | // * Redistributions of source code must retain the above copyright |
| 10 | // notice, this list of conditions and the following disclaimer. |
| 11 | // * Redistributions in binary form must reproduce the above |
| 12 | // copyright notice, this list of conditions and the following disclaimer |
| 13 | // in the documentation and/or other materials provided with the |
| 14 | // distribution. |
| 15 | // * Neither the name of Google Inc. nor the names of its |
| 16 | // contributors may be used to endorse or promote products derived from |
| 17 | // this software without specific prior written permission. |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 18 | // |
kenton@google.com | 24bf56f | 2008-09-24 20:31:01 +0000 | [diff] [blame] | 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 30 | |
| 31 | // Author: kenton@google.com (Kenton Varda) |
| 32 | // Based on original Protocol Buffers design by |
| 33 | // Sanjay Ghemawat, Jeff Dean, and others. |
| 34 | |
temporal | 779f61c | 2008-08-13 03:15:00 +0000 | [diff] [blame] | 35 | #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 |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 43 | #include <vector> |
| 44 | |
temporal | 779f61c | 2008-08-13 03:15:00 +0000 | [diff] [blame] | 45 | #include <google/protobuf/descriptor.pb.h> |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 46 | #include <google/protobuf/descriptor.h> |
| 47 | #include <google/protobuf/io/zero_copy_stream.h> |
| 48 | #include <google/protobuf/compiler/command_line_interface.h> |
| 49 | #include <google/protobuf/compiler/code_generator.h> |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 50 | #include <google/protobuf/compiler/mock_code_generator.h> |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 51 | #include <google/protobuf/io/printer.h> |
temporal | 779f61c | 2008-08-13 03:15:00 +0000 | [diff] [blame] | 52 | #include <google/protobuf/unittest.pb.h> |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 53 | #include <google/protobuf/testing/file.h> |
| 54 | #include <google/protobuf/stubs/strutil.h> |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 55 | #include <google/protobuf/stubs/substitute.h> |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 56 | |
| 57 | #include <google/protobuf/testing/googletest.h> |
| 58 | #include <gtest/gtest.h> |
| 59 | |
| 60 | namespace google { |
| 61 | namespace protobuf { |
| 62 | namespace compiler { |
| 63 | |
temporal | 779f61c | 2008-08-13 03:15:00 +0000 | [diff] [blame] | 64 | #if defined(_WIN32) |
| 65 | #ifndef STDIN_FILENO |
| 66 | #define STDIN_FILENO 0 |
| 67 | #endif |
| 68 | #ifndef STDOUT_FILENO |
| 69 | #define STDOUT_FILENO 1 |
| 70 | #endif |
kenton@google.com | c0ee4d2 | 2009-12-22 02:05:33 +0000 | [diff] [blame] | 71 | #ifndef F_OK |
| 72 | #define F_OK 00 // not defined by MSVC for whatever reason |
| 73 | #endif |
temporal | 779f61c | 2008-08-13 03:15:00 +0000 | [diff] [blame] | 74 | #endif |
| 75 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 76 | namespace { |
| 77 | |
| 78 | class CommandLineInterfaceTest : public testing::Test { |
| 79 | protected: |
| 80 | virtual void SetUp(); |
| 81 | virtual void TearDown(); |
| 82 | |
| 83 | // Runs the CommandLineInterface with the given command line. The |
| 84 | // command is automatically split on spaces, and the string "$tmpdir" |
| 85 | // is replaced with TestTempDir(). |
| 86 | void Run(const string& command); |
| 87 | |
| 88 | // ----------------------------------------------------------------- |
| 89 | // Methods to set up the test (called before Run()). |
| 90 | |
temporal | cc93043 | 2008-07-21 20:28:30 +0000 | [diff] [blame] | 91 | class NullCodeGenerator; |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 92 | |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 93 | // Normally plugins are allowed for all tests. Call this to explicitly |
| 94 | // disable them. |
| 95 | void DisallowPlugins() { disallow_plugins_ = true; } |
temporal | cc93043 | 2008-07-21 20:28:30 +0000 | [diff] [blame] | 96 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 97 | // Create a temp file within temp_directory_ with the given name. |
| 98 | // The containing directory is also created if necessary. |
| 99 | void CreateTempFile(const string& name, const string& contents); |
| 100 | |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 101 | // Create a subdirectory within temp_directory_. |
| 102 | void CreateTempDir(const string& name); |
| 103 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 104 | void SetInputsAreProtoPathRelative(bool enable) { |
| 105 | cli_.SetInputsAreProtoPathRelative(enable); |
| 106 | } |
| 107 | |
| 108 | // ----------------------------------------------------------------- |
| 109 | // Methods to check the test results (called after Run()). |
| 110 | |
| 111 | // Checks that no text was written to stderr during Run(), and Run() |
| 112 | // returned 0. |
| 113 | void ExpectNoErrors(); |
| 114 | |
| 115 | // Checks that Run() returned non-zero and the stderr output is exactly |
| 116 | // the text given. expected_test may contain references to "$tmpdir", |
| 117 | // which will be replaced by the temporary directory path. |
| 118 | void ExpectErrorText(const string& expected_text); |
| 119 | |
| 120 | // Checks that Run() returned non-zero and the stderr contains the given |
| 121 | // substring. |
| 122 | void ExpectErrorSubstring(const string& expected_substring); |
| 123 | |
| 124 | // Returns true if ExpectErrorSubstring(expected_substring) would pass, but |
| 125 | // does not fail otherwise. |
| 126 | bool HasAlternateErrorSubstring(const string& expected_substring); |
| 127 | |
| 128 | // Checks that MockCodeGenerator::Generate() was called in the given |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 129 | // context (or the generator in test_plugin.cc, which produces the same |
| 130 | // output). That is, this tests if the generator with the given name |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 131 | // was called with the given parameter and proto file and produced the |
| 132 | // given output file. This is checked by reading the output file and |
| 133 | // checking that it contains the content that MockCodeGenerator would |
| 134 | // generate given these inputs. message_name is the name of the first |
| 135 | // message that appeared in the proto file; this is just to make extra |
| 136 | // sure that the correct file was parsed. |
| 137 | void ExpectGenerated(const string& generator_name, |
| 138 | const string& parameter, |
| 139 | const string& proto_name, |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 140 | const string& message_name); |
| 141 | void ExpectGenerated(const string& generator_name, |
| 142 | const string& parameter, |
| 143 | const string& proto_name, |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 144 | const string& message_name, |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 145 | const string& output_directory); |
liujisi@google.com | 33165fe | 2010-11-02 13:14:58 +0000 | [diff] [blame^] | 146 | void ExpectGeneratedWithMultipleInputs(const string& generator_name, |
| 147 | const string& all_proto_names, |
| 148 | const string& proto_name, |
| 149 | const string& message_name); |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 150 | void ExpectGeneratedWithInsertions(const string& generator_name, |
| 151 | const string& parameter, |
| 152 | const string& insertions, |
| 153 | const string& proto_name, |
| 154 | const string& message_name); |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 155 | |
kenton@google.com | 91218af | 2009-12-18 07:20:43 +0000 | [diff] [blame] | 156 | void ExpectNullCodeGeneratorCalled(const string& parameter); |
| 157 | |
temporal | 779f61c | 2008-08-13 03:15:00 +0000 | [diff] [blame] | 158 | void ReadDescriptorSet(const string& filename, |
| 159 | FileDescriptorSet* descriptor_set); |
| 160 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 161 | private: |
| 162 | // The object we are testing. |
| 163 | CommandLineInterface cli_; |
| 164 | |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 165 | // Was DisallowPlugins() called? |
| 166 | bool disallow_plugins_; |
| 167 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 168 | // We create a directory within TestTempDir() in order to add extra |
| 169 | // protection against accidentally deleting user files (since we recursively |
| 170 | // delete this directory during the test). This is the full path of that |
| 171 | // directory. |
| 172 | string temp_directory_; |
| 173 | |
| 174 | // The result of Run(). |
| 175 | int return_code_; |
| 176 | |
| 177 | // The captured stderr output. |
| 178 | string error_text_; |
| 179 | |
| 180 | // Pointers which need to be deleted later. |
temporal | cc93043 | 2008-07-21 20:28:30 +0000 | [diff] [blame] | 181 | vector<CodeGenerator*> mock_generators_to_delete_; |
kenton@google.com | 91218af | 2009-12-18 07:20:43 +0000 | [diff] [blame] | 182 | |
| 183 | NullCodeGenerator* null_generator_; |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 184 | }; |
| 185 | |
temporal | cc93043 | 2008-07-21 20:28:30 +0000 | [diff] [blame] | 186 | class CommandLineInterfaceTest::NullCodeGenerator : public CodeGenerator { |
| 187 | public: |
| 188 | NullCodeGenerator() : called_(false) {} |
| 189 | ~NullCodeGenerator() {} |
| 190 | |
| 191 | mutable bool called_; |
| 192 | mutable string parameter_; |
| 193 | |
| 194 | // implements CodeGenerator ---------------------------------------- |
| 195 | bool Generate(const FileDescriptor* file, |
| 196 | const string& parameter, |
liujisi@google.com | 33165fe | 2010-11-02 13:14:58 +0000 | [diff] [blame^] | 197 | GeneratorContext* context, |
temporal | cc93043 | 2008-07-21 20:28:30 +0000 | [diff] [blame] | 198 | string* error) const { |
| 199 | called_ = true; |
| 200 | parameter_ = parameter; |
| 201 | return true; |
| 202 | } |
| 203 | }; |
| 204 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 205 | // =================================================================== |
| 206 | |
| 207 | void CommandLineInterfaceTest::SetUp() { |
| 208 | // Most of these tests were written before this option was added, so we |
| 209 | // run with the option on (which used to be the only way) except in certain |
| 210 | // tests where we turn it off. |
| 211 | cli_.SetInputsAreProtoPathRelative(true); |
| 212 | |
| 213 | temp_directory_ = TestTempDir() + "/proto2_cli_test_temp"; |
| 214 | |
| 215 | // If the temp directory already exists, it must be left over from a |
| 216 | // previous run. Delete it. |
| 217 | if (File::Exists(temp_directory_)) { |
| 218 | File::DeleteRecursively(temp_directory_, NULL, NULL); |
| 219 | } |
| 220 | |
| 221 | // Create the temp directory. |
| 222 | GOOGLE_CHECK(File::CreateDir(temp_directory_.c_str(), DEFAULT_FILE_MODE)); |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 223 | |
| 224 | // Register generators. |
| 225 | CodeGenerator* generator = new MockCodeGenerator("test_generator"); |
| 226 | mock_generators_to_delete_.push_back(generator); |
| 227 | cli_.RegisterGenerator("--test_out", generator, "Test output."); |
| 228 | cli_.RegisterGenerator("-t", generator, "Test output."); |
| 229 | |
| 230 | generator = new MockCodeGenerator("alt_generator"); |
| 231 | mock_generators_to_delete_.push_back(generator); |
| 232 | cli_.RegisterGenerator("--alt_out", generator, "Alt output."); |
| 233 | |
kenton@google.com | 91218af | 2009-12-18 07:20:43 +0000 | [diff] [blame] | 234 | generator = null_generator_ = new NullCodeGenerator(); |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 235 | mock_generators_to_delete_.push_back(generator); |
| 236 | cli_.RegisterGenerator("--null_out", generator, "Null output."); |
| 237 | |
| 238 | disallow_plugins_ = false; |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 239 | } |
| 240 | |
| 241 | void CommandLineInterfaceTest::TearDown() { |
| 242 | // Delete the temp directory. |
| 243 | File::DeleteRecursively(temp_directory_, NULL, NULL); |
| 244 | |
| 245 | // Delete all the MockCodeGenerators. |
| 246 | for (int i = 0; i < mock_generators_to_delete_.size(); i++) { |
| 247 | delete mock_generators_to_delete_[i]; |
| 248 | } |
| 249 | mock_generators_to_delete_.clear(); |
| 250 | } |
| 251 | |
| 252 | void CommandLineInterfaceTest::Run(const string& command) { |
| 253 | vector<string> args; |
| 254 | SplitStringUsing(command, " ", &args); |
| 255 | |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 256 | if (!disallow_plugins_) { |
| 257 | cli_.AllowPlugins("prefix-"); |
kenton@google.com | c0ee4d2 | 2009-12-22 02:05:33 +0000 | [diff] [blame] | 258 | const char* possible_paths[] = { |
| 259 | // When building with shared libraries, libtool hides the real executable |
| 260 | // in .libs and puts a fake wrapper in the current directory. |
| 261 | // Unfortunately, due to an apparent bug on Cygwin/MinGW, if one program |
| 262 | // wrapped in this way (e.g. protobuf-tests.exe) tries to execute another |
| 263 | // program wrapped in this way (e.g. test_plugin.exe), the latter fails |
| 264 | // with error code 127 and no explanation message. Presumably the problem |
| 265 | // is that the wrapper for protobuf-tests.exe set some environment |
| 266 | // variables that confuse the wrapper for test_plugin.exe. Luckily, it |
| 267 | // turns out that if we simply invoke the wrapped test_plugin.exe |
| 268 | // directly, it works -- I guess the environment variables set by the |
| 269 | // protobuf-tests.exe wrapper happen to be correct for it too. So we do |
| 270 | // that. |
| 271 | ".libs/test_plugin.exe", // Win32 w/autotool (Cygwin / MinGW) |
| 272 | "test_plugin.exe", // Other Win32 (MSVC) |
| 273 | "test_plugin", // Unix |
| 274 | }; |
| 275 | |
| 276 | string plugin_path; |
| 277 | |
| 278 | for (int i = 0; i < GOOGLE_ARRAYSIZE(possible_paths); i++) { |
| 279 | if (access(possible_paths[i], F_OK) == 0) { |
| 280 | plugin_path = possible_paths[i]; |
| 281 | break; |
| 282 | } |
| 283 | } |
| 284 | |
| 285 | if (plugin_path.empty()) { |
| 286 | GOOGLE_LOG(ERROR) |
| 287 | << "Plugin executable not found. Plugin tests are likely to fail."; |
| 288 | } else { |
| 289 | args.push_back("--plugin=prefix-gen-plug=" + plugin_path); |
| 290 | } |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 291 | } |
| 292 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 293 | scoped_array<const char*> argv(new const char*[args.size()]); |
| 294 | |
| 295 | for (int i = 0; i < args.size(); i++) { |
| 296 | args[i] = StringReplace(args[i], "$tmpdir", temp_directory_, true); |
| 297 | argv[i] = args[i].c_str(); |
| 298 | } |
| 299 | |
| 300 | CaptureTestStderr(); |
| 301 | |
| 302 | return_code_ = cli_.Run(args.size(), argv.get()); |
| 303 | |
| 304 | error_text_ = GetCapturedTestStderr(); |
| 305 | } |
| 306 | |
| 307 | // ------------------------------------------------------------------- |
| 308 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 309 | void CommandLineInterfaceTest::CreateTempFile( |
| 310 | const string& name, |
| 311 | const string& contents) { |
| 312 | // Create parent directory, if necessary. |
| 313 | string::size_type slash_pos = name.find_last_of('/'); |
| 314 | if (slash_pos != string::npos) { |
| 315 | string dir = name.substr(0, slash_pos); |
| 316 | File::RecursivelyCreateDir(temp_directory_ + "/" + dir, 0777); |
| 317 | } |
| 318 | |
| 319 | // Write file. |
| 320 | string full_name = temp_directory_ + "/" + name; |
| 321 | File::WriteStringToFileOrDie(contents, full_name); |
| 322 | } |
| 323 | |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 324 | void CommandLineInterfaceTest::CreateTempDir(const string& name) { |
| 325 | File::RecursivelyCreateDir(temp_directory_ + "/" + name, 0777); |
| 326 | } |
| 327 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 328 | // ------------------------------------------------------------------- |
| 329 | |
| 330 | void CommandLineInterfaceTest::ExpectNoErrors() { |
| 331 | EXPECT_EQ(0, return_code_); |
| 332 | EXPECT_EQ("", error_text_); |
| 333 | } |
| 334 | |
| 335 | void CommandLineInterfaceTest::ExpectErrorText(const string& expected_text) { |
| 336 | EXPECT_NE(0, return_code_); |
| 337 | EXPECT_EQ(StringReplace(expected_text, "$tmpdir", temp_directory_, true), |
| 338 | error_text_); |
| 339 | } |
| 340 | |
| 341 | void CommandLineInterfaceTest::ExpectErrorSubstring( |
| 342 | const string& expected_substring) { |
| 343 | EXPECT_NE(0, return_code_); |
| 344 | EXPECT_PRED_FORMAT2(testing::IsSubstring, expected_substring, error_text_); |
| 345 | } |
| 346 | |
| 347 | bool CommandLineInterfaceTest::HasAlternateErrorSubstring( |
| 348 | const string& expected_substring) { |
| 349 | EXPECT_NE(0, return_code_); |
| 350 | return error_text_.find(expected_substring) != string::npos; |
| 351 | } |
| 352 | |
| 353 | void CommandLineInterfaceTest::ExpectGenerated( |
| 354 | const string& generator_name, |
| 355 | const string& parameter, |
| 356 | const string& proto_name, |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 357 | const string& message_name) { |
| 358 | MockCodeGenerator::ExpectGenerated( |
liujisi@google.com | 33165fe | 2010-11-02 13:14:58 +0000 | [diff] [blame^] | 359 | generator_name, parameter, "", proto_name, message_name, proto_name, |
| 360 | temp_directory_); |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 361 | } |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 362 | |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 363 | void CommandLineInterfaceTest::ExpectGenerated( |
| 364 | const string& generator_name, |
| 365 | const string& parameter, |
| 366 | const string& proto_name, |
| 367 | const string& message_name, |
| 368 | const string& output_directory) { |
| 369 | MockCodeGenerator::ExpectGenerated( |
liujisi@google.com | 33165fe | 2010-11-02 13:14:58 +0000 | [diff] [blame^] | 370 | generator_name, parameter, "", proto_name, message_name, proto_name, |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 371 | temp_directory_ + "/" + output_directory); |
| 372 | } |
| 373 | |
liujisi@google.com | 33165fe | 2010-11-02 13:14:58 +0000 | [diff] [blame^] | 374 | void CommandLineInterfaceTest::ExpectGeneratedWithMultipleInputs( |
| 375 | const string& generator_name, |
| 376 | const string& all_proto_names, |
| 377 | const string& proto_name, |
| 378 | const string& message_name) { |
| 379 | MockCodeGenerator::ExpectGenerated( |
| 380 | generator_name, "", "", proto_name, message_name, |
| 381 | all_proto_names, |
| 382 | temp_directory_); |
| 383 | } |
| 384 | |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 385 | void CommandLineInterfaceTest::ExpectGeneratedWithInsertions( |
| 386 | const string& generator_name, |
| 387 | const string& parameter, |
| 388 | const string& insertions, |
| 389 | const string& proto_name, |
| 390 | const string& message_name) { |
| 391 | MockCodeGenerator::ExpectGenerated( |
| 392 | generator_name, parameter, insertions, proto_name, message_name, |
liujisi@google.com | 33165fe | 2010-11-02 13:14:58 +0000 | [diff] [blame^] | 393 | proto_name, temp_directory_); |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 394 | } |
| 395 | |
kenton@google.com | 91218af | 2009-12-18 07:20:43 +0000 | [diff] [blame] | 396 | void CommandLineInterfaceTest::ExpectNullCodeGeneratorCalled( |
| 397 | const string& parameter) { |
| 398 | EXPECT_TRUE(null_generator_->called_); |
| 399 | EXPECT_EQ(parameter, null_generator_->parameter_); |
| 400 | } |
| 401 | |
temporal | 779f61c | 2008-08-13 03:15:00 +0000 | [diff] [blame] | 402 | void CommandLineInterfaceTest::ReadDescriptorSet( |
| 403 | const string& filename, FileDescriptorSet* descriptor_set) { |
| 404 | string path = temp_directory_ + "/" + filename; |
| 405 | string file_contents; |
| 406 | if (!File::ReadFileToString(path, &file_contents)) { |
| 407 | FAIL() << "File not found: " << path; |
| 408 | } |
| 409 | if (!descriptor_set->ParseFromString(file_contents)) { |
| 410 | FAIL() << "Could not parse file contents: " << path; |
| 411 | } |
| 412 | } |
| 413 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 414 | // =================================================================== |
| 415 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 416 | TEST_F(CommandLineInterfaceTest, BasicOutput) { |
| 417 | // Test that the common case works. |
| 418 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 419 | CreateTempFile("foo.proto", |
| 420 | "syntax = \"proto2\";\n" |
| 421 | "message Foo {}\n"); |
| 422 | |
| 423 | Run("protocol_compiler --test_out=$tmpdir " |
| 424 | "--proto_path=$tmpdir foo.proto"); |
| 425 | |
| 426 | ExpectNoErrors(); |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 427 | ExpectGenerated("test_generator", "", "foo.proto", "Foo"); |
| 428 | } |
| 429 | |
| 430 | TEST_F(CommandLineInterfaceTest, BasicPlugin) { |
| 431 | // Test that basic plugins work. |
| 432 | |
| 433 | CreateTempFile("foo.proto", |
| 434 | "syntax = \"proto2\";\n" |
| 435 | "message Foo {}\n"); |
| 436 | |
| 437 | Run("protocol_compiler --plug_out=$tmpdir " |
| 438 | "--proto_path=$tmpdir foo.proto"); |
| 439 | |
| 440 | ExpectNoErrors(); |
| 441 | ExpectGenerated("test_plugin", "", "foo.proto", "Foo"); |
| 442 | } |
| 443 | |
| 444 | TEST_F(CommandLineInterfaceTest, GeneratorAndPlugin) { |
| 445 | // Invoke a generator and a plugin at the same time. |
| 446 | |
| 447 | CreateTempFile("foo.proto", |
| 448 | "syntax = \"proto2\";\n" |
| 449 | "message Foo {}\n"); |
| 450 | |
| 451 | Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir " |
| 452 | "--proto_path=$tmpdir foo.proto"); |
| 453 | |
| 454 | ExpectNoErrors(); |
| 455 | ExpectGenerated("test_generator", "", "foo.proto", "Foo"); |
| 456 | ExpectGenerated("test_plugin", "", "foo.proto", "Foo"); |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 457 | } |
| 458 | |
| 459 | TEST_F(CommandLineInterfaceTest, MultipleInputs) { |
| 460 | // Test parsing multiple input files. |
| 461 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 462 | CreateTempFile("foo.proto", |
| 463 | "syntax = \"proto2\";\n" |
| 464 | "message Foo {}\n"); |
| 465 | CreateTempFile("bar.proto", |
| 466 | "syntax = \"proto2\";\n" |
| 467 | "message Bar {}\n"); |
| 468 | |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 469 | Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir " |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 470 | "--proto_path=$tmpdir foo.proto bar.proto"); |
| 471 | |
| 472 | ExpectNoErrors(); |
liujisi@google.com | 33165fe | 2010-11-02 13:14:58 +0000 | [diff] [blame^] | 473 | ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto", |
| 474 | "foo.proto", "Foo"); |
| 475 | ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto", |
| 476 | "bar.proto", "Bar"); |
| 477 | ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto", |
| 478 | "foo.proto", "Foo"); |
| 479 | ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto", |
| 480 | "bar.proto", "Bar"); |
| 481 | } |
| 482 | |
| 483 | TEST_F(CommandLineInterfaceTest, MultipleInputsWithImport) { |
| 484 | // Test parsing multiple input files with an import of a separate file. |
| 485 | |
| 486 | CreateTempFile("foo.proto", |
| 487 | "syntax = \"proto2\";\n" |
| 488 | "message Foo {}\n"); |
| 489 | CreateTempFile("bar.proto", |
| 490 | "syntax = \"proto2\";\n" |
| 491 | "import \"baz.proto\";\n" |
| 492 | "message Bar {\n" |
| 493 | " optional Baz a = 1;\n" |
| 494 | "}\n"); |
| 495 | CreateTempFile("baz.proto", |
| 496 | "syntax = \"proto2\";\n" |
| 497 | "message Baz {}\n"); |
| 498 | |
| 499 | Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir " |
| 500 | "--proto_path=$tmpdir foo.proto bar.proto"); |
| 501 | |
| 502 | ExpectNoErrors(); |
| 503 | ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto", |
| 504 | "foo.proto", "Foo"); |
| 505 | ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto", |
| 506 | "bar.proto", "Bar"); |
| 507 | ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto", |
| 508 | "foo.proto", "Foo"); |
| 509 | ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto", |
| 510 | "bar.proto", "Bar"); |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 511 | } |
| 512 | |
| 513 | TEST_F(CommandLineInterfaceTest, CreateDirectory) { |
| 514 | // Test that when we output to a sub-directory, it is created. |
| 515 | |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 516 | CreateTempFile("bar/baz/foo.proto", |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 517 | "syntax = \"proto2\";\n" |
| 518 | "message Foo {}\n"); |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 519 | CreateTempDir("out"); |
| 520 | CreateTempDir("plugout"); |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 521 | |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 522 | Run("protocol_compiler --test_out=$tmpdir/out --plug_out=$tmpdir/plugout " |
| 523 | "--proto_path=$tmpdir bar/baz/foo.proto"); |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 524 | |
| 525 | ExpectNoErrors(); |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 526 | ExpectGenerated("test_generator", "", "bar/baz/foo.proto", "Foo", "out"); |
| 527 | ExpectGenerated("test_plugin", "", "bar/baz/foo.proto", "Foo", "plugout"); |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 528 | } |
| 529 | |
| 530 | TEST_F(CommandLineInterfaceTest, GeneratorParameters) { |
| 531 | // Test that generator parameters are correctly parsed from the command line. |
| 532 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 533 | CreateTempFile("foo.proto", |
| 534 | "syntax = \"proto2\";\n" |
| 535 | "message Foo {}\n"); |
| 536 | |
| 537 | Run("protocol_compiler --test_out=TestParameter:$tmpdir " |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 538 | "--plug_out=TestPluginParameter:$tmpdir " |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 539 | "--proto_path=$tmpdir foo.proto"); |
| 540 | |
| 541 | ExpectNoErrors(); |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 542 | ExpectGenerated("test_generator", "TestParameter", "foo.proto", "Foo"); |
| 543 | ExpectGenerated("test_plugin", "TestPluginParameter", "foo.proto", "Foo"); |
| 544 | } |
| 545 | |
| 546 | TEST_F(CommandLineInterfaceTest, Insert) { |
| 547 | // Test running a generator that inserts code into another's output. |
| 548 | |
| 549 | CreateTempFile("foo.proto", |
| 550 | "syntax = \"proto2\";\n" |
| 551 | "message Foo {}\n"); |
| 552 | |
| 553 | Run("protocol_compiler " |
| 554 | "--test_out=TestParameter:$tmpdir " |
| 555 | "--plug_out=TestPluginParameter:$tmpdir " |
| 556 | "--test_out=insert=test_generator,test_plugin:$tmpdir " |
| 557 | "--plug_out=insert=test_generator,test_plugin:$tmpdir " |
| 558 | "--proto_path=$tmpdir foo.proto"); |
| 559 | |
| 560 | ExpectNoErrors(); |
| 561 | ExpectGeneratedWithInsertions( |
| 562 | "test_generator", "TestParameter", "test_generator,test_plugin", |
| 563 | "foo.proto", "Foo"); |
| 564 | ExpectGeneratedWithInsertions( |
| 565 | "test_plugin", "TestPluginParameter", "test_generator,test_plugin", |
| 566 | "foo.proto", "Foo"); |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 567 | } |
| 568 | |
temporal | cc93043 | 2008-07-21 20:28:30 +0000 | [diff] [blame] | 569 | #if defined(_WIN32) || defined(__CYGWIN__) |
| 570 | |
| 571 | TEST_F(CommandLineInterfaceTest, WindowsOutputPath) { |
| 572 | // Test that the output path can be a Windows-style path. |
| 573 | |
temporal | cc93043 | 2008-07-21 20:28:30 +0000 | [diff] [blame] | 574 | CreateTempFile("foo.proto", |
| 575 | "syntax = \"proto2\";\n"); |
| 576 | |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 577 | Run("protocol_compiler --null_out=C:\\ " |
temporal | cc93043 | 2008-07-21 20:28:30 +0000 | [diff] [blame] | 578 | "--proto_path=$tmpdir foo.proto"); |
| 579 | |
| 580 | ExpectNoErrors(); |
kenton@google.com | 91218af | 2009-12-18 07:20:43 +0000 | [diff] [blame] | 581 | ExpectNullCodeGeneratorCalled(""); |
temporal | cc93043 | 2008-07-21 20:28:30 +0000 | [diff] [blame] | 582 | } |
| 583 | |
| 584 | TEST_F(CommandLineInterfaceTest, WindowsOutputPathAndParameter) { |
| 585 | // Test that we can have a windows-style output path and a parameter. |
| 586 | |
temporal | cc93043 | 2008-07-21 20:28:30 +0000 | [diff] [blame] | 587 | CreateTempFile("foo.proto", |
| 588 | "syntax = \"proto2\";\n"); |
| 589 | |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 590 | Run("protocol_compiler --null_out=bar:C:\\ " |
temporal | cc93043 | 2008-07-21 20:28:30 +0000 | [diff] [blame] | 591 | "--proto_path=$tmpdir foo.proto"); |
| 592 | |
| 593 | ExpectNoErrors(); |
kenton@google.com | 91218af | 2009-12-18 07:20:43 +0000 | [diff] [blame] | 594 | ExpectNullCodeGeneratorCalled("bar"); |
temporal | cc93043 | 2008-07-21 20:28:30 +0000 | [diff] [blame] | 595 | } |
| 596 | |
kenton@google.com | ef3730c | 2009-04-28 00:49:36 +0000 | [diff] [blame] | 597 | TEST_F(CommandLineInterfaceTest, TrailingBackslash) { |
| 598 | // Test that the directories can end in backslashes. Some users claim this |
| 599 | // doesn't work on their system. |
| 600 | |
kenton@google.com | ef3730c | 2009-04-28 00:49:36 +0000 | [diff] [blame] | 601 | CreateTempFile("foo.proto", |
| 602 | "syntax = \"proto2\";\n" |
| 603 | "message Foo {}\n"); |
| 604 | |
| 605 | Run("protocol_compiler --test_out=$tmpdir\\ " |
| 606 | "--proto_path=$tmpdir\\ foo.proto"); |
| 607 | |
| 608 | ExpectNoErrors(); |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 609 | ExpectGenerated("test_generator", "", "foo.proto", "Foo"); |
kenton@google.com | ef3730c | 2009-04-28 00:49:36 +0000 | [diff] [blame] | 610 | } |
| 611 | |
temporal | cc93043 | 2008-07-21 20:28:30 +0000 | [diff] [blame] | 612 | #endif // defined(_WIN32) || defined(__CYGWIN__) |
| 613 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 614 | TEST_F(CommandLineInterfaceTest, PathLookup) { |
| 615 | // Test that specifying multiple directories in the proto search path works. |
| 616 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 617 | CreateTempFile("b/bar.proto", |
| 618 | "syntax = \"proto2\";\n" |
| 619 | "message Bar {}\n"); |
| 620 | CreateTempFile("a/foo.proto", |
| 621 | "syntax = \"proto2\";\n" |
| 622 | "import \"bar.proto\";\n" |
| 623 | "message Foo {\n" |
| 624 | " optional Bar a = 1;\n" |
| 625 | "}\n"); |
| 626 | CreateTempFile("b/foo.proto", "this should not be parsed\n"); |
| 627 | |
| 628 | Run("protocol_compiler --test_out=$tmpdir " |
| 629 | "--proto_path=$tmpdir/a --proto_path=$tmpdir/b foo.proto"); |
| 630 | |
| 631 | ExpectNoErrors(); |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 632 | ExpectGenerated("test_generator", "", "foo.proto", "Foo"); |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 633 | } |
| 634 | |
| 635 | TEST_F(CommandLineInterfaceTest, ColonDelimitedPath) { |
| 636 | // Same as PathLookup, but we provide the proto_path in a single flag. |
| 637 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 638 | CreateTempFile("b/bar.proto", |
| 639 | "syntax = \"proto2\";\n" |
| 640 | "message Bar {}\n"); |
| 641 | CreateTempFile("a/foo.proto", |
| 642 | "syntax = \"proto2\";\n" |
| 643 | "import \"bar.proto\";\n" |
| 644 | "message Foo {\n" |
| 645 | " optional Bar a = 1;\n" |
| 646 | "}\n"); |
| 647 | CreateTempFile("b/foo.proto", "this should not be parsed\n"); |
| 648 | |
| 649 | #undef PATH_SEPARATOR |
| 650 | #if defined(_WIN32) |
| 651 | #define PATH_SEPARATOR ";" |
| 652 | #else |
| 653 | #define PATH_SEPARATOR ":" |
| 654 | #endif |
| 655 | |
| 656 | Run("protocol_compiler --test_out=$tmpdir " |
| 657 | "--proto_path=$tmpdir/a"PATH_SEPARATOR"$tmpdir/b foo.proto"); |
| 658 | |
| 659 | #undef PATH_SEPARATOR |
| 660 | |
| 661 | ExpectNoErrors(); |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 662 | ExpectGenerated("test_generator", "", "foo.proto", "Foo"); |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 663 | } |
| 664 | |
| 665 | TEST_F(CommandLineInterfaceTest, NonRootMapping) { |
| 666 | // Test setting up a search path mapping a directory to a non-root location. |
| 667 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 668 | CreateTempFile("foo.proto", |
| 669 | "syntax = \"proto2\";\n" |
| 670 | "message Foo {}\n"); |
| 671 | |
| 672 | Run("protocol_compiler --test_out=$tmpdir " |
| 673 | "--proto_path=bar=$tmpdir bar/foo.proto"); |
| 674 | |
| 675 | ExpectNoErrors(); |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 676 | ExpectGenerated("test_generator", "", "bar/foo.proto", "Foo"); |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 677 | } |
| 678 | |
| 679 | TEST_F(CommandLineInterfaceTest, MultipleGenerators) { |
| 680 | // Test that we can have multiple generators and use both in one invocation, |
| 681 | // each with a different output directory. |
| 682 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 683 | CreateTempFile("foo.proto", |
| 684 | "syntax = \"proto2\";\n" |
| 685 | "message Foo {}\n"); |
| 686 | // Create the "a" and "b" sub-directories. |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 687 | CreateTempDir("a"); |
| 688 | CreateTempDir("b"); |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 689 | |
| 690 | Run("protocol_compiler " |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 691 | "--test_out=$tmpdir/a " |
| 692 | "--alt_out=$tmpdir/b " |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 693 | "--proto_path=$tmpdir foo.proto"); |
| 694 | |
| 695 | ExpectNoErrors(); |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 696 | ExpectGenerated("test_generator", "", "foo.proto", "Foo", "a"); |
| 697 | ExpectGenerated("alt_generator", "", "foo.proto", "Foo", "b"); |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 698 | } |
| 699 | |
| 700 | TEST_F(CommandLineInterfaceTest, DisallowServicesNoServices) { |
| 701 | // Test that --disallow_services doesn't cause a problem when there are no |
| 702 | // services. |
| 703 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 704 | CreateTempFile("foo.proto", |
| 705 | "syntax = \"proto2\";\n" |
| 706 | "message Foo {}\n"); |
| 707 | |
| 708 | Run("protocol_compiler --disallow_services --test_out=$tmpdir " |
| 709 | "--proto_path=$tmpdir foo.proto"); |
| 710 | |
| 711 | ExpectNoErrors(); |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 712 | ExpectGenerated("test_generator", "", "foo.proto", "Foo"); |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 713 | } |
| 714 | |
| 715 | TEST_F(CommandLineInterfaceTest, DisallowServicesHasService) { |
| 716 | // Test that --disallow_services produces an error when there are services. |
| 717 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 718 | CreateTempFile("foo.proto", |
| 719 | "syntax = \"proto2\";\n" |
| 720 | "message Foo {}\n" |
| 721 | "service Bar {}\n"); |
| 722 | |
| 723 | Run("protocol_compiler --disallow_services --test_out=$tmpdir " |
| 724 | "--proto_path=$tmpdir foo.proto"); |
| 725 | |
| 726 | ExpectErrorSubstring("foo.proto: This file contains services"); |
| 727 | } |
| 728 | |
| 729 | TEST_F(CommandLineInterfaceTest, AllowServicesHasService) { |
| 730 | // Test that services work fine as long as --disallow_services is not used. |
| 731 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 732 | CreateTempFile("foo.proto", |
| 733 | "syntax = \"proto2\";\n" |
| 734 | "message Foo {}\n" |
| 735 | "service Bar {}\n"); |
| 736 | |
| 737 | Run("protocol_compiler --test_out=$tmpdir " |
| 738 | "--proto_path=$tmpdir foo.proto"); |
| 739 | |
| 740 | ExpectNoErrors(); |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 741 | ExpectGenerated("test_generator", "", "foo.proto", "Foo"); |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 742 | } |
| 743 | |
| 744 | TEST_F(CommandLineInterfaceTest, CwdRelativeInputs) { |
| 745 | // Test that we can accept working-directory-relative input files. |
| 746 | |
| 747 | SetInputsAreProtoPathRelative(false); |
| 748 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 749 | CreateTempFile("foo.proto", |
| 750 | "syntax = \"proto2\";\n" |
| 751 | "message Foo {}\n"); |
| 752 | |
| 753 | Run("protocol_compiler --test_out=$tmpdir " |
| 754 | "--proto_path=$tmpdir $tmpdir/foo.proto"); |
| 755 | |
| 756 | ExpectNoErrors(); |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 757 | ExpectGenerated("test_generator", "", "foo.proto", "Foo"); |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 758 | } |
| 759 | |
temporal | 779f61c | 2008-08-13 03:15:00 +0000 | [diff] [blame] | 760 | TEST_F(CommandLineInterfaceTest, WriteDescriptorSet) { |
| 761 | CreateTempFile("foo.proto", |
| 762 | "syntax = \"proto2\";\n" |
| 763 | "message Foo {}\n"); |
| 764 | CreateTempFile("bar.proto", |
| 765 | "syntax = \"proto2\";\n" |
| 766 | "import \"foo.proto\";\n" |
| 767 | "message Bar {\n" |
| 768 | " optional Foo foo = 1;\n" |
| 769 | "}\n"); |
| 770 | |
| 771 | Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set " |
| 772 | "--proto_path=$tmpdir bar.proto"); |
| 773 | |
| 774 | ExpectNoErrors(); |
| 775 | |
| 776 | FileDescriptorSet descriptor_set; |
| 777 | ReadDescriptorSet("descriptor_set", &descriptor_set); |
| 778 | if (HasFatalFailure()) return; |
| 779 | ASSERT_EQ(1, descriptor_set.file_size()); |
| 780 | EXPECT_EQ("bar.proto", descriptor_set.file(0).name()); |
| 781 | } |
| 782 | |
| 783 | TEST_F(CommandLineInterfaceTest, WriteTransitiveDescriptorSet) { |
| 784 | CreateTempFile("foo.proto", |
| 785 | "syntax = \"proto2\";\n" |
| 786 | "message Foo {}\n"); |
| 787 | CreateTempFile("bar.proto", |
| 788 | "syntax = \"proto2\";\n" |
| 789 | "import \"foo.proto\";\n" |
| 790 | "message Bar {\n" |
| 791 | " optional Foo foo = 1;\n" |
| 792 | "}\n"); |
| 793 | |
| 794 | Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set " |
| 795 | "--include_imports --proto_path=$tmpdir bar.proto"); |
| 796 | |
| 797 | ExpectNoErrors(); |
| 798 | |
| 799 | FileDescriptorSet descriptor_set; |
| 800 | ReadDescriptorSet("descriptor_set", &descriptor_set); |
| 801 | if (HasFatalFailure()) return; |
| 802 | ASSERT_EQ(2, descriptor_set.file_size()); |
| 803 | if (descriptor_set.file(0).name() == "bar.proto") { |
kenton@google.com | 7fb9ae9 | 2009-09-02 02:42:56 +0000 | [diff] [blame] | 804 | std::swap(descriptor_set.mutable_file()->mutable_data()[0], |
| 805 | descriptor_set.mutable_file()->mutable_data()[1]); |
temporal | 779f61c | 2008-08-13 03:15:00 +0000 | [diff] [blame] | 806 | } |
| 807 | EXPECT_EQ("foo.proto", descriptor_set.file(0).name()); |
| 808 | EXPECT_EQ("bar.proto", descriptor_set.file(1).name()); |
| 809 | } |
| 810 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 811 | // ------------------------------------------------------------------- |
| 812 | |
| 813 | TEST_F(CommandLineInterfaceTest, ParseErrors) { |
| 814 | // Test that parse errors are reported. |
| 815 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 816 | CreateTempFile("foo.proto", |
| 817 | "syntax = \"proto2\";\n" |
| 818 | "badsyntax\n"); |
| 819 | |
| 820 | Run("protocol_compiler --test_out=$tmpdir " |
| 821 | "--proto_path=$tmpdir foo.proto"); |
| 822 | |
| 823 | ExpectErrorText( |
| 824 | "foo.proto:2:1: Expected top-level statement (e.g. \"message\").\n"); |
| 825 | } |
| 826 | |
| 827 | TEST_F(CommandLineInterfaceTest, ParseErrorsMultipleFiles) { |
| 828 | // Test that parse errors are reported from multiple files. |
| 829 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 830 | // We set up files such that foo.proto actually depends on bar.proto in |
| 831 | // two ways: Directly and through baz.proto. bar.proto's errors should |
| 832 | // only be reported once. |
| 833 | CreateTempFile("bar.proto", |
| 834 | "syntax = \"proto2\";\n" |
| 835 | "badsyntax\n"); |
| 836 | CreateTempFile("baz.proto", |
| 837 | "syntax = \"proto2\";\n" |
| 838 | "import \"bar.proto\";\n"); |
| 839 | CreateTempFile("foo.proto", |
| 840 | "syntax = \"proto2\";\n" |
| 841 | "import \"bar.proto\";\n" |
| 842 | "import \"baz.proto\";\n"); |
| 843 | |
| 844 | Run("protocol_compiler --test_out=$tmpdir " |
| 845 | "--proto_path=$tmpdir foo.proto"); |
| 846 | |
| 847 | ExpectErrorText( |
| 848 | "bar.proto:2:1: Expected top-level statement (e.g. \"message\").\n" |
| 849 | "baz.proto: Import \"bar.proto\" was not found or had errors.\n" |
| 850 | "foo.proto: Import \"bar.proto\" was not found or had errors.\n" |
| 851 | "foo.proto: Import \"baz.proto\" was not found or had errors.\n"); |
| 852 | } |
| 853 | |
| 854 | TEST_F(CommandLineInterfaceTest, InputNotFoundError) { |
| 855 | // Test what happens if the input file is not found. |
| 856 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 857 | Run("protocol_compiler --test_out=$tmpdir " |
| 858 | "--proto_path=$tmpdir foo.proto"); |
| 859 | |
| 860 | ExpectErrorText( |
| 861 | "foo.proto: File not found.\n"); |
| 862 | } |
| 863 | |
| 864 | TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotFoundError) { |
| 865 | // Test what happens when a working-directory-relative input file is not |
| 866 | // found. |
| 867 | |
| 868 | SetInputsAreProtoPathRelative(false); |
| 869 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 870 | Run("protocol_compiler --test_out=$tmpdir " |
| 871 | "--proto_path=$tmpdir $tmpdir/foo.proto"); |
| 872 | |
| 873 | ExpectErrorText( |
| 874 | "$tmpdir/foo.proto: No such file or directory\n"); |
| 875 | } |
| 876 | |
| 877 | TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotMappedError) { |
| 878 | // Test what happens when a working-directory-relative input file is not |
| 879 | // mapped to a virtual path. |
| 880 | |
| 881 | SetInputsAreProtoPathRelative(false); |
| 882 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 883 | CreateTempFile("foo.proto", |
| 884 | "syntax = \"proto2\";\n" |
| 885 | "message Foo {}\n"); |
| 886 | |
| 887 | // Create a directory called "bar" so that we can point --proto_path at it. |
| 888 | CreateTempFile("bar/dummy", ""); |
| 889 | |
| 890 | Run("protocol_compiler --test_out=$tmpdir " |
| 891 | "--proto_path=$tmpdir/bar $tmpdir/foo.proto"); |
| 892 | |
| 893 | ExpectErrorText( |
| 894 | "$tmpdir/foo.proto: File does not reside within any path " |
| 895 | "specified using --proto_path (or -I). You must specify a " |
kenton@google.com | 477f799 | 2009-10-07 21:38:11 +0000 | [diff] [blame] | 896 | "--proto_path which encompasses this file. Note that the " |
| 897 | "proto_path must be an exact prefix of the .proto file " |
| 898 | "names -- protoc is too dumb to figure out when two paths " |
| 899 | "(e.g. absolute and relative) are equivalent (it's harder " |
| 900 | "than you think).\n"); |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 901 | } |
| 902 | |
| 903 | TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotFoundAndNotMappedError) { |
| 904 | // Check what happens if the input file is not found *and* is not mapped |
| 905 | // in the proto_path. |
| 906 | |
| 907 | SetInputsAreProtoPathRelative(false); |
| 908 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 909 | // Create a directory called "bar" so that we can point --proto_path at it. |
| 910 | CreateTempFile("bar/dummy", ""); |
| 911 | |
| 912 | Run("protocol_compiler --test_out=$tmpdir " |
| 913 | "--proto_path=$tmpdir/bar $tmpdir/foo.proto"); |
| 914 | |
| 915 | ExpectErrorText( |
| 916 | "$tmpdir/foo.proto: No such file or directory\n"); |
| 917 | } |
| 918 | |
| 919 | TEST_F(CommandLineInterfaceTest, CwdRelativeInputShadowedError) { |
| 920 | // Test what happens when a working-directory-relative input file is shadowed |
| 921 | // by another file in the virtual path. |
| 922 | |
| 923 | SetInputsAreProtoPathRelative(false); |
| 924 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 925 | CreateTempFile("foo/foo.proto", |
| 926 | "syntax = \"proto2\";\n" |
| 927 | "message Foo {}\n"); |
| 928 | CreateTempFile("bar/foo.proto", |
| 929 | "syntax = \"proto2\";\n" |
| 930 | "message Bar {}\n"); |
| 931 | |
| 932 | Run("protocol_compiler --test_out=$tmpdir " |
| 933 | "--proto_path=$tmpdir/foo --proto_path=$tmpdir/bar " |
| 934 | "$tmpdir/bar/foo.proto"); |
| 935 | |
| 936 | ExpectErrorText( |
| 937 | "$tmpdir/bar/foo.proto: Input is shadowed in the --proto_path " |
| 938 | "by \"$tmpdir/foo/foo.proto\". Either use the latter " |
| 939 | "file as your input or reorder the --proto_path so that the " |
| 940 | "former file's location comes first.\n"); |
| 941 | } |
| 942 | |
| 943 | TEST_F(CommandLineInterfaceTest, ProtoPathNotFoundError) { |
| 944 | // Test what happens if the input file is not found. |
| 945 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 946 | Run("protocol_compiler --test_out=$tmpdir " |
| 947 | "--proto_path=$tmpdir/foo foo.proto"); |
| 948 | |
| 949 | ExpectErrorText( |
| 950 | "$tmpdir/foo: warning: directory does not exist.\n" |
| 951 | "foo.proto: File not found.\n"); |
| 952 | } |
| 953 | |
| 954 | TEST_F(CommandLineInterfaceTest, MissingInputError) { |
| 955 | // Test that we get an error if no inputs are given. |
| 956 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 957 | Run("protocol_compiler --test_out=$tmpdir " |
| 958 | "--proto_path=$tmpdir"); |
| 959 | |
| 960 | ExpectErrorText("Missing input file.\n"); |
| 961 | } |
| 962 | |
| 963 | TEST_F(CommandLineInterfaceTest, MissingOutputError) { |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 964 | CreateTempFile("foo.proto", |
| 965 | "syntax = \"proto2\";\n" |
| 966 | "message Foo {}\n"); |
| 967 | |
| 968 | Run("protocol_compiler --proto_path=$tmpdir foo.proto"); |
| 969 | |
| 970 | ExpectErrorText("Missing output directives.\n"); |
| 971 | } |
| 972 | |
| 973 | TEST_F(CommandLineInterfaceTest, OutputWriteError) { |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 974 | CreateTempFile("foo.proto", |
| 975 | "syntax = \"proto2\";\n" |
| 976 | "message Foo {}\n"); |
| 977 | |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 978 | string output_file = |
| 979 | MockCodeGenerator::GetOutputFileName("test_generator", "foo.proto"); |
| 980 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 981 | // Create a directory blocking our output location. |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 982 | CreateTempDir(output_file); |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 983 | |
| 984 | Run("protocol_compiler --test_out=$tmpdir " |
| 985 | "--proto_path=$tmpdir foo.proto"); |
| 986 | |
kenton@google.com | 5f12164 | 2009-12-23 07:03:06 +0000 | [diff] [blame] | 987 | // MockCodeGenerator no longer detects an error because we actually write to |
| 988 | // an in-memory location first, then dump to disk at the end. This is no |
| 989 | // big deal. |
| 990 | // ExpectErrorSubstring("MockCodeGenerator detected write error."); |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 991 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 992 | #if defined(_WIN32) && !defined(__CYGWIN__) |
| 993 | // Windows with MSVCRT.dll produces EPERM instead of EISDIR. |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 994 | if (HasAlternateErrorSubstring(output_file + ": Permission denied")) { |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 995 | return; |
| 996 | } |
| 997 | #endif |
| 998 | |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 999 | ExpectErrorSubstring(output_file + ": Is a directory"); |
| 1000 | } |
| 1001 | |
| 1002 | TEST_F(CommandLineInterfaceTest, PluginOutputWriteError) { |
| 1003 | CreateTempFile("foo.proto", |
| 1004 | "syntax = \"proto2\";\n" |
| 1005 | "message Foo {}\n"); |
| 1006 | |
| 1007 | string output_file = |
| 1008 | MockCodeGenerator::GetOutputFileName("test_plugin", "foo.proto"); |
| 1009 | |
| 1010 | // Create a directory blocking our output location. |
| 1011 | CreateTempDir(output_file); |
| 1012 | |
| 1013 | Run("protocol_compiler --plug_out=$tmpdir " |
| 1014 | "--proto_path=$tmpdir foo.proto"); |
| 1015 | |
| 1016 | #if defined(_WIN32) && !defined(__CYGWIN__) |
| 1017 | // Windows with MSVCRT.dll produces EPERM instead of EISDIR. |
| 1018 | if (HasAlternateErrorSubstring(output_file + ": Permission denied")) { |
| 1019 | return; |
| 1020 | } |
| 1021 | #endif |
| 1022 | |
| 1023 | ExpectErrorSubstring(output_file + ": Is a directory"); |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 1024 | } |
| 1025 | |
| 1026 | TEST_F(CommandLineInterfaceTest, OutputDirectoryNotFoundError) { |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 1027 | CreateTempFile("foo.proto", |
| 1028 | "syntax = \"proto2\";\n" |
| 1029 | "message Foo {}\n"); |
| 1030 | |
| 1031 | Run("protocol_compiler --test_out=$tmpdir/nosuchdir " |
| 1032 | "--proto_path=$tmpdir foo.proto"); |
| 1033 | |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 1034 | ExpectErrorSubstring("nosuchdir/: No such file or directory"); |
| 1035 | } |
| 1036 | |
| 1037 | TEST_F(CommandLineInterfaceTest, PluginOutputDirectoryNotFoundError) { |
| 1038 | CreateTempFile("foo.proto", |
| 1039 | "syntax = \"proto2\";\n" |
| 1040 | "message Foo {}\n"); |
| 1041 | |
| 1042 | Run("protocol_compiler --plug_out=$tmpdir/nosuchdir " |
| 1043 | "--proto_path=$tmpdir foo.proto"); |
| 1044 | |
| 1045 | ExpectErrorSubstring("nosuchdir/: No such file or directory"); |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 1046 | } |
| 1047 | |
| 1048 | TEST_F(CommandLineInterfaceTest, OutputDirectoryIsFileError) { |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 1049 | CreateTempFile("foo.proto", |
| 1050 | "syntax = \"proto2\";\n" |
| 1051 | "message Foo {}\n"); |
| 1052 | |
| 1053 | Run("protocol_compiler --test_out=$tmpdir/foo.proto " |
| 1054 | "--proto_path=$tmpdir foo.proto"); |
| 1055 | |
| 1056 | #if defined(_WIN32) && !defined(__CYGWIN__) |
| 1057 | // Windows with MSVCRT.dll produces EINVAL instead of ENOTDIR. |
| 1058 | if (HasAlternateErrorSubstring("foo.proto/: Invalid argument")) { |
| 1059 | return; |
| 1060 | } |
| 1061 | #endif |
| 1062 | |
| 1063 | ExpectErrorSubstring("foo.proto/: Not a directory"); |
| 1064 | } |
| 1065 | |
| 1066 | TEST_F(CommandLineInterfaceTest, GeneratorError) { |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 1067 | CreateTempFile("foo.proto", |
| 1068 | "syntax = \"proto2\";\n" |
| 1069 | "message MockCodeGenerator_Error {}\n"); |
| 1070 | |
| 1071 | Run("protocol_compiler --test_out=$tmpdir " |
| 1072 | "--proto_path=$tmpdir foo.proto"); |
| 1073 | |
| 1074 | ExpectErrorSubstring( |
| 1075 | "--test_out: foo.proto: Saw message type MockCodeGenerator_Error."); |
| 1076 | } |
| 1077 | |
| 1078 | TEST_F(CommandLineInterfaceTest, GeneratorPluginError) { |
| 1079 | // Test a generator plugin that returns an error. |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 1080 | |
| 1081 | CreateTempFile("foo.proto", |
| 1082 | "syntax = \"proto2\";\n" |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 1083 | "message MockCodeGenerator_Error {}\n"); |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 1084 | |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 1085 | Run("protocol_compiler --plug_out=TestParameter:$tmpdir " |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 1086 | "--proto_path=$tmpdir foo.proto"); |
| 1087 | |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 1088 | ExpectErrorSubstring( |
| 1089 | "--plug_out: foo.proto: Saw message type MockCodeGenerator_Error."); |
| 1090 | } |
| 1091 | |
| 1092 | TEST_F(CommandLineInterfaceTest, GeneratorPluginFail) { |
| 1093 | // Test a generator plugin that exits with an error code. |
| 1094 | |
| 1095 | CreateTempFile("foo.proto", |
| 1096 | "syntax = \"proto2\";\n" |
| 1097 | "message MockCodeGenerator_Exit {}\n"); |
| 1098 | |
| 1099 | Run("protocol_compiler --plug_out=TestParameter:$tmpdir " |
| 1100 | "--proto_path=$tmpdir foo.proto"); |
| 1101 | |
| 1102 | ExpectErrorSubstring("Saw message type MockCodeGenerator_Exit."); |
| 1103 | ExpectErrorSubstring( |
| 1104 | "--plug_out: prefix-gen-plug: Plugin failed with status code 123."); |
| 1105 | } |
| 1106 | |
| 1107 | TEST_F(CommandLineInterfaceTest, GeneratorPluginCrash) { |
| 1108 | // Test a generator plugin that crashes. |
| 1109 | |
| 1110 | CreateTempFile("foo.proto", |
| 1111 | "syntax = \"proto2\";\n" |
| 1112 | "message MockCodeGenerator_Abort {}\n"); |
| 1113 | |
| 1114 | Run("protocol_compiler --plug_out=TestParameter:$tmpdir " |
| 1115 | "--proto_path=$tmpdir foo.proto"); |
| 1116 | |
| 1117 | ExpectErrorSubstring("Saw message type MockCodeGenerator_Abort."); |
| 1118 | |
kenton@google.com | 684d45b | 2009-12-19 04:50:00 +0000 | [diff] [blame] | 1119 | #ifdef _WIN32 |
| 1120 | // Windows doesn't have signals. It looks like abort()ing causes the process |
| 1121 | // to exit with status code 3, but let's not depend on the exact number here. |
| 1122 | ExpectErrorSubstring( |
| 1123 | "--plug_out: prefix-gen-plug: Plugin failed with status code"); |
| 1124 | #else |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 1125 | // Don't depend on the exact signal number. |
| 1126 | ExpectErrorSubstring( |
| 1127 | "--plug_out: prefix-gen-plug: Plugin killed by signal"); |
kenton@google.com | 684d45b | 2009-12-19 04:50:00 +0000 | [diff] [blame] | 1128 | #endif |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 1129 | } |
| 1130 | |
| 1131 | TEST_F(CommandLineInterfaceTest, GeneratorPluginNotFound) { |
| 1132 | // Test what happens if the plugin isn't found. |
| 1133 | |
| 1134 | CreateTempFile("error.proto", |
| 1135 | "syntax = \"proto2\";\n" |
| 1136 | "message Foo {}\n"); |
| 1137 | |
| 1138 | Run("protocol_compiler --badplug_out=TestParameter:$tmpdir " |
| 1139 | "--plugin=prefix-gen-badplug=no_such_file " |
| 1140 | "--proto_path=$tmpdir error.proto"); |
| 1141 | |
kenton@google.com | 684d45b | 2009-12-19 04:50:00 +0000 | [diff] [blame] | 1142 | #ifdef _WIN32 |
| 1143 | ExpectErrorSubstring( |
| 1144 | "--badplug_out: prefix-gen-badplug: The system cannot find the file " |
| 1145 | "specified."); |
| 1146 | #else |
| 1147 | // Error written to stdout by child process after exec() fails. |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 1148 | ExpectErrorSubstring( |
| 1149 | "no_such_file: program not found or is not executable"); |
| 1150 | |
kenton@google.com | 684d45b | 2009-12-19 04:50:00 +0000 | [diff] [blame] | 1151 | // Error written by parent process when child fails. |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 1152 | ExpectErrorSubstring( |
| 1153 | "--badplug_out: prefix-gen-badplug: Plugin failed with status code 1."); |
kenton@google.com | 684d45b | 2009-12-19 04:50:00 +0000 | [diff] [blame] | 1154 | #endif |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 1155 | } |
| 1156 | |
| 1157 | TEST_F(CommandLineInterfaceTest, GeneratorPluginNotAllowed) { |
| 1158 | // Test what happens if plugins aren't allowed. |
| 1159 | |
| 1160 | CreateTempFile("error.proto", |
| 1161 | "syntax = \"proto2\";\n" |
| 1162 | "message Foo {}\n"); |
| 1163 | |
| 1164 | DisallowPlugins(); |
| 1165 | Run("protocol_compiler --plug_out=TestParameter:$tmpdir " |
| 1166 | "--proto_path=$tmpdir error.proto"); |
| 1167 | |
| 1168 | ExpectErrorSubstring("Unknown flag: --plug_out"); |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 1169 | } |
| 1170 | |
| 1171 | TEST_F(CommandLineInterfaceTest, HelpText) { |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 1172 | Run("test_exec_name --help"); |
| 1173 | |
| 1174 | ExpectErrorSubstring("Usage: test_exec_name "); |
| 1175 | ExpectErrorSubstring("--test_out=OUT_DIR"); |
| 1176 | ExpectErrorSubstring("Test output."); |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 1177 | ExpectErrorSubstring("--alt_out=OUT_DIR"); |
| 1178 | ExpectErrorSubstring("Alt output."); |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 1179 | } |
| 1180 | |
kenton@google.com | f663b16 | 2009-04-15 19:50:54 +0000 | [diff] [blame] | 1181 | TEST_F(CommandLineInterfaceTest, GccFormatErrors) { |
| 1182 | // Test --error_format=gcc (which is the default, but we want to verify |
| 1183 | // that it can be set explicitly). |
| 1184 | |
kenton@google.com | f663b16 | 2009-04-15 19:50:54 +0000 | [diff] [blame] | 1185 | CreateTempFile("foo.proto", |
| 1186 | "syntax = \"proto2\";\n" |
| 1187 | "badsyntax\n"); |
| 1188 | |
| 1189 | Run("protocol_compiler --test_out=$tmpdir " |
| 1190 | "--proto_path=$tmpdir --error_format=gcc foo.proto"); |
| 1191 | |
| 1192 | ExpectErrorText( |
| 1193 | "foo.proto:2:1: Expected top-level statement (e.g. \"message\").\n"); |
| 1194 | } |
| 1195 | |
| 1196 | TEST_F(CommandLineInterfaceTest, MsvsFormatErrors) { |
| 1197 | // Test --error_format=msvs |
| 1198 | |
kenton@google.com | f663b16 | 2009-04-15 19:50:54 +0000 | [diff] [blame] | 1199 | CreateTempFile("foo.proto", |
| 1200 | "syntax = \"proto2\";\n" |
| 1201 | "badsyntax\n"); |
| 1202 | |
| 1203 | Run("protocol_compiler --test_out=$tmpdir " |
| 1204 | "--proto_path=$tmpdir --error_format=msvs foo.proto"); |
| 1205 | |
| 1206 | ExpectErrorText( |
kenton@google.com | 6793c1a | 2010-04-05 21:45:45 +0000 | [diff] [blame] | 1207 | "$tmpdir/foo.proto(2) : error in column=1: Expected top-level statement " |
kenton@google.com | f663b16 | 2009-04-15 19:50:54 +0000 | [diff] [blame] | 1208 | "(e.g. \"message\").\n"); |
| 1209 | } |
| 1210 | |
| 1211 | TEST_F(CommandLineInterfaceTest, InvalidErrorFormat) { |
| 1212 | // Test --error_format=msvs |
| 1213 | |
kenton@google.com | f663b16 | 2009-04-15 19:50:54 +0000 | [diff] [blame] | 1214 | CreateTempFile("foo.proto", |
| 1215 | "syntax = \"proto2\";\n" |
| 1216 | "badsyntax\n"); |
| 1217 | |
| 1218 | Run("protocol_compiler --test_out=$tmpdir " |
| 1219 | "--proto_path=$tmpdir --error_format=invalid foo.proto"); |
| 1220 | |
| 1221 | ExpectErrorText( |
| 1222 | "Unknown error format: invalid\n"); |
| 1223 | } |
| 1224 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 1225 | // ------------------------------------------------------------------- |
| 1226 | // Flag parsing tests |
| 1227 | |
| 1228 | TEST_F(CommandLineInterfaceTest, ParseSingleCharacterFlag) { |
| 1229 | // Test that a single-character flag works. |
| 1230 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 1231 | CreateTempFile("foo.proto", |
| 1232 | "syntax = \"proto2\";\n" |
| 1233 | "message Foo {}\n"); |
| 1234 | |
temporal | 779f61c | 2008-08-13 03:15:00 +0000 | [diff] [blame] | 1235 | Run("protocol_compiler -t$tmpdir " |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 1236 | "--proto_path=$tmpdir foo.proto"); |
| 1237 | |
| 1238 | ExpectNoErrors(); |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 1239 | ExpectGenerated("test_generator", "", "foo.proto", "Foo"); |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 1240 | } |
| 1241 | |
| 1242 | TEST_F(CommandLineInterfaceTest, ParseSpaceDelimitedValue) { |
| 1243 | // Test that separating the flag value with a space works. |
| 1244 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 1245 | CreateTempFile("foo.proto", |
| 1246 | "syntax = \"proto2\";\n" |
| 1247 | "message Foo {}\n"); |
| 1248 | |
| 1249 | Run("protocol_compiler --test_out $tmpdir " |
| 1250 | "--proto_path=$tmpdir foo.proto"); |
| 1251 | |
| 1252 | ExpectNoErrors(); |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 1253 | ExpectGenerated("test_generator", "", "foo.proto", "Foo"); |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 1254 | } |
| 1255 | |
| 1256 | TEST_F(CommandLineInterfaceTest, ParseSingleCharacterSpaceDelimitedValue) { |
| 1257 | // Test that separating the flag value with a space works for |
| 1258 | // single-character flags. |
| 1259 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 1260 | CreateTempFile("foo.proto", |
| 1261 | "syntax = \"proto2\";\n" |
| 1262 | "message Foo {}\n"); |
| 1263 | |
temporal | 779f61c | 2008-08-13 03:15:00 +0000 | [diff] [blame] | 1264 | Run("protocol_compiler -t $tmpdir " |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 1265 | "--proto_path=$tmpdir foo.proto"); |
| 1266 | |
| 1267 | ExpectNoErrors(); |
kenton@google.com | fccb146 | 2009-12-18 02:11:36 +0000 | [diff] [blame] | 1268 | ExpectGenerated("test_generator", "", "foo.proto", "Foo"); |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 1269 | } |
| 1270 | |
| 1271 | TEST_F(CommandLineInterfaceTest, MissingValueError) { |
| 1272 | // Test that we get an error if a flag is missing its value. |
| 1273 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 1274 | Run("protocol_compiler --test_out --proto_path=$tmpdir foo.proto"); |
| 1275 | |
| 1276 | ExpectErrorText("Missing value for flag: --test_out\n"); |
| 1277 | } |
| 1278 | |
| 1279 | TEST_F(CommandLineInterfaceTest, MissingValueAtEndError) { |
| 1280 | // Test that we get an error if the last argument is a flag requiring a |
| 1281 | // value. |
| 1282 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 1283 | Run("protocol_compiler --test_out"); |
| 1284 | |
| 1285 | ExpectErrorText("Missing value for flag: --test_out\n"); |
| 1286 | } |
| 1287 | |
temporal | 779f61c | 2008-08-13 03:15:00 +0000 | [diff] [blame] | 1288 | // =================================================================== |
| 1289 | |
| 1290 | // Test for --encode and --decode. Note that it would be easier to do this |
| 1291 | // test as a shell script, but we'd like to be able to run the test on |
| 1292 | // platforms that don't have a Bourne-compatible shell available (especially |
| 1293 | // Windows/MSVC). |
| 1294 | class EncodeDecodeTest : public testing::Test { |
| 1295 | protected: |
| 1296 | virtual void SetUp() { |
| 1297 | duped_stdin_ = dup(STDIN_FILENO); |
| 1298 | } |
| 1299 | |
| 1300 | virtual void TearDown() { |
| 1301 | dup2(duped_stdin_, STDIN_FILENO); |
| 1302 | close(duped_stdin_); |
| 1303 | } |
| 1304 | |
| 1305 | void RedirectStdinFromText(const string& input) { |
| 1306 | string filename = TestTempDir() + "/test_stdin"; |
| 1307 | File::WriteStringToFileOrDie(input, filename); |
| 1308 | GOOGLE_CHECK(RedirectStdinFromFile(filename)); |
| 1309 | } |
| 1310 | |
| 1311 | bool RedirectStdinFromFile(const string& filename) { |
| 1312 | int fd = open(filename.c_str(), O_RDONLY); |
| 1313 | if (fd < 0) return false; |
| 1314 | dup2(fd, STDIN_FILENO); |
| 1315 | close(fd); |
| 1316 | return true; |
| 1317 | } |
| 1318 | |
| 1319 | // Remove '\r' characters from text. |
| 1320 | string StripCR(const string& text) { |
| 1321 | string result; |
| 1322 | |
| 1323 | for (int i = 0; i < text.size(); i++) { |
| 1324 | if (text[i] != '\r') { |
| 1325 | result.push_back(text[i]); |
| 1326 | } |
| 1327 | } |
| 1328 | |
| 1329 | return result; |
| 1330 | } |
| 1331 | |
| 1332 | enum Type { TEXT, BINARY }; |
| 1333 | enum ReturnCode { SUCCESS, ERROR }; |
| 1334 | |
| 1335 | bool Run(const string& command) { |
| 1336 | vector<string> args; |
| 1337 | args.push_back("protoc"); |
| 1338 | SplitStringUsing(command, " ", &args); |
| 1339 | args.push_back("--proto_path=" + TestSourceDir()); |
| 1340 | |
| 1341 | scoped_array<const char*> argv(new const char*[args.size()]); |
| 1342 | for (int i = 0; i < args.size(); i++) { |
| 1343 | argv[i] = args[i].c_str(); |
| 1344 | } |
| 1345 | |
| 1346 | CommandLineInterface cli; |
| 1347 | cli.SetInputsAreProtoPathRelative(true); |
| 1348 | |
| 1349 | CaptureTestStdout(); |
| 1350 | CaptureTestStderr(); |
| 1351 | |
| 1352 | int result = cli.Run(args.size(), argv.get()); |
| 1353 | |
| 1354 | captured_stdout_ = GetCapturedTestStdout(); |
| 1355 | captured_stderr_ = GetCapturedTestStderr(); |
| 1356 | |
| 1357 | return result == 0; |
| 1358 | } |
| 1359 | |
| 1360 | void ExpectStdoutMatchesBinaryFile(const string& filename) { |
| 1361 | string expected_output; |
| 1362 | ASSERT_TRUE(File::ReadFileToString(filename, &expected_output)); |
| 1363 | |
| 1364 | // Don't use EXPECT_EQ because we don't want to print raw binary data to |
| 1365 | // stdout on failure. |
| 1366 | EXPECT_TRUE(captured_stdout_ == expected_output); |
| 1367 | } |
| 1368 | |
| 1369 | void ExpectStdoutMatchesTextFile(const string& filename) { |
| 1370 | string expected_output; |
| 1371 | ASSERT_TRUE(File::ReadFileToString(filename, &expected_output)); |
| 1372 | |
| 1373 | ExpectStdoutMatchesText(expected_output); |
| 1374 | } |
| 1375 | |
| 1376 | void ExpectStdoutMatchesText(const string& expected_text) { |
| 1377 | EXPECT_EQ(StripCR(expected_text), StripCR(captured_stdout_)); |
| 1378 | } |
| 1379 | |
| 1380 | void ExpectStderrMatchesText(const string& expected_text) { |
| 1381 | EXPECT_EQ(StripCR(expected_text), StripCR(captured_stderr_)); |
| 1382 | } |
| 1383 | |
| 1384 | private: |
| 1385 | int duped_stdin_; |
| 1386 | string captured_stdout_; |
| 1387 | string captured_stderr_; |
| 1388 | }; |
| 1389 | |
| 1390 | TEST_F(EncodeDecodeTest, Encode) { |
| 1391 | RedirectStdinFromFile(TestSourceDir() + |
| 1392 | "/google/protobuf/testdata/text_format_unittest_data.txt"); |
| 1393 | EXPECT_TRUE(Run("google/protobuf/unittest.proto " |
| 1394 | "--encode=protobuf_unittest.TestAllTypes")); |
| 1395 | ExpectStdoutMatchesBinaryFile(TestSourceDir() + |
| 1396 | "/google/protobuf/testdata/golden_message"); |
| 1397 | ExpectStderrMatchesText(""); |
| 1398 | } |
| 1399 | |
| 1400 | TEST_F(EncodeDecodeTest, Decode) { |
| 1401 | RedirectStdinFromFile(TestSourceDir() + |
| 1402 | "/google/protobuf/testdata/golden_message"); |
| 1403 | EXPECT_TRUE(Run("google/protobuf/unittest.proto " |
| 1404 | "--decode=protobuf_unittest.TestAllTypes")); |
| 1405 | ExpectStdoutMatchesTextFile(TestSourceDir() + |
| 1406 | "/google/protobuf/testdata/text_format_unittest_data.txt"); |
| 1407 | ExpectStderrMatchesText(""); |
| 1408 | } |
| 1409 | |
| 1410 | TEST_F(EncodeDecodeTest, Partial) { |
| 1411 | RedirectStdinFromText(""); |
| 1412 | EXPECT_TRUE(Run("google/protobuf/unittest.proto " |
| 1413 | "--encode=protobuf_unittest.TestRequired")); |
| 1414 | ExpectStdoutMatchesText(""); |
| 1415 | ExpectStderrMatchesText( |
| 1416 | "warning: Input message is missing required fields: a, b, c\n"); |
| 1417 | } |
| 1418 | |
| 1419 | TEST_F(EncodeDecodeTest, DecodeRaw) { |
| 1420 | protobuf_unittest::TestAllTypes message; |
| 1421 | message.set_optional_int32(123); |
| 1422 | message.set_optional_string("foo"); |
| 1423 | string data; |
| 1424 | message.SerializeToString(&data); |
| 1425 | |
| 1426 | RedirectStdinFromText(data); |
| 1427 | EXPECT_TRUE(Run("--decode_raw")); |
| 1428 | ExpectStdoutMatchesText("1: 123\n" |
| 1429 | "14: \"foo\"\n"); |
| 1430 | ExpectStderrMatchesText(""); |
| 1431 | } |
| 1432 | |
| 1433 | TEST_F(EncodeDecodeTest, UnknownType) { |
| 1434 | EXPECT_FALSE(Run("google/protobuf/unittest.proto " |
| 1435 | "--encode=NoSuchType")); |
| 1436 | ExpectStdoutMatchesText(""); |
| 1437 | ExpectStderrMatchesText("Type not defined: NoSuchType\n"); |
| 1438 | } |
| 1439 | |
| 1440 | TEST_F(EncodeDecodeTest, ProtoParseError) { |
| 1441 | EXPECT_FALSE(Run("google/protobuf/no_such_file.proto " |
| 1442 | "--encode=NoSuchType")); |
| 1443 | ExpectStdoutMatchesText(""); |
| 1444 | ExpectStderrMatchesText( |
| 1445 | "google/protobuf/no_such_file.proto: File not found.\n"); |
| 1446 | } |
| 1447 | |
temporal | 40ee551 | 2008-07-10 02:12:20 +0000 | [diff] [blame] | 1448 | } // anonymous namespace |
| 1449 | |
| 1450 | } // namespace compiler |
| 1451 | } // namespace protobuf |
| 1452 | } // namespace google |