blob: 5b76af257792472748d0ac397580d7c1d05f5343 [file] [log] [blame]
kenton@google.com5e744ff2009-12-18 04:51:42 +00001// 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
44namespace google {
45namespace protobuf {
46namespace compiler {
47
liujisi@google.com33165fe2010-11-02 13:14:58 +000048// Returns the list of the names of files in all_files in the form of a
49// comma-separated string.
50string CommaSeparatedList(const vector<const FileDescriptor*> all_files) {
51 vector<string> names;
52 for (int i = 0; i < all_files.size(); i++) {
53 names.push_back(all_files[i]->name());
54 }
55 return JoinStrings(names, ",");
56}
57
kenton@google.com5e744ff2009-12-18 04:51:42 +000058static const char* kFirstInsertionPointName = "first_mock_insertion_point";
59static const char* kSecondInsertionPointName = "second_mock_insertion_point";
60static const char* kFirstInsertionPoint =
61 "# @@protoc_insertion_point(first_mock_insertion_point) is here\n";
62static const char* kSecondInsertionPoint =
kenton@google.com5f121642009-12-23 07:03:06 +000063 " # @@protoc_insertion_point(second_mock_insertion_point) is here\n";
kenton@google.com5e744ff2009-12-18 04:51:42 +000064
65MockCodeGenerator::MockCodeGenerator(const string& name)
66 : name_(name) {}
67
68MockCodeGenerator::~MockCodeGenerator() {}
69
70void MockCodeGenerator::ExpectGenerated(
71 const string& name,
72 const string& parameter,
73 const string& insertions,
74 const string& file,
75 const string& first_message_name,
liujisi@google.com33165fe2010-11-02 13:14:58 +000076 const string& first_parsed_file_name,
kenton@google.com5e744ff2009-12-18 04:51:42 +000077 const string& output_directory) {
78 string content;
79 ASSERT_TRUE(File::ReadFileToString(
80 output_directory + "/" + GetOutputFileName(name, file), &content));
81
82 vector<string> lines;
83 SplitStringUsing(content, "\n", &lines);
84
85 while (!lines.empty() && lines.back().empty()) {
86 lines.pop_back();
87 }
88 for (int i = 0; i < lines.size(); i++) {
89 lines[i] += "\n";
90 }
91
92 vector<string> insertion_list;
93 if (!insertions.empty()) {
94 SplitStringUsing(insertions, ",", &insertion_list);
95 }
96
97 ASSERT_EQ(lines.size(), 3 + insertion_list.size() * 2);
liujisi@google.com33165fe2010-11-02 13:14:58 +000098 EXPECT_EQ(GetOutputFileContent(name, parameter, file,
99 first_parsed_file_name, first_message_name),
kenton@google.com5e744ff2009-12-18 04:51:42 +0000100 lines[0]);
101
102 EXPECT_EQ(kFirstInsertionPoint, lines[1 + insertion_list.size()]);
103 EXPECT_EQ(kSecondInsertionPoint, lines[2 + insertion_list.size() * 2]);
104
105 for (int i = 0; i < insertion_list.size(); i++) {
106 EXPECT_EQ(GetOutputFileContent(insertion_list[i], "first_insert",
liujisi@google.com33165fe2010-11-02 13:14:58 +0000107 file, file, first_message_name),
kenton@google.com5e744ff2009-12-18 04:51:42 +0000108 lines[1 + i]);
kenton@google.com5f121642009-12-23 07:03:06 +0000109 // Second insertion point is indented, so the inserted text should
110 // automatically be indented too.
111 EXPECT_EQ(" " + GetOutputFileContent(insertion_list[i], "second_insert",
liujisi@google.com33165fe2010-11-02 13:14:58 +0000112 file, file, first_message_name),
kenton@google.com5e744ff2009-12-18 04:51:42 +0000113 lines[2 + insertion_list.size() + i]);
114 }
115}
116
117bool MockCodeGenerator::Generate(
118 const FileDescriptor* file,
119 const string& parameter,
liujisi@google.com33165fe2010-11-02 13:14:58 +0000120 GeneratorContext* context,
kenton@google.com5e744ff2009-12-18 04:51:42 +0000121 string* error) const {
122 for (int i = 0; i < file->message_type_count(); i++) {
123 if (HasPrefixString(file->message_type(i)->name(), "MockCodeGenerator_")) {
124 string command = StripPrefixString(file->message_type(i)->name(),
125 "MockCodeGenerator_");
126 if (command == "Error") {
127 *error = "Saw message type MockCodeGenerator_Error.";
128 return false;
129 } else if (command == "Exit") {
130 cerr << "Saw message type MockCodeGenerator_Exit." << endl;
131 exit(123);
132 } else if (command == "Abort") {
133 cerr << "Saw message type MockCodeGenerator_Abort." << endl;
134 abort();
135 } else {
136 GOOGLE_LOG(FATAL) << "Unknown MockCodeGenerator command: " << command;
137 }
138 }
139 }
140
141 if (HasPrefixString(parameter, "insert=")) {
142 vector<string> insert_into;
143 SplitStringUsing(StripPrefixString(parameter, "insert="),
144 ",", &insert_into);
145
146 for (int i = 0; i < insert_into.size(); i++) {
147 {
148 scoped_ptr<io::ZeroCopyOutputStream> output(
liujisi@google.com33165fe2010-11-02 13:14:58 +0000149 context->OpenForInsert(
kenton@google.com5e744ff2009-12-18 04:51:42 +0000150 GetOutputFileName(insert_into[i], file),
151 kFirstInsertionPointName));
152 io::Printer printer(output.get(), '$');
liujisi@google.com33165fe2010-11-02 13:14:58 +0000153 printer.PrintRaw(GetOutputFileContent(name_, "first_insert",
154 file, context));
kenton@google.com5e744ff2009-12-18 04:51:42 +0000155 if (printer.failed()) {
156 *error = "MockCodeGenerator detected write error.";
157 return false;
158 }
159 }
160
161 {
162 scoped_ptr<io::ZeroCopyOutputStream> output(
liujisi@google.com33165fe2010-11-02 13:14:58 +0000163 context->OpenForInsert(
kenton@google.com5e744ff2009-12-18 04:51:42 +0000164 GetOutputFileName(insert_into[i], file),
165 kSecondInsertionPointName));
166 io::Printer printer(output.get(), '$');
liujisi@google.com33165fe2010-11-02 13:14:58 +0000167 printer.PrintRaw(GetOutputFileContent(name_, "second_insert",
168 file, context));
kenton@google.com5e744ff2009-12-18 04:51:42 +0000169 if (printer.failed()) {
170 *error = "MockCodeGenerator detected write error.";
171 return false;
172 }
173 }
174 }
175 } else {
176 scoped_ptr<io::ZeroCopyOutputStream> output(
liujisi@google.com33165fe2010-11-02 13:14:58 +0000177 context->Open(GetOutputFileName(name_, file)));
kenton@google.com5e744ff2009-12-18 04:51:42 +0000178
179 io::Printer printer(output.get(), '$');
liujisi@google.com33165fe2010-11-02 13:14:58 +0000180 printer.PrintRaw(GetOutputFileContent(name_, parameter,
181 file, context));
kenton@google.com5e744ff2009-12-18 04:51:42 +0000182 printer.PrintRaw(kFirstInsertionPoint);
183 printer.PrintRaw(kSecondInsertionPoint);
184
185 if (printer.failed()) {
186 *error = "MockCodeGenerator detected write error.";
187 return false;
188 }
189 }
190
191 return true;
192}
193
194string MockCodeGenerator::GetOutputFileName(const string& generator_name,
195 const FileDescriptor* file) {
196 return GetOutputFileName(generator_name, file->name());
197}
198
199string MockCodeGenerator::GetOutputFileName(const string& generator_name,
200 const string& file) {
201 return file + ".MockCodeGenerator." + generator_name;
202}
203
liujisi@google.com33165fe2010-11-02 13:14:58 +0000204string MockCodeGenerator::GetOutputFileContent(
205 const string& generator_name,
206 const string& parameter,
207 const FileDescriptor* file,
208 GeneratorContext *context) {
209 vector<const FileDescriptor*> all_files;
210 context->ListParsedFiles(&all_files);
kenton@google.com5e744ff2009-12-18 04:51:42 +0000211 return GetOutputFileContent(
212 generator_name, parameter, file->name(),
liujisi@google.com33165fe2010-11-02 13:14:58 +0000213 CommaSeparatedList(all_files),
kenton@google.com5e744ff2009-12-18 04:51:42 +0000214 file->message_type_count() > 0 ?
215 file->message_type(0)->name() : "(none)");
216}
217
218string MockCodeGenerator::GetOutputFileContent(
219 const string& generator_name,
220 const string& parameter,
221 const string& file,
liujisi@google.com33165fe2010-11-02 13:14:58 +0000222 const string& parsed_file_list,
kenton@google.com5e744ff2009-12-18 04:51:42 +0000223 const string& first_message_name) {
liujisi@google.com33165fe2010-11-02 13:14:58 +0000224 return strings::Substitute("$0: $1, $2, $3, $4\n",
225 generator_name, parameter, file,
226 first_message_name, parsed_file_list);
kenton@google.com5e744ff2009-12-18 04:51:42 +0000227}
228
229} // namespace compiler
230} // namespace protobuf
231} // namespace google