• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7 
8 // Author: kenton@google.com (Kenton Varda)
9 //  Based on original Protocol Buffers design by
10 //  Sanjay Ghemawat, Jeff Dean, and others.
11 //
12 // This test insures that google/protobuf/descriptor.pb.{h,cc} match exactly
13 // what would be generated by the protocol compiler.  These files are not
14 // generated automatically at build time because they are compiled into the
15 // protocol compiler itself.  So, if they were auto-generated, you'd have a
16 // chicken-and-egg problem.
17 //
18 // If this test fails, run the script
19 // "generate_descriptor_proto.sh" and add
20 // descriptor.pb.{h,cc} to your changelist.
21 
22 #include <memory>
23 #include <string>
24 
25 #include "google/protobuf/testing/file.h"
26 #include "google/protobuf/testing/file.h"
27 #include "google/protobuf/compiler/cpp/generator.h"
28 #include "google/protobuf/testing/googletest.h"
29 #include <gtest/gtest.h>
30 #include "absl/container/flat_hash_map.h"
31 #include "absl/log/absl_check.h"
32 #include "absl/strings/substitute.h"
33 #include "google/protobuf/compiler/cpp/helpers.h"
34 #include "google/protobuf/compiler/importer.h"
35 #include "google/protobuf/descriptor.h"
36 #include "google/protobuf/io/zero_copy_stream_impl.h"
37 #include "google/protobuf/test_util2.h"
38 
39 namespace google {
40 namespace protobuf {
41 namespace compiler {
42 namespace cpp {
43 namespace {
FindWithDefault(const absl::flat_hash_map<absl::string_view,std::string> & m,const std::string & k,const std::string & v)44 std::string FindWithDefault(
45     const absl::flat_hash_map<absl::string_view, std::string>& m,
46     const std::string& k, const std::string& v) {
47   auto it = m.find(k);
48   if (it == m.end()) return v;
49   return it->second;
50 }
51 
52 class MockErrorCollector : public MultiFileErrorCollector {
53  public:
MockErrorCollector()54   MockErrorCollector() {}
~MockErrorCollector()55   ~MockErrorCollector() override {}
56 
57   std::string text_;
58 
59   // implements ErrorCollector ---------------------------------------
RecordError(absl::string_view filename,int line,int column,absl::string_view message)60   void RecordError(absl::string_view filename, int line, int column,
61                    absl::string_view message) override {
62     absl::SubstituteAndAppend(&text_, "$0:$1:$2: $3\n", filename, line, column,
63                               message);
64   }
65 };
66 
67 class MockGeneratorContext : public GeneratorContext {
68  public:
ExpectFileMatches(const std::string & virtual_filename,const std::string & physical_filename)69   void ExpectFileMatches(const std::string& virtual_filename,
70                          const std::string& physical_filename) {
71     auto it = files_.find(virtual_filename);
72     ASSERT_TRUE(it != files_.end())
73         << "Generator failed to generate file: " << virtual_filename;
74 
75     std::string expected_contents = *it->second;
76     std::string actual_contents;
77     ABSL_CHECK_OK(File::GetContents(
78         absl::StrCat(TestUtil::TestSourceDir(), "/", physical_filename),
79         &actual_contents, true))
80         << physical_filename;
81 
82 #ifdef WRITE_FILES  // Define to debug mismatched files.
83     ABSL_CHECK_OK(File::SetContents("/tmp/expected.cc", expected_contents,
84                                     true));
85     ABSL_CHECK_OK(
86         File::SetContents("/tmp/actual.cc", actual_contents, true));
87 #endif
88 
89     ASSERT_EQ(expected_contents, actual_contents)
90         << physical_filename
91         << " needs to be regenerated.  Please run "
92            "generate_descriptor_proto.sh. "
93            "Then add this file to your CL.";
94   }
95 
96   // implements GeneratorContext --------------------------------------
97 
Open(const std::string & filename)98   io::ZeroCopyOutputStream* Open(const std::string& filename) override {
99     auto& map_slot = files_[filename];
100     map_slot.reset(new std::string);
101     return new io::StringOutputStream(map_slot.get());
102   }
103 
104  private:
105   absl::flat_hash_map<std::string, std::unique_ptr<std::string>> files_;
106 };
107 
108 const char kDescriptorParameter[] = "dllexport_decl=PROTOBUF_EXPORT";
109 const char kCppFeaturesParameter[] = "dllexport_decl=PROTOBUF_EXPORT";
110 const char kPluginParameter[] = "dllexport_decl=PROTOC_EXPORT";
111 
112 
113 const char* test_protos[][2] = {
114     {"google/protobuf/descriptor", kDescriptorParameter},
115     {"google/protobuf/cpp_features", kCppFeaturesParameter},
116     {"google/protobuf/compiler/plugin", kPluginParameter},
117 };
118 
TEST(BootstrapTest,GeneratedFilesMatch)119 TEST(BootstrapTest, GeneratedFilesMatch) {
120   // We need a mapping from the actual file to virtual and actual path
121   // of the data to compare to.
122   absl::flat_hash_map<absl::string_view, std::string> vpath_map;
123   absl::flat_hash_map<absl::string_view, std::string> rpath_map;
124   rpath_map["google/protobuf/test_messages_proto2"] =
125       "net/proto2/z_generated_example/test_messages_proto2";
126   rpath_map["google/protobuf/test_messages_proto3"] =
127       "net/proto2/z_generated_example/test_messages_proto3";
128   rpath_map["net/proto2/internal/proto2_weak"] =
129       "net/proto2/z_generated_example/proto2_weak";
130 
131   DiskSourceTree source_tree;
132   source_tree.MapPath("", TestUtil::TestSourceDir());
133 
134   for (auto file_parameter : test_protos) {
135     MockErrorCollector error_collector;
136     Importer importer(&source_tree, &error_collector);
137     const FileDescriptor* file =
138         importer.Import(file_parameter[0] + std::string(".proto"));
139     ASSERT_TRUE(file != nullptr)
140         << "Can't import file " << file_parameter[0] + std::string(".proto")
141         << "\n";
142     EXPECT_EQ("", error_collector.text_);
143     CppGenerator generator;
144     MockGeneratorContext context;
145 #ifdef GOOGLE_PROTOBUF_RUNTIME_INCLUDE_BASE
146     generator.set_opensource_runtime(true);
147     generator.set_runtime_include_base(GOOGLE_PROTOBUF_RUNTIME_INCLUDE_BASE);
148 #endif
149     std::string error;
150     ASSERT_TRUE(generator.Generate(file, file_parameter[1], &context, &error));
151 
152     std::string vpath =
153         FindWithDefault(vpath_map, file_parameter[0], file_parameter[0]);
154     std::string rpath =
155         FindWithDefault(rpath_map, file_parameter[0], file_parameter[0]);
156     context.ExpectFileMatches(absl::StrCat(vpath, ".pb.cc"),
157                               absl::StrCat(rpath, ".pb.cc"));
158     context.ExpectFileMatches(absl::StrCat(vpath, ".pb.h"),
159                               absl::StrCat(rpath, ".pb.h"));
160   }
161 }
162 
163 // test Generate in cpp_generator.cc
TEST(BootstrapTest,OptionNotExist)164 TEST(BootstrapTest, OptionNotExist) {
165   cpp::CppGenerator generator;
166   DescriptorPool pool;
167   GeneratorContext* generator_context = nullptr;
168   std::string parameter = "aaa";
169   std::string error;
170   ASSERT_FALSE(generator.Generate(
171       pool.FindFileByName("google/protobuf/descriptor.proto"), parameter,
172       generator_context, &error));
173   EXPECT_EQ(error, absl::StrCat("Unknown generator option: ", parameter));
174 }
175 
176 }  // namespace
177 
178 }  // namespace cpp
179 }  // namespace compiler
180 }  // namespace protobuf
181 }  // namespace google
182