userver: /data/code/userver/libraries/protobuf/tests/json/utils.hpp Source File
Loading...
Searching...
No Matches
utils.hpp
1#pragma once
2
3#include <cstdint>
4#include <limits>
5#include <map>
6#include <optional>
7#include <string>
8#include <string_view>
9#include <type_traits>
10#include <variant>
11#include <vector>
12
13#include <fmt/format.h>
14#include <google/protobuf/message.h>
15#include <gtest/gtest.h>
16
17#include <userver/formats/json/value.hpp>
18#include <userver/formats/parse/common.hpp>
19#include <userver/protobuf/json/convert_options.hpp>
20#include <userver/protobuf/json/exceptions.hpp>
21
22#include "proto_json/messages.pb.h"
23
24// True if tests are built with protobuf library with version newer than
25// the one used as a reference for ProtoJSON implementation.
26// We use non-constexpr variable to avoid stripping checks from compilation
27// when building for older protobuf versions (checks must still compile).
28inline const /*deliberately non-constexpr*/ bool
29 kIsModernProtoJson = (GOOGLE_PROTOBUF_VERSION >= 4022005); // NOLINT(misc-redundant-expression)
30
31// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
32#define EXPECT_PRINT_ERROR(EXPR, CODE, PATH)
33 try {
34 (EXPR);
35 ADD_FAILURE() << "Should throw 'PrintError' exception";
36 } catch (const protobuf::json::PrintError& error) {
37 EXPECT_EQ(error.GetErrorInfo().GetCode(), (CODE));
38 EXPECT_EQ(error.GetErrorInfo().GetPath(), (PATH));
39 } catch (...) {
40 ADD_FAILURE() << "Unexpected exception, should be 'PrintError'";
41 }
42
43// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
44#define EXPECT_PARSE_ERROR(EXPR, CODE, PATH)
45 try {
46 (EXPR);
47 ADD_FAILURE() << "Should throw 'ParseError' exception";
48 } catch (const protobuf::json::ParseError& error) {
49 EXPECT_EQ(error.GetErrorInfo().GetCode(), (CODE));
50 EXPECT_EQ(error.GetErrorInfo().GetPath(), (PATH));
51 } catch (...) {
52 ADD_FAILURE() << "Unexpected exception, should be 'ParseError'";
53 }
54
55template <>
56struct fmt::formatter<USERVER_NAMESPACE::protobuf::json::PrintOptions> {
57 constexpr auto parse(fmt::format_parse_context& ctx) {
58 auto it = ctx.begin();
59 if (it != ctx.end() && *it != '}') {
60 throw fmt::format_error("invalid format");
61 }
62 return it;
63 }
64
65 template <typename FormatContext>
66 auto format(const USERVER_NAMESPACE::protobuf::json::PrintOptions& options, FormatContext& ctx) const
67 -> decltype(ctx.out()) {
68 return fmt::format_to(
69 ctx.out(),
70 "{{always_print_fields_with_no_presence={}, always_print_enums_as_ints={}, preserve_proto_field_names={}}}",
71 options.always_print_fields_with_no_presence,
72 options.always_print_enums_as_ints,
73 options.preserve_proto_field_names
74 );
75 }
76};
77
78template <>
79struct fmt::formatter<USERVER_NAMESPACE::protobuf::json::ParseOptions> {
80 constexpr auto parse(fmt::format_parse_context& ctx) {
81 auto it = ctx.begin();
82 if (it != ctx.end() && *it != '}') {
83 throw fmt::format_error("invalid format");
84 }
85 return it;
86 }
87
88 template <typename FormatContext>
89 auto format(const USERVER_NAMESPACE::protobuf::json::ParseOptions& options, FormatContext& ctx) const
90 -> decltype(ctx.out()) {
91 return fmt::format_to(ctx.out(), "{{ignore_unknown_fields={}}}", options.ignore_unknown_fields);
92 }
93};
94
95template <>
96struct fmt::formatter<USERVER_NAMESPACE::protobuf::json::PrintErrorCode> {
97 constexpr auto parse(fmt::format_parse_context& ctx) {
98 auto it = ctx.begin();
99 if (it != ctx.end() && *it != '}') {
100 throw fmt::format_error("invalid format");
101 }
102 return it;
103 }
104
105 template <typename FormatContext>
106 auto format(const USERVER_NAMESPACE::protobuf::json::PrintErrorCode& code, FormatContext& ctx) const
107 -> decltype(ctx.out()) {
108 switch (code) {
109 case USERVER_NAMESPACE::protobuf::json::PrintErrorCode::kInvalidValue:
110 return fmt::format_to(ctx.out(), "kInvalidValue");
111 default:
112 return fmt::format_to(ctx.out(), "UNKNOWN");
113 }
114 }
115};
116
117template <>
118struct fmt::formatter<USERVER_NAMESPACE::protobuf::json::ParseErrorCode> {
119 constexpr auto parse(fmt::format_parse_context& ctx) {
120 auto it = ctx.begin();
121 if (it != ctx.end() && *it != '}') {
122 throw fmt::format_error("invalid format");
123 }
124 return it;
125 }
126
127 template <typename FormatContext>
128 auto format(const USERVER_NAMESPACE::protobuf::json::ParseErrorCode& code, FormatContext& ctx) const
129 -> decltype(ctx.out()) {
130 switch (code) {
131 case USERVER_NAMESPACE::protobuf::json::ParseErrorCode::kUnknownField:
132 return fmt::format_to(ctx.out(), "kUnknownField");
133 case USERVER_NAMESPACE::protobuf::json::ParseErrorCode::kUnknownEnum:
134 return fmt::format_to(ctx.out(), "kUnknownEnum");
135 case USERVER_NAMESPACE::protobuf::json::ParseErrorCode::kMultipleOneofFields:
136 return fmt::format_to(ctx.out(), "kMultipleOneofFields");
137 case USERVER_NAMESPACE::protobuf::json::ParseErrorCode::kInvalidType:
138 return fmt::format_to(ctx.out(), "kInvalidType");
139 case USERVER_NAMESPACE::protobuf::json::ParseErrorCode::kInvalidValue:
140 return fmt::format_to(ctx.out(), "kInvalidValue");
141 default:
142 return fmt::format_to(ctx.out(), "UNKNOWN");
143 }
144 }
145};
146
147USERVER_NAMESPACE_BEGIN
148
149namespace protobuf::json::tests {
150
151constexpr inline ::google::protobuf::NullValue kNullConst = ::google::protobuf::NullValue::NULL_VALUE;
152
153class SampleError : public std::runtime_error {
154public:
155 using std::runtime_error::runtime_error;
156};
157
159constexpr inline ProtoNullValue kProtoNullValue{};
160
161using ProtoValue = std::variant<
162 std::monostate,
163 ProtoNullValue,
164 double,
165 std::string,
166 bool,
167 std::vector<double>,
168 std::map<std::string, std::string>>;
169
170formats::json::Value PrepareJsonTestData(const std::string& json_data);
171
172formats::json::Value CreateSampleJson(const ::google::protobuf::Message& message, const PrintOptions& options = {});
173
174void InitSampleMessage(const std::string& json, ::google::protobuf::Message& message, const ParseOptions& options = {});
175
176::google::protobuf::Value ProtoValueToNative(const ProtoValue& data);
177
179 std::int32_t field1 = 0;
180 std::int32_t field2 = 0;
181 std::int32_t field3 = 0;
182};
183
185 std::uint32_t field1 = 0;
186 std::uint32_t field2 = 0;
187};
188
190 std::int64_t field1 = 0;
191 std::int64_t field2 = 0;
192 std::int64_t field3 = 0;
193};
194
196 std::uint64_t field1 = 0;
197 std::uint64_t field2 = 0;
198};
199
201 float field1 = 0;
202};
203
205 double field1 = 0;
206};
207
209 bool field1 = false;
210};
211
213 std::string field1 = {};
214};
215
217 std::string field1 = {};
218};
219
221 proto_json::messages::EnumMessage::Test field1 = proto_json::messages::EnumMessage::TEST_UNSPECIFIED;
222 std::optional<proto_json::messages::EnumMessage::Test> field2 = {};
223};
224
226 std::optional<std::int32_t> field1 = {};
227};
228
230 std::optional<double> field1 = {};
231 std::optional<float> field2 = {};
232 std::optional<std::int64_t> field3 = {};
233 std::optional<std::uint64_t> field4 = {};
234 std::optional<std::int32_t> field5 = {};
235 std::optional<std::uint32_t> field6 = {};
236 std::optional<bool> field7 = {};
237 std::optional<std::string> field8 = {};
238 std::optional<std::string> field9 = {};
239};
240
242 std::optional<std::vector<std::string>> field1 = {};
243};
244
246 std::int64_t seconds = 0;
247 std::int32_t nanos = 0;
248 bool do_not_set = false;
249};
250
252 std::int64_t seconds = 0;
253 std::int32_t nanos = 0;
254 bool do_not_set = false;
255};
256
260
262 std::string type_url = {};
263 std::string value = {};
264};
265
267 std::optional<std::variant<Int32MessageData, DurationMessageData, ValueMessageData, RawAnyData>> field1 = {};
268 bool add_nesting = false;
269};
270
272 std::optional<std::int32_t> field1 = {};
273 std::optional<std::string> field2 = {};
274};
275
277 std::vector<std::int32_t> field1 = {};
278 std::vector<BoolMessageData> field2 = {};
279 std::vector<DurationMessageData> field3 = {};
280 std::vector<ProtoValue> field4 = {};
281 std::vector<std::uint32_t> field5 = {};
282 std::vector<std::int64_t> field6 = {};
283 std::vector<std::uint64_t> field7 = {};
284 std::vector<float> field8 = {};
285 std::vector<double> field9 = {};
286 std::vector<bool> field10 = {};
287 std::vector<std::string> field11 = {};
288 std::vector<proto_json::messages::RepeatedMessage::Test> field12 = {};
289};
290
292 std::map<std::int32_t, std::int32_t> field1 = {};
293 std::map<std::uint32_t, double> field2 = {};
294 std::map<std::int64_t, bool> field3 = {};
295 std::map<std::uint64_t, std::string> field4 = {};
296 std::map<bool, proto_json::messages::MapMessage::TestEnum> field5 = {};
297 std::map<std::string, BoolMessageData> field6 = {};
298 std::map<std::string, DurationMessageData> field7 = {};
299 std::map<std::int32_t, ProtoValue> field8 = {};
300};
301
303 ::google::protobuf::NullValue field1 = {};
304 std::optional<::google::protobuf::NullValue> field2 = {};
305 std::vector<::google::protobuf::NullValue> field3 = {};
306 std::map<std::int32_t, ::google::protobuf::NullValue> field4 = {};
307};
308
310 bool field1 = false;
311 std::optional<bool> field2 = {};
312 std::optional<Int32MessageData> field3 = {};
313 std::vector<bool> field4 = {};
314 std::map<std::int32_t, bool> field5 = {};
315};
316
318 StringMessageData field1 = {};
319 std::vector<StringMessageData> field2 = {};
320 std::map<std::int32_t, StringMessageData> field3 = {};
321};
322
323proto_json::messages::Int32Message PrepareTestData(const Int32MessageData& message_data);
324void CheckMessageEqual(const proto_json::messages::Int32Message& lhs, const proto_json::messages::Int32Message& rhs);
325
326proto_json::messages::UInt32Message PrepareTestData(const UInt32MessageData& message_data);
327void CheckMessageEqual(const proto_json::messages::UInt32Message& lhs, const proto_json::messages::UInt32Message& rhs);
328
329proto_json::messages::Int64Message PrepareTestData(const Int64MessageData& message_data);
330void CheckMessageEqual(const proto_json::messages::Int64Message& lhs, const proto_json::messages::Int64Message& rhs);
331
332proto_json::messages::UInt64Message PrepareTestData(const UInt64MessageData& message_data);
333void CheckMessageEqual(const proto_json::messages::UInt64Message& lhs, const proto_json::messages::UInt64Message& rhs);
334
335proto_json::messages::FloatMessage PrepareTestData(const FloatMessageData& message_data);
336void CheckMessageEqual(const proto_json::messages::FloatMessage& lhs, const proto_json::messages::FloatMessage& rhs);
337
338proto_json::messages::DoubleMessage PrepareTestData(const DoubleMessageData& message_data);
339void CheckMessageEqual(const proto_json::messages::DoubleMessage& lhs, const proto_json::messages::DoubleMessage& rhs);
340
341proto_json::messages::BoolMessage PrepareTestData(const BoolMessageData& message_data);
342void CheckMessageEqual(const proto_json::messages::BoolMessage& lhs, const proto_json::messages::BoolMessage& rhs);
343
344proto_json::messages::StringMessage PrepareTestData(const StringMessageData& message_data);
345void CheckMessageEqual(const proto_json::messages::StringMessage& lhs, const proto_json::messages::StringMessage& rhs);
346
347proto_json::messages::BytesMessage PrepareTestData(const BytesMessageData& message_data);
348void CheckMessageEqual(const proto_json::messages::BytesMessage& lhs, const proto_json::messages::BytesMessage& rhs);
349
350proto_json::messages::EnumMessage PrepareTestData(const EnumMessageData& message_data);
351void CheckMessageEqual(const proto_json::messages::EnumMessage& lhs, const proto_json::messages::EnumMessage& rhs);
352
353proto_json::messages::NestedMessage PrepareTestData(const NestedMessageData& message_data);
354void CheckMessageEqual(const proto_json::messages::NestedMessage& lhs, const proto_json::messages::NestedMessage& rhs);
355
356proto_json::messages::WrapperMessage PrepareTestData(const WrapperMessageData& message_data);
357void CheckMessageEqual(
358 const proto_json::messages::WrapperMessage& lhs,
359 const proto_json::messages::WrapperMessage& rhs
360);
361
362proto_json::messages::FieldMaskMessage PrepareTestData(const FieldMaskMessageData& message_data);
363void CheckMessageEqual(const ::google::protobuf::FieldMask& lhs, const ::google::protobuf::FieldMask& rhs);
364void CheckMessageEqual(
365 const proto_json::messages::FieldMaskMessage& lhs,
366 const proto_json::messages::FieldMaskMessage& rhs
367);
368
369proto_json::messages::DurationMessage PrepareTestData(const DurationMessageData& message_data);
370void CheckMessageEqual(const ::google::protobuf::Duration& lhs, const ::google::protobuf::Duration& rhs);
371void CheckMessageEqual(
372 const proto_json::messages::DurationMessage& lhs,
373 const proto_json::messages::DurationMessage& rhs
374);
375
376proto_json::messages::TimestampMessage PrepareTestData(const TimestampMessageData& message_data);
377void CheckMessageEqual(const ::google::protobuf::Timestamp& lhs, const ::google::protobuf::Timestamp& rhs);
378void CheckMessageEqual(
379 const proto_json::messages::TimestampMessage& lhs,
380 const proto_json::messages::TimestampMessage& rhs
381);
382
383proto_json::messages::ValueMessage PrepareTestData(const ValueMessageData& message_data);
384void CheckMessageEqual(const ::google::protobuf::Value& lhs, const ::google::protobuf::Value& rhs);
385void CheckMessageEqual(const ::google::protobuf::ListValue& lhs, const ::google::protobuf::ListValue& rhs);
386void CheckMessageEqual(const ::google::protobuf::Struct& lhs, const ::google::protobuf::Struct& rhs);
387void CheckMessageEqual(const proto_json::messages::ValueMessage& lhs, const proto_json::messages::ValueMessage& rhs);
388
389proto_json::messages::AnyMessage PrepareTestData(const AnyMessageData& message_data);
390void CheckMessageEqual(const ::google::protobuf::Any& lhs, const ::google::protobuf::Any& rhs);
391void CheckMessageEqual(const proto_json::messages::AnyMessage& lhs, const proto_json::messages::AnyMessage& rhs);
392
393proto_json::messages::OneofMessage PrepareTestData(const OneofMessageData& message_data);
394void CheckMessageEqual(const proto_json::messages::OneofMessage& lhs, const proto_json::messages::OneofMessage& rhs);
395
396proto_json::messages::RepeatedMessage PrepareTestData(const RepeatedMessageData& message_data);
397void CheckMessageEqual(
398 const proto_json::messages::RepeatedMessage& lhs,
399 const proto_json::messages::RepeatedMessage& rhs
400);
401
402proto_json::messages::MapMessage PrepareTestData(const MapMessageData& message_data);
403void CheckMessageEqual(const proto_json::messages::MapMessage& lhs, const proto_json::messages::MapMessage& rhs);
404
405proto_json::messages::NullValueMessage PrepareTestData(const NullValueMessageData& message_data);
406void CheckMessageEqual(
407 const proto_json::messages::NullValueMessage& lhs,
408 const proto_json::messages::NullValueMessage& rhs
409);
410
411proto_json::messages::PresenceMessage PrepareTestData(const PresenceMessageData& message_data);
412void CheckMessageEqual(
413 const proto_json::messages::PresenceMessage& lhs,
414 const proto_json::messages::PresenceMessage& rhs
415);
416
417proto_json::messages::UnknownFieldMessage PrepareTestData(const UnknownFieldMessageData& message_data);
418void CheckMessageEqual(
419 const proto_json::messages::UnknownFieldMessage& lhs,
420 const proto_json::messages::UnknownFieldMessage& rhs
421);
422
425 static constexpr const char* kJson = R"({"field1":"NaN"})";
426 static constexpr FloatMessageData kValue = {std::numeric_limits<float>::quiet_NaN()};
427 static constexpr bool kSkip = !std::numeric_limits<float>::has_quiet_NaN;
428};
429
432 static constexpr const char* kJson = R"({"field1":"NaN"})";
433 static constexpr FloatMessageData kValue = {std::numeric_limits<float>::signaling_NaN()};
434 static constexpr bool kSkip = !std::numeric_limits<float>::has_signaling_NaN;
435};
436
439 static constexpr const char* kJson = R"({"field1":"Infinity"})";
440 static constexpr FloatMessageData kValue = {std::numeric_limits<float>::infinity()};
441 static constexpr bool kSkip = !std::numeric_limits<float>::has_infinity;
442};
443
446 static constexpr const char* kJson = R"({"field1":"-Infinity"})";
447 static constexpr FloatMessageData kValue = {-std::numeric_limits<float>::infinity()};
448 static constexpr bool kSkip = !std::numeric_limits<float>::has_infinity;
449};
450
453 static constexpr const char* kJson = R"({"field1":"NaN"})";
454 static constexpr DoubleMessageData kValue = {std::numeric_limits<double>::quiet_NaN()};
455 static constexpr bool kSkip = !std::numeric_limits<double>::has_quiet_NaN;
456};
457
460 static constexpr const char* kJson = R"({"field1":"NaN"})";
461 static constexpr DoubleMessageData kValue = {std::numeric_limits<double>::signaling_NaN()};
462 static constexpr bool kSkip = !std::numeric_limits<double>::has_signaling_NaN;
463};
464
467 static constexpr const char* kJson = R"({"field1":"Infinity"})";
468 static constexpr DoubleMessageData kValue = {std::numeric_limits<double>::infinity()};
469 static constexpr bool kSkip = !std::numeric_limits<double>::has_infinity;
470};
471
474 static constexpr const char* kJson = R"({"field1":"-Infinity"})";
475 static constexpr DoubleMessageData kValue = {-std::numeric_limits<double>::infinity()};
476 static constexpr bool kSkip = !std::numeric_limits<double>::has_infinity;
477};
478
481 static constexpr double kValue = 1.5;
482 static constexpr const char* kJson = "1.5";
483};
484
486 using Message = ::google::protobuf::FloatValue;
487 static constexpr float kValue = 1.5;
488 static constexpr const char* kJson = "1.5";
489};
490
492 using Message = ::google::protobuf::Int64Value;
493 static constexpr std::int64_t kValue = -123;
494 static constexpr const char* kJson = "-123";
495};
496
499 static constexpr std::uint64_t kValue = 123;
500 static constexpr const char* kJson = "123";
501};
502
504 using Message = ::google::protobuf::Int32Value;
505 static constexpr std::int32_t kValue = -321;
506 static constexpr const char* kJson = "-321";
507};
508
511 static constexpr std::uint32_t kValue = 321;
512 static constexpr const char* kJson = "321";
513};
514
515struct BoolValue {
516 using Message = ::google::protobuf::BoolValue;
517 static constexpr bool kValue = true;
518 static constexpr const char* kJson = "true";
519};
520
523 static constexpr std::string_view kValue = "hello";
524 static constexpr const char* kJson = "\"hello\"";
525};
526
528 using Message = ::google::protobuf::BytesValue;
529 static constexpr std::string_view kValue = "world";
530 static constexpr const char* kJson = "\"d29ybGQ=\"";
531};
532
533template <typename TParam>
534using WrappedType = std::conditional_t<
535 !std::is_same_v<std::decay_t<decltype(TParam::kValue)>, std::string_view>,
536 std::decay_t<decltype(TParam::kValue)>,
537 std::string>;
538
539} // namespace protobuf::json::tests
540
541USERVER_NAMESPACE_END