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) { return ctx.begin(); }
58
59 template <typename FormatContext>
60 auto format(const USERVER_NAMESPACE::protobuf::json::PrintOptions& options, FormatContext& ctx) const
61 -> decltype(ctx.out()) {
62 return fmt::format_to(
63 ctx.out(),
64 "{{always_print_fields_with_no_presence={}, always_print_enums_as_ints={}, preserve_proto_field_names={}, "
65 "nonportable_raw_any={}}}",
66 options.always_print_fields_with_no_presence,
67 options.always_print_enums_as_ints,
68 options.preserve_proto_field_names,
69 options.nonportable_raw_any
70 );
71 }
72};
73
74template <>
75struct fmt::formatter<USERVER_NAMESPACE::protobuf::json::ParseOptions> {
76 constexpr auto parse(fmt::format_parse_context& ctx) { return ctx.begin(); }
77
78 template <typename FormatContext>
79 auto format(const USERVER_NAMESPACE::protobuf::json::ParseOptions& options, FormatContext& ctx) const
80 -> decltype(ctx.out()) {
81 return fmt::format_to(
82 ctx.out(),
83 "{{ignore_unknown_fields={}, nonportable_raw_any={}}}",
84 options.ignore_unknown_fields,
85 options.nonportable_raw_any
86 );
87 }
88};
89
90template <>
91struct fmt::formatter<USERVER_NAMESPACE::protobuf::json::PrintErrorCode> {
92 constexpr auto parse(fmt::format_parse_context& ctx) { return ctx.begin(); }
93
94 template <typename FormatContext>
95 auto format(const USERVER_NAMESPACE::protobuf::json::PrintErrorCode& code, FormatContext& ctx) const
96 -> decltype(ctx.out()) {
97 switch (code) {
98 case USERVER_NAMESPACE::protobuf::json::PrintErrorCode::kInvalidValue:
99 return fmt::format_to(ctx.out(), "kInvalidValue");
100 default:
101 return fmt::format_to(ctx.out(), "UNKNOWN");
102 }
103 }
104};
105
106template <>
107struct fmt::formatter<USERVER_NAMESPACE::protobuf::json::ParseErrorCode> {
108 constexpr auto parse(fmt::format_parse_context& ctx) { return ctx.begin(); }
109
110 template <typename FormatContext>
111 auto format(const USERVER_NAMESPACE::protobuf::json::ParseErrorCode& code, FormatContext& ctx) const
112 -> decltype(ctx.out()) {
113 switch (code) {
114 case USERVER_NAMESPACE::protobuf::json::ParseErrorCode::kUnknownField:
115 return fmt::format_to(ctx.out(), "kUnknownField");
116 case USERVER_NAMESPACE::protobuf::json::ParseErrorCode::kUnknownEnum:
117 return fmt::format_to(ctx.out(), "kUnknownEnum");
118 case USERVER_NAMESPACE::protobuf::json::ParseErrorCode::kMultipleOneofFields:
119 return fmt::format_to(ctx.out(), "kMultipleOneofFields");
120 case USERVER_NAMESPACE::protobuf::json::ParseErrorCode::kInvalidType:
121 return fmt::format_to(ctx.out(), "kInvalidType");
122 case USERVER_NAMESPACE::protobuf::json::ParseErrorCode::kInvalidValue:
123 return fmt::format_to(ctx.out(), "kInvalidValue");
124 default:
125 return fmt::format_to(ctx.out(), "UNKNOWN");
126 }
127 }
128};
129
130USERVER_NAMESPACE_BEGIN
131
132namespace protobuf::json::tests {
133
134constexpr inline ::google::protobuf::NullValue kNullConst = ::google::protobuf::NullValue::NULL_VALUE;
135
136class SampleError : public std::runtime_error {
137public:
138 using std::runtime_error::runtime_error;
139};
140
142constexpr inline ProtoNullValue kProtoNullValue{};
143
144using ProtoValue = std::variant<
145 std::monostate,
146 ProtoNullValue,
147 double,
148 std::string,
149 bool,
150 std::vector<double>,
151 std::map<std::string, std::string>>;
152
153formats::json::Value PrepareJsonTestData(const std::string& json_data);
154
155formats::json::Value CreateSampleJson(const ::google::protobuf::Message& message, const PrintOptions& options = {});
156
157void InitSampleMessage(const std::string& json, ::google::protobuf::Message& message, const ParseOptions& options = {});
158
159::google::protobuf::Value ProtoValueToNative(const ProtoValue& data);
160
162 std::int32_t field1 = 0;
163 std::int32_t field2 = 0;
164 std::int32_t field3 = 0;
165};
166
168 std::uint32_t field1 = 0;
169 std::uint32_t field2 = 0;
170};
171
173 std::int64_t field1 = 0;
174 std::int64_t field2 = 0;
175 std::int64_t field3 = 0;
176};
177
179 std::uint64_t field1 = 0;
180 std::uint64_t field2 = 0;
181};
182
184 float field1 = 0;
185};
186
188 double field1 = 0;
189};
190
192 bool field1 = false;
193};
194
196 std::string field1 = {};
197};
198
200 std::string field1 = {};
201};
202
204 proto_json::messages::EnumMessage::Test field1 = proto_json::messages::EnumMessage::TEST_UNSPECIFIED;
205 std::optional<proto_json::messages::EnumMessage::Test> field2 = {};
206};
207
209 std::optional<std::int32_t> field1 = {};
210};
211
213 std::optional<double> field1 = {};
214 std::optional<float> field2 = {};
215 std::optional<std::int64_t> field3 = {};
216 std::optional<std::uint64_t> field4 = {};
217 std::optional<std::int32_t> field5 = {};
218 std::optional<std::uint32_t> field6 = {};
219 std::optional<bool> field7 = {};
220 std::optional<std::string> field8 = {};
221 std::optional<std::string> field9 = {};
222};
223
225 std::optional<std::vector<std::string>> field1 = {};
226};
227
229 std::int64_t seconds = 0;
230 std::int32_t nanos = 0;
231 bool do_not_set = false;
232};
233
235 std::int64_t seconds = 0;
236 std::int32_t nanos = 0;
237 bool do_not_set = false;
238};
239
243
245 std::string type_url = {};
246 std::string value = {};
247};
248
250 std::optional<std::variant<Int32MessageData, DurationMessageData, ValueMessageData, RawAnyData>> field1 = {};
251 bool add_nesting = false;
252};
253
255 std::optional<std::int32_t> field1 = {};
256 std::optional<std::string> field2 = {};
257};
258
260 std::vector<std::int32_t> field1 = {};
261 std::vector<BoolMessageData> field2 = {};
262 std::vector<DurationMessageData> field3 = {};
263 std::vector<ProtoValue> field4 = {};
264 std::vector<std::uint32_t> field5 = {};
265 std::vector<std::int64_t> field6 = {};
266 std::vector<std::uint64_t> field7 = {};
267 std::vector<float> field8 = {};
268 std::vector<double> field9 = {};
269 std::vector<bool> field10 = {};
270 std::vector<std::string> field11 = {};
271 std::vector<proto_json::messages::RepeatedMessage::Test> field12 = {};
272};
273
275 std::map<std::int32_t, std::int32_t> field1 = {};
276 std::map<std::uint32_t, double> field2 = {};
277 std::map<std::int64_t, bool> field3 = {};
278 std::map<std::uint64_t, std::string> field4 = {};
279 std::map<bool, proto_json::messages::MapMessage::TestEnum> field5 = {};
280 std::map<std::string, BoolMessageData> field6 = {};
281 std::map<std::string, DurationMessageData> field7 = {};
282 std::map<std::int32_t, ProtoValue> field8 = {};
283};
284
286 ::google::protobuf::NullValue field1 = {};
287 std::optional<::google::protobuf::NullValue> field2 = {};
288 std::vector<::google::protobuf::NullValue> field3 = {};
289 std::map<std::int32_t, ::google::protobuf::NullValue> field4 = {};
290};
291
293 bool field1 = false;
294 std::optional<bool> field2 = {};
295 std::optional<Int32MessageData> field3 = {};
296 std::vector<bool> field4 = {};
297 std::map<std::int32_t, bool> field5 = {};
298};
299
301 StringMessageData field1 = {};
302 std::vector<StringMessageData> field2 = {};
303 std::map<std::int32_t, StringMessageData> field3 = {};
304};
305
306proto_json::messages::Int32Message PrepareTestData(const Int32MessageData& message_data);
307void CheckMessageEqual(const proto_json::messages::Int32Message& lhs, const proto_json::messages::Int32Message& rhs);
308
309proto_json::messages::UInt32Message PrepareTestData(const UInt32MessageData& message_data);
310void CheckMessageEqual(const proto_json::messages::UInt32Message& lhs, const proto_json::messages::UInt32Message& rhs);
311
312proto_json::messages::Int64Message PrepareTestData(const Int64MessageData& message_data);
313void CheckMessageEqual(const proto_json::messages::Int64Message& lhs, const proto_json::messages::Int64Message& rhs);
314
315proto_json::messages::UInt64Message PrepareTestData(const UInt64MessageData& message_data);
316void CheckMessageEqual(const proto_json::messages::UInt64Message& lhs, const proto_json::messages::UInt64Message& rhs);
317
318proto_json::messages::FloatMessage PrepareTestData(const FloatMessageData& message_data);
319void CheckMessageEqual(const proto_json::messages::FloatMessage& lhs, const proto_json::messages::FloatMessage& rhs);
320
321proto_json::messages::DoubleMessage PrepareTestData(const DoubleMessageData& message_data);
322void CheckMessageEqual(const proto_json::messages::DoubleMessage& lhs, const proto_json::messages::DoubleMessage& rhs);
323
324proto_json::messages::BoolMessage PrepareTestData(const BoolMessageData& message_data);
325void CheckMessageEqual(const proto_json::messages::BoolMessage& lhs, const proto_json::messages::BoolMessage& rhs);
326
327proto_json::messages::StringMessage PrepareTestData(const StringMessageData& message_data);
328void CheckMessageEqual(const proto_json::messages::StringMessage& lhs, const proto_json::messages::StringMessage& rhs);
329
330proto_json::messages::BytesMessage PrepareTestData(const BytesMessageData& message_data);
331void CheckMessageEqual(const proto_json::messages::BytesMessage& lhs, const proto_json::messages::BytesMessage& rhs);
332
333proto_json::messages::EnumMessage PrepareTestData(const EnumMessageData& message_data);
334void CheckMessageEqual(const proto_json::messages::EnumMessage& lhs, const proto_json::messages::EnumMessage& rhs);
335
336proto_json::messages::NestedMessage PrepareTestData(const NestedMessageData& message_data);
337void CheckMessageEqual(const proto_json::messages::NestedMessage& lhs, const proto_json::messages::NestedMessage& rhs);
338
339proto_json::messages::WrapperMessage PrepareTestData(const WrapperMessageData& message_data);
340void CheckMessageEqual(
341 const proto_json::messages::WrapperMessage& lhs,
342 const proto_json::messages::WrapperMessage& rhs
343);
344
345proto_json::messages::FieldMaskMessage PrepareTestData(const FieldMaskMessageData& message_data);
346void CheckMessageEqual(const ::google::protobuf::FieldMask& lhs, const ::google::protobuf::FieldMask& rhs);
347void CheckMessageEqual(
348 const proto_json::messages::FieldMaskMessage& lhs,
349 const proto_json::messages::FieldMaskMessage& rhs
350);
351
352proto_json::messages::DurationMessage PrepareTestData(const DurationMessageData& message_data);
353void CheckMessageEqual(const ::google::protobuf::Duration& lhs, const ::google::protobuf::Duration& rhs);
354void CheckMessageEqual(
355 const proto_json::messages::DurationMessage& lhs,
356 const proto_json::messages::DurationMessage& rhs
357);
358
359proto_json::messages::TimestampMessage PrepareTestData(const TimestampMessageData& message_data);
360void CheckMessageEqual(const ::google::protobuf::Timestamp& lhs, const ::google::protobuf::Timestamp& rhs);
361void CheckMessageEqual(
362 const proto_json::messages::TimestampMessage& lhs,
363 const proto_json::messages::TimestampMessage& rhs
364);
365
366proto_json::messages::ValueMessage PrepareTestData(const ValueMessageData& message_data);
367void CheckMessageEqual(const ::google::protobuf::Value& lhs, const ::google::protobuf::Value& rhs);
368void CheckMessageEqual(const ::google::protobuf::ListValue& lhs, const ::google::protobuf::ListValue& rhs);
369void CheckMessageEqual(const ::google::protobuf::Struct& lhs, const ::google::protobuf::Struct& rhs);
370void CheckMessageEqual(const proto_json::messages::ValueMessage& lhs, const proto_json::messages::ValueMessage& rhs);
371
372proto_json::messages::AnyMessage PrepareTestData(const AnyMessageData& message_data);
373void CheckMessageEqual(const ::google::protobuf::Any& lhs, const ::google::protobuf::Any& rhs);
374void CheckMessageEqual(const proto_json::messages::AnyMessage& lhs, const proto_json::messages::AnyMessage& rhs);
375
376proto_json::messages::OneofMessage PrepareTestData(const OneofMessageData& message_data);
377void CheckMessageEqual(const proto_json::messages::OneofMessage& lhs, const proto_json::messages::OneofMessage& rhs);
378
379proto_json::messages::RepeatedMessage PrepareTestData(const RepeatedMessageData& message_data);
380void CheckMessageEqual(
381 const proto_json::messages::RepeatedMessage& lhs,
382 const proto_json::messages::RepeatedMessage& rhs
383);
384
385proto_json::messages::MapMessage PrepareTestData(const MapMessageData& message_data);
386void CheckMessageEqual(const proto_json::messages::MapMessage& lhs, const proto_json::messages::MapMessage& rhs);
387
388proto_json::messages::NullValueMessage PrepareTestData(const NullValueMessageData& message_data);
389void CheckMessageEqual(
390 const proto_json::messages::NullValueMessage& lhs,
391 const proto_json::messages::NullValueMessage& rhs
392);
393
394proto_json::messages::PresenceMessage PrepareTestData(const PresenceMessageData& message_data);
395void CheckMessageEqual(
396 const proto_json::messages::PresenceMessage& lhs,
397 const proto_json::messages::PresenceMessage& rhs
398);
399
400proto_json::messages::UnknownFieldMessage PrepareTestData(const UnknownFieldMessageData& message_data);
401void CheckMessageEqual(
402 const proto_json::messages::UnknownFieldMessage& lhs,
403 const proto_json::messages::UnknownFieldMessage& rhs
404);
405
408 static constexpr const char* kJson = R"({"field1":"NaN"})";
409 static constexpr FloatMessageData kValue = {std::numeric_limits<float>::quiet_NaN()};
410 static constexpr bool kSkip = !std::numeric_limits<float>::has_quiet_NaN;
411};
412
415 static constexpr const char* kJson = R"({"field1":"NaN"})";
416 static constexpr FloatMessageData kValue = {std::numeric_limits<float>::signaling_NaN()};
417 static constexpr bool kSkip = !std::numeric_limits<float>::has_signaling_NaN;
418};
419
422 static constexpr const char* kJson = R"({"field1":"Infinity"})";
423 static constexpr FloatMessageData kValue = {std::numeric_limits<float>::infinity()};
424 static constexpr bool kSkip = !std::numeric_limits<float>::has_infinity;
425};
426
429 static constexpr const char* kJson = R"({"field1":"-Infinity"})";
430 static constexpr FloatMessageData kValue = {-std::numeric_limits<float>::infinity()};
431 static constexpr bool kSkip = !std::numeric_limits<float>::has_infinity;
432};
433
436 static constexpr const char* kJson = R"({"field1":"NaN"})";
437 static constexpr DoubleMessageData kValue = {std::numeric_limits<double>::quiet_NaN()};
438 static constexpr bool kSkip = !std::numeric_limits<double>::has_quiet_NaN;
439};
440
443 static constexpr const char* kJson = R"({"field1":"NaN"})";
444 static constexpr DoubleMessageData kValue = {std::numeric_limits<double>::signaling_NaN()};
445 static constexpr bool kSkip = !std::numeric_limits<double>::has_signaling_NaN;
446};
447
450 static constexpr const char* kJson = R"({"field1":"Infinity"})";
451 static constexpr DoubleMessageData kValue = {std::numeric_limits<double>::infinity()};
452 static constexpr bool kSkip = !std::numeric_limits<double>::has_infinity;
453};
454
457 static constexpr const char* kJson = R"({"field1":"-Infinity"})";
458 static constexpr DoubleMessageData kValue = {-std::numeric_limits<double>::infinity()};
459 static constexpr bool kSkip = !std::numeric_limits<double>::has_infinity;
460};
461
464 static constexpr double kValue = 1.5;
465 static constexpr const char* kJson = "1.5";
466};
467
469 using Message = ::google::protobuf::FloatValue;
470 static constexpr float kValue = 1.5;
471 static constexpr const char* kJson = "1.5";
472};
473
475 using Message = ::google::protobuf::Int64Value;
476 static constexpr std::int64_t kValue = -123;
477 static constexpr const char* kJson = "-123";
478};
479
482 static constexpr std::uint64_t kValue = 123;
483 static constexpr const char* kJson = "123";
484};
485
487 using Message = ::google::protobuf::Int32Value;
488 static constexpr std::int32_t kValue = -321;
489 static constexpr const char* kJson = "-321";
490};
491
494 static constexpr std::uint32_t kValue = 321;
495 static constexpr const char* kJson = "321";
496};
497
498struct BoolValue {
499 using Message = ::google::protobuf::BoolValue;
500 static constexpr bool kValue = true;
501 static constexpr const char* kJson = "true";
502};
503
506 static constexpr std::string_view kValue = "hello";
507 static constexpr const char* kJson = "\"hello\"";
508};
509
511 using Message = ::google::protobuf::BytesValue;
512 static constexpr std::string_view kValue = "world";
513 static constexpr const char* kJson = "\"d29ybGQ=\"";
514};
515
516template <typename TParam>
517using WrappedType = std::conditional_t<
518 !std::is_same_v<std::decay_t<decltype(TParam::kValue)>, std::string_view>,
519 std::decay_t<decltype(TParam::kValue)>,
520 std::string>;
521
522} // namespace protobuf::json::tests
523
524USERVER_NAMESPACE_END