1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 // https://developers.google.com/protocol-buffers/
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
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
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.
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.
31 // Author: kenton@google.com (Kenton Varda)
33 #include <google/protobuf/compiler/mock_code_generator.h>
39 #include <google/protobuf/stubs/strutil.h>
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/compiler/plugin.pb.h>
49 #include <google/protobuf/io/printer.h>
50 #include <google/protobuf/io/zero_copy_stream.h>
51 #include <google/protobuf/descriptor.pb.h>
52 #include <google/protobuf/descriptor.h>
53 #include <google/protobuf/text_format.h>
54 #include <google/protobuf/stubs/substitute.h>
55 #include <gtest/gtest.h>
68 // Returns the list of the names of files in all_files in the form of a
69 // comma-separated string.
70 std::string CommaSeparatedList(
71 const std::vector<const FileDescriptor*>& all_files) {
72 std::vector<std::string> names;
73 for (size_t i = 0; i < all_files.size(); i++) {
74 names.push_back(all_files[i]->name());
76 return Join(names, ",");
79 static const char* kFirstInsertionPointName = "first_mock_insertion_point";
80 static const char* kSecondInsertionPointName = "second_mock_insertion_point";
81 static const char* kFirstInsertionPoint =
82 "# @@protoc_insertion_point(first_mock_insertion_point) is here\n";
83 static const char* kSecondInsertionPoint =
84 " # @@protoc_insertion_point(second_mock_insertion_point) is here\n";
86 MockCodeGenerator::MockCodeGenerator(const std::string& name) : name_(name) {}
88 MockCodeGenerator::~MockCodeGenerator() {}
90 void MockCodeGenerator::ExpectGenerated(
91 const std::string& name, const std::string& parameter,
92 const std::string& insertions, const std::string& file,
93 const std::string& first_message_name,
94 const std::string& first_parsed_file_name,
95 const std::string& output_directory) {
98 File::GetContents(output_directory + "/" + GetOutputFileName(name, file),
101 std::vector<std::string> lines =
102 Split(content, "\n", true);
104 while (!lines.empty() && lines.back().empty()) {
107 for (size_t i = 0; i < lines.size(); i++) {
111 std::vector<std::string> insertion_list;
112 if (!insertions.empty()) {
113 SplitStringUsing(insertions, ",", &insertion_list);
116 EXPECT_EQ(lines.size(), 3 + insertion_list.size() * 2);
117 EXPECT_EQ(GetOutputFileContent(name, parameter, file, first_parsed_file_name,
121 EXPECT_EQ(kFirstInsertionPoint, lines[1 + insertion_list.size()]);
122 EXPECT_EQ(kSecondInsertionPoint, lines[2 + insertion_list.size() * 2]);
124 for (size_t i = 0; i < insertion_list.size(); i++) {
125 EXPECT_EQ(GetOutputFileContent(insertion_list[i], "first_insert", file,
126 file, first_message_name),
128 // Second insertion point is indented, so the inserted text should
129 // automatically be indented too.
130 EXPECT_EQ(" " + GetOutputFileContent(insertion_list[i], "second_insert",
131 file, file, first_message_name),
132 lines[2 + insertion_list.size() + i]);
137 void CheckSingleAnnotation(const std::string& expected_file,
138 const std::string& expected_text,
139 const std::string& file_content,
140 const GeneratedCodeInfo::Annotation& annotation) {
141 EXPECT_EQ(expected_file, annotation.source_file());
142 ASSERT_GE(file_content.size(), annotation.begin());
143 ASSERT_GE(file_content.size(), annotation.end());
144 ASSERT_LE(annotation.begin(), annotation.end());
145 EXPECT_EQ(expected_text.size(), annotation.end() - annotation.begin());
146 EXPECT_EQ(expected_text,
147 file_content.substr(annotation.begin(), expected_text.size()));
149 } // anonymous namespace
151 void MockCodeGenerator::CheckGeneratedAnnotations(
152 const string& name, const std::string& file,
153 const std::string& output_directory) {
154 std::string file_content;
156 File::GetContents(output_directory + "/" + GetOutputFileName(name, file),
157 &file_content, true));
158 std::string meta_content;
159 GOOGLE_CHECK_OK(File::GetContents(
160 output_directory + "/" + GetOutputFileName(name, file) + ".meta",
161 &meta_content, true));
162 GeneratedCodeInfo annotations;
163 GOOGLE_CHECK(TextFormat::ParseFromString(meta_content, &annotations));
164 ASSERT_EQ(3, annotations.annotation_size());
165 CheckSingleAnnotation("first_annotation", "first", file_content,
166 annotations.annotation(0));
167 CheckSingleAnnotation("second_annotation", "second", file_content,
168 annotations.annotation(1));
169 CheckSingleAnnotation("third_annotation", "third", file_content,
170 annotations.annotation(2));
173 bool MockCodeGenerator::Generate(const FileDescriptor* file,
174 const std::string& parameter,
175 GeneratorContext* context,
176 std::string* error) const {
177 bool annotate = false;
178 for (int i = 0; i < file->message_type_count(); i++) {
179 if (HasPrefixString(file->message_type(i)->name(), "MockCodeGenerator_")) {
180 std::string command = StripPrefixString(
181 file->message_type(i)->name(), "MockCodeGenerator_");
182 if (command == "Error") {
183 *error = "Saw message type MockCodeGenerator_Error.";
185 } else if (command == "Exit") {
186 std::cerr << "Saw message type MockCodeGenerator_Exit." << std::endl;
188 } else if (command == "Abort") {
189 std::cerr << "Saw message type MockCodeGenerator_Abort." << std::endl;
191 } else if (command == "HasSourceCodeInfo") {
192 FileDescriptorProto file_descriptor_proto;
193 file->CopySourceCodeInfoTo(&file_descriptor_proto);
194 bool has_source_code_info =
195 file_descriptor_proto.has_source_code_info() &&
196 file_descriptor_proto.source_code_info().location_size() > 0;
197 std::cerr << "Saw message type MockCodeGenerator_HasSourceCodeInfo: "
198 << has_source_code_info << "." << std::endl;
200 } else if (command == "HasJsonName") {
201 FieldDescriptorProto field_descriptor_proto;
202 file->message_type(i)->field(0)->CopyTo(&field_descriptor_proto);
203 std::cerr << "Saw json_name: " << field_descriptor_proto.has_json_name()
206 } else if (command == "Annotate") {
208 } else if (command == "ShowVersionNumber") {
209 Version compiler_version;
210 context->GetCompilerVersion(&compiler_version);
211 std::cerr << "Saw compiler_version: "
212 << compiler_version.major() * 1000000 +
213 compiler_version.minor() * 1000 +
214 compiler_version.patch()
215 << " " << compiler_version.suffix() << std::endl;
218 GOOGLE_LOG(FATAL) << "Unknown MockCodeGenerator command: " << command;
223 if (HasPrefixString(parameter, "insert=")) {
224 std::vector<std::string> insert_into;
225 SplitStringUsing(StripPrefixString(parameter, "insert="), ",",
228 for (size_t i = 0; i < insert_into.size(); i++) {
230 std::unique_ptr<io::ZeroCopyOutputStream> output(context->OpenForInsert(
231 GetOutputFileName(insert_into[i], file), kFirstInsertionPointName));
232 io::Printer printer(output.get(), '$');
234 GetOutputFileContent(name_, "first_insert", file, context));
235 if (printer.failed()) {
236 *error = "MockCodeGenerator detected write error.";
242 std::unique_ptr<io::ZeroCopyOutputStream> output(
243 context->OpenForInsert(GetOutputFileName(insert_into[i], file),
244 kSecondInsertionPointName));
245 io::Printer printer(output.get(), '$');
247 GetOutputFileContent(name_, "second_insert", file, context));
248 if (printer.failed()) {
249 *error = "MockCodeGenerator detected write error.";
255 std::unique_ptr<io::ZeroCopyOutputStream> output(
256 context->Open(GetOutputFileName(name_, file)));
258 GeneratedCodeInfo annotations;
259 io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector(
261 io::Printer printer(output.get(), '$',
262 annotate ? &annotation_collector : NULL);
263 printer.PrintRaw(GetOutputFileContent(name_, parameter, file, context));
264 std::string annotate_suffix = "_annotation";
266 printer.Print("$p$", "p", "first");
267 printer.Annotate("p", "first" + annotate_suffix);
269 printer.PrintRaw(kFirstInsertionPoint);
271 printer.Print("$p$", "p", "second");
272 printer.Annotate("p", "second" + annotate_suffix);
274 printer.PrintRaw(kSecondInsertionPoint);
276 printer.Print("$p$", "p", "third");
277 printer.Annotate("p", "third" + annotate_suffix);
280 if (printer.failed()) {
281 *error = "MockCodeGenerator detected write error.";
285 std::unique_ptr<io::ZeroCopyOutputStream> meta_output(
286 context->Open(GetOutputFileName(name_, file) + ".meta"));
287 if (!TextFormat::Print(annotations, meta_output.get())) {
288 *error = "MockCodeGenerator couldn't write .meta";
297 std::string MockCodeGenerator::GetOutputFileName(
298 const std::string& generator_name, const FileDescriptor* file) {
299 return GetOutputFileName(generator_name, file->name());
302 std::string MockCodeGenerator::GetOutputFileName(
303 const std::string& generator_name, const std::string& file) {
304 return file + ".MockCodeGenerator." + generator_name;
307 std::string MockCodeGenerator::GetOutputFileContent(
308 const std::string& generator_name, const std::string& parameter,
309 const FileDescriptor* file, GeneratorContext* context) {
310 std::vector<const FileDescriptor*> all_files;
311 context->ListParsedFiles(&all_files);
312 return GetOutputFileContent(
313 generator_name, parameter, file->name(), CommaSeparatedList(all_files),
314 file->message_type_count() > 0 ? file->message_type(0)->name()
318 std::string MockCodeGenerator::GetOutputFileContent(
319 const std::string& generator_name, const std::string& parameter,
320 const std::string& file, const std::string& parsed_file_list,
321 const std::string& first_message_name) {
322 return strings::Substitute("$0: $1, $2, $3, $4\n", generator_name, parameter,
323 file, first_message_name, parsed_file_list);
326 } // namespace compiler
327 } // namespace protobuf
328 } // namespace google