1 #include "proto_test.h"
2
3 #include "flatbuffers/code_generator.h"
4 #include "idl_gen_fbs.h"
5 #include "test_assert.h"
6
7 namespace flatbuffers {
8 namespace tests {
9
RunTest(const flatbuffers::IDLOptions & opts,const std::string & proto_path,const std::string & proto_file,const std::string & golden_file,const std::string import_proto_file)10 void RunTest(const flatbuffers::IDLOptions &opts, const std::string &proto_path,
11 const std::string &proto_file, const std::string &golden_file,
12 const std::string import_proto_file) {
13 const char *include_directories[] = { proto_path.c_str(), nullptr };
14
15 // Parse proto.
16 flatbuffers::Parser parser(opts);
17 TEST_EQ(parser.Parse(proto_file.c_str(), include_directories), true);
18
19 // Generate fbs.
20 std::unique_ptr<CodeGenerator> fbs_generator = NewFBSCodeGenerator(true);
21 std::string fbs;
22 TEST_EQ(fbs_generator->GenerateCodeString(parser, "test", fbs),
23 CodeGenerator::Status::OK);
24
25 // Ensure generated file is parsable.
26 flatbuffers::Parser parser2;
27
28 if (!import_proto_file.empty()) {
29 // Generate fbs from import.proto
30 flatbuffers::Parser import_parser(opts);
31 TEST_EQ(import_parser.Parse(import_proto_file.c_str(), include_directories),
32 true);
33 std::string import_fbs;
34 TEST_EQ(
35 fbs_generator->GenerateCodeString(import_parser, "test", import_fbs),
36 CodeGenerator::Status::OK);
37 // auto import_fbs = flatbuffers::GenerateFBS(import_parser, "test", true);
38 // Since `imported.fbs` isn't in the filesystem AbsolutePath can't figure it
39 // out by itself. We manually construct it so Parser works.
40 std::string imported_fbs = flatbuffers::PosixPath(
41 flatbuffers::AbsolutePath(proto_path) + "/imported.fbs");
42 TEST_EQ(parser2.Parse(import_fbs.c_str(), include_directories,
43 imported_fbs.c_str()),
44 true);
45 }
46
47 TEST_EQ(parser2.Parse(fbs.c_str(), nullptr), true);
48 TEST_EQ_STR(fbs.c_str(), golden_file.c_str());
49 }
50
proto_test(const std::string & proto_path,const std::string & proto_file)51 void proto_test(const std::string &proto_path, const std::string &proto_file) {
52 flatbuffers::IDLOptions opts;
53 opts.include_dependence_headers = false;
54 opts.proto_mode = true;
55 opts.proto_id_gap_action = IDLOptions::ProtoIdGapAction::NO_OP;
56
57 // load the .proto and the golden file from disk
58 std::string golden_file;
59 TEST_EQ(flatbuffers::LoadFile((proto_path + "test.golden.fbs").c_str(), false,
60 &golden_file),
61 true);
62
63 RunTest(opts, proto_path, proto_file, golden_file);
64 }
65
proto_test_id(const std::string & proto_path,const std::string & proto_file)66 void proto_test_id(const std::string &proto_path,
67 const std::string &proto_file) {
68 flatbuffers::IDLOptions opts;
69 opts.include_dependence_headers = false;
70 opts.proto_mode = true;
71 opts.keep_proto_id = true;
72 opts.proto_id_gap_action = IDLOptions::ProtoIdGapAction::NO_OP;
73
74 // load the .proto and the golden file from disk
75 std::string golden_file;
76 TEST_EQ(flatbuffers::LoadFile((proto_path + "test_id.golden.fbs").c_str(),
77 false, &golden_file),
78 true);
79
80 RunTest(opts, proto_path, proto_file, golden_file);
81 }
82
proto_test_union(const std::string & proto_path,const std::string & proto_file)83 void proto_test_union(const std::string &proto_path,
84 const std::string &proto_file) {
85 // Parse proto with --oneof-union option.
86 flatbuffers::IDLOptions opts;
87 opts.include_dependence_headers = false;
88 opts.proto_mode = true;
89 opts.proto_oneof_union = true;
90 opts.proto_id_gap_action = IDLOptions::ProtoIdGapAction::NO_OP;
91
92 std::string golden_file;
93 TEST_EQ(flatbuffers::LoadFile((proto_path + "test_union.golden.fbs").c_str(),
94 false, &golden_file),
95 true);
96 RunTest(opts, proto_path, proto_file, golden_file);
97 }
98
proto_test_union_id(const std::string & proto_path,const std::string & proto_file)99 void proto_test_union_id(const std::string &proto_path,
100 const std::string &proto_file) {
101 // Parse proto with --oneof-union option.
102 flatbuffers::IDLOptions opts;
103 opts.include_dependence_headers = false;
104 opts.proto_mode = true;
105 opts.proto_oneof_union = true;
106 opts.keep_proto_id = true;
107 opts.proto_id_gap_action = IDLOptions::ProtoIdGapAction::NO_OP;
108
109 std::string golden_file;
110 TEST_EQ(
111 flatbuffers::LoadFile((proto_path + "test_union_id.golden.fbs").c_str(),
112 false, &golden_file),
113 true);
114 RunTest(opts, proto_path, proto_file, golden_file);
115 }
116
proto_test_union_suffix(const std::string & proto_path,const std::string & proto_file)117 void proto_test_union_suffix(const std::string &proto_path,
118 const std::string &proto_file) {
119 flatbuffers::IDLOptions opts;
120 opts.include_dependence_headers = false;
121 opts.proto_mode = true;
122 opts.proto_namespace_suffix = "test_namespace_suffix";
123 opts.proto_oneof_union = true;
124 opts.proto_id_gap_action = IDLOptions::ProtoIdGapAction::NO_OP;
125
126 std::string golden_file;
127 TEST_EQ(flatbuffers::LoadFile(
128 (proto_path + "test_union_suffix.golden.fbs").c_str(), false,
129 &golden_file),
130 true);
131 RunTest(opts, proto_path, proto_file, golden_file);
132 }
133
proto_test_union_suffix_id(const std::string & proto_path,const std::string & proto_file)134 void proto_test_union_suffix_id(const std::string &proto_path,
135 const std::string &proto_file) {
136 flatbuffers::IDLOptions opts;
137 opts.include_dependence_headers = false;
138 opts.proto_mode = true;
139 opts.proto_namespace_suffix = "test_namespace_suffix";
140 opts.proto_oneof_union = true;
141 opts.keep_proto_id = true;
142 opts.proto_id_gap_action = IDLOptions::ProtoIdGapAction::NO_OP;
143
144 std::string golden_file;
145 TEST_EQ(flatbuffers::LoadFile(
146 (proto_path + "test_union_suffix_id.golden.fbs").c_str(), false,
147 &golden_file),
148 true);
149 RunTest(opts, proto_path, proto_file, golden_file);
150 }
151
proto_test_include(const std::string & proto_path,const std::string & proto_file,const std::string & import_proto_file)152 void proto_test_include(const std::string &proto_path,
153 const std::string &proto_file,
154 const std::string &import_proto_file) {
155 flatbuffers::IDLOptions opts;
156 opts.include_dependence_headers = true;
157 opts.proto_mode = true;
158 opts.proto_id_gap_action = IDLOptions::ProtoIdGapAction::NO_OP;
159
160 std::string golden_file;
161 TEST_EQ(
162 flatbuffers::LoadFile((proto_path + "test_include.golden.fbs").c_str(),
163 false, &golden_file),
164 true);
165
166 RunTest(opts, proto_path, proto_file, golden_file, import_proto_file);
167 }
168
proto_test_include_id(const std::string & proto_path,const std::string & proto_file,const std::string & import_proto_file)169 void proto_test_include_id(const std::string &proto_path,
170 const std::string &proto_file,
171 const std::string &import_proto_file) {
172 flatbuffers::IDLOptions opts;
173 opts.include_dependence_headers = true;
174 opts.proto_mode = true;
175 opts.keep_proto_id = true;
176 opts.proto_id_gap_action = IDLOptions::ProtoIdGapAction::NO_OP;
177
178 std::string golden_file;
179 TEST_EQ(
180 flatbuffers::LoadFile((proto_path + "test_include_id.golden.fbs").c_str(),
181 false, &golden_file),
182 true);
183
184 RunTest(opts, proto_path, proto_file, golden_file, import_proto_file);
185 }
186
proto_test_include_union(const std::string & proto_path,const std::string & proto_file,const std::string & import_proto_file)187 void proto_test_include_union(const std::string &proto_path,
188 const std::string &proto_file,
189 const std::string &import_proto_file) {
190 flatbuffers::IDLOptions opts;
191 opts.include_dependence_headers = true;
192 opts.proto_mode = true;
193 opts.proto_oneof_union = true;
194 opts.proto_id_gap_action = IDLOptions::ProtoIdGapAction::NO_OP;
195
196 std::string golden_file;
197 TEST_EQ(flatbuffers::LoadFile(
198 (proto_path + "test_union_include.golden.fbs").c_str(), false,
199 &golden_file),
200 true);
201
202 RunTest(opts, proto_path, proto_file, golden_file, import_proto_file);
203 }
204
proto_test_include_union_id(const std::string & proto_path,const std::string & proto_file,const std::string & import_proto_file)205 void proto_test_include_union_id(const std::string &proto_path,
206 const std::string &proto_file,
207 const std::string &import_proto_file) {
208 flatbuffers::IDLOptions opts;
209 opts.include_dependence_headers = true;
210 opts.proto_mode = true;
211 opts.proto_oneof_union = true;
212 opts.keep_proto_id = true;
213 opts.proto_id_gap_action = IDLOptions::ProtoIdGapAction::NO_OP;
214
215 std::string golden_file;
216 TEST_EQ(flatbuffers::LoadFile(
217 (proto_path + "test_union_include_id.golden.fbs").c_str(), false,
218 &golden_file),
219 true);
220
221 RunTest(opts, proto_path, proto_file, golden_file, import_proto_file);
222 }
223
ParseCorruptedProto(const std::string & proto_path)224 void ParseCorruptedProto(const std::string &proto_path) {
225 const char *include_directories[] = { proto_path.c_str(), nullptr };
226
227 flatbuffers::IDLOptions opts;
228 opts.include_dependence_headers = true;
229 opts.proto_mode = true;
230 opts.proto_oneof_union = true;
231
232 std::string proto_file;
233
234 std::unique_ptr<CodeGenerator> fbs_generator = NewFBSCodeGenerator(true);
235
236 // Parse proto with non positive id.
237 {
238 flatbuffers::Parser parser(opts);
239 TEST_EQ(
240 flatbuffers::LoadFile((proto_path + "non-positive-id.proto").c_str(),
241 false, &proto_file),
242 true);
243 TEST_EQ(parser.Parse(proto_file.c_str(), include_directories), true);
244 TEST_NE(fbs_generator->GenerateCode(parser, "temp.fbs", "test"),
245 CodeGenerator::Status::OK);
246 }
247
248 // Parse proto with twice id.
249 {
250 flatbuffers::Parser parser(opts);
251 TEST_EQ(flatbuffers::LoadFile((proto_path + "twice-id.proto").c_str(),
252 false, &proto_file),
253 true);
254 TEST_EQ(parser.Parse(proto_file.c_str(), include_directories), true);
255 TEST_NE(fbs_generator->GenerateCode(parser, "temp.fbs", "test"),
256 CodeGenerator::Status::OK);
257 }
258
259 // Parse proto with using reserved id.
260 {
261 flatbuffers::Parser parser(opts);
262 TEST_EQ(flatbuffers::LoadFile((proto_path + "twice-id.proto").c_str(),
263 false, &proto_file),
264 true);
265 TEST_EQ(parser.Parse(proto_file.c_str(), include_directories), true);
266 TEST_NE(fbs_generator->GenerateCode(parser, "temp.fbs", "test"),
267 CodeGenerator::Status::OK);
268 }
269
270 // Parse proto with error on gap.
271 {
272 opts.proto_id_gap_action = IDLOptions::ProtoIdGapAction::ERROR;
273 flatbuffers::Parser parser(opts);
274 TEST_EQ(flatbuffers::LoadFile((proto_path + "test.proto").c_str(), false,
275 &proto_file),
276 true);
277 TEST_EQ(parser.Parse(proto_file.c_str(), include_directories), true);
278
279 TEST_NE(fbs_generator->GenerateCode(parser, "temp.fbs", "test"),
280 CodeGenerator::Status::OK);
281 }
282 }
283
284 // Parse a .proto schema, output as .fbs
ParseProtoTest(const std::string & tests_data_path)285 void ParseProtoTest(const std::string &tests_data_path) {
286 auto proto_path = tests_data_path + "prototest/";
287 std::string proto_file;
288 TEST_EQ(
289 flatbuffers::LoadFile((tests_data_path + "prototest/test.proto").c_str(),
290 false, &proto_file),
291 true);
292
293 std::string import_proto_file;
294 TEST_EQ(flatbuffers::LoadFile(
295 (tests_data_path + "prototest/imported.proto").c_str(), false,
296 &import_proto_file),
297 true);
298
299 proto_test(proto_path, proto_file);
300 proto_test_union(proto_path, proto_file);
301 proto_test_union_suffix(proto_path, proto_file);
302 proto_test_include(proto_path, proto_file, import_proto_file);
303 proto_test_include_union(proto_path, proto_file, import_proto_file);
304
305 proto_test_id(proto_path, proto_file);
306 proto_test_union_id(proto_path, proto_file);
307 proto_test_union_suffix_id(proto_path, proto_file);
308 proto_test_include_id(proto_path, proto_file, import_proto_file);
309 proto_test_include_union_id(proto_path, proto_file, import_proto_file);
310
311 ParseCorruptedProto(proto_path);
312 }
313
ParseProtoBufAsciiTest()314 void ParseProtoBufAsciiTest() {
315 // We can put the parser in a mode where it will accept JSON that looks more
316 // like Protobuf ASCII, for users that have data in that format.
317 // This uses no "" for field names (which we already support by default,
318 // omits `,`, `:` before `{` and a couple of other features.
319 flatbuffers::Parser parser;
320 parser.opts.protobuf_ascii_alike = true;
321 TEST_EQ(
322 parser.Parse("table S { B:int; } table T { A:[int]; C:S; } root_type T;"),
323 true);
324 TEST_EQ(parser.Parse("{ A [1 2] C { B:2 }}"), true);
325 // Similarly, in text output, it should omit these.
326 std::string text;
327 auto err =
328 flatbuffers::GenText(
329 parser, parser.builder_.GetBufferPointer(), &text);
330 TEST_NULL(err);
331 TEST_EQ_STR(text.c_str(),
332 "{\n A [\n 1\n 2\n ]\n C {\n B: 2\n }\n}\n");
333 }
334
335 } // namespace tests
336 } // namespace flatbuffers
337