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>
37 #include <google/protobuf/testing/file.h>
38 #include <google/protobuf/descriptor.pb.h>
39 #include <google/protobuf/descriptor.h>
40 #include <google/protobuf/io/zero_copy_stream.h>
41 #include <google/protobuf/io/printer.h>
42 #include <google/protobuf/stubs/strutil.h>
43 #include <google/protobuf/stubs/substitute.h>
44 #include <gtest/gtest.h>
45 #include <google/protobuf/stubs/stl_util.h>
51 // Returns the list of the names of files in all_files in the form of a
52 // comma-separated string.
53 string CommaSeparatedList(const vector<const FileDescriptor*> all_files) {
55 for (int i = 0; i < all_files.size(); i++) {
56 names.push_back(all_files[i]->name());
58 return Join(names, ",");
61 static const char* kFirstInsertionPointName = "first_mock_insertion_point";
62 static const char* kSecondInsertionPointName = "second_mock_insertion_point";
63 static const char* kFirstInsertionPoint =
64 "# @@protoc_insertion_point(first_mock_insertion_point) is here\n";
65 static const char* kSecondInsertionPoint =
66 " # @@protoc_insertion_point(second_mock_insertion_point) is here\n";
68 MockCodeGenerator::MockCodeGenerator(const string& name)
71 MockCodeGenerator::~MockCodeGenerator() {}
73 void MockCodeGenerator::ExpectGenerated(
75 const string& parameter,
76 const string& insertions,
78 const string& first_message_name,
79 const string& first_parsed_file_name,
80 const string& output_directory) {
83 File::GetContents(output_directory + "/" + GetOutputFileName(name, file),
86 vector<string> lines = Split(content, "\n", true);
88 while (!lines.empty() && lines.back().empty()) {
91 for (int i = 0; i < lines.size(); i++) {
95 vector<string> insertion_list;
96 if (!insertions.empty()) {
97 SplitStringUsing(insertions, ",", &insertion_list);
100 EXPECT_EQ(lines.size(), 3 + insertion_list.size() * 2);
101 EXPECT_EQ(GetOutputFileContent(name, parameter, file,
102 first_parsed_file_name, first_message_name),
105 EXPECT_EQ(kFirstInsertionPoint, lines[1 + insertion_list.size()]);
106 EXPECT_EQ(kSecondInsertionPoint, lines[2 + insertion_list.size() * 2]);
108 for (int i = 0; i < insertion_list.size(); i++) {
109 EXPECT_EQ(GetOutputFileContent(insertion_list[i], "first_insert",
110 file, file, first_message_name),
112 // Second insertion point is indented, so the inserted text should
113 // automatically be indented too.
114 EXPECT_EQ(" " + GetOutputFileContent(insertion_list[i], "second_insert",
115 file, file, first_message_name),
116 lines[2 + insertion_list.size() + i]);
120 bool MockCodeGenerator::Generate(
121 const FileDescriptor* file,
122 const string& parameter,
123 GeneratorContext* context,
124 string* error) const {
125 for (int i = 0; i < file->message_type_count(); i++) {
126 if (HasPrefixString(file->message_type(i)->name(), "MockCodeGenerator_")) {
127 string command = StripPrefixString(file->message_type(i)->name(),
128 "MockCodeGenerator_");
129 if (command == "Error") {
130 *error = "Saw message type MockCodeGenerator_Error.";
132 } else if (command == "Exit") {
133 cerr << "Saw message type MockCodeGenerator_Exit." << endl;
135 } else if (command == "Abort") {
136 cerr << "Saw message type MockCodeGenerator_Abort." << endl;
138 } else if (command == "HasSourceCodeInfo") {
139 FileDescriptorProto file_descriptor_proto;
140 file->CopySourceCodeInfoTo(&file_descriptor_proto);
141 bool has_source_code_info =
142 file_descriptor_proto.has_source_code_info() &&
143 file_descriptor_proto.source_code_info().location_size() > 0;
144 cerr << "Saw message type MockCodeGenerator_HasSourceCodeInfo: "
145 << has_source_code_info << "." << endl;
148 GOOGLE_LOG(FATAL) << "Unknown MockCodeGenerator command: " << command;
153 if (HasPrefixString(parameter, "insert=")) {
154 vector<string> insert_into;
155 SplitStringUsing(StripPrefixString(parameter, "insert="),
158 for (int i = 0; i < insert_into.size(); i++) {
160 scoped_ptr<io::ZeroCopyOutputStream> output(context->OpenForInsert(
161 GetOutputFileName(insert_into[i], file), kFirstInsertionPointName));
162 io::Printer printer(output.get(), '$');
163 printer.PrintRaw(GetOutputFileContent(name_, "first_insert",
165 if (printer.failed()) {
166 *error = "MockCodeGenerator detected write error.";
172 scoped_ptr<io::ZeroCopyOutputStream> output(
173 context->OpenForInsert(GetOutputFileName(insert_into[i], file),
174 kSecondInsertionPointName));
175 io::Printer printer(output.get(), '$');
176 printer.PrintRaw(GetOutputFileContent(name_, "second_insert",
178 if (printer.failed()) {
179 *error = "MockCodeGenerator detected write error.";
185 scoped_ptr<io::ZeroCopyOutputStream> output(
186 context->Open(GetOutputFileName(name_, file)));
188 io::Printer printer(output.get(), '$');
189 printer.PrintRaw(GetOutputFileContent(name_, parameter,
191 printer.PrintRaw(kFirstInsertionPoint);
192 printer.PrintRaw(kSecondInsertionPoint);
194 if (printer.failed()) {
195 *error = "MockCodeGenerator detected write error.";
203 string MockCodeGenerator::GetOutputFileName(const string& generator_name,
204 const FileDescriptor* file) {
205 return GetOutputFileName(generator_name, file->name());
208 string MockCodeGenerator::GetOutputFileName(const string& generator_name,
209 const string& file) {
210 return file + ".MockCodeGenerator." + generator_name;
213 string MockCodeGenerator::GetOutputFileContent(
214 const string& generator_name,
215 const string& parameter,
216 const FileDescriptor* file,
217 GeneratorContext *context) {
218 vector<const FileDescriptor*> all_files;
219 context->ListParsedFiles(&all_files);
220 return GetOutputFileContent(
221 generator_name, parameter, file->name(),
222 CommaSeparatedList(all_files),
223 file->message_type_count() > 0 ?
224 file->message_type(0)->name() : "(none)");
227 string MockCodeGenerator::GetOutputFileContent(
228 const string& generator_name,
229 const string& parameter,
231 const string& parsed_file_list,
232 const string& first_message_name) {
233 return strings::Substitute("$0: $1, $2, $3, $4\n",
234 generator_name, parameter, file,
235 first_message_name, parsed_file_list);
238 } // namespace compiler
239 } // namespace protobuf
240 } // namespace google