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