mock_code_generator.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  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. // Author: kenton@google.com (Kenton Varda)
  31. #include <google/protobuf/compiler/mock_code_generator.h>
  32. #include <stdlib.h>
  33. #include <iostream>
  34. #include <memory>
  35. #include <vector>
  36. #include <google/protobuf/stubs/strutil.h>
  37. #include <google/protobuf/stubs/logging.h>
  38. #include <google/protobuf/stubs/common.h>
  39. #include <google/protobuf/testing/file.h>
  40. #include <google/protobuf/testing/file.h>
  41. #include <google/protobuf/testing/file.h>
  42. #include <google/protobuf/compiler/plugin.pb.h>
  43. #include <google/protobuf/io/printer.h>
  44. #include <google/protobuf/io/zero_copy_stream.h>
  45. #include <google/protobuf/descriptor.pb.h>
  46. #include <google/protobuf/descriptor.h>
  47. #include <google/protobuf/text_format.h>
  48. #include <google/protobuf/stubs/substitute.h>
  49. #include <gtest/gtest.h>
  50. #ifdef major
  51. #undef major
  52. #endif
  53. #ifdef minor
  54. #undef minor
  55. #endif
  56. namespace google {
  57. namespace protobuf {
  58. namespace compiler {
  59. // Returns the list of the names of files in all_files in the form of a
  60. // comma-separated string.
  61. string CommaSeparatedList(const std::vector<const FileDescriptor*>& all_files) {
  62. std::vector<string> names;
  63. for (size_t i = 0; i < all_files.size(); i++) {
  64. names.push_back(all_files[i]->name());
  65. }
  66. return Join(names, ",");
  67. }
  68. static const char* kFirstInsertionPointName = "first_mock_insertion_point";
  69. static const char* kSecondInsertionPointName = "second_mock_insertion_point";
  70. static const char* kFirstInsertionPoint =
  71. "# @@protoc_insertion_point(first_mock_insertion_point) is here\n";
  72. static const char* kSecondInsertionPoint =
  73. " # @@protoc_insertion_point(second_mock_insertion_point) is here\n";
  74. MockCodeGenerator::MockCodeGenerator(const string& name)
  75. : name_(name) {}
  76. MockCodeGenerator::~MockCodeGenerator() {}
  77. void MockCodeGenerator::ExpectGenerated(
  78. const string& name,
  79. const string& parameter,
  80. const string& insertions,
  81. const string& file,
  82. const string& first_message_name,
  83. const string& first_parsed_file_name,
  84. const string& output_directory) {
  85. string content;
  86. GOOGLE_CHECK_OK(
  87. File::GetContents(output_directory + "/" + GetOutputFileName(name, file),
  88. &content, true));
  89. std::vector<string> lines = Split(content, "\n", true);
  90. while (!lines.empty() && lines.back().empty()) {
  91. lines.pop_back();
  92. }
  93. for (size_t i = 0; i < lines.size(); i++) {
  94. lines[i] += "\n";
  95. }
  96. std::vector<string> insertion_list;
  97. if (!insertions.empty()) {
  98. SplitStringUsing(insertions, ",", &insertion_list);
  99. }
  100. EXPECT_EQ(lines.size(), 3 + insertion_list.size() * 2);
  101. EXPECT_EQ(GetOutputFileContent(name, parameter, file,
  102. first_parsed_file_name, first_message_name),
  103. lines[0]);
  104. EXPECT_EQ(kFirstInsertionPoint, lines[1 + insertion_list.size()]);
  105. EXPECT_EQ(kSecondInsertionPoint, lines[2 + insertion_list.size() * 2]);
  106. for (size_t i = 0; i < insertion_list.size(); i++) {
  107. EXPECT_EQ(GetOutputFileContent(insertion_list[i], "first_insert",
  108. file, file, first_message_name),
  109. lines[1 + i]);
  110. // Second insertion point is indented, so the inserted text should
  111. // automatically be indented too.
  112. EXPECT_EQ(" " + GetOutputFileContent(insertion_list[i], "second_insert",
  113. file, file, first_message_name),
  114. lines[2 + insertion_list.size() + i]);
  115. }
  116. }
  117. namespace {
  118. void CheckSingleAnnotation(const string& expected_file,
  119. const string& expected_text,
  120. const string& file_content,
  121. const GeneratedCodeInfo::Annotation& annotation) {
  122. EXPECT_EQ(expected_file, annotation.source_file());
  123. ASSERT_GE(file_content.size(), annotation.begin());
  124. ASSERT_GE(file_content.size(), annotation.end());
  125. ASSERT_LE(annotation.begin(), annotation.end());
  126. EXPECT_EQ(expected_text.size(), annotation.end() - annotation.begin());
  127. EXPECT_EQ(expected_text,
  128. file_content.substr(annotation.begin(), expected_text.size()));
  129. }
  130. } // anonymous namespace
  131. void MockCodeGenerator::CheckGeneratedAnnotations(
  132. const string& name, const string& file, const string& output_directory) {
  133. string file_content;
  134. GOOGLE_CHECK_OK(
  135. File::GetContents(output_directory + "/" + GetOutputFileName(name, file),
  136. &file_content, true));
  137. string meta_content;
  138. GOOGLE_CHECK_OK(File::GetContents(
  139. output_directory + "/" + GetOutputFileName(name, file) + ".meta",
  140. &meta_content, true));
  141. GeneratedCodeInfo annotations;
  142. GOOGLE_CHECK(TextFormat::ParseFromString(meta_content, &annotations));
  143. ASSERT_EQ(3, annotations.annotation_size());
  144. CheckSingleAnnotation("first_annotation", "first", file_content,
  145. annotations.annotation(0));
  146. CheckSingleAnnotation("second_annotation", "second", file_content,
  147. annotations.annotation(1));
  148. CheckSingleAnnotation("third_annotation", "third", file_content,
  149. annotations.annotation(2));
  150. }
  151. bool MockCodeGenerator::Generate(
  152. const FileDescriptor* file,
  153. const string& parameter,
  154. GeneratorContext* context,
  155. string* error) const {
  156. bool annotate = false;
  157. for (int i = 0; i < file->message_type_count(); i++) {
  158. if (HasPrefixString(file->message_type(i)->name(), "MockCodeGenerator_")) {
  159. string command = StripPrefixString(file->message_type(i)->name(),
  160. "MockCodeGenerator_");
  161. if (command == "Error") {
  162. *error = "Saw message type MockCodeGenerator_Error.";
  163. return false;
  164. } else if (command == "Exit") {
  165. std::cerr << "Saw message type MockCodeGenerator_Exit." << std::endl;
  166. exit(123);
  167. } else if (command == "Abort") {
  168. std::cerr << "Saw message type MockCodeGenerator_Abort." << std::endl;
  169. abort();
  170. } else if (command == "HasSourceCodeInfo") {
  171. FileDescriptorProto file_descriptor_proto;
  172. file->CopySourceCodeInfoTo(&file_descriptor_proto);
  173. bool has_source_code_info =
  174. file_descriptor_proto.has_source_code_info() &&
  175. file_descriptor_proto.source_code_info().location_size() > 0;
  176. std::cerr << "Saw message type MockCodeGenerator_HasSourceCodeInfo: "
  177. << has_source_code_info << "." << std::endl;
  178. abort();
  179. } else if (command == "HasJsonName") {
  180. FieldDescriptorProto field_descriptor_proto;
  181. file->message_type(i)->field(0)->CopyTo(&field_descriptor_proto);
  182. std::cerr << "Saw json_name: "
  183. << field_descriptor_proto.has_json_name() << std::endl;
  184. abort();
  185. } else if (command == "Annotate") {
  186. annotate = true;
  187. } else if (command == "ShowVersionNumber") {
  188. Version compiler_version;
  189. context->GetCompilerVersion(&compiler_version);
  190. std::cerr << "Saw compiler_version: "
  191. << compiler_version.major() * 1000000 +
  192. compiler_version.minor() * 1000 +
  193. compiler_version.patch()
  194. << " " << compiler_version.suffix() << std::endl;
  195. abort();
  196. } else {
  197. GOOGLE_LOG(FATAL) << "Unknown MockCodeGenerator command: " << command;
  198. }
  199. }
  200. }
  201. if (HasPrefixString(parameter, "insert=")) {
  202. std::vector<string> insert_into;
  203. SplitStringUsing(StripPrefixString(parameter, "insert="),
  204. ",", &insert_into);
  205. for (size_t i = 0; i < insert_into.size(); i++) {
  206. {
  207. std::unique_ptr<io::ZeroCopyOutputStream> output(context->OpenForInsert(
  208. GetOutputFileName(insert_into[i], file), kFirstInsertionPointName));
  209. io::Printer printer(output.get(), '$');
  210. printer.PrintRaw(GetOutputFileContent(name_, "first_insert",
  211. file, context));
  212. if (printer.failed()) {
  213. *error = "MockCodeGenerator detected write error.";
  214. return false;
  215. }
  216. }
  217. {
  218. std::unique_ptr<io::ZeroCopyOutputStream> output(
  219. context->OpenForInsert(GetOutputFileName(insert_into[i], file),
  220. kSecondInsertionPointName));
  221. io::Printer printer(output.get(), '$');
  222. printer.PrintRaw(GetOutputFileContent(name_, "second_insert",
  223. file, context));
  224. if (printer.failed()) {
  225. *error = "MockCodeGenerator detected write error.";
  226. return false;
  227. }
  228. }
  229. }
  230. } else {
  231. std::unique_ptr<io::ZeroCopyOutputStream> output(
  232. context->Open(GetOutputFileName(name_, file)));
  233. GeneratedCodeInfo annotations;
  234. io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector(
  235. &annotations);
  236. io::Printer printer(output.get(), '$',
  237. annotate ? &annotation_collector : NULL);
  238. printer.PrintRaw(GetOutputFileContent(name_, parameter, file, context));
  239. string annotate_suffix = "_annotation";
  240. if (annotate) {
  241. printer.Print("$p$", "p", "first");
  242. printer.Annotate("p", "first" + annotate_suffix);
  243. }
  244. printer.PrintRaw(kFirstInsertionPoint);
  245. if (annotate) {
  246. printer.Print("$p$", "p", "second");
  247. printer.Annotate("p", "second" + annotate_suffix);
  248. }
  249. printer.PrintRaw(kSecondInsertionPoint);
  250. if (annotate) {
  251. printer.Print("$p$", "p", "third");
  252. printer.Annotate("p", "third" + annotate_suffix);
  253. }
  254. if (printer.failed()) {
  255. *error = "MockCodeGenerator detected write error.";
  256. return false;
  257. }
  258. if (annotate) {
  259. std::unique_ptr<io::ZeroCopyOutputStream> meta_output(
  260. context->Open(GetOutputFileName(name_, file) + ".meta"));
  261. if (!TextFormat::Print(annotations, meta_output.get())) {
  262. *error = "MockCodeGenerator couldn't write .meta";
  263. return false;
  264. }
  265. }
  266. }
  267. return true;
  268. }
  269. string MockCodeGenerator::GetOutputFileName(const string& generator_name,
  270. const FileDescriptor* file) {
  271. return GetOutputFileName(generator_name, file->name());
  272. }
  273. string MockCodeGenerator::GetOutputFileName(const string& generator_name,
  274. const string& file) {
  275. return file + ".MockCodeGenerator." + generator_name;
  276. }
  277. string MockCodeGenerator::GetOutputFileContent(
  278. const string& generator_name,
  279. const string& parameter,
  280. const FileDescriptor* file,
  281. GeneratorContext *context) {
  282. std::vector<const FileDescriptor*> all_files;
  283. context->ListParsedFiles(&all_files);
  284. return GetOutputFileContent(
  285. generator_name, parameter, file->name(),
  286. CommaSeparatedList(all_files),
  287. file->message_type_count() > 0 ?
  288. file->message_type(0)->name() : "(none)");
  289. }
  290. string MockCodeGenerator::GetOutputFileContent(
  291. const string& generator_name,
  292. const string& parameter,
  293. const string& file,
  294. const string& parsed_file_list,
  295. const string& first_message_name) {
  296. return strings::Substitute("$0: $1, $2, $3, $4\n",
  297. generator_name, parameter, file,
  298. first_message_name, parsed_file_list);
  299. }
  300. } // namespace compiler
  301. } // namespace protobuf
  302. } // namespace google