blob: 61dc283ac5ddfb75601226017afd686574416d71 [file] [log] [blame]
kenton@google.com15566232009-02-10 17:47:02 +00001// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc. All rights reserved.
Feng Xiaoe4288622014-10-01 16:26:23 -07003// https://developers.google.com/protocol-buffers/
kenton@google.com15566232009-02-10 17:47:02 +00004//
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
Jisi Liu5221dcb2016-01-29 13:51:05 -080031#include <memory>
32#ifndef _SHARED_PTR_H
33#include <google/protobuf/stubs/shared_ptr.h>
34#endif
kenton@google.com15566232009-02-10 17:47:02 +000035
Jisi Liu5221dcb2016-01-29 13:51:05 -080036#include <google/protobuf/compiler/cpp/cpp_helpers.h>
37#include <google/protobuf/compiler/cpp/cpp_generator.h>
38#include <google/protobuf/compiler/command_line_interface.h>
39#include <google/protobuf/io/zero_copy_stream.h>
40#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
41#include <google/protobuf/io/printer.h>
42#include <google/protobuf/descriptor.pb.h>
kenton@google.com15566232009-02-10 17:47:02 +000043
Jisi Liu5221dcb2016-01-29 13:51:05 -080044#include <google/protobuf/testing/googletest.h>
45#include <gtest/gtest.h>
46#include <google/protobuf/testing/file.h>
kenton@google.com15566232009-02-10 17:47:02 +000047
Jisi Liu5221dcb2016-01-29 13:51:05 -080048namespace google {
49namespace protobuf {
50namespace compiler {
51namespace cpp {
52namespace {
kenton@google.com15566232009-02-10 17:47:02 +000053
Jisi Liu3b3c8ab2016-03-30 11:39:59 -070054// A CodeGenerator that captures the FileDescriptor it's passed as a
55// FileDescriptorProto.
56class DescriptorCapturingGenerator : public CodeGenerator {
57 public:
58 // Does not own file; file must outlive the Generator.
59 explicit DescriptorCapturingGenerator(FileDescriptorProto* file)
60 : file_(file) {}
61
62 virtual bool Generate(const FileDescriptor* file, const string& parameter,
63 GeneratorContext* context, string* error) const {
64 file->CopyTo(file_);
65 return true;
66 }
67
68 private:
69 FileDescriptorProto* file_;
70};
71
72class CppMetadataTest : public ::testing::Test {
73 public:
74 // Adds a file with name `filename` and content `data`.
75 void AddFile(const string& filename, const string& data) {
76 GOOGLE_CHECK_OK(File::SetContents(TestTempDir() + "/" + filename, data,
77 true));
78 }
79
80 // Tries to capture a FileDescriptorProto, GeneratedCodeInfo, and output
81 // code from the previously added file with name `filename`. Returns true on
82 // success. If pb_h is non-null, expects a .pb.h and a .pb.h.meta (copied to
83 // pb_h and pb_h_info respecfively); similarly for proto_h and proto_h_info.
84 bool CaptureMetadata(const string& filename, FileDescriptorProto* file,
85 string* pb_h, GeneratedCodeInfo* pb_h_info,
86 string* proto_h, GeneratedCodeInfo* proto_h_info,
87 string* pb_cc) {
88 google::protobuf::compiler::CommandLineInterface cli;
89 cli.SetInputsAreProtoPathRelative(true);
90
91 CppGenerator cpp_generator;
92 DescriptorCapturingGenerator capturing_generator(file);
93 cli.RegisterGenerator("--cpp_out", &cpp_generator, "");
94 cli.RegisterGenerator("--capture_out", &capturing_generator, "");
95
96 string proto_path = "-I" + TestTempDir();
97 string cpp_out =
98 "--cpp_out=annotate_headers=true,"
99 "annotation_pragma_name=pragma_name,"
100 "annotation_guard_name=guard_name:" +
101 TestTempDir();
102 string capture_out = "--capture_out=" + TestTempDir();
103
104 const char* argv[] = {"protoc", proto_path.c_str(), cpp_out.c_str(),
105 capture_out.c_str(), filename.c_str()};
106
107 if (cli.Run(5, argv) != 0) {
108 return false;
109 }
110
111 string output_base = TestTempDir() + "/" + StripProto(filename);
112
113 if (pb_cc != NULL) {
114 GOOGLE_CHECK_OK(
115 File::GetContents(output_base + ".pb.cc", pb_cc, true));
116 }
117
118 if (pb_h != NULL && pb_h_info != NULL) {
119 GOOGLE_CHECK_OK(
120 File::GetContents(output_base + ".pb.h", pb_h, true));
121 if (!DecodeMetadata(output_base + ".pb.h.meta", pb_h_info)) {
122 return false;
123 }
124 }
125
126 if (proto_h != NULL && proto_h_info != NULL) {
127 GOOGLE_CHECK_OK(File::GetContents(output_base + ".proto.h", proto_h,
128 true));
129 if (!DecodeMetadata(output_base + ".proto.h.meta", proto_h_info)) {
130 return false;
131 }
132 }
133
134 return true;
135 }
136
137 private:
138 // Decodes GeneratedCodeInfo stored in path and copies it to info.
139 // Returns true on success.
140 bool DecodeMetadata(const string& path, GeneratedCodeInfo* info) {
141 string data;
142 GOOGLE_CHECK_OK(File::GetContents(path, &data, true));
143 io::ArrayInputStream input(data.data(), data.size());
144 return info->ParseFromZeroCopyStream(&input);
145 }
146};
147
148const char kSmallTestFile[] =
149 "syntax = \"proto2\";\n"
150 "package foo;\n"
151 "enum Enum { VALUE = 0; }\n"
152 "message Message { }\n";
153
154// Finds the Annotation for a given source file and path (or returns null if it
155// couldn't).
156const GeneratedCodeInfo::Annotation* FindAnnotationOnPath(
157 const GeneratedCodeInfo& info, const string& source_file,
158 const vector<int>& path) {
159 for (int i = 0; i < info.annotation_size(); ++i) {
160 const GeneratedCodeInfo::Annotation* annotation = &info.annotation(i);
161 if (annotation->source_file() != source_file ||
162 annotation->path_size() != path.size()) {
163 continue;
164 }
165 int node = 0;
166 for (; node < path.size(); ++node) {
167 if (annotation->path(node) != path[node]) {
168 break;
169 }
170 }
171 if (node == path.size()) {
172 return annotation;
173 }
174 }
175 return NULL;
176}
177
178// Returns true if the provided annotation covers a given substring in
179// file_content.
180bool AnnotationMatchesSubstring(const string& file_content,
181 const GeneratedCodeInfo::Annotation* annotation,
182 const string& expected_text) {
183 uint32 begin = annotation->begin();
184 uint32 end = annotation->end();
185 if (end < begin || end > file_content.size()) {
186 return false;
187 }
188 return file_content.substr(begin, end - begin) == expected_text;
189}
190
191TEST_F(CppMetadataTest, CapturesEnumNames) {
192 FileDescriptorProto file;
193 GeneratedCodeInfo info;
194 string pb_h;
195 AddFile("test.proto", kSmallTestFile);
196 EXPECT_TRUE(
197 CaptureMetadata("test.proto", &file, &pb_h, &info, NULL, NULL, NULL));
198 EXPECT_EQ("Enum", file.enum_type(0).name());
199 vector<int> enum_path;
200 enum_path.push_back(FileDescriptorProto::kEnumTypeFieldNumber);
201 enum_path.push_back(0);
202 const GeneratedCodeInfo::Annotation* enum_annotation =
203 FindAnnotationOnPath(info, "test.proto", enum_path);
204 EXPECT_TRUE(NULL != enum_annotation);
205 EXPECT_TRUE(AnnotationMatchesSubstring(pb_h, enum_annotation, "Enum"));
206}
207
208TEST_F(CppMetadataTest, AddsPragma) {
209 FileDescriptorProto file;
210 GeneratedCodeInfo info;
211 string pb_h;
212 AddFile("test.proto", kSmallTestFile);
213 EXPECT_TRUE(
214 CaptureMetadata("test.proto", &file, &pb_h, &info, NULL, NULL, NULL));
215 EXPECT_TRUE(pb_h.find("#ifdef guard_name") != string::npos);
216 EXPECT_TRUE(pb_h.find("#pragma pragma_name \"test.pb.h.meta\"") !=
217 string::npos);
218}
219
220TEST_F(CppMetadataTest, CapturesMessageNames) {
221 FileDescriptorProto file;
222 GeneratedCodeInfo info;
223 string pb_h;
224 AddFile("test.proto", kSmallTestFile);
225 EXPECT_TRUE(
226 CaptureMetadata("test.proto", &file, &pb_h, &info, NULL, NULL, NULL));
227 EXPECT_EQ("Message", file.message_type(0).name());
228 vector<int> message_path;
229 message_path.push_back(FileDescriptorProto::kMessageTypeFieldNumber);
230 message_path.push_back(0);
231 const GeneratedCodeInfo::Annotation* message_annotation =
232 FindAnnotationOnPath(info, "test.proto", message_path);
233 EXPECT_TRUE(NULL != message_annotation);
234 EXPECT_TRUE(AnnotationMatchesSubstring(pb_h, message_annotation, "Message"));
235}
236
Jisi Liu5221dcb2016-01-29 13:51:05 -0800237} // namespace
238} // namespace cpp
239} // namespace compiler
240} // namespace protobuf
241} // namespace google