1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // https://developers.google.com/protocol-buffers/ 4 // 5 // Redistribution and use in source and binary forms, with or without 6 // modification, are permitted provided that the following conditions are 7 // met: 8 // 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. 18 // 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. 30 31 // Author: kenton (at) google.com (Kenton Varda) 32 33 #include <google/protobuf/compiler/mock_code_generator.h> 34 35 #include <stdlib.h> 36 #include <iostream> 37 #include <memory> 38 #ifndef _SHARED_PTR_H 39 #include <google/protobuf/stubs/shared_ptr.h> 40 #endif 41 #include <vector> 42 43 #include <google/protobuf/stubs/logging.h> 44 #include <google/protobuf/stubs/common.h> 45 #include <google/protobuf/testing/file.h> 46 #include <google/protobuf/testing/file.h> 47 #include <google/protobuf/testing/file.h> 48 #include <google/protobuf/io/printer.h> 49 #include <google/protobuf/io/zero_copy_stream.h> 50 #include <google/protobuf/descriptor.pb.h> 51 #include <google/protobuf/descriptor.h> 52 #include <google/protobuf/stubs/strutil.h> 53 #include <google/protobuf/stubs/substitute.h> 54 #include <gtest/gtest.h> 55 56 namespace google { 57 namespace protobuf { 58 namespace compiler { 59 60 // Returns the list of the names of files in all_files in the form of a 61 // comma-separated string. 62 string CommaSeparatedList(const vector<const FileDescriptor*> all_files) { 63 vector<string> names; 64 for (int i = 0; i < all_files.size(); i++) { 65 names.push_back(all_files[i]->name()); 66 } 67 return Join(names, ","); 68 } 69 70 static const char* kFirstInsertionPointName = "first_mock_insertion_point"; 71 static const char* kSecondInsertionPointName = "second_mock_insertion_point"; 72 static const char* kFirstInsertionPoint = 73 "# @@protoc_insertion_point(first_mock_insertion_point) is here\n"; 74 static const char* kSecondInsertionPoint = 75 " # @@protoc_insertion_point(second_mock_insertion_point) is here\n"; 76 77 MockCodeGenerator::MockCodeGenerator(const string& name) 78 : name_(name) {} 79 80 MockCodeGenerator::~MockCodeGenerator() {} 81 82 void MockCodeGenerator::ExpectGenerated( 83 const string& name, 84 const string& parameter, 85 const string& insertions, 86 const string& file, 87 const string& first_message_name, 88 const string& first_parsed_file_name, 89 const string& output_directory) { 90 string content; 91 GOOGLE_CHECK_OK( 92 File::GetContents(output_directory + "/" + GetOutputFileName(name, file), 93 &content, true)); 94 95 vector<string> lines = Split(content, "\n", true); 96 97 while (!lines.empty() && lines.back().empty()) { 98 lines.pop_back(); 99 } 100 for (int i = 0; i < lines.size(); i++) { 101 lines[i] += "\n"; 102 } 103 104 vector<string> insertion_list; 105 if (!insertions.empty()) { 106 SplitStringUsing(insertions, ",", &insertion_list); 107 } 108 109 EXPECT_EQ(lines.size(), 3 + insertion_list.size() * 2); 110 EXPECT_EQ(GetOutputFileContent(name, parameter, file, 111 first_parsed_file_name, first_message_name), 112 lines[0]); 113 114 EXPECT_EQ(kFirstInsertionPoint, lines[1 + insertion_list.size()]); 115 EXPECT_EQ(kSecondInsertionPoint, lines[2 + insertion_list.size() * 2]); 116 117 for (int i = 0; i < insertion_list.size(); i++) { 118 EXPECT_EQ(GetOutputFileContent(insertion_list[i], "first_insert", 119 file, file, first_message_name), 120 lines[1 + i]); 121 // Second insertion point is indented, so the inserted text should 122 // automatically be indented too. 123 EXPECT_EQ(" " + GetOutputFileContent(insertion_list[i], "second_insert", 124 file, file, first_message_name), 125 lines[2 + insertion_list.size() + i]); 126 } 127 } 128 129 bool MockCodeGenerator::Generate( 130 const FileDescriptor* file, 131 const string& parameter, 132 GeneratorContext* context, 133 string* error) const { 134 for (int i = 0; i < file->message_type_count(); i++) { 135 if (HasPrefixString(file->message_type(i)->name(), "MockCodeGenerator_")) { 136 string command = StripPrefixString(file->message_type(i)->name(), 137 "MockCodeGenerator_"); 138 if (command == "Error") { 139 *error = "Saw message type MockCodeGenerator_Error."; 140 return false; 141 } else if (command == "Exit") { 142 std::cerr << "Saw message type MockCodeGenerator_Exit." << std::endl; 143 exit(123); 144 } else if (command == "Abort") { 145 std::cerr << "Saw message type MockCodeGenerator_Abort." << std::endl; 146 abort(); 147 } else if (command == "HasSourceCodeInfo") { 148 FileDescriptorProto file_descriptor_proto; 149 file->CopySourceCodeInfoTo(&file_descriptor_proto); 150 bool has_source_code_info = 151 file_descriptor_proto.has_source_code_info() && 152 file_descriptor_proto.source_code_info().location_size() > 0; 153 std::cerr << "Saw message type MockCodeGenerator_HasSourceCodeInfo: " 154 << has_source_code_info << "." << std::endl; 155 abort(); 156 } else if (command == "HasJsonName") { 157 FieldDescriptorProto field_descriptor_proto; 158 file->message_type(i)->field(0)->CopyTo(&field_descriptor_proto); 159 std::cerr << "Saw json_name: " 160 << field_descriptor_proto.has_json_name() << std::endl; 161 abort(); 162 } else { 163 GOOGLE_LOG(FATAL) << "Unknown MockCodeGenerator command: " << command; 164 } 165 } 166 } 167 168 if (HasPrefixString(parameter, "insert=")) { 169 vector<string> insert_into; 170 SplitStringUsing(StripPrefixString(parameter, "insert="), 171 ",", &insert_into); 172 173 for (int i = 0; i < insert_into.size(); i++) { 174 { 175 google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(context->OpenForInsert( 176 GetOutputFileName(insert_into[i], file), kFirstInsertionPointName)); 177 io::Printer printer(output.get(), '$'); 178 printer.PrintRaw(GetOutputFileContent(name_, "first_insert", 179 file, context)); 180 if (printer.failed()) { 181 *error = "MockCodeGenerator detected write error."; 182 return false; 183 } 184 } 185 186 { 187 google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output( 188 context->OpenForInsert(GetOutputFileName(insert_into[i], file), 189 kSecondInsertionPointName)); 190 io::Printer printer(output.get(), '$'); 191 printer.PrintRaw(GetOutputFileContent(name_, "second_insert", 192 file, context)); 193 if (printer.failed()) { 194 *error = "MockCodeGenerator detected write error."; 195 return false; 196 } 197 } 198 } 199 } else { 200 google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output( 201 context->Open(GetOutputFileName(name_, file))); 202 203 io::Printer printer(output.get(), '$'); 204 printer.PrintRaw(GetOutputFileContent(name_, parameter, 205 file, context)); 206 printer.PrintRaw(kFirstInsertionPoint); 207 printer.PrintRaw(kSecondInsertionPoint); 208 209 if (printer.failed()) { 210 *error = "MockCodeGenerator detected write error."; 211 return false; 212 } 213 } 214 215 return true; 216 } 217 218 string MockCodeGenerator::GetOutputFileName(const string& generator_name, 219 const FileDescriptor* file) { 220 return GetOutputFileName(generator_name, file->name()); 221 } 222 223 string MockCodeGenerator::GetOutputFileName(const string& generator_name, 224 const string& file) { 225 return file + ".MockCodeGenerator." + generator_name; 226 } 227 228 string MockCodeGenerator::GetOutputFileContent( 229 const string& generator_name, 230 const string& parameter, 231 const FileDescriptor* file, 232 GeneratorContext *context) { 233 vector<const FileDescriptor*> all_files; 234 context->ListParsedFiles(&all_files); 235 return GetOutputFileContent( 236 generator_name, parameter, file->name(), 237 CommaSeparatedList(all_files), 238 file->message_type_count() > 0 ? 239 file->message_type(0)->name() : "(none)"); 240 } 241 242 string MockCodeGenerator::GetOutputFileContent( 243 const string& generator_name, 244 const string& parameter, 245 const string& file, 246 const string& parsed_file_list, 247 const string& first_message_name) { 248 return strings::Substitute("$0: $1, $2, $3, $4\n", 249 generator_name, parameter, file, 250 first_message_name, parsed_file_list); 251 } 252 253 } // namespace compiler 254 } // namespace protobuf 255 } // namespace google 256