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@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.
CommaSeparatedList(const vector<const FileDescriptor * > all_files)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
MockCodeGenerator(const string & name)77 MockCodeGenerator::MockCodeGenerator(const string& name)
78 : name_(name) {}
79
~MockCodeGenerator()80 MockCodeGenerator::~MockCodeGenerator() {}
81
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)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
Generate(const FileDescriptor * file,const string & parameter,GeneratorContext * context,string * error) const129 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
GetOutputFileName(const string & generator_name,const FileDescriptor * file)218 string MockCodeGenerator::GetOutputFileName(const string& generator_name,
219 const FileDescriptor* file) {
220 return GetOutputFileName(generator_name, file->name());
221 }
222
GetOutputFileName(const string & generator_name,const string & file)223 string MockCodeGenerator::GetOutputFileName(const string& generator_name,
224 const string& file) {
225 return file + ".MockCodeGenerator." + generator_name;
226 }
227
GetOutputFileContent(const string & generator_name,const string & parameter,const FileDescriptor * file,GeneratorContext * context)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
GetOutputFileContent(const string & generator_name,const string & parameter,const string & file,const string & parsed_file_list,const string & first_message_name)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