1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 // http://code.google.com/p/protobuf/
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 <google/protobuf/testing/file.h>
36 #include <google/protobuf/descriptor.h>
37 #include <google/protobuf/io/zero_copy_stream.h>
38 #include <google/protobuf/io/printer.h>
39 #include <google/protobuf/stubs/strutil.h>
40 #include <google/protobuf/stubs/substitute.h>
41 #include <gtest/gtest.h>
42 #include <google/protobuf/stubs/stl_util-inl.h>
43
44 namespace google {
45 namespace protobuf {
46 namespace compiler {
47
48 static const char* kFirstInsertionPointName = "first_mock_insertion_point";
49 static const char* kSecondInsertionPointName = "second_mock_insertion_point";
50 static const char* kFirstInsertionPoint =
51 "# @@protoc_insertion_point(first_mock_insertion_point) is here\n";
52 static const char* kSecondInsertionPoint =
53 " # @@protoc_insertion_point(second_mock_insertion_point) is here\n";
54
MockCodeGenerator(const string & name)55 MockCodeGenerator::MockCodeGenerator(const string& name)
56 : name_(name) {}
57
~MockCodeGenerator()58 MockCodeGenerator::~MockCodeGenerator() {}
59
ExpectGenerated(const string & name,const string & parameter,const string & insertions,const string & file,const string & first_message_name,const string & output_directory)60 void MockCodeGenerator::ExpectGenerated(
61 const string& name,
62 const string& parameter,
63 const string& insertions,
64 const string& file,
65 const string& first_message_name,
66 const string& output_directory) {
67 string content;
68 ASSERT_TRUE(File::ReadFileToString(
69 output_directory + "/" + GetOutputFileName(name, file), &content));
70
71 vector<string> lines;
72 SplitStringUsing(content, "\n", &lines);
73
74 while (!lines.empty() && lines.back().empty()) {
75 lines.pop_back();
76 }
77 for (int i = 0; i < lines.size(); i++) {
78 lines[i] += "\n";
79 }
80
81 vector<string> insertion_list;
82 if (!insertions.empty()) {
83 SplitStringUsing(insertions, ",", &insertion_list);
84 }
85
86 ASSERT_EQ(lines.size(), 3 + insertion_list.size() * 2);
87 EXPECT_EQ(GetOutputFileContent(name, parameter, file, first_message_name),
88 lines[0]);
89
90 EXPECT_EQ(kFirstInsertionPoint, lines[1 + insertion_list.size()]);
91 EXPECT_EQ(kSecondInsertionPoint, lines[2 + insertion_list.size() * 2]);
92
93 for (int i = 0; i < insertion_list.size(); i++) {
94 EXPECT_EQ(GetOutputFileContent(insertion_list[i], "first_insert",
95 file, first_message_name),
96 lines[1 + i]);
97 // Second insertion point is indented, so the inserted text should
98 // automatically be indented too.
99 EXPECT_EQ(" " + GetOutputFileContent(insertion_list[i], "second_insert",
100 file, first_message_name),
101 lines[2 + insertion_list.size() + i]);
102 }
103 }
104
Generate(const FileDescriptor * file,const string & parameter,OutputDirectory * output_directory,string * error) const105 bool MockCodeGenerator::Generate(
106 const FileDescriptor* file,
107 const string& parameter,
108 OutputDirectory* output_directory,
109 string* error) const {
110 for (int i = 0; i < file->message_type_count(); i++) {
111 if (HasPrefixString(file->message_type(i)->name(), "MockCodeGenerator_")) {
112 string command = StripPrefixString(file->message_type(i)->name(),
113 "MockCodeGenerator_");
114 if (command == "Error") {
115 *error = "Saw message type MockCodeGenerator_Error.";
116 return false;
117 } else if (command == "Exit") {
118 cerr << "Saw message type MockCodeGenerator_Exit." << endl;
119 exit(123);
120 } else if (command == "Abort") {
121 cerr << "Saw message type MockCodeGenerator_Abort." << endl;
122 abort();
123 } else {
124 GOOGLE_LOG(FATAL) << "Unknown MockCodeGenerator command: " << command;
125 }
126 }
127 }
128
129 if (HasPrefixString(parameter, "insert=")) {
130 vector<string> insert_into;
131 SplitStringUsing(StripPrefixString(parameter, "insert="),
132 ",", &insert_into);
133
134 for (int i = 0; i < insert_into.size(); i++) {
135 {
136 scoped_ptr<io::ZeroCopyOutputStream> output(
137 output_directory->OpenForInsert(
138 GetOutputFileName(insert_into[i], file),
139 kFirstInsertionPointName));
140 io::Printer printer(output.get(), '$');
141 printer.PrintRaw(GetOutputFileContent(name_, "first_insert", file));
142 if (printer.failed()) {
143 *error = "MockCodeGenerator detected write error.";
144 return false;
145 }
146 }
147
148 {
149 scoped_ptr<io::ZeroCopyOutputStream> output(
150 output_directory->OpenForInsert(
151 GetOutputFileName(insert_into[i], file),
152 kSecondInsertionPointName));
153 io::Printer printer(output.get(), '$');
154 printer.PrintRaw(GetOutputFileContent(name_, "second_insert", file));
155 if (printer.failed()) {
156 *error = "MockCodeGenerator detected write error.";
157 return false;
158 }
159 }
160 }
161 } else {
162 scoped_ptr<io::ZeroCopyOutputStream> output(
163 output_directory->Open(GetOutputFileName(name_, file)));
164
165 io::Printer printer(output.get(), '$');
166 printer.PrintRaw(GetOutputFileContent(name_, parameter, file));
167 printer.PrintRaw(kFirstInsertionPoint);
168 printer.PrintRaw(kSecondInsertionPoint);
169
170 if (printer.failed()) {
171 *error = "MockCodeGenerator detected write error.";
172 return false;
173 }
174 }
175
176 return true;
177 }
178
GetOutputFileName(const string & generator_name,const FileDescriptor * file)179 string MockCodeGenerator::GetOutputFileName(const string& generator_name,
180 const FileDescriptor* file) {
181 return GetOutputFileName(generator_name, file->name());
182 }
183
GetOutputFileName(const string & generator_name,const string & file)184 string MockCodeGenerator::GetOutputFileName(const string& generator_name,
185 const string& file) {
186 return file + ".MockCodeGenerator." + generator_name;
187 }
188
GetOutputFileContent(const string & generator_name,const string & parameter,const FileDescriptor * file)189 string MockCodeGenerator::GetOutputFileContent(const string& generator_name,
190 const string& parameter,
191 const FileDescriptor* file) {
192 return GetOutputFileContent(
193 generator_name, parameter, file->name(),
194 file->message_type_count() > 0 ?
195 file->message_type(0)->name() : "(none)");
196 }
197
GetOutputFileContent(const string & generator_name,const string & parameter,const string & file,const string & first_message_name)198 string MockCodeGenerator::GetOutputFileContent(
199 const string& generator_name,
200 const string& parameter,
201 const string& file,
202 const string& first_message_name) {
203 return strings::Substitute("$0: $1, $2, $3\n",
204 generator_name, parameter, file, first_message_name);
205 }
206
207 } // namespace compiler
208 } // namespace protobuf
209 } // namespace google
210