Skip to content

Commit 4fe1f6d

Browse files
author
Lukasz A.J. Wrona
committed
JSON equality operator and tests
Because it'd be useful to be able to compare whole JSON objects (for instance in unit tests)
1 parent c24ff8a commit 4fe1f6d

File tree

3 files changed

+156
-0
lines changed

3 files changed

+156
-0
lines changed

src/util/json.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ Author: Daniel Kroening, [email protected]
77
\*******************************************************************/
88

99
#include "json.h"
10+
#include <util/narrow.h>
1011

1112
#include <ostream>
13+
#include <algorithm>
1214

1315
const jsont jsont::null_json_object(jsont::kindt::J_NULL);
1416

@@ -162,3 +164,48 @@ void jsont::swap(jsont &other)
162164
other.object.swap(object);
163165
other.value.swap(value);
164166
}
167+
168+
bool operator==(const jsont &left, const jsont &right)
169+
{
170+
if(left.kind != right.kind)
171+
return false;
172+
switch(left.kind)
173+
{
174+
case jsont::kindt::J_NULL:
175+
return true;
176+
case jsont::kindt::J_TRUE:
177+
return true;
178+
case jsont::kindt::J_FALSE:
179+
return true;
180+
case jsont::kindt::J_NUMBER:
181+
return left.value == right.value;
182+
case jsont::kindt::J_STRING:
183+
return left.value == right.value;
184+
case jsont::kindt::J_ARRAY:
185+
{
186+
const auto &left_array = static_cast<const json_arrayt &>(left);
187+
const auto &right_array = static_cast<const json_arrayt &>(right);
188+
return left_array.size() == right_array.size() &&
189+
std::equal(
190+
left_array.begin(), left_array.end(), right_array.begin());
191+
}
192+
case jsont::kindt::J_OBJECT:
193+
{
194+
const auto &left_object = static_cast<const json_objectt &>(left);
195+
const auto &right_object = static_cast<const json_objectt &>(right);
196+
const size_t left_size =
197+
narrow<size_t>(std::distance(left_object.begin(), left_object.end()));
198+
const size_t right_size =
199+
narrow<size_t>(std::distance(right_object.begin(), right_object.end()));
200+
if(left_size != right_size)
201+
return false;
202+
return std::all_of(
203+
left_object.begin(),
204+
left_object.end(),
205+
[&](const std::pair<std::string, jsont> &pair) {
206+
return right_object[pair.first] == pair.second;
207+
});
208+
}
209+
}
210+
UNREACHABLE;
211+
}

src/util/json.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,4 +458,6 @@ inline const json_stringt &to_json_string(const jsont &json)
458458
return static_cast<const json_stringt &>(json);
459459
}
460460

461+
bool operator==(const jsont &left, const jsont &right);
462+
461463
#endif // CPROVER_UTIL_JSON_H

unit/json/json_parser.cpp

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,3 +133,110 @@ SCENARIO("Loading JSON files")
133133
}
134134
}
135135
}
136+
137+
TEST_CASE("json equality", "[core][test-gen-util][state][type]")
138+
{
139+
SECTION("null")
140+
{
141+
REQUIRE(jsont::null_json_object == jsont::null_json_object);
142+
}
143+
144+
SECTION("boolean")
145+
{
146+
REQUIRE(jsont::json_boolean(false) == jsont::json_boolean(false));
147+
REQUIRE(jsont::json_boolean(true) == jsont::json_boolean(true));
148+
REQUIRE_FALSE(jsont::json_boolean(true) == jsont::json_boolean(false));
149+
REQUIRE_FALSE(jsont::json_boolean(false) == jsont::null_json_object);
150+
}
151+
152+
SECTION("number")
153+
{
154+
REQUIRE(json_numbert("0") == json_numbert("0"));
155+
REQUIRE(json_numbert("1") == json_numbert("1"));
156+
REQUIRE(json_numbert("-1") == json_numbert("-1"));
157+
REQUIRE(json_numbert("1.578") == json_numbert("1.578"));
158+
REQUIRE_FALSE(json_numbert("0") == json_numbert("1"));
159+
REQUIRE_FALSE(json_numbert("1") == json_numbert("-1"));
160+
REQUIRE_FALSE(json_numbert("-1") == json_numbert("1"));
161+
REQUIRE_FALSE(json_numbert("1.578") == json_numbert("1.5789"));
162+
REQUIRE_FALSE(json_numbert("0") == jsont::json_boolean(false));
163+
REQUIRE_FALSE(jsont::json_boolean(false) == json_numbert("0"));
164+
REQUIRE_FALSE(json_numbert("0") == jsont::null_json_object);
165+
REQUIRE_FALSE(jsont::null_json_object == json_numbert("0"));
166+
}
167+
168+
SECTION("string")
169+
{
170+
REQUIRE(json_stringt("") == json_stringt(""));
171+
REQUIRE(json_stringt("foo") == json_stringt("foo"));
172+
REQUIRE(json_stringt("bar") == json_stringt("bar"));
173+
REQUIRE_FALSE(json_stringt("foo") == json_stringt("bar"));
174+
REQUIRE_FALSE(json_stringt("bar") == json_stringt("baz"));
175+
REQUIRE_FALSE(json_stringt("foo") == json_stringt("food"));
176+
REQUIRE_FALSE(json_stringt("1") == json_numbert("1"));
177+
REQUIRE_FALSE(json_stringt("true") == jsont::json_boolean("true"));
178+
REQUIRE_FALSE(json_stringt("") == jsont::json_boolean("false"));
179+
REQUIRE_FALSE(json_stringt("") == jsont::null_json_object);
180+
}
181+
182+
SECTION("array")
183+
{
184+
REQUIRE(json_arrayt{} == json_arrayt{});
185+
REQUIRE(
186+
json_arrayt{jsont::null_json_object} ==
187+
json_arrayt{jsont::null_json_object});
188+
REQUIRE(
189+
json_arrayt{json_numbert{"9"}, json_numbert{"6"}} ==
190+
json_arrayt{json_numbert{"9"}, json_numbert{"6"}});
191+
REQUIRE(
192+
json_arrayt{
193+
json_stringt{"foo"}, json_stringt{"bar"}, json_stringt{"baz"}} ==
194+
json_arrayt{
195+
json_stringt{"foo"}, json_stringt{"bar"}, json_stringt{"baz"}});
196+
197+
// different lengths
198+
REQUIRE_FALSE(
199+
json_arrayt{json_stringt{"foo"}, json_stringt{"bar"}} ==
200+
json_arrayt{
201+
json_stringt{"foo"}, json_stringt{"bar"}, json_stringt{"baz"}});
202+
// different elements
203+
REQUIRE_FALSE(
204+
json_arrayt{
205+
json_stringt{"foo"}, json_stringt{"bar"}, json_stringt{"foo"}} ==
206+
json_arrayt{
207+
json_stringt{"foo"}, json_stringt{"bar"}, json_stringt{"baz"}});
208+
// different kind
209+
REQUIRE_FALSE(json_arrayt{} == jsont::json_boolean(false));
210+
REQUIRE_FALSE(json_arrayt{} == jsont::null_json_object);
211+
}
212+
213+
SECTION("object")
214+
{
215+
REQUIRE(json_objectt{} == json_objectt{});
216+
REQUIRE(
217+
json_objectt{{"key", json_stringt{"value"}}} ==
218+
json_objectt{{"key", json_stringt{"value"}}});
219+
REQUIRE(
220+
json_objectt{{"key1", json_stringt{"value1"}},
221+
{"key2", json_stringt{"value2"}}} ==
222+
json_objectt{{"key1", json_stringt{"value1"}},
223+
{"key2", json_stringt{"value2"}}});
224+
225+
// Extra property
226+
REQUIRE_FALSE(
227+
json_objectt{{"key1", json_stringt{"value1"}},
228+
{"key2", json_stringt{"value2"}},
229+
{"key3", json_stringt{"value3"}}} ==
230+
json_objectt{{"key1", json_stringt{"value1"}},
231+
{"key2", json_stringt{"value2"}}});
232+
// different field values
233+
REQUIRE_FALSE(
234+
json_objectt{{"key1", json_stringt{"foo"}},
235+
{"key2", json_stringt{"bar"}}} ==
236+
json_objectt{{"key1", json_stringt{"foo"}},
237+
{"key2", json_stringt{"baz"}}});
238+
// different kind
239+
REQUIRE_FALSE(json_objectt{} == json_arrayt{});
240+
REQUIRE_FALSE(json_objectt{} == jsont::null_json_object);
241+
}
242+
}

0 commit comments

Comments
 (0)