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 44namespace google { 45namespace protobuf { 46namespace compiler { 47 48static const char* kFirstInsertionPointName = "first_mock_insertion_point"; 49static const char* kSecondInsertionPointName = "second_mock_insertion_point"; 50static const char* kFirstInsertionPoint = 51 "# @@protoc_insertion_point(first_mock_insertion_point) is here\n"; 52static const char* kSecondInsertionPoint = 53 " # @@protoc_insertion_point(second_mock_insertion_point) is here\n"; 54 55MockCodeGenerator::MockCodeGenerator(const string& name) 56 : name_(name) {} 57 58MockCodeGenerator::~MockCodeGenerator() {} 59 60void 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 105bool 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 179string MockCodeGenerator::GetOutputFileName(const string& generator_name, 180 const FileDescriptor* file) { 181 return GetOutputFileName(generator_name, file->name()); 182} 183 184string MockCodeGenerator::GetOutputFileName(const string& generator_name, 185 const string& file) { 186 return file + ".MockCodeGenerator." + generator_name; 187} 188 189string 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 198string 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