1 //     __ _____ _____ _____
2 //  __|  |   __|     |   | |  JSON for Modern C++ (supporting code)
3 // |  |  |__   |  |  | | | |  version 3.11.2
4 // |_____|_____|_____|_|___|  https://github.com/nlohmann/json
5 //
6 // Copyright (c) 2013-2022 Niels Lohmann <http://nlohmann.me>.
7 // SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
8 // SPDX-License-Identifier: MIT
9 
10 #include "doctest_compatibility.h"
11 
12 #ifdef JSON_DIAGNOSTICS
13     #undef JSON_DIAGNOSTICS
14 #endif
15 
16 #define JSON_DIAGNOSTICS 1
17 
18 #include <nlohmann/json.hpp>
19 using nlohmann::json;
20 
21 TEST_CASE("Better diagnostics")
22 {
23     SECTION("empty JSON Pointer")
24     {
25         json j = 1;
26         std::string s;
27         CHECK_THROWS_WITH_AS(s = j.get<std::string>(), "[json.exception.type_error.302] type must be string, but is number", json::type_error);
28     }
29 
30     SECTION("invalid type")
31     {
32         json j;
33         j["a"]["b"]["c"] = 1;
34         std::string s;
35         CHECK_THROWS_WITH_AS(s = j["a"]["b"]["c"].get<std::string>(), "[json.exception.type_error.302] (/a/b/c) type must be string, but is number", json::type_error);
36     }
37 
38     SECTION("missing key")
39     {
40         json j;
41         j["object"]["object"] = true;
42         CHECK_THROWS_WITH_AS(j["object"].at("not_found"), "[json.exception.out_of_range.403] (/object) key 'not_found' not found", json::out_of_range);
43     }
44 
45     SECTION("array index out of range")
46     {
47         json j;
48         j["array"][4] = true;
49         CHECK_THROWS_WITH_AS(j["array"].at(5), "[json.exception.out_of_range.401] (/array) array index 5 is out of range", json::out_of_range);
50     }
51 
52     SECTION("array index at wrong type")
53     {
54         json j;
55         j["array"][4] = true;
56         CHECK_THROWS_WITH_AS(j["array"][4][5], "[json.exception.type_error.305] (/array/4) cannot use operator[] with a numeric argument with boolean", json::type_error);
57     }
58 
59     SECTION("wrong iterator")
60     {
61         json j;
62         j["array"] = json::array();
63         CHECK_THROWS_WITH_AS(j["array"].erase(j.begin()), "[json.exception.invalid_iterator.202] (/array) iterator does not fit current value", json::invalid_iterator);
64     }
65 
66     SECTION("JSON Pointer escaping")
67     {
68         json j;
69         j["a/b"]["m~n"] = 1;
70         std::string s;
71         CHECK_THROWS_WITH_AS(s = j["a/b"]["m~n"].get<std::string>(), "[json.exception.type_error.302] (/a~1b/m~0n) type must be string, but is number", json::type_error);
72     }
73 
74     SECTION("Parse error")
75     {
76         json _;
77         CHECK_THROWS_WITH_AS(_ = json::parse(""), "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error);
78     }
79 
80     SECTION("Wrong type in update()")
81     {
82         json j = {{"foo", "bar"}};
83         json k = {{"bla", 1}};
84 
85         CHECK_THROWS_WITH_AS(j.update(k["bla"].begin(), k["bla"].end()), "[json.exception.type_error.312] (/bla) cannot use update() with number", json::type_error);
86         CHECK_THROWS_WITH_AS(j.update(k["bla"]), "[json.exception.type_error.312] (/bla) cannot use update() with number", json::type_error);
87     }
88 }
89 
90 TEST_CASE("Regression tests for extended diagnostics")
91 {
92     SECTION("Regression test for https://github.com/nlohmann/json/pull/2562#pullrequestreview-574858448")
93     {
94         CHECK_THROWS_WITH_AS(json({"0", "0"})[1].get<int>(), "[json.exception.type_error.302] (/1) type must be number, but is string", json::type_error);
95         CHECK_THROWS_WITH_AS(json({"0", "1"})[1].get<int>(), "[json.exception.type_error.302] (/1) type must be number, but is string", json::type_error);
96     }
97 
98     SECTION("Regression test for https://github.com/nlohmann/json/pull/2562/files/380a613f2b5d32425021129cd1f371ddcfd54ddf#r563259793")
99     {
100         json j;
101         j["/foo"] = {1, 2, 3};
102         CHECK_THROWS_WITH_AS(j.unflatten(), "[json.exception.type_error.315] (/~1foo) values in object must be primitive", json::type_error);
103     }
104 
105     SECTION("Regression test for issue #2838 - Assertion failure when inserting into arrays with JSON_DIAGNOSTICS set")
106     {
107         // void push_back(basic_json&& val)
108         {
109             json j_arr = json::array();
110             j_arr.push_back(json::object());
111             j_arr.push_back(json::object());
112             j_arr.push_back(json::object());
113             j_arr.push_back(json::object());
114             json j_obj = json::object();
115             j_obj["key"] = j_arr;
116         }
117 
118         // void push_back(const basic_json& val)
119         {
120             json j_arr = json::array();
121             auto object = json::object();
122             j_arr.push_back(object);
123             j_arr.push_back(object);
124             j_arr.push_back(object);
125             j_arr.push_back(object);
126             json j_obj = json::object();
127             j_obj["key"] = j_arr;
128         }
129 
130         // reference emplace_back(Args&& ... args)
131         {
132             json j_arr = json::array();
133             j_arr.emplace_back(json::object());
134             j_arr.emplace_back(json::object());
135             j_arr.emplace_back(json::object());
136             j_arr.emplace_back(json::object());
137             json j_obj = json::object();
138             j_obj["key"] = j_arr;
139         }
140 
141         // iterator insert(const_iterator pos, const basic_json& val)
142         {
143             json j_arr = json::array();
144             j_arr.insert(j_arr.begin(), json::object());
145             j_arr.insert(j_arr.begin(), json::object());
146             j_arr.insert(j_arr.begin(), json::object());
147             j_arr.insert(j_arr.begin(), json::object());
148             json j_obj = json::object();
149             j_obj["key"] = j_arr;
150         }
151 
152         // iterator insert(const_iterator pos, size_type cnt, const basic_json& val)
153         {
154             json j_arr = json::array();
155             j_arr.insert(j_arr.begin(), 2, json::object());
156             json j_obj = json::object();
157             j_obj["key"] = j_arr;
158         }
159 
160         // iterator insert(const_iterator pos, const_iterator first, const_iterator last)
161         {
162             json j_arr = json::array();
163             json j_objects = {json::object(), json::object()};
164             j_arr.insert(j_arr.begin(), j_objects.begin(), j_objects.end());
165             json j_obj = json::object();
166             j_obj["key"] = j_arr;
167         }
168     }
169 
170     SECTION("Regression test for issue #2962 - JSON_DIAGNOSTICS assertion for ordered_json")
171     {
172         nlohmann::ordered_json j;
173         nlohmann::ordered_json j2;
174         const std::string value;
175         j["first"] = value;
176         j["second"] = value;
177         j2["something"] = j;
178     }
179 
180     SECTION("Regression test for issue #3007 - Parent pointers properly set when using update()")
181     {
182         // void update(const_reference j)
183         {
184             json j = json::object();
185 
186             {
187                 json j2 = json::object();
188                 j2["one"] = 1;
189 
190                 j.update(j2);
191             }
192 
193             // Must call operator[] on const element, otherwise m_parent gets updated.
194             auto const& constJ = j;
195             CHECK_THROWS_WITH_AS(constJ["one"].at(0), "[json.exception.type_error.304] (/one) cannot use at() with number", json::type_error);
196         }
197 
198         // void update(const_iterator first, const_iterator last)
199         {
200             json j = json::object();
201 
202             {
203                 json j2 = json::object();
204                 j2["one"] = 1;
205 
206                 j.update(j2.begin(), j2.end());
207             }
208 
209             // Must call operator[] on const element, otherwise m_parent gets updated.
210             auto const& constJ = j;
211             CHECK_THROWS_WITH_AS(constJ["one"].at(0), "[json.exception.type_error.304] (/one) cannot use at() with number", json::type_error);
212         }
213 
214         // Code from #3007 triggering unwanted assertion without fix to update().
215         {
216             json root = json::array();
217             json lower = json::object();
218 
219             {
220                 json lowest = json::object();
221                 lowest["one"] = 1;
222 
223                 lower.update(lowest);
224             }
225 
226             root.push_back(lower);
227         }
228     }
229 
230     SECTION("Regression test for issue #3032 - Yet another assertion failure when inserting into arrays with JSON_DIAGNOSTICS set")
231     {
232         // reference operator[](size_type idx)
233         {
234             json j_arr = json::array();
235             j_arr[0] = 0;
236             j_arr[1] = 1;
237             j_arr[2] = 2;
238             j_arr[3] = 3;
239             j_arr[4] = 4;
240             j_arr[5] = 5;
241             j_arr[6] = 6;
242             j_arr[7] = 7;
243             json j_arr_copy = j_arr;
244         }
245     }
246 }
247