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