aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am1
-rw-r--r--src/google/protobuf/compiler/ruby/ruby_generator.cc64
-rw-r--r--src/google/protobuf/compiler/ruby/ruby_generator_unittest.cc119
3 files changed, 166 insertions, 18 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 3a469fd7..6fd8bd2b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -458,6 +458,7 @@ protobuf_test_SOURCES = \
google/protobuf/compiler/java/java_plugin_unittest.cc \
google/protobuf/compiler/java/java_doc_comment_unittest.cc \
google/protobuf/compiler/python/python_plugin_unittest.cc \
+ google/protobuf/compiler/ruby/ruby_generator_unittest.cc \
$(COMMON_TEST_SOURCES)
nodist_protobuf_test_SOURCES = $(protoc_outputs)
diff --git a/src/google/protobuf/compiler/ruby/ruby_generator.cc b/src/google/protobuf/compiler/ruby/ruby_generator.cc
index 22a64de9..be59fafd 100644
--- a/src/google/protobuf/compiler/ruby/ruby_generator.cc
+++ b/src/google/protobuf/compiler/ruby/ruby_generator.cc
@@ -100,6 +100,45 @@ std::string TypeName(const google::protobuf::FieldDescriptor* field) {
}
}
+void GenerateField(const google::protobuf::FieldDescriptor* field,
+ google::protobuf::io::Printer* printer) {
+ printer->Print(
+ "$label$ :$name$, ",
+ "label", LabelForField(field),
+ "name", field->name());
+ printer->Print(
+ ":$type$, $number$",
+ "type", TypeName(field),
+ "number", IntToString(field->number()));
+ if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ printer->Print(
+ ", \"$subtype$\"\n",
+ "subtype", field->message_type()->full_name());
+ } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
+ printer->Print(
+ ", \"$subtype$\"\n",
+ "subtype", field->enum_type()->full_name());
+ } else {
+ printer->Print("\n");
+ }
+}
+
+void GenerateOneof(const google::protobuf::OneofDescriptor* oneof,
+ google::protobuf::io::Printer* printer) {
+ printer->Print(
+ "oneof :$name$ do\n",
+ "name", oneof->name());
+ printer->Indent();
+
+ for (int i = 0; i < oneof->field_count(); i++) {
+ const FieldDescriptor* field = oneof->field(i);
+ GenerateField(field, printer);
+ }
+
+ printer->Outdent();
+ printer->Print("end\n");
+}
+
void GenerateMessage(const google::protobuf::Descriptor* message,
google::protobuf::io::Printer* printer) {
printer->Print(
@@ -109,27 +148,16 @@ void GenerateMessage(const google::protobuf::Descriptor* message,
for (int i = 0; i < message->field_count(); i++) {
const FieldDescriptor* field = message->field(i);
- printer->Print(
- "$label$ :$name$, ",
- "label", LabelForField(field),
- "name", field->name());
- printer->Print(
- ":$type$, $number$",
- "type", TypeName(field),
- "number", IntToString(field->number()));
- if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
- printer->Print(
- ", \"$subtype$\"\n",
- "subtype", field->message_type()->full_name());
- } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
- printer->Print(
- ", \"$subtype$\"\n",
- "subtype", field->enum_type()->full_name());
- } else {
- printer->Print("\n");
+ if (!field->containing_oneof()) {
+ GenerateField(field, printer);
}
}
+ for (int i = 0; i < message->oneof_decl_count(); i++) {
+ const OneofDescriptor* oneof = message->oneof_decl(i);
+ GenerateOneof(oneof, printer);
+ }
+
printer->Outdent();
printer->Print("end\n");
diff --git a/src/google/protobuf/compiler/ruby/ruby_generator_unittest.cc b/src/google/protobuf/compiler/ruby/ruby_generator_unittest.cc
new file mode 100644
index 00000000..e35ca695
--- /dev/null
+++ b/src/google/protobuf/compiler/ruby/ruby_generator_unittest.cc
@@ -0,0 +1,119 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2014 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <memory>
+
+#include <google/protobuf/compiler/ruby/ruby_generator.h>
+#include <google/protobuf/compiler/command_line_interface.h>
+#include <google/protobuf/io/zero_copy_stream.h>
+#include <google/protobuf/io/printer.h>
+
+#include <google/protobuf/testing/googletest.h>
+#include <gtest/gtest.h>
+#include <google/protobuf/testing/file.h>
+
+namespace google {
+namespace protobuf {
+namespace compiler {
+namespace ruby {
+namespace {
+
+string FindRubyTestDir() {
+ // Inspired by TestSourceDir() in src/google/protobuf/testing/googletest.cc.
+ string prefix = ".";
+ while (!File::Exists(prefix + "/ruby/tests")) {
+ if (!File::Exists(prefix)) {
+ GOOGLE_LOG(FATAL)
+ << "Could not find Ruby test directory. Please run tests from "
+ "somewhere within the protobuf source package.";
+ }
+ prefix += "/..";
+ }
+ return prefix + "/ruby/tests";
+}
+
+// This test is a simple golden-file test over the output of the Ruby code
+// generator. When we make changes to the Ruby extension and alter the Ruby code
+// generator to use those changes, we should (i) manually test the output of the
+// code generator with the extension, and (ii) update the golden output above.
+// Some day, we may integrate build systems between protoc and the language
+// extensions to the point where we can do this test in a more automated way.
+
+TEST(RubyGeneratorTest, GeneratorTest) {
+ string ruby_tests = FindRubyTestDir();
+
+ google::protobuf::compiler::CommandLineInterface cli;
+ cli.SetInputsAreProtoPathRelative(true);
+
+ ruby::Generator ruby_generator;
+ cli.RegisterGenerator("--ruby_out", &ruby_generator, "");
+
+ // Copy generated_code.proto to the temporary test directory.
+ string test_input;
+ GOOGLE_CHECK_OK(File::GetContents(
+ ruby_tests + "/generated_code.proto",
+ &test_input,
+ true));
+ GOOGLE_CHECK_OK(File::SetContents(
+ TestTempDir() + "/generated_code.proto",
+ test_input,
+ true));
+
+ // Invoke the proto compiler (we will be inside TestTempDir() at this point).
+ string ruby_out = "--ruby_out=" + TestTempDir();
+ string proto_path = "--proto_path=" + TestTempDir();
+ const char* argv[] = {
+ "protoc",
+ ruby_out.c_str(),
+ proto_path.c_str(),
+ "generated_code.proto",
+ };
+
+ EXPECT_EQ(0, cli.Run(4, argv));
+
+ // Load the generated output and compare to the expected result.
+ string output;
+ GOOGLE_CHECK_OK(File::GetContents(
+ TestTempDir() + "/generated_code.rb",
+ &output,
+ true));
+ string expected_output;
+ GOOGLE_CHECK_OK(File::GetContents(
+ ruby_tests + "/generated_code.rb",
+ &expected_output,
+ true));
+ EXPECT_EQ(expected_output, output);
+}
+
+} // namespace
+} // namespace ruby
+} // namespace compiler
+} // namespace protobuf
+} // namespace google