| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335 |
- // Protocol Buffers - Google's data interchange format
- // Copyright 2008 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.
- // Author: kenton@google.com (Kenton Varda)
- #include <google/protobuf/compiler/mock_code_generator.h>
- #include <stdlib.h>
- #include <iostream>
- #include <memory>
- #include <vector>
- #include <google/protobuf/stubs/strutil.h>
- #include <google/protobuf/stubs/logging.h>
- #include <google/protobuf/stubs/common.h>
- #include <google/protobuf/testing/file.h>
- #include <google/protobuf/testing/file.h>
- #include <google/protobuf/testing/file.h>
- #include <google/protobuf/compiler/plugin.pb.h>
- #include <google/protobuf/io/printer.h>
- #include <google/protobuf/io/zero_copy_stream.h>
- #include <google/protobuf/descriptor.pb.h>
- #include <google/protobuf/descriptor.h>
- #include <google/protobuf/text_format.h>
- #include <google/protobuf/stubs/substitute.h>
- #include <gtest/gtest.h>
- #ifdef major
- #undef major
- #endif
- #ifdef minor
- #undef minor
- #endif
- namespace google {
- namespace protobuf {
- namespace compiler {
- // Returns the list of the names of files in all_files in the form of a
- // comma-separated string.
- string CommaSeparatedList(const std::vector<const FileDescriptor*>& all_files) {
- std::vector<string> names;
- for (size_t i = 0; i < all_files.size(); i++) {
- names.push_back(all_files[i]->name());
- }
- return Join(names, ",");
- }
- static const char* kFirstInsertionPointName = "first_mock_insertion_point";
- static const char* kSecondInsertionPointName = "second_mock_insertion_point";
- static const char* kFirstInsertionPoint =
- "# @@protoc_insertion_point(first_mock_insertion_point) is here\n";
- static const char* kSecondInsertionPoint =
- " # @@protoc_insertion_point(second_mock_insertion_point) is here\n";
- MockCodeGenerator::MockCodeGenerator(const string& name)
- : name_(name) {}
- MockCodeGenerator::~MockCodeGenerator() {}
- void MockCodeGenerator::ExpectGenerated(
- const string& name,
- const string& parameter,
- const string& insertions,
- const string& file,
- const string& first_message_name,
- const string& first_parsed_file_name,
- const string& output_directory) {
- string content;
- GOOGLE_CHECK_OK(
- File::GetContents(output_directory + "/" + GetOutputFileName(name, file),
- &content, true));
- std::vector<string> lines = Split(content, "\n", true);
- while (!lines.empty() && lines.back().empty()) {
- lines.pop_back();
- }
- for (size_t i = 0; i < lines.size(); i++) {
- lines[i] += "\n";
- }
- std::vector<string> insertion_list;
- if (!insertions.empty()) {
- SplitStringUsing(insertions, ",", &insertion_list);
- }
- EXPECT_EQ(lines.size(), 3 + insertion_list.size() * 2);
- EXPECT_EQ(GetOutputFileContent(name, parameter, file,
- first_parsed_file_name, first_message_name),
- lines[0]);
- EXPECT_EQ(kFirstInsertionPoint, lines[1 + insertion_list.size()]);
- EXPECT_EQ(kSecondInsertionPoint, lines[2 + insertion_list.size() * 2]);
- for (size_t i = 0; i < insertion_list.size(); i++) {
- EXPECT_EQ(GetOutputFileContent(insertion_list[i], "first_insert",
- file, file, first_message_name),
- lines[1 + i]);
- // Second insertion point is indented, so the inserted text should
- // automatically be indented too.
- EXPECT_EQ(" " + GetOutputFileContent(insertion_list[i], "second_insert",
- file, file, first_message_name),
- lines[2 + insertion_list.size() + i]);
- }
- }
- namespace {
- void CheckSingleAnnotation(const string& expected_file,
- const string& expected_text,
- const string& file_content,
- const GeneratedCodeInfo::Annotation& annotation) {
- EXPECT_EQ(expected_file, annotation.source_file());
- ASSERT_GE(file_content.size(), annotation.begin());
- ASSERT_GE(file_content.size(), annotation.end());
- ASSERT_LE(annotation.begin(), annotation.end());
- EXPECT_EQ(expected_text.size(), annotation.end() - annotation.begin());
- EXPECT_EQ(expected_text,
- file_content.substr(annotation.begin(), expected_text.size()));
- }
- } // anonymous namespace
- void MockCodeGenerator::CheckGeneratedAnnotations(
- const string& name, const string& file, const string& output_directory) {
- string file_content;
- GOOGLE_CHECK_OK(
- File::GetContents(output_directory + "/" + GetOutputFileName(name, file),
- &file_content, true));
- string meta_content;
- GOOGLE_CHECK_OK(File::GetContents(
- output_directory + "/" + GetOutputFileName(name, file) + ".meta",
- &meta_content, true));
- GeneratedCodeInfo annotations;
- GOOGLE_CHECK(TextFormat::ParseFromString(meta_content, &annotations));
- ASSERT_EQ(3, annotations.annotation_size());
- CheckSingleAnnotation("first_annotation", "first", file_content,
- annotations.annotation(0));
- CheckSingleAnnotation("second_annotation", "second", file_content,
- annotations.annotation(1));
- CheckSingleAnnotation("third_annotation", "third", file_content,
- annotations.annotation(2));
- }
- bool MockCodeGenerator::Generate(
- const FileDescriptor* file,
- const string& parameter,
- GeneratorContext* context,
- string* error) const {
- bool annotate = false;
- for (int i = 0; i < file->message_type_count(); i++) {
- if (HasPrefixString(file->message_type(i)->name(), "MockCodeGenerator_")) {
- string command = StripPrefixString(file->message_type(i)->name(),
- "MockCodeGenerator_");
- if (command == "Error") {
- *error = "Saw message type MockCodeGenerator_Error.";
- return false;
- } else if (command == "Exit") {
- std::cerr << "Saw message type MockCodeGenerator_Exit." << std::endl;
- exit(123);
- } else if (command == "Abort") {
- std::cerr << "Saw message type MockCodeGenerator_Abort." << std::endl;
- abort();
- } else if (command == "HasSourceCodeInfo") {
- FileDescriptorProto file_descriptor_proto;
- file->CopySourceCodeInfoTo(&file_descriptor_proto);
- bool has_source_code_info =
- file_descriptor_proto.has_source_code_info() &&
- file_descriptor_proto.source_code_info().location_size() > 0;
- std::cerr << "Saw message type MockCodeGenerator_HasSourceCodeInfo: "
- << has_source_code_info << "." << std::endl;
- abort();
- } else if (command == "HasJsonName") {
- FieldDescriptorProto field_descriptor_proto;
- file->message_type(i)->field(0)->CopyTo(&field_descriptor_proto);
- std::cerr << "Saw json_name: "
- << field_descriptor_proto.has_json_name() << std::endl;
- abort();
- } else if (command == "Annotate") {
- annotate = true;
- } else if (command == "ShowVersionNumber") {
- Version compiler_version;
- context->GetCompilerVersion(&compiler_version);
- std::cerr << "Saw compiler_version: "
- << compiler_version.major() * 1000000 +
- compiler_version.minor() * 1000 +
- compiler_version.patch()
- << " " << compiler_version.suffix() << std::endl;
- abort();
- } else {
- GOOGLE_LOG(FATAL) << "Unknown MockCodeGenerator command: " << command;
- }
- }
- }
- if (HasPrefixString(parameter, "insert=")) {
- std::vector<string> insert_into;
- SplitStringUsing(StripPrefixString(parameter, "insert="),
- ",", &insert_into);
- for (size_t i = 0; i < insert_into.size(); i++) {
- {
- std::unique_ptr<io::ZeroCopyOutputStream> output(context->OpenForInsert(
- GetOutputFileName(insert_into[i], file), kFirstInsertionPointName));
- io::Printer printer(output.get(), '$');
- printer.PrintRaw(GetOutputFileContent(name_, "first_insert",
- file, context));
- if (printer.failed()) {
- *error = "MockCodeGenerator detected write error.";
- return false;
- }
- }
- {
- std::unique_ptr<io::ZeroCopyOutputStream> output(
- context->OpenForInsert(GetOutputFileName(insert_into[i], file),
- kSecondInsertionPointName));
- io::Printer printer(output.get(), '$');
- printer.PrintRaw(GetOutputFileContent(name_, "second_insert",
- file, context));
- if (printer.failed()) {
- *error = "MockCodeGenerator detected write error.";
- return false;
- }
- }
- }
- } else {
- std::unique_ptr<io::ZeroCopyOutputStream> output(
- context->Open(GetOutputFileName(name_, file)));
- GeneratedCodeInfo annotations;
- io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector(
- &annotations);
- io::Printer printer(output.get(), '$',
- annotate ? &annotation_collector : NULL);
- printer.PrintRaw(GetOutputFileContent(name_, parameter, file, context));
- string annotate_suffix = "_annotation";
- if (annotate) {
- printer.Print("$p$", "p", "first");
- printer.Annotate("p", "first" + annotate_suffix);
- }
- printer.PrintRaw(kFirstInsertionPoint);
- if (annotate) {
- printer.Print("$p$", "p", "second");
- printer.Annotate("p", "second" + annotate_suffix);
- }
- printer.PrintRaw(kSecondInsertionPoint);
- if (annotate) {
- printer.Print("$p$", "p", "third");
- printer.Annotate("p", "third" + annotate_suffix);
- }
- if (printer.failed()) {
- *error = "MockCodeGenerator detected write error.";
- return false;
- }
- if (annotate) {
- std::unique_ptr<io::ZeroCopyOutputStream> meta_output(
- context->Open(GetOutputFileName(name_, file) + ".meta"));
- if (!TextFormat::Print(annotations, meta_output.get())) {
- *error = "MockCodeGenerator couldn't write .meta";
- return false;
- }
- }
- }
- return true;
- }
- string MockCodeGenerator::GetOutputFileName(const string& generator_name,
- const FileDescriptor* file) {
- return GetOutputFileName(generator_name, file->name());
- }
- string MockCodeGenerator::GetOutputFileName(const string& generator_name,
- const string& file) {
- return file + ".MockCodeGenerator." + generator_name;
- }
- string MockCodeGenerator::GetOutputFileContent(
- const string& generator_name,
- const string& parameter,
- const FileDescriptor* file,
- GeneratorContext *context) {
- std::vector<const FileDescriptor*> all_files;
- context->ListParsedFiles(&all_files);
- return GetOutputFileContent(
- generator_name, parameter, file->name(),
- CommaSeparatedList(all_files),
- file->message_type_count() > 0 ?
- file->message_type(0)->name() : "(none)");
- }
- string MockCodeGenerator::GetOutputFileContent(
- const string& generator_name,
- const string& parameter,
- const string& file,
- const string& parsed_file_list,
- const string& first_message_name) {
- return strings::Substitute("$0: $1, $2, $3, $4\n",
- generator_name, parameter, file,
- first_message_name, parsed_file_list);
- }
- } // namespace compiler
- } // namespace protobuf
- } // namespace google
|