2022-07-20 13:38:07 +03:00
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
2022-08-12 16:04:06 +03:00
// | | |__ | | | | | | version 3.11.2
2022-07-20 13:38:07 +03:00
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
2016-08-04 08:24:46 +03:00
2019-01-13 19:41:21 +03:00
# include "doctest_compatibility.h"
2016-08-04 22:55:47 +03:00
2018-01-29 13:21:11 +03:00
# include <nlohmann/json.hpp>
2016-08-04 22:55:47 +03:00
using nlohmann : : json ;
2019-01-13 19:41:21 +03:00
# include <sstream>
# include <iomanip>
2016-08-04 22:55:47 +03:00
TEST_CASE ( " serialization " )
{
SECTION ( " operator<< " )
{
SECTION ( " no given width " )
{
std : : stringstream ss ;
2022-09-13 13:58:26 +03:00
const json j = { " foo " , 1 , 2 , 3 , false , { { " one " , 1 } } } ;
2016-08-04 22:55:47 +03:00
ss < < j ;
CHECK ( ss . str ( ) = = " [ \" foo \" ,1,2,3,false,{ \" one \" :1}] " ) ;
}
SECTION ( " given width " )
{
std : : stringstream ss ;
2022-09-13 13:58:26 +03:00
const json j = { " foo " , 1 , 2 , 3 , false , { { " one " , 1 } } } ;
2016-08-04 22:55:47 +03:00
ss < < std : : setw ( 4 ) < < j ;
CHECK ( ss . str ( ) = =
" [ \n \" foo \" , \n 1, \n 2, \n 3, \n false, \n { \n \" one \" : 1 \n } \n ] " ) ;
}
2017-05-07 20:27:40 +03:00
SECTION ( " given fill " )
{
std : : stringstream ss ;
2022-09-13 13:58:26 +03:00
const json j = { " foo " , 1 , 2 , 3 , false , { { " one " , 1 } } } ;
2017-05-07 20:27:40 +03:00
ss < < std : : setw ( 1 ) < < std : : setfill ( ' \t ' ) < < j ;
CHECK ( ss . str ( ) = =
" [ \n \t \" foo \" , \n \t 1, \n \t 2, \n \t 3, \n \t false, \n \t { \n \t \t \" one \" : 1 \n \t } \n ] " ) ;
}
2016-08-04 22:55:47 +03:00
}
SECTION ( " operator>> " )
{
SECTION ( " no given width " )
{
std : : stringstream ss ;
2022-09-13 13:58:26 +03:00
const json j = { " foo " , 1 , 2 , 3 , false , { { " one " , 1 } } } ;
2016-08-04 22:55:47 +03:00
j > > ss ;
CHECK ( ss . str ( ) = = " [ \" foo \" ,1,2,3,false,{ \" one \" :1}] " ) ;
}
SECTION ( " given width " )
{
std : : stringstream ss ;
2022-09-13 13:58:26 +03:00
const json j = { " foo " , 1 , 2 , 3 , false , { { " one " , 1 } } } ;
2016-08-04 22:55:47 +03:00
ss . width ( 4 ) ;
j > > ss ;
CHECK ( ss . str ( ) = =
" [ \n \" foo \" , \n 1, \n 2, \n 3, \n false, \n { \n \" one \" : 1 \n } \n ] " ) ;
}
2017-05-07 20:27:40 +03:00
SECTION ( " given fill " )
{
std : : stringstream ss ;
2022-09-13 13:58:26 +03:00
const json j = { " foo " , 1 , 2 , 3 , false , { { " one " , 1 } } } ;
2017-05-07 20:27:40 +03:00
ss . width ( 1 ) ;
ss . fill ( ' \t ' ) ;
j > > ss ;
CHECK ( ss . str ( ) = =
" [ \n \t \" foo \" , \n \t 1, \n \t 2, \n \t 3, \n \t false, \n \t { \n \t \t \" one \" : 1 \n \t } \n ] " ) ;
}
2016-08-04 22:55:47 +03:00
}
2018-10-21 12:49:37 +03:00
SECTION ( " dump " )
{
SECTION ( " invalid character " )
{
2022-09-13 13:58:26 +03:00
const json j = " ä \xA9 ü " ;
2018-10-21 12:49:37 +03:00
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( j . dump ( ) , " [json.exception.type_error.316] invalid UTF-8 byte at index 2: 0xA9 " , json : : type_error & ) ;
CHECK_THROWS_WITH_AS ( j . dump ( 1 , ' ' , false , json : : error_handler_t : : strict ) , " [json.exception.type_error.316] invalid UTF-8 byte at index 2: 0xA9 " , json : : type_error & ) ;
2018-10-21 12:49:37 +03:00
CHECK ( j . dump ( - 1 , ' ' , false , json : : error_handler_t : : ignore ) = = " \" äü \" " ) ;
2018-10-22 19:20:45 +03:00
CHECK ( j . dump ( - 1 , ' ' , false , json : : error_handler_t : : replace ) = = " \" ä \xEF \xBF \xBD ü \" " ) ;
CHECK ( j . dump ( - 1 , ' ' , true , json : : error_handler_t : : replace ) = = " \" \\ u00e4 \\ ufffd \\ u00fc \" " ) ;
2018-10-21 12:49:37 +03:00
}
SECTION ( " ending with incomplete character " )
{
2022-09-13 13:58:26 +03:00
const json j = " 123 \xC2 " ;
2018-10-21 12:49:37 +03:00
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( j . dump ( ) , " [json.exception.type_error.316] incomplete UTF-8 string; last byte: 0xC2 " , json : : type_error & ) ;
2018-10-21 12:49:37 +03:00
CHECK_THROWS_AS ( j . dump ( 1 , ' ' , false , json : : error_handler_t : : strict ) , json : : type_error & ) ;
CHECK ( j . dump ( - 1 , ' ' , false , json : : error_handler_t : : ignore ) = = " \" 123 \" " ) ;
2018-10-22 19:20:45 +03:00
CHECK ( j . dump ( - 1 , ' ' , false , json : : error_handler_t : : replace ) = = " \" 123 \xEF \xBF \xBD \" " ) ;
CHECK ( j . dump ( - 1 , ' ' , true , json : : error_handler_t : : replace ) = = " \" 123 \\ ufffd \" " ) ;
2018-10-21 12:49:37 +03:00
}
SECTION ( " unexpected character " )
{
2022-09-13 13:58:26 +03:00
const json j = " 123 \xF1 \xB0 \x34 \x35 \x36 " ;
2018-10-21 12:49:37 +03:00
2022-03-24 17:55:35 +03:00
CHECK_THROWS_WITH_AS ( j . dump ( ) , " [json.exception.type_error.316] invalid UTF-8 byte at index 5: 0x34 " , json : : type_error & ) ;
2018-10-21 12:49:37 +03:00
CHECK_THROWS_AS ( j . dump ( 1 , ' ' , false , json : : error_handler_t : : strict ) , json : : type_error & ) ;
CHECK ( j . dump ( - 1 , ' ' , false , json : : error_handler_t : : ignore ) = = " \" 123456 \" " ) ;
2018-10-22 19:20:45 +03:00
CHECK ( j . dump ( - 1 , ' ' , false , json : : error_handler_t : : replace ) = = " \" 123 \xEF \xBF \xBD \x34 \x35 \x36 \" " ) ;
CHECK ( j . dump ( - 1 , ' ' , true , json : : error_handler_t : : replace ) = = " \" 123 \\ ufffd456 \" " ) ;
2018-10-21 12:49:37 +03:00
}
2018-10-23 18:22:13 +03:00
SECTION ( " U+FFFD Substitution of Maximal Subparts " )
{
// Some tests (mostly) from
// https://www.unicode.org/versions/Unicode11.0.0/ch03.pdf
// Section 3.9 -- U+FFFD Substitution of Maximal Subparts
auto test = [ & ] ( std : : string const & input , std : : string const & expected )
{
2022-09-13 13:58:26 +03:00
const json j = input ;
2018-10-23 18:22:13 +03:00
CHECK ( j . dump ( - 1 , ' ' , true , json : : error_handler_t : : replace ) = = " \" " + expected + " \" " ) ;
} ;
test ( " \xC2 " , " \\ ufffd " ) ;
test ( " \xC2 \x41 \x42 " , " \\ ufffd " " \x41 " " \x42 " ) ;
test ( " \xC2 \xF4 " , " \\ ufffd " " \\ ufffd " ) ;
test ( " \xF0 \x80 \x80 \x41 " , " \\ ufffd " " \\ ufffd " " \\ ufffd " " \x41 " ) ;
test ( " \xF1 \x80 \x80 \x41 " , " \\ ufffd " " \x41 " ) ;
test ( " \xF2 \x80 \x80 \x41 " , " \\ ufffd " " \x41 " ) ;
test ( " \xF3 \x80 \x80 \x41 " , " \\ ufffd " " \x41 " ) ;
test ( " \xF4 \x80 \x80 \x41 " , " \\ ufffd " " \x41 " ) ;
test ( " \xF5 \x80 \x80 \x41 " , " \\ ufffd " " \\ ufffd " " \\ ufffd " " \x41 " ) ;
test ( " \xF0 \x90 \x80 \x41 " , " \\ ufffd " " \x41 " ) ;
test ( " \xF1 \x90 \x80 \x41 " , " \\ ufffd " " \x41 " ) ;
test ( " \xF2 \x90 \x80 \x41 " , " \\ ufffd " " \x41 " ) ;
test ( " \xF3 \x90 \x80 \x41 " , " \\ ufffd " " \x41 " ) ;
test ( " \xF4 \x90 \x80 \x41 " , " \\ ufffd " " \\ ufffd " " \\ ufffd " " \x41 " ) ;
test ( " \xF5 \x90 \x80 \x41 " , " \\ ufffd " " \\ ufffd " " \\ ufffd " " \x41 " ) ;
test ( " \xC0 \xAF \xE0 \x80 \xBF \xF0 \x81 \x82 \x41 " , " \\ ufffd " " \\ ufffd " " \\ ufffd " " \\ ufffd " " \\ ufffd " " \\ ufffd " " \\ ufffd " " \\ ufffd " " \x41 " ) ;
test ( " \xED \xA0 \x80 \xED \xBF \xBF \xED \xAF \x41 " , " \\ ufffd " " \\ ufffd " " \\ ufffd " " \\ ufffd " " \\ ufffd " " \\ ufffd " " \\ ufffd " " \\ ufffd " " \x41 " ) ;
test ( " \xF4 \x91 \x92 \x93 \xFF \x41 \x80 \xBF \x42 " , " \\ ufffd " " \\ ufffd " " \\ ufffd " " \\ ufffd " " \\ ufffd " " \x41 " " \\ ufffd " " \\ ufffd " " \x42 " ) ;
test ( " \xE1 \x80 \xE2 \xF0 \x91 \x92 \xF1 \xBF \x41 " , " \\ ufffd " " \\ ufffd " " \\ ufffd " " \\ ufffd " " \x41 " ) ;
}
2018-10-21 12:49:37 +03:00
}
2019-04-26 06:12:15 +03:00
SECTION ( " to_string " )
{
auto test = [ & ] ( std : : string const & input , std : : string const & expected )
{
using std : : to_string ;
2022-09-13 13:58:26 +03:00
const json j = input ;
2019-04-26 06:12:15 +03:00
CHECK ( to_string ( j ) = = " \" " + expected + " \" " ) ;
} ;
2021-03-24 09:15:18 +03:00
test ( R " ({ " x " :5, " y " :6}) " , R " ({ \" x \" :5, \" y \" :6}) " ) ;
test ( " { \" x \" :[10,null,null,null]} " , R " ({ \" x \" :[10,null,null,null]}) " ) ;
2019-04-26 06:12:15 +03:00
test ( " test " , " test " ) ;
2021-03-24 09:15:18 +03:00
test ( " [3, \" false \" ,false] " , R " ([3, \" false \" ,false]) " ) ;
2019-04-26 06:12:15 +03:00
}
2016-08-04 22:55:47 +03:00
}
2019-08-25 23:02:49 +03:00
TEST_CASE_TEMPLATE ( " serialization for extreme integer values " , T , int32_t , uint32_t , int64_t , uint64_t )
{
SECTION ( " minimum " )
{
constexpr auto minimum = ( std : : numeric_limits < T > : : min ) ( ) ;
2022-09-13 13:58:26 +03:00
const json j = minimum ;
2019-08-25 23:02:49 +03:00
CHECK ( j . dump ( ) = = std : : to_string ( minimum ) ) ;
}
SECTION ( " maximum " )
{
constexpr auto maximum = ( std : : numeric_limits < T > : : max ) ( ) ;
2022-09-13 13:58:26 +03:00
const json j = maximum ;
2019-08-25 23:02:49 +03:00
CHECK ( j . dump ( ) = = std : : to_string ( maximum ) ) ;
}
}
2020-05-06 23:13:31 +03:00
TEST_CASE ( " dump with binary values " )
{
2020-05-19 14:30:22 +03:00
auto binary = json : : binary ( { 1 , 2 , 3 , 4 } ) ;
auto binary_empty = json : : binary ( { } ) ;
auto binary_with_subtype = json : : binary ( { 1 , 2 , 3 , 4 } , 128 ) ;
auto binary_empty_with_subtype = json : : binary ( { } , 128 ) ;
2020-05-18 14:53:20 +03:00
2022-09-13 13:58:26 +03:00
const json object = { { " key " , binary } } ;
const json object_empty = { { " key " , binary_empty } } ;
const json object_with_subtype = { { " key " , binary_with_subtype } } ;
const json object_empty_with_subtype = { { " key " , binary_empty_with_subtype } } ;
2020-05-18 14:53:20 +03:00
2022-09-13 13:58:26 +03:00
const json array = { " value " , 1 , binary } ;
const json array_empty = { " value " , 1 , binary_empty } ;
const json array_with_subtype = { " value " , 1 , binary_with_subtype } ;
const json array_empty_with_subtype = { " value " , 1 , binary_empty_with_subtype } ;
2020-05-18 14:53:20 +03:00
SECTION ( " normal " )
2020-05-06 23:13:31 +03:00
{
2020-05-18 14:53:20 +03:00
CHECK ( binary . dump ( ) = = " { \" bytes \" :[1,2,3,4], \" subtype \" :null} " ) ;
CHECK ( binary_empty . dump ( ) = = " { \" bytes \" :[], \" subtype \" :null} " ) ;
CHECK ( binary_with_subtype . dump ( ) = = " { \" bytes \" :[1,2,3,4], \" subtype \" :128} " ) ;
CHECK ( binary_empty_with_subtype . dump ( ) = = " { \" bytes \" :[], \" subtype \" :128} " ) ;
CHECK ( object . dump ( ) = = " { \" key \" :{ \" bytes \" :[1,2,3,4], \" subtype \" :null}} " ) ;
CHECK ( object_empty . dump ( ) = = " { \" key \" :{ \" bytes \" :[], \" subtype \" :null}} " ) ;
CHECK ( object_with_subtype . dump ( ) = = " { \" key \" :{ \" bytes \" :[1,2,3,4], \" subtype \" :128}} " ) ;
CHECK ( object_empty_with_subtype . dump ( ) = = " { \" key \" :{ \" bytes \" :[], \" subtype \" :128}} " ) ;
CHECK ( array . dump ( ) = = " [ \" value \" ,1,{ \" bytes \" :[1,2,3,4], \" subtype \" :null}] " ) ;
CHECK ( array_empty . dump ( ) = = " [ \" value \" ,1,{ \" bytes \" :[], \" subtype \" :null}] " ) ;
CHECK ( array_with_subtype . dump ( ) = = " [ \" value \" ,1,{ \" bytes \" :[1,2,3,4], \" subtype \" :128}] " ) ;
CHECK ( array_empty_with_subtype . dump ( ) = = " [ \" value \" ,1,{ \" bytes \" :[], \" subtype \" :128}] " ) ;
2020-05-06 23:13:31 +03:00
}
2020-05-18 14:53:20 +03:00
SECTION ( " pretty-printed " )
2020-05-06 23:13:31 +03:00
{
2020-05-18 14:53:20 +03:00
CHECK ( binary . dump ( 4 ) = = " { \n "
" \" bytes \" : [1, 2, 3, 4], \n "
" \" subtype \" : null \n "
" } " ) ;
CHECK ( binary_empty . dump ( 4 ) = = " { \n "
" \" bytes \" : [], \n "
" \" subtype \" : null \n "
" } " ) ;
CHECK ( binary_with_subtype . dump ( 4 ) = = " { \n "
" \" bytes \" : [1, 2, 3, 4], \n "
" \" subtype \" : 128 \n "
" } " ) ;
CHECK ( binary_empty_with_subtype . dump ( 4 ) = = " { \n "
" \" bytes \" : [], \n "
" \" subtype \" : 128 \n "
" } " ) ;
2020-05-06 23:13:31 +03:00
2020-05-18 14:53:20 +03:00
CHECK ( object . dump ( 4 ) = = " { \n "
" \" key \" : { \n "
" \" bytes \" : [1, 2, 3, 4], \n "
" \" subtype \" : null \n "
" } \n "
" } " ) ;
CHECK ( object_empty . dump ( 4 ) = = " { \n "
" \" key \" : { \n "
" \" bytes \" : [], \n "
" \" subtype \" : null \n "
" } \n "
" } " ) ;
CHECK ( object_with_subtype . dump ( 4 ) = = " { \n "
" \" key \" : { \n "
" \" bytes \" : [1, 2, 3, 4], \n "
" \" subtype \" : 128 \n "
" } \n "
2020-05-06 23:13:31 +03:00
" } " ) ;
2020-05-18 14:53:20 +03:00
CHECK ( object_empty_with_subtype . dump ( 4 ) = = " { \n "
" \" key \" : { \n "
" \" bytes \" : [], \n "
" \" subtype \" : 128 \n "
" } \n "
" } " ) ;
CHECK ( array . dump ( 4 ) = = " [ \n "
" \" value \" , \n "
" 1, \n "
" { \n "
" \" bytes \" : [1, 2, 3, 4], \n "
" \" subtype \" : null \n "
" } \n "
" ] " ) ;
CHECK ( array_empty . dump ( 4 ) = = " [ \n "
" \" value \" , \n "
" 1, \n "
" { \n "
" \" bytes \" : [], \n "
" \" subtype \" : null \n "
" } \n "
" ] " ) ;
CHECK ( array_with_subtype . dump ( 4 ) = = " [ \n "
" \" value \" , \n "
" 1, \n "
" { \n "
" \" bytes \" : [1, 2, 3, 4], \n "
" \" subtype \" : 128 \n "
" } \n "
" ] " ) ;
CHECK ( array_empty_with_subtype . dump ( 4 ) = = " [ \n "
2020-05-06 23:13:31 +03:00
" \" value \" , \n "
" 1, \n "
2020-05-18 14:53:20 +03:00
" { \n "
" \" bytes \" : [], \n "
" \" subtype \" : 128 \n "
" } \n "
2020-05-06 23:13:31 +03:00
" ] " ) ;
}
2020-05-13 13:35:11 +03:00
}