• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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