diff --git a/Makefile b/Makefile index 199d65da..b6116cc4 100644 --- a/Makefile +++ b/Makefile @@ -79,7 +79,8 @@ pretty: --indent-col1-comments --pad-oper --pad-header --align-pointer=type \ --align-reference=type --add-brackets --convert-tabs --close-templates \ --lineend=linux --preserve-date --suffix=none --formatted \ - src/json.hpp src/json.hpp.re2c test/src/unit.cpp test/src/fuzz.cpp benchmarks/benchmarks.cpp doc/examples/*.cpp + src/json.hpp src/json.hpp.re2c test/src/*.cpp \ + benchmarks/benchmarks.cpp doc/examples/*.cpp ########################################################################## diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4d12de37..696a7f3f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -3,7 +3,33 @@ set(JSON_UNITTEST_TARGET_NAME "json_unit") add_executable(${JSON_UNITTEST_TARGET_NAME} "src/catch.hpp" "src/unit.cpp" - "src/unit-runner.cpp" + "src/unit-algorithms.cpp" + "src/unit-allocator.cpp" + "src/unit-capacity.cpp" + "src/unit-class_const_iterator.cpp" + "src/unit-class_iterator.cpp" + "src/unit-class_lexer.cpp" + "src/unit-class_parser.cpp" + "src/unit-comparison.cpp" + "src/unit-concepts.cpp" + "src/unit-constructor.cpp" + "src/unit-convenience.cpp" + "src/unit-conversions.cpp" + "src/unit-deserialization.cpp" + "src/unit-element_access.cpp" + "src/unit-inspection.cpp" + "src/unit-iterator_wrapper.cpp" + "src/unit-iterators.cpp" + "src/unit-json_patch.cpp" + "src/unit-json_pointer.cpp" + "src/unit-modifiers.cpp" + "src/unit-pointer_access.cpp" + "src/unit-readme.cpp" + "src/unit-reference_access.cpp" + "src/unit-regression.cpp" + "src/unit-serialization.cpp" + "src/unit-testsuites.cpp" + "src/unit-unicode.cpp" ) set_target_properties(${JSON_UNITTEST_TARGET_NAME} PROPERTIES diff --git a/test/Makefile b/test/Makefile index d11e1e34..c56a2ae9 100644 --- a/test/Makefile +++ b/test/Makefile @@ -3,19 +3,49 @@ ########################################################################## # additional flags -CXXFLAGS += -std=c++11 -Wall -Wextra -pedantic -Weffc++ -Wcast-align -Wcast-qual -Wctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-declarations -Wmissing-include-dirs -Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-conversion -Wsign-promo -Wstrict-overflow=5 -Wswitch -Wundef -Wno-unused -Wnon-virtual-dtor -Wreorder -Wdeprecated -Wfloat-equal +CXXFLAGS += -std=c++11 -Wall -Wextra -pedantic -Weffc++ -Wcast-align -Wcast-qual -Wctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-declarations -Wmissing-include-dirs -Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-conversion -Wsign-promo -Wstrict-overflow=5 -Wswitch -Wundef -Wno-unused -Wnon-virtual-dtor -Wreorder -Wdeprecated -Wno-keyword-macro -Wno-float-equal CPPFLAGS += -I ../src -I . -SOURCES = src/unit-runner.cpp src/unit.cpp +SOURCES = src/unit.cpp \ + src/unit-algorithms.cpp \ + src/unit-allocator.cpp \ + src/unit-capacity.cpp \ + src/unit-class_const_iterator.cpp \ + src/unit-class_iterator.cpp \ + src/unit-class_lexer.cpp \ + src/unit-class_parser.cpp \ + src/unit-comparison.cpp \ + src/unit-concepts.cpp \ + src/unit-constructor.cpp \ + src/unit-convenience.cpp \ + src/unit-conversions.cpp \ + src/unit-deserialization.cpp \ + src/unit-element_access.cpp \ + src/unit-inspection.cpp \ + src/unit-iterator_wrapper.cpp \ + src/unit-iterators.cpp \ + src/unit-json_patch.cpp \ + src/unit-json_pointer.cpp \ + src/unit-modifiers.cpp \ + src/unit-pointer_access.cpp \ + src/unit-readme.cpp \ + src/unit-reference_access.cpp \ + src/unit-regression.cpp \ + src/unit-serialization.cpp \ + src/unit-unicode.cpp \ + src/unit-testsuites.cpp + OBJECTS = $(SOURCES:.cpp=.o) all: json_unit json_unit: $(OBJECTS) ../src/json.hpp src/catch.hpp - $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJECTS) -o $@ + @echo "[CXXLD] $@" + @$(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJECTS) -o $@ -%.o: %.cpp - $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@ +%.o: %.cpp ../src/json.hpp src/catch.hpp + @echo "[CXX] $@" + @$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@ clean: rm -fr json_unit $(OBJECTS) diff --git a/test/src/unit-algorithms.cpp b/test/src/unit-algorithms.cpp new file mode 100644 index 00000000..75c69da5 --- /dev/null +++ b/test/src/unit-algorithms.cpp @@ -0,0 +1,318 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("algorithms") +{ + json j_array = {13, 29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz"}; + json j_object = {{"one", 1}, {"two", 2}}; + + SECTION("non-modifying sequence operations") + { + SECTION("std::all_of") + { + CHECK(std::all_of(j_array.begin(), j_array.end(), [](const json & value) + { + return value.size() > 0; + })); + CHECK(std::all_of(j_object.begin(), j_object.end(), [](const json & value) + { + return value.type() == json::value_t::number_integer; + })); + } + + SECTION("std::any_of") + { + CHECK(std::any_of(j_array.begin(), j_array.end(), [](const json & value) + { + return value.is_string() and value.get() == "foo"; + })); + CHECK(std::any_of(j_object.begin(), j_object.end(), [](const json & value) + { + return value.get() > 1; + })); + } + + SECTION("std::none_of") + { + CHECK(std::none_of(j_array.begin(), j_array.end(), [](const json & value) + { + return value.size() == 0; + })); + CHECK(std::none_of(j_object.begin(), j_object.end(), [](const json & value) + { + return value.get() <= 0; + })); + } + + SECTION("std::for_each") + { + SECTION("reading") + { + int sum = 0; + + std::for_each(j_array.cbegin(), j_array.cend(), [&sum](const json & value) + { + if (value.is_number()) + { + sum += static_cast(value); + } + }); + + CHECK(sum == 45); + } + + SECTION("writing") + { + auto add17 = [](json & value) + { + if (value.is_array()) + { + value.push_back(17); + } + }; + + std::for_each(j_array.begin(), j_array.end(), add17); + + CHECK(j_array[6] == json({1, 2, 3, 17})); + } + } + + SECTION("std::count") + { + CHECK(std::count(j_array.begin(), j_array.end(), json(true)) == 1); + } + + SECTION("std::count_if") + { + CHECK(std::count_if(j_array.begin(), j_array.end(), [](const json & value) + { + return (value.is_number()); + }) == 3); + CHECK(std::count_if(j_array.begin(), j_array.end(), [](const json&) + { + return true; + }) == 9); + } + + SECTION("std::mismatch") + { + json j_array2 = {13, 29, 3, {{"one", 1}, {"two", 2}, {"three", 3}}, true, false, {1, 2, 3}, "foo", "baz"}; + auto res = std::mismatch(j_array.begin(), j_array.end(), j_array2.begin()); + CHECK(*res.first == json({{"one", 1}, {"two", 2}})); + CHECK(*res.second == json({{"one", 1}, {"two", 2}, {"three", 3}})); + } + + SECTION("std::equal") + { + SECTION("using operator==") + { + CHECK(std::equal(j_array.begin(), j_array.end(), j_array.begin())); + CHECK(std::equal(j_object.begin(), j_object.end(), j_object.begin())); + CHECK(not std::equal(j_array.begin(), j_array.end(), j_object.begin())); + } + + SECTION("using user-defined comparison") + { + // compare objects only by size of its elements + json j_array2 = {13, 29, 3, {"Hello", "World"}, true, false, {{"one", 1}, {"two", 2}, {"three", 3}}, "foo", "baz"}; + CHECK(not std::equal(j_array.begin(), j_array.end(), j_array2.begin())); + CHECK(std::equal(j_array.begin(), j_array.end(), j_array2.begin(), + [](const json & a, const json & b) + { + return (a.size() == b.size()); + })); + } + } + + SECTION("std::find") + { + auto it = std::find(j_array.begin(), j_array.end(), json(false)); + CHECK(std::distance(j_array.begin(), it) == 5); + } + + SECTION("std::find_if") + { + auto it = std::find_if(j_array.begin(), j_array.end(), + [](const json & value) + { + return value.is_boolean(); + }); + CHECK(std::distance(j_array.begin(), it) == 4); + } + + SECTION("std::find_if_not") + { + auto it = std::find_if_not(j_array.begin(), j_array.end(), + [](const json & value) + { + return value.is_number(); + }); + CHECK(std::distance(j_array.begin(), it) == 3); + } + + SECTION("std::adjacent_find") + { + CHECK(std::adjacent_find(j_array.begin(), j_array.end()) == j_array.end()); + CHECK(std::adjacent_find(j_array.begin(), j_array.end(), + [](const json & v1, const json & v2) + { + return v1.type() == v2.type(); + }) == j_array.begin()); + } + } + + SECTION("modifying sequence operations") + { + SECTION("std::reverse") + { + std::reverse(j_array.begin(), j_array.end()); + CHECK(j_array == json({"baz", "foo", {1, 2, 3}, false, true, {{"one", 1}, {"two", 2}}, 3, 29, 13})); + } + + SECTION("std::rotate") + { + std::rotate(j_array.begin(), j_array.begin() + 1, j_array.end()); + CHECK(j_array == json({29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz", 13})); + } + + SECTION("std::partition") + { + auto it = std::partition(j_array.begin(), j_array.end(), [](const json & v) + { + return v.is_string(); + }); + CHECK(std::distance(j_array.begin(), it) == 2); + CHECK(not it[2].is_string()); + } + } + + SECTION("sorting operations") + { + SECTION("std::sort") + { + SECTION("with standard comparison") + { + json j = {13, 29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz", nullptr}; + std::sort(j.begin(), j.end()); + CHECK(j == json({nullptr, false, true, 3, 13, 29, {{"one", 1}, {"two", 2}}, {1, 2, 3}, "baz", "foo"})); + } + + SECTION("with user-defined comparison") + { + json j = {3, {{"one", 1}, {"two", 2}}, {1, 2, 3}, nullptr}; + std::sort(j.begin(), j.end(), [](const json & a, const json & b) + { + return a.size() < b.size(); + }); + CHECK(j == json({nullptr, 3, {{"one", 1}, {"two", 2}}, {1, 2, 3}})); + } + + SECTION("sorting an object") + { + json j({{"one", 1}, {"two", 2}}); + CHECK_THROWS_AS(std::sort(j.begin(), j.end()), std::domain_error); + CHECK_THROWS_WITH(std::sort(j.begin(), j.end()), "cannot use offsets with object iterators"); + } + } + + SECTION("std::partial_sort") + { + json j = {13, 29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz", nullptr}; + std::partial_sort(j.begin(), j.begin() + 4, j.end()); + CHECK(j == json({nullptr, false, true, 3, {{"one", 1}, {"two", 2}}, 29, {1, 2, 3}, "foo", "baz", 13})); + } + } + + SECTION("set operations") + { + SECTION("std::merge") + { + { + json j1 = {2, 4, 6, 8}; + json j2 = {1, 2, 3, 5, 7}; + json j3; + + std::merge(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3)); + CHECK(j3 == json({1, 2, 2, 3, 4, 5, 6, 7, 8})); + } + } + + SECTION("std::set_difference") + { + json j1 = {1, 2, 3, 4, 5, 6, 7, 8}; + json j2 = {1, 2, 3, 5, 7}; + json j3; + + std::set_difference(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3)); + CHECK(j3 == json({4, 6, 8})); + } + + SECTION("std::set_intersection") + { + json j1 = {1, 2, 3, 4, 5, 6, 7, 8}; + json j2 = {1, 2, 3, 5, 7}; + json j3; + + std::set_intersection(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3)); + CHECK(j3 == json({1, 2, 3, 5, 7})); + } + + SECTION("std::set_union") + { + json j1 = {2, 4, 6, 8}; + json j2 = {1, 2, 3, 5, 7}; + json j3; + + std::set_union(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3)); + CHECK(j3 == json({1, 2, 3, 4, 5, 6, 7, 8})); + } + + SECTION("std::set_symmetric_difference") + { + json j1 = {2, 4, 6, 8}; + json j2 = {1, 2, 3, 5, 7}; + json j3; + + std::set_symmetric_difference(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3)); + CHECK(j3 == json({1, 3, 4, 5, 6, 7, 8})); + } + } + + SECTION("heap operations") + { + std::make_heap(j_array.begin(), j_array.end()); + CHECK(std::is_heap(j_array.begin(), j_array.end())); + std::sort_heap(j_array.begin(), j_array.end()); + CHECK(j_array == json({false, true, 3, 13, 29, {{"one", 1}, {"two", 2}}, {1, 2, 3}, "baz", "foo"})); + } +} diff --git a/test/src/unit-runner.cpp b/test/src/unit-allocator.cpp similarity index 64% rename from test/src/unit-runner.cpp rename to test/src/unit-allocator.cpp index ec957b7a..dcf8aa35 100644 --- a/test/src/unit-runner.cpp +++ b/test/src/unit-allocator.cpp @@ -26,5 +26,38 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#define CATCH_CONFIG_MAIN #include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +// special test case to check if memory is leaked if constructor throws + +template +struct my_allocator : std::allocator +{ + template + void construct(T*, Args&& ...) + { + throw std::bad_alloc(); + } +}; + +TEST_CASE("bad_alloc") +{ + SECTION("bad_alloc") + { + // create JSON type using the throwing allocator + using my_json = nlohmann::basic_json; + + // creating an object should throw + CHECK_THROWS_AS(my_json j(my_json::value_t::object), std::bad_alloc); + } +} diff --git a/test/src/unit-capacity.cpp b/test/src/unit-capacity.cpp new file mode 100644 index 00000000..95100da9 --- /dev/null +++ b/test/src/unit-capacity.cpp @@ -0,0 +1,562 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("capacity") +{ + SECTION("empty()") + { + SECTION("boolean") + { + json j = true; + json j_const(j); + + SECTION("result of empty") + { + CHECK(j.empty() == false); + CHECK(j_const.empty() == false); + } + + SECTION("definition of empty") + { + CHECK(j.begin() != j.end()); + CHECK(j_const.begin() != j_const.end()); + } + } + + SECTION("string") + { + json j = "hello world"; + json j_const(j); + + SECTION("result of empty") + { + CHECK(j.empty() == false); + CHECK(j_const.empty() == false); + } + + SECTION("definition of empty") + { + CHECK(j.begin() != j.end()); + CHECK(j_const.begin() != j_const.end()); + } + } + + SECTION("array") + { + SECTION("empty array") + { + json j = json::array(); + json j_const(j); + + SECTION("result of empty") + { + CHECK(j.empty() == true); + CHECK(j_const.empty() == true); + } + + SECTION("definition of empty") + { + CHECK(j.begin() == j.end()); + CHECK(j_const.begin() == j_const.end()); + } + } + + SECTION("filled array") + { + json j = {1, 2, 3}; + json j_const(j); + + SECTION("result of empty") + { + CHECK(j.empty() == false); + CHECK(j_const.empty() == false); + } + + SECTION("definition of empty") + { + CHECK(j.begin() != j.end()); + CHECK(j_const.begin() != j_const.end()); + } + } + } + + SECTION("object") + { + SECTION("empty object") + { + json j = json::object(); + json j_const(j); + + SECTION("result of empty") + { + CHECK(j.empty() == true); + CHECK(j_const.empty() == true); + } + + SECTION("definition of empty") + { + CHECK(j.begin() == j.end()); + CHECK(j_const.begin() == j_const.end()); + } + } + + SECTION("filled object") + { + json j = {{"one", 1}, {"two", 2}, {"three", 3}}; + json j_const(j); + + SECTION("result of empty") + { + CHECK(j.empty() == false); + CHECK(j_const.empty() == false); + } + + SECTION("definition of empty") + { + CHECK(j.begin() != j.end()); + CHECK(j_const.begin() != j_const.end()); + } + } + } + + SECTION("number (integer)") + { + json j = 23; + json j_const(j); + + SECTION("result of empty") + { + CHECK(j.empty() == false); + CHECK(j_const.empty() == false); + } + + SECTION("definition of empty") + { + CHECK(j.begin() != j.end()); + CHECK(j_const.begin() != j_const.end()); + } + } + + SECTION("number (unsigned)") + { + json j = 23u; + json j_const(j); + + SECTION("result of empty") + { + CHECK(j.empty() == false); + CHECK(j_const.empty() == false); + } + + SECTION("definition of empty") + { + CHECK(j.begin() != j.end()); + CHECK(j_const.begin() != j_const.end()); + } + } + + SECTION("number (float)") + { + json j = 23.42; + json j_const(j); + + SECTION("result of empty") + { + CHECK(j.empty() == false); + CHECK(j_const.empty() == false); + } + + SECTION("definition of empty") + { + CHECK(j.begin() != j.end()); + CHECK(j_const.begin() != j_const.end()); + } + } + + SECTION("null") + { + json j = nullptr; + json j_const(j); + + SECTION("result of empty") + { + CHECK(j.empty() == true); + CHECK(j_const.empty() == true); + } + + SECTION("definition of empty") + { + CHECK(j.begin() == j.end()); + CHECK(j_const.begin() == j_const.end()); + } + } + } + + SECTION("size()") + { + SECTION("boolean") + { + json j = true; + json j_const(j); + + SECTION("result of size") + { + CHECK(j.size() == 1); + CHECK(j_const.size() == 1); + } + + SECTION("definition of size") + { + CHECK(std::distance(j.begin(), j.end()) == j.size()); + CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); + CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); + CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); + } + } + + SECTION("string") + { + json j = "hello world"; + json j_const(j); + + SECTION("result of size") + { + CHECK(j.size() == 1); + CHECK(j_const.size() == 1); + } + + SECTION("definition of size") + { + CHECK(std::distance(j.begin(), j.end()) == j.size()); + CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); + CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); + CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); + } + } + + SECTION("array") + { + SECTION("empty array") + { + json j = json::array(); + json j_const(j); + + SECTION("result of size") + { + CHECK(j.size() == 0); + CHECK(j_const.size() == 0); + } + + SECTION("definition of size") + { + CHECK(std::distance(j.begin(), j.end()) == j.size()); + CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); + CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); + CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); + } + } + + SECTION("filled array") + { + json j = {1, 2, 3}; + json j_const(j); + + SECTION("result of size") + { + CHECK(j.size() == 3); + CHECK(j_const.size() == 3); + } + + SECTION("definition of size") + { + CHECK(std::distance(j.begin(), j.end()) == j.size()); + CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); + CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); + CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); + } + } + } + + SECTION("object") + { + SECTION("empty object") + { + json j = json::object(); + json j_const(j); + + SECTION("result of size") + { + CHECK(j.size() == 0); + CHECK(j_const.size() == 0); + } + + SECTION("definition of size") + { + CHECK(std::distance(j.begin(), j.end()) == j.size()); + CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); + CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); + CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); + } + } + + SECTION("filled object") + { + json j = {{"one", 1}, {"two", 2}, {"three", 3}}; + json j_const(j); + + SECTION("result of size") + { + CHECK(j.size() == 3); + CHECK(j_const.size() == 3); + } + + SECTION("definition of size") + { + CHECK(std::distance(j.begin(), j.end()) == j.size()); + CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); + CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); + CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); + } + } + } + + SECTION("number (integer)") + { + json j = 23; + json j_const(j); + + SECTION("result of size") + { + CHECK(j.size() == 1); + CHECK(j_const.size() == 1); + } + + SECTION("definition of size") + { + CHECK(std::distance(j.begin(), j.end()) == j.size()); + CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); + CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); + CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); + } + } + + SECTION("number (unsigned)") + { + json j = 23u; + json j_const(j); + + SECTION("result of size") + { + CHECK(j.size() == 1); + CHECK(j_const.size() == 1); + } + + SECTION("definition of size") + { + CHECK(std::distance(j.begin(), j.end()) == j.size()); + CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); + CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); + CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); + } + } + + SECTION("number (float)") + { + json j = 23.42; + json j_const(j); + + SECTION("result of size") + { + CHECK(j.size() == 1); + CHECK(j_const.size() == 1); + } + + SECTION("definition of size") + { + CHECK(std::distance(j.begin(), j.end()) == j.size()); + CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); + CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); + CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); + } + } + + SECTION("null") + { + json j = nullptr; + json j_const(j); + + SECTION("result of size") + { + CHECK(j.size() == 0); + CHECK(j_const.size() == 0); + } + + SECTION("definition of size") + { + CHECK(std::distance(j.begin(), j.end()) == j.size()); + CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); + CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); + CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); + } + } + } + + SECTION("max_size()") + { + SECTION("boolean") + { + json j = true; + json j_const(j); + + SECTION("result of max_size") + { + CHECK(j.max_size() == 1); + CHECK(j_const.max_size() == 1); + } + } + + SECTION("string") + { + json j = "hello world"; + json j_const(j); + + SECTION("result of max_size") + { + CHECK(j.max_size() == 1); + CHECK(j_const.max_size() == 1); + } + } + + SECTION("array") + { + SECTION("empty array") + { + json j = json::array(); + json j_const(j); + + SECTION("result of max_size") + { + CHECK(j.max_size() >= j.size()); + CHECK(j_const.max_size() >= j_const.size()); + } + } + + SECTION("filled array") + { + json j = {1, 2, 3}; + json j_const(j); + + SECTION("result of max_size") + { + CHECK(j.max_size() >= j.size()); + CHECK(j_const.max_size() >= j_const.size()); + } + } + } + + SECTION("object") + { + SECTION("empty object") + { + json j = json::object(); + json j_const(j); + + SECTION("result of max_size") + { + CHECK(j.max_size() >= j.size()); + CHECK(j_const.max_size() >= j_const.size()); + } + } + + SECTION("filled object") + { + json j = {{"one", 1}, {"two", 2}, {"three", 3}}; + json j_const(j); + + SECTION("result of max_size") + { + CHECK(j.max_size() >= j.size()); + CHECK(j_const.max_size() >= j_const.size()); + } + } + } + + SECTION("number (integer)") + { + json j = 23; + json j_const(j); + + SECTION("result of max_size") + { + CHECK(j.max_size() == 1); + CHECK(j_const.max_size() == 1); + } + } + + SECTION("number (unsigned)") + { + json j = 23u; + json j_const(j); + + SECTION("result of max_size") + { + CHECK(j.max_size() == 1); + CHECK(j_const.max_size() == 1); + } + } + + SECTION("number (float)") + { + json j = 23.42; + json j_const(j); + + SECTION("result of max_size") + { + CHECK(j.max_size() == 1); + CHECK(j_const.max_size() == 1); + } + } + + SECTION("null") + { + json j = nullptr; + json j_const(j); + + SECTION("result of max_size") + { + CHECK(j.max_size() == 0); + CHECK(j_const.max_size() == 0); + } + } + } +} diff --git a/test/src/unit-class_const_iterator.cpp b/test/src/unit-class_const_iterator.cpp new file mode 100644 index 00000000..4908b5ce --- /dev/null +++ b/test/src/unit-class_const_iterator.cpp @@ -0,0 +1,401 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#define private public +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("const_iterator class") +{ + SECTION("construction") + { + SECTION("constructor") + { + SECTION("null") + { + json j(json::value_t::null); + json::const_iterator it(&j); + } + + SECTION("object") + { + json j(json::value_t::object); + json::const_iterator it(&j); + } + + SECTION("array") + { + json j(json::value_t::array); + json::const_iterator it(&j); + } + } + + SECTION("copy assignment") + { + json j(json::value_t::null); + json::const_iterator it(&j); + json::const_iterator it2(&j); + it2 = it; + } + } + + SECTION("initialization") + { + SECTION("set_begin") + { + SECTION("null") + { + json j(json::value_t::null); + json::const_iterator it(&j); + it.set_begin(); + CHECK(it == j.cbegin()); + } + + SECTION("object") + { + json j(json::value_t::object); + json::const_iterator it(&j); + it.set_begin(); + CHECK(it == j.cbegin()); + } + + SECTION("array") + { + json j(json::value_t::array); + json::const_iterator it(&j); + it.set_begin(); + CHECK(it == j.cbegin()); + } + } + + SECTION("set_end") + { + SECTION("null") + { + json j(json::value_t::null); + json::const_iterator it(&j); + it.set_end(); + CHECK(it == j.cend()); + } + + SECTION("object") + { + json j(json::value_t::object); + json::const_iterator it(&j); + it.set_end(); + CHECK(it == j.cend()); + } + + SECTION("array") + { + json j(json::value_t::array); + json::const_iterator it(&j); + it.set_end(); + CHECK(it == j.cend()); + } + } + } + + SECTION("element access") + { + SECTION("operator*") + { + SECTION("null") + { + json j(json::value_t::null); + json::const_iterator it = j.cbegin(); + CHECK_THROWS_AS(*it, std::out_of_range); + CHECK_THROWS_WITH(*it, "cannot get value"); + } + + SECTION("number") + { + json j(17); + json::const_iterator it = j.cbegin(); + CHECK(*it == json(17)); + it = j.cend(); + CHECK_THROWS_AS(*it, std::out_of_range); + CHECK_THROWS_WITH(*it, "cannot get value"); + } + + SECTION("object") + { + json j({{"foo", "bar"}}); + json::const_iterator it = j.cbegin(); + CHECK(*it == json("bar")); + } + + SECTION("array") + { + json j({1, 2, 3, 4}); + json::const_iterator it = j.cbegin(); + CHECK(*it == json(1)); + } + } + + SECTION("operator->") + { + SECTION("null") + { + json j(json::value_t::null); + json::const_iterator it = j.cbegin(); + CHECK_THROWS_AS(it->type_name(), std::out_of_range); + CHECK_THROWS_WITH(it->type_name(), "cannot get value"); + } + + SECTION("number") + { + json j(17); + json::const_iterator it = j.cbegin(); + CHECK(it->type_name() == "number"); + it = j.cend(); + CHECK_THROWS_AS(it->type_name(), std::out_of_range); + CHECK_THROWS_WITH(it->type_name(), "cannot get value"); + } + + SECTION("object") + { + json j({{"foo", "bar"}}); + json::const_iterator it = j.cbegin(); + CHECK(it->type_name() == "string"); + } + + SECTION("array") + { + json j({1, 2, 3, 4}); + json::const_iterator it = j.cbegin(); + CHECK(it->type_name() == "number"); + } + } + } + + SECTION("increment/decrement") + { + SECTION("post-increment") + { + SECTION("null") + { + json j(json::value_t::null); + json::const_iterator it = j.cbegin(); + CHECK(it.m_it.primitive_iterator == 1); + it++; + CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + } + + SECTION("number") + { + json j(17); + json::const_iterator it = j.cbegin(); + CHECK(it.m_it.primitive_iterator == 0); + it++; + CHECK(it.m_it.primitive_iterator == 1); + it++; + CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + } + + SECTION("object") + { + json j({{"foo", "bar"}}); + json::const_iterator it = j.cbegin(); + CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); + it++; + CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); + } + + SECTION("array") + { + json j({1, 2, 3, 4}); + json::const_iterator it = j.cbegin(); + CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); + it++; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + it++; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + it++; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + it++; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); + } + } + + SECTION("pre-increment") + { + SECTION("null") + { + json j(json::value_t::null); + json::const_iterator it = j.cbegin(); + CHECK(it.m_it.primitive_iterator == 1); + ++it; + CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + } + + SECTION("number") + { + json j(17); + json::const_iterator it = j.cbegin(); + CHECK(it.m_it.primitive_iterator == 0); + ++it; + CHECK(it.m_it.primitive_iterator == 1); + ++it; + CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + } + + SECTION("object") + { + json j({{"foo", "bar"}}); + json::const_iterator it = j.cbegin(); + CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); + ++it; + CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); + } + + SECTION("array") + { + json j({1, 2, 3, 4}); + json::const_iterator it = j.cbegin(); + CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); + ++it; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + ++it; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + ++it; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + ++it; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); + } + } + + SECTION("post-decrement") + { + SECTION("null") + { + json j(json::value_t::null); + json::const_iterator it = j.cend(); + CHECK(it.m_it.primitive_iterator == 1); + } + + SECTION("number") + { + json j(17); + json::const_iterator it = j.cend(); + CHECK(it.m_it.primitive_iterator == 1); + it--; + CHECK(it.m_it.primitive_iterator == 0); + it--; + CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + } + + SECTION("object") + { + json j({{"foo", "bar"}}); + json::const_iterator it = j.cend(); + CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); + it--; + CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); + } + + SECTION("array") + { + json j({1, 2, 3, 4}); + json::const_iterator it = j.cend(); + CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); + it--; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + it--; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + it--; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + it--; + CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + } + } + + SECTION("pre-decrement") + { + SECTION("null") + { + json j(json::value_t::null); + json::const_iterator it = j.cend(); + CHECK(it.m_it.primitive_iterator == 1); + } + + SECTION("number") + { + json j(17); + json::const_iterator it = j.cend(); + CHECK(it.m_it.primitive_iterator == 1); + --it; + CHECK(it.m_it.primitive_iterator == 0); + --it; + CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + } + + SECTION("object") + { + json j({{"foo", "bar"}}); + json::const_iterator it = j.cend(); + CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); + --it; + CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); + } + + SECTION("array") + { + json j({1, 2, 3, 4}); + json::const_iterator it = j.cend(); + CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); + --it; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + --it; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + --it; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + --it; + CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + } + } + } +} diff --git a/test/src/unit-class_iterator.cpp b/test/src/unit-class_iterator.cpp new file mode 100644 index 00000000..1d4b2901 --- /dev/null +++ b/test/src/unit-class_iterator.cpp @@ -0,0 +1,401 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#define private public +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("iterator class") +{ + SECTION("construction") + { + SECTION("constructor") + { + SECTION("null") + { + json j(json::value_t::null); + json::iterator it(&j); + } + + SECTION("object") + { + json j(json::value_t::object); + json::iterator it(&j); + } + + SECTION("array") + { + json j(json::value_t::array); + json::iterator it(&j); + } + } + + SECTION("copy assignment") + { + json j(json::value_t::null); + json::iterator it(&j); + json::iterator it2(&j); + it2 = it; + } + } + + SECTION("initialization") + { + SECTION("set_begin") + { + SECTION("null") + { + json j(json::value_t::null); + json::iterator it(&j); + it.set_begin(); + CHECK(it == j.begin()); + } + + SECTION("object") + { + json j(json::value_t::object); + json::iterator it(&j); + it.set_begin(); + CHECK(it == j.begin()); + } + + SECTION("array") + { + json j(json::value_t::array); + json::iterator it(&j); + it.set_begin(); + CHECK(it == j.begin()); + } + } + + SECTION("set_end") + { + SECTION("null") + { + json j(json::value_t::null); + json::iterator it(&j); + it.set_end(); + CHECK(it == j.end()); + } + + SECTION("object") + { + json j(json::value_t::object); + json::iterator it(&j); + it.set_end(); + CHECK(it == j.end()); + } + + SECTION("array") + { + json j(json::value_t::array); + json::iterator it(&j); + it.set_end(); + CHECK(it == j.end()); + } + } + } + + SECTION("element access") + { + SECTION("operator*") + { + SECTION("null") + { + json j(json::value_t::null); + json::iterator it = j.begin(); + CHECK_THROWS_AS(*it, std::out_of_range); + CHECK_THROWS_WITH(*it, "cannot get value"); + } + + SECTION("number") + { + json j(17); + json::iterator it = j.begin(); + CHECK(*it == json(17)); + it = j.end(); + CHECK_THROWS_AS(*it, std::out_of_range); + CHECK_THROWS_WITH(*it, "cannot get value"); + } + + SECTION("object") + { + json j({{"foo", "bar"}}); + json::iterator it = j.begin(); + CHECK(*it == json("bar")); + } + + SECTION("array") + { + json j({1, 2, 3, 4}); + json::iterator it = j.begin(); + CHECK(*it == json(1)); + } + } + + SECTION("operator->") + { + SECTION("null") + { + json j(json::value_t::null); + json::iterator it = j.begin(); + CHECK_THROWS_AS(it->type_name(), std::out_of_range); + CHECK_THROWS_WITH(it->type_name(), "cannot get value"); + } + + SECTION("number") + { + json j(17); + json::iterator it = j.begin(); + CHECK(it->type_name() == "number"); + it = j.end(); + CHECK_THROWS_AS(it->type_name(), std::out_of_range); + CHECK_THROWS_WITH(it->type_name(), "cannot get value"); + } + + SECTION("object") + { + json j({{"foo", "bar"}}); + json::iterator it = j.begin(); + CHECK(it->type_name() == "string"); + } + + SECTION("array") + { + json j({1, 2, 3, 4}); + json::iterator it = j.begin(); + CHECK(it->type_name() == "number"); + } + } + } + + SECTION("increment/decrement") + { + SECTION("post-increment") + { + SECTION("null") + { + json j(json::value_t::null); + json::iterator it = j.begin(); + CHECK(it.m_it.primitive_iterator == 1); + it++; + CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + } + + SECTION("number") + { + json j(17); + json::iterator it = j.begin(); + CHECK(it.m_it.primitive_iterator == 0); + it++; + CHECK(it.m_it.primitive_iterator == 1); + it++; + CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + } + + SECTION("object") + { + json j({{"foo", "bar"}}); + json::iterator it = j.begin(); + CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); + it++; + CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); + } + + SECTION("array") + { + json j({1, 2, 3, 4}); + json::iterator it = j.begin(); + CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); + it++; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + it++; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + it++; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + it++; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); + } + } + + SECTION("pre-increment") + { + SECTION("null") + { + json j(json::value_t::null); + json::iterator it = j.begin(); + CHECK(it.m_it.primitive_iterator == 1); + ++it; + CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + } + + SECTION("number") + { + json j(17); + json::iterator it = j.begin(); + CHECK(it.m_it.primitive_iterator == 0); + ++it; + CHECK(it.m_it.primitive_iterator == 1); + ++it; + CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + } + + SECTION("object") + { + json j({{"foo", "bar"}}); + json::iterator it = j.begin(); + CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); + ++it; + CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); + } + + SECTION("array") + { + json j({1, 2, 3, 4}); + json::iterator it = j.begin(); + CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); + ++it; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + ++it; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + ++it; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + ++it; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); + } + } + + SECTION("post-decrement") + { + SECTION("null") + { + json j(json::value_t::null); + json::iterator it = j.end(); + CHECK(it.m_it.primitive_iterator == 1); + } + + SECTION("number") + { + json j(17); + json::iterator it = j.end(); + CHECK(it.m_it.primitive_iterator == 1); + it--; + CHECK(it.m_it.primitive_iterator == 0); + it--; + CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + } + + SECTION("object") + { + json j({{"foo", "bar"}}); + json::iterator it = j.end(); + CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); + it--; + CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); + } + + SECTION("array") + { + json j({1, 2, 3, 4}); + json::iterator it = j.end(); + CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); + it--; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + it--; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + it--; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + it--; + CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + } + } + + SECTION("pre-decrement") + { + SECTION("null") + { + json j(json::value_t::null); + json::iterator it = j.end(); + CHECK(it.m_it.primitive_iterator == 1); + } + + SECTION("number") + { + json j(17); + json::iterator it = j.end(); + CHECK(it.m_it.primitive_iterator == 1); + --it; + CHECK(it.m_it.primitive_iterator == 0); + --it; + CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); + } + + SECTION("object") + { + json j({{"foo", "bar"}}); + json::iterator it = j.end(); + CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); + --it; + CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); + } + + SECTION("array") + { + json j({1, 2, 3, 4}); + json::iterator it = j.end(); + CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); + --it; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + --it; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + --it; + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + --it; + CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); + CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); + } + } + } +} diff --git a/test/src/unit-class_lexer.cpp b/test/src/unit-class_lexer.cpp new file mode 100644 index 00000000..708a8cbf --- /dev/null +++ b/test/src/unit-class_lexer.cpp @@ -0,0 +1,155 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#define private public +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("lexer class") +{ + SECTION("scan") + { + SECTION("structural characters") + { + CHECK(json::lexer("[").scan() == json::lexer::token_type::begin_array); + CHECK(json::lexer("]").scan() == json::lexer::token_type::end_array); + CHECK(json::lexer("{").scan() == json::lexer::token_type::begin_object); + CHECK(json::lexer("}").scan() == json::lexer::token_type::end_object); + CHECK(json::lexer(",").scan() == json::lexer::token_type::value_separator); + CHECK(json::lexer(":").scan() == json::lexer::token_type::name_separator); + } + + SECTION("literal names") + { + CHECK(json::lexer("null").scan() == json::lexer::token_type::literal_null); + CHECK(json::lexer("true").scan() == json::lexer::token_type::literal_true); + CHECK(json::lexer("false").scan() == json::lexer::token_type::literal_false); + } + + SECTION("numbers") + { + CHECK(json::lexer("0").scan() == json::lexer::token_type::value_number); + CHECK(json::lexer("1").scan() == json::lexer::token_type::value_number); + CHECK(json::lexer("2").scan() == json::lexer::token_type::value_number); + CHECK(json::lexer("3").scan() == json::lexer::token_type::value_number); + CHECK(json::lexer("4").scan() == json::lexer::token_type::value_number); + CHECK(json::lexer("5").scan() == json::lexer::token_type::value_number); + CHECK(json::lexer("6").scan() == json::lexer::token_type::value_number); + CHECK(json::lexer("7").scan() == json::lexer::token_type::value_number); + CHECK(json::lexer("8").scan() == json::lexer::token_type::value_number); + CHECK(json::lexer("9").scan() == json::lexer::token_type::value_number); + } + + SECTION("whitespace") + { + // result is end_of_input, because not token is following + CHECK(json::lexer(" ").scan() == json::lexer::token_type::end_of_input); + CHECK(json::lexer("\t").scan() == json::lexer::token_type::end_of_input); + CHECK(json::lexer("\n").scan() == json::lexer::token_type::end_of_input); + CHECK(json::lexer("\r").scan() == json::lexer::token_type::end_of_input); + CHECK(json::lexer(" \t\n\r\n\t ").scan() == json::lexer::token_type::end_of_input); + } + } + + SECTION("token_type_name") + { + CHECK(json::lexer::token_type_name(json::lexer::token_type::uninitialized) == ""); + CHECK(json::lexer::token_type_name(json::lexer::token_type::literal_true) == "true literal"); + CHECK(json::lexer::token_type_name(json::lexer::token_type::literal_false) == "false literal"); + CHECK(json::lexer::token_type_name(json::lexer::token_type::literal_null) == "null literal"); + CHECK(json::lexer::token_type_name(json::lexer::token_type::value_string) == "string literal"); + CHECK(json::lexer::token_type_name(json::lexer::token_type::value_number) == "number literal"); + CHECK(json::lexer::token_type_name(json::lexer::token_type::begin_array) == "'['"); + CHECK(json::lexer::token_type_name(json::lexer::token_type::begin_object) == "'{'"); + CHECK(json::lexer::token_type_name(json::lexer::token_type::end_array) == "']'"); + CHECK(json::lexer::token_type_name(json::lexer::token_type::end_object) == "'}'"); + CHECK(json::lexer::token_type_name(json::lexer::token_type::name_separator) == "':'"); + CHECK(json::lexer::token_type_name(json::lexer::token_type::value_separator) == "','"); + CHECK(json::lexer::token_type_name(json::lexer::token_type::parse_error) == ""); + CHECK(json::lexer::token_type_name(json::lexer::token_type::end_of_input) == "end of input"); + } + + SECTION("parse errors on first character") + { + for (int c = 1; c < 128; ++c) + { + auto s = std::string(1, c); + + switch (c) + { + // single characters that are valid tokens + case ('['): + case (']'): + case ('{'): + case ('}'): + case (','): + case (':'): + case ('0'): + case ('1'): + case ('2'): + case ('3'): + case ('4'): + case ('5'): + case ('6'): + case ('7'): + case ('8'): + case ('9'): + { + CHECK(json::lexer(s.c_str()).scan() != json::lexer::token_type::parse_error); + break; + } + + // whitespace + case (' '): + case ('\t'): + case ('\n'): + case ('\r'): + { + CHECK(json::lexer(s.c_str()).scan() == json::lexer::token_type::end_of_input); + break; + } + + // anything else is not expected + default: + { + CHECK(json::lexer(s.c_str()).scan() == json::lexer::token_type::parse_error); + break; + } + } + } + } + + SECTION("to_unicode") + { + CHECK(json::lexer::to_unicode(0x1F4A9) == "💩"); + CHECK_THROWS_AS(json::lexer::to_unicode(0x200000), std::out_of_range); + CHECK_THROWS_WITH(json::lexer::to_unicode(0x200000), "code points above 0x10FFFF are invalid"); + } +} diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp new file mode 100644 index 00000000..fe005503 --- /dev/null +++ b/test/src/unit-class_parser.cpp @@ -0,0 +1,753 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#define private public +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("parser class") +{ + SECTION("parse") + { + SECTION("null") + { + CHECK(json::parser("null").parse() == json(nullptr)); + } + + SECTION("true") + { + CHECK(json::parser("true").parse() == json(true)); + } + + SECTION("false") + { + CHECK(json::parser("false").parse() == json(false)); + } + + SECTION("array") + { + SECTION("empty array") + { + CHECK(json::parser("[]").parse() == json(json::value_t::array)); + CHECK(json::parser("[ ]").parse() == json(json::value_t::array)); + } + + SECTION("nonempty array") + { + CHECK(json::parser("[true, false, null]").parse() == json({true, false, nullptr})); + } + } + + SECTION("object") + { + SECTION("empty object") + { + CHECK(json::parser("{}").parse() == json(json::value_t::object)); + CHECK(json::parser("{ }").parse() == json(json::value_t::object)); + } + + SECTION("nonempty object") + { + CHECK(json::parser("{\"\": true, \"one\": 1, \"two\": null}").parse() == json({{"", true}, {"one", 1}, {"two", nullptr}})); + } + } + + SECTION("string") + { + // empty string + CHECK(json::parser("\"\"").parse() == json(json::value_t::string)); + + SECTION("errors") + { + // error: tab in string + CHECK_THROWS_AS(json::parser("\"\t\"").parse(), std::invalid_argument); + CHECK_THROWS_WITH(json::parser("\"\t\"").parse(), "parse error - unexpected '\"'"); + // error: newline in string + CHECK_THROWS_AS(json::parser("\"\n\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\r\"").parse(), std::invalid_argument); + CHECK_THROWS_WITH(json::parser("\"\n\"").parse(), "parse error - unexpected '\"'"); + CHECK_THROWS_WITH(json::parser("\"\r\"").parse(), "parse error - unexpected '\"'"); + // error: backspace in string + CHECK_THROWS_AS(json::parser("\"\b\"").parse(), std::invalid_argument); + CHECK_THROWS_WITH(json::parser("\"\b\"").parse(), "parse error - unexpected '\"'"); + // improve code coverage + CHECK_THROWS_AS(json::parser("\uFF01").parse(), std::invalid_argument); + // unescaped control characters + CHECK_THROWS_AS(json::parser("\"\x00\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x01\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x02\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x03\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x04\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x05\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x06\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x07\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x08\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x09\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x0a\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x0b\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x0c\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x0d\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x0e\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x0f\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x10\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x11\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x12\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x13\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x14\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x15\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x16\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x17\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x18\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x19\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x1a\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x1b\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x1c\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x1d\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x1e\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\x1f\"").parse(), std::invalid_argument); + } + + SECTION("escaped") + { + // quotation mark "\"" + auto r1 = R"("\"")"_json; + CHECK(json::parser("\"\\\"\"").parse() == r1); + // reverse solidus "\\" + auto r2 = R"("\\")"_json; + CHECK(json::parser("\"\\\\\"").parse() == r2); + // solidus + CHECK(json::parser("\"\\/\"").parse() == R"("/")"_json); + // backspace + CHECK(json::parser("\"\\b\"").parse() == json("\b")); + // formfeed + CHECK(json::parser("\"\\f\"").parse() == json("\f")); + // newline + CHECK(json::parser("\"\\n\"").parse() == json("\n")); + // carriage return + CHECK(json::parser("\"\\r\"").parse() == json("\r")); + // horizontal tab + CHECK(json::parser("\"\\t\"").parse() == json("\t")); + + CHECK(json::parser("\"\\u0001\"").parse().get() == "\x01"); + CHECK(json::parser("\"\\u000a\"").parse().get() == "\n"); + CHECK(json::parser("\"\\u00b0\"").parse().get() == "°"); + CHECK(json::parser("\"\\u0c00\"").parse().get() == "ఀ"); + CHECK(json::parser("\"\\ud000\"").parse().get() == "퀀"); + CHECK(json::parser("\"\\u000E\"").parse().get() == "\x0E"); + CHECK(json::parser("\"\\u00F0\"").parse().get() == "ð"); + CHECK(json::parser("\"\\u0100\"").parse().get() == "Ā"); + CHECK(json::parser("\"\\u2000\"").parse().get() == " "); + CHECK(json::parser("\"\\uFFFF\"").parse().get() == "￿"); + CHECK(json::parser("\"\\u20AC\"").parse().get() == "€"); + CHECK(json::parser("\"€\"").parse().get() == "€"); + CHECK(json::parser("\"🎈\"").parse().get() == "🎈"); + + CHECK(json::parse("\"\\ud80c\\udc60\"").get() == u8"\U00013060"); + CHECK(json::parse("\"\\ud83c\\udf1e\"").get() == "🌞"); + } + } + + SECTION("number") + { + SECTION("integers") + { + SECTION("without exponent") + { + CHECK(json::parser("-128").parse() == json(-128)); + CHECK(json::parser("-0").parse() == json(-0)); + CHECK(json::parser("0").parse() == json(0)); + CHECK(json::parser("128").parse() == json(128)); + } + + SECTION("with exponent") + { + CHECK(json::parser("0e1").parse() == json(0e1)); + CHECK(json::parser("0E1").parse() == json(0e1)); + + CHECK(json::parser("10000E-4").parse() == json(10000e-4)); + CHECK(json::parser("10000E-3").parse() == json(10000e-3)); + CHECK(json::parser("10000E-2").parse() == json(10000e-2)); + CHECK(json::parser("10000E-1").parse() == json(10000e-1)); + CHECK(json::parser("10000E0").parse() == json(10000e0)); + CHECK(json::parser("10000E1").parse() == json(10000e1)); + CHECK(json::parser("10000E2").parse() == json(10000e2)); + CHECK(json::parser("10000E3").parse() == json(10000e3)); + CHECK(json::parser("10000E4").parse() == json(10000e4)); + + CHECK(json::parser("10000e-4").parse() == json(10000e-4)); + CHECK(json::parser("10000e-3").parse() == json(10000e-3)); + CHECK(json::parser("10000e-2").parse() == json(10000e-2)); + CHECK(json::parser("10000e-1").parse() == json(10000e-1)); + CHECK(json::parser("10000e0").parse() == json(10000e0)); + CHECK(json::parser("10000e1").parse() == json(10000e1)); + CHECK(json::parser("10000e2").parse() == json(10000e2)); + CHECK(json::parser("10000e3").parse() == json(10000e3)); + CHECK(json::parser("10000e4").parse() == json(10000e4)); + + CHECK(json::parser("-0e1").parse() == json(-0e1)); + CHECK(json::parser("-0E1").parse() == json(-0e1)); + CHECK(json::parser("-0E123").parse() == json(-0e123)); + } + + SECTION("edge cases") + { + // From RFC7159, Section 6: + // Note that when such software is used, numbers that are + // integers and are in the range [-(2**53)+1, (2**53)-1] + // are interoperable in the sense that implementations will + // agree exactly on their numeric values. + + // -(2**53)+1 + CHECK(json::parser("-9007199254740991").parse().get() == -9007199254740991); + // (2**53)-1 + CHECK(json::parser("9007199254740991").parse().get() == 9007199254740991); + } + + SECTION("over the edge cases") // issue #178 - Integer conversion to unsigned (incorrect handling of 64 bit integers) + { + // While RFC7159, Section 6 specifies a preference for support + // for ranges in range of IEEE 754-2008 binary64 (double precision) + // this does not accommodate 64 bit integers without loss of accuracy. + // As 64 bit integers are now widely used in software, it is desirable + // to expand support to to the full 64 bit (signed and unsigned) range + // i.e. -(2**63) -> (2**64)-1. + + // -(2**63) ** Note: compilers see negative literals as negated positive numbers (hence the -1)) + CHECK(json::parser("-9223372036854775808").parse().get() == -9223372036854775807 - 1); + // (2**63)-1 + CHECK(json::parser("9223372036854775807").parse().get() == 9223372036854775807); + // (2**64)-1 + CHECK(json::parser("18446744073709551615").parse().get() == 18446744073709551615u); + } + } + + SECTION("floating-point") + { + SECTION("without exponent") + { + CHECK(json::parser("-128.5").parse() == json(-128.5)); + CHECK(json::parser("0.999").parse() == json(0.999)); + CHECK(json::parser("128.5").parse() == json(128.5)); + CHECK(json::parser("-0.0").parse() == json(-0.0)); + } + + SECTION("with exponent") + { + CHECK(json::parser("-128.5E3").parse() == json(-128.5E3)); + CHECK(json::parser("-128.5E-3").parse() == json(-128.5E-3)); + CHECK(json::parser("-0.0e1").parse() == json(-0.0e1)); + CHECK(json::parser("-0.0E1").parse() == json(-0.0e1)); + } + } + + SECTION("invalid numbers") + { + CHECK_THROWS_AS(json::parser("01").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("--1").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("1.").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("1E").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("1E-").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("1.E1").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("-1E").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("-0E#").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("-0E-#").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("-0#").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("-0.0:").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("-0.0Z").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("-0E123:").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("-0e0-:").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("-0e-:").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("-0f").parse(), std::invalid_argument); + + // numbers must not begin with "+" + CHECK_THROWS_AS(json::parser("+1").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("+0").parse(), std::invalid_argument); + + CHECK_THROWS_WITH(json::parser("01").parse(), + "parse error - unexpected number literal; expected end of input"); + CHECK_THROWS_WITH(json::parser("--1").parse(), "parse error - unexpected '-'"); + CHECK_THROWS_WITH(json::parser("1.").parse(), + "parse error - unexpected '.'; expected end of input"); + CHECK_THROWS_WITH(json::parser("1E").parse(), + "parse error - unexpected 'E'; expected end of input"); + CHECK_THROWS_WITH(json::parser("1E-").parse(), + "parse error - unexpected 'E'; expected end of input"); + CHECK_THROWS_WITH(json::parser("1.E1").parse(), + "parse error - unexpected '.'; expected end of input"); + CHECK_THROWS_WITH(json::parser("-1E").parse(), + "parse error - unexpected 'E'; expected end of input"); + CHECK_THROWS_WITH(json::parser("-0E#").parse(), + "parse error - unexpected 'E'; expected end of input"); + CHECK_THROWS_WITH(json::parser("-0E-#").parse(), + "parse error - unexpected 'E'; expected end of input"); + CHECK_THROWS_WITH(json::parser("-0#").parse(), + "parse error - unexpected '#'; expected end of input"); + CHECK_THROWS_WITH(json::parser("-0.0:").parse(), + "parse error - unexpected ':'; expected end of input"); + CHECK_THROWS_WITH(json::parser("-0.0Z").parse(), + "parse error - unexpected 'Z'; expected end of input"); + CHECK_THROWS_WITH(json::parser("-0E123:").parse(), + "parse error - unexpected ':'; expected end of input"); + CHECK_THROWS_WITH(json::parser("-0e0-:").parse(), + "parse error - unexpected '-'; expected end of input"); + CHECK_THROWS_WITH(json::parser("-0e-:").parse(), + "parse error - unexpected 'e'; expected end of input"); + CHECK_THROWS_WITH(json::parser("-0f").parse(), + "parse error - unexpected 'f'; expected end of input"); + } + } + } + + SECTION("parse errors") + { + // unexpected end of number + CHECK_THROWS_AS(json::parser("0.").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("-").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("--").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("-0.").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("-.").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("-:").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("0.:").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("e.").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("1e.").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("1e/").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("1e:").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("1E.").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("1E/").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("1E:").parse(), std::invalid_argument); + CHECK_THROWS_WITH(json::parser("0.").parse(), + "parse error - unexpected '.'; expected end of input"); + CHECK_THROWS_WITH(json::parser("-").parse(), "parse error - unexpected '-'"); + CHECK_THROWS_WITH(json::parser("--").parse(), + "parse error - unexpected '-'"); + CHECK_THROWS_WITH(json::parser("-0.").parse(), + "parse error - unexpected '.'; expected end of input"); + CHECK_THROWS_WITH(json::parser("-.").parse(), + "parse error - unexpected '-'"); + CHECK_THROWS_WITH(json::parser("-:").parse(), + "parse error - unexpected '-'"); + CHECK_THROWS_WITH(json::parser("0.:").parse(), + "parse error - unexpected '.'; expected end of input"); + CHECK_THROWS_WITH(json::parser("e.").parse(), + "parse error - unexpected 'e'"); + CHECK_THROWS_WITH(json::parser("1e.").parse(), + "parse error - unexpected 'e'; expected end of input"); + CHECK_THROWS_WITH(json::parser("1e/").parse(), + "parse error - unexpected 'e'; expected end of input"); + CHECK_THROWS_WITH(json::parser("1e:").parse(), + "parse error - unexpected 'e'; expected end of input"); + CHECK_THROWS_WITH(json::parser("1E.").parse(), + "parse error - unexpected 'E'; expected end of input"); + CHECK_THROWS_WITH(json::parser("1E/").parse(), + "parse error - unexpected 'E'; expected end of input"); + CHECK_THROWS_WITH(json::parser("1E:").parse(), + "parse error - unexpected 'E'; expected end of input"); + + // unexpected end of null + CHECK_THROWS_AS(json::parser("n").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("nu").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("nul").parse(), std::invalid_argument); + CHECK_THROWS_WITH(json::parser("n").parse(), "parse error - unexpected 'n'"); + CHECK_THROWS_WITH(json::parser("nu").parse(), + "parse error - unexpected 'n'"); + CHECK_THROWS_WITH(json::parser("nul").parse(), + "parse error - unexpected 'n'"); + + // unexpected end of true + CHECK_THROWS_AS(json::parser("t").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("tr").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("tru").parse(), std::invalid_argument); + CHECK_THROWS_WITH(json::parser("t").parse(), "parse error - unexpected 't'"); + CHECK_THROWS_WITH(json::parser("tr").parse(), + "parse error - unexpected 't'"); + CHECK_THROWS_WITH(json::parser("tru").parse(), + "parse error - unexpected 't'"); + + // unexpected end of false + CHECK_THROWS_AS(json::parser("f").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("fa").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("fal").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("fals").parse(), std::invalid_argument); + CHECK_THROWS_WITH(json::parser("f").parse(), "parse error - unexpected 'f'"); + CHECK_THROWS_WITH(json::parser("fa").parse(), + "parse error - unexpected 'f'"); + CHECK_THROWS_WITH(json::parser("fal").parse(), + "parse error - unexpected 'f'"); + CHECK_THROWS_WITH(json::parser("fals").parse(), + "parse error - unexpected 'f'"); + + // missing/unexpected end of array + CHECK_THROWS_AS(json::parser("[").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("[1").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("[1,").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("[1,]").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("]").parse(), std::invalid_argument); + CHECK_THROWS_WITH(json::parser("[").parse(), + "parse error - unexpected end of input"); + CHECK_THROWS_WITH(json::parser("[1").parse(), + "parse error - unexpected end of input; expected ']'"); + CHECK_THROWS_WITH(json::parser("[1,").parse(), + "parse error - unexpected end of input"); + CHECK_THROWS_WITH(json::parser("[1,]").parse(), + "parse error - unexpected ']'"); + CHECK_THROWS_WITH(json::parser("]").parse(), "parse error - unexpected ']'"); + + // missing/unexpected end of object + CHECK_THROWS_AS(json::parser("{").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("{\"foo\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("{\"foo\":").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("{\"foo\":}").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("{\"foo\":1,}").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("}").parse(), std::invalid_argument); + CHECK_THROWS_WITH(json::parser("{").parse(), + "parse error - unexpected end of input; expected string literal"); + CHECK_THROWS_WITH(json::parser("{\"foo\"").parse(), + "parse error - unexpected end of input; expected ':'"); + CHECK_THROWS_WITH(json::parser("{\"foo\":").parse(), + "parse error - unexpected end of input"); + CHECK_THROWS_WITH(json::parser("{\"foo\":}").parse(), + "parse error - unexpected '}'"); + CHECK_THROWS_WITH(json::parser("{\"foo\":1,}").parse(), + "parse error - unexpected '}'; expected string literal"); + CHECK_THROWS_WITH(json::parser("}").parse(), "parse error - unexpected '}'"); + + // missing/unexpected end of string + CHECK_THROWS_AS(json::parser("\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\\\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\\u\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\\u0\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\\u01\"").parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser("\"\\u012\"").parse(), std::invalid_argument); + CHECK_THROWS_WITH(json::parser("\"").parse(), + "parse error - unexpected '\"'"); + CHECK_THROWS_WITH(json::parser("\"\\\"").parse(), + "parse error - unexpected '\"'"); + CHECK_THROWS_WITH(json::parser("\"\\u\"").parse(), + "parse error - unexpected '\"'"); + CHECK_THROWS_WITH(json::parser("\"\\u0\"").parse(), + "parse error - unexpected '\"'"); + CHECK_THROWS_WITH(json::parser("\"\\u01\"").parse(), + "parse error - unexpected '\"'"); + CHECK_THROWS_WITH(json::parser("\"\\u012\"").parse(), + "parse error - unexpected '\"'"); + + // invalid escapes + for (int c = 1; c < 128; ++c) + { + auto s = std::string("\"\\") + std::string(1, c) + "\""; + + switch (c) + { + // valid escapes + case ('"'): + case ('\\'): + case ('/'): + case ('b'): + case ('f'): + case ('n'): + case ('r'): + case ('t'): + { + CHECK_NOTHROW(json::parser(s).parse()); + break; + } + + // \u must be followed with four numbers, so we skip it here + case ('u'): + { + break; + } + + // any other combination of backslash and character is invalid + default: + { + CHECK_THROWS_AS(json::parser(s).parse(), std::invalid_argument); + CHECK_THROWS_WITH(json::parser(s).parse(), "parse error - unexpected '\"'"); + break; + } + } + } + + // invalid \uxxxx escapes + { + // check whether character is a valid hex character + const auto valid = [](int c) + { + switch (c) + { + case ('0'): + case ('1'): + case ('2'): + case ('3'): + case ('4'): + case ('5'): + case ('6'): + case ('7'): + case ('8'): + case ('9'): + case ('a'): + case ('b'): + case ('c'): + case ('d'): + case ('e'): + case ('f'): + case ('A'): + case ('B'): + case ('C'): + case ('D'): + case ('E'): + case ('F'): + { + return true; + } + + default: + { + return false; + } + } + }; + + for (int c = 1; c < 128; ++c) + { + std::string s = "\"\\u"; + + // create a string with the iterated character at each position + auto s1 = s + "000" + std::string(1, c) + "\""; + auto s2 = s + "00" + std::string(1, c) + "0\""; + auto s3 = s + "0" + std::string(1, c) + "00\""; + auto s4 = s + std::string(1, c) + "000\""; + + if (valid(c)) + { + CHECK_NOTHROW(json::parser(s1).parse()); + CHECK_NOTHROW(json::parser(s2).parse()); + CHECK_NOTHROW(json::parser(s3).parse()); + CHECK_NOTHROW(json::parser(s4).parse()); + } + else + { + CHECK_THROWS_AS(json::parser(s1).parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser(s2).parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser(s3).parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser(s4).parse(), std::invalid_argument); + + CHECK_THROWS_WITH(json::parser(s1).parse(), "parse error - unexpected '\"'"); + CHECK_THROWS_WITH(json::parser(s2).parse(), "parse error - unexpected '\"'"); + CHECK_THROWS_WITH(json::parser(s3).parse(), "parse error - unexpected '\"'"); + CHECK_THROWS_WITH(json::parser(s4).parse(), "parse error - unexpected '\"'"); + } + } + } + + // missing part of a surrogate pair + CHECK_THROWS_AS(json::parse("\"\\uD80C\""), std::invalid_argument); + CHECK_THROWS_WITH(json::parse("\"\\uD80C\""), "missing low surrogate"); + // invalid surrogate pair + CHECK_THROWS_AS(json::parse("\"\\uD80C\\uD80C\""), std::invalid_argument); + CHECK_THROWS_AS(json::parse("\"\\uD80C\\u0000\""), std::invalid_argument); + CHECK_THROWS_AS(json::parse("\"\\uD80C\\uFFFF\""), std::invalid_argument); + CHECK_THROWS_WITH(json::parse("\"\\uD80C\\uD80C\""), + "missing or wrong low surrogate"); + CHECK_THROWS_WITH(json::parse("\"\\uD80C\\u0000\""), + "missing or wrong low surrogate"); + CHECK_THROWS_WITH(json::parse("\"\\uD80C\\uFFFF\""), + "missing or wrong low surrogate"); + } + + SECTION("callback function") + { + auto s_object = R"( + { + "foo": 2, + "bar": { + "baz": 1 + } + } + )"; + + auto s_array = R"( + [1,2,[3,4,5],4,5] + )"; + + SECTION("filter nothing") + { + json j_object = json::parse(s_object, [](int, json::parse_event_t, const json&) + { + return true; + }); + + CHECK (j_object == json({{"foo", 2}, {"bar", {{"baz", 1}}}})); + + json j_array = json::parse(s_array, [](int, json::parse_event_t, const json&) + { + return true; + }); + + CHECK (j_array == json({1, 2, {3, 4, 5}, 4, 5})); + } + + SECTION("filter everything") + { + json j_object = json::parse(s_object, [](int, json::parse_event_t, const json&) + { + return false; + }); + + // the top-level object will be discarded, leaving a null + CHECK (j_object.is_null()); + + json j_array = json::parse(s_array, [](int, json::parse_event_t, const json&) + { + return false; + }); + + // the top-level array will be discarded, leaving a null + CHECK (j_array.is_null()); + } + + SECTION("filter specific element") + { + json j_object = json::parse(s_object, [](int, json::parse_event_t, const json & j) + { + // filter all number(2) elements + if (j == json(2)) + { + return false; + } + else + { + return true; + } + }); + + CHECK (j_object == json({{"bar", {{"baz", 1}}}})); + + json j_array = json::parse(s_array, [](int, json::parse_event_t, const json & j) + { + if (j == json(2)) + { + return false; + } + else + { + return true; + } + }); + + CHECK (j_array == json({1, {3, 4, 5}, 4, 5})); + } + + SECTION("filter specific events") + { + SECTION("first closing event") + { + { + json j_object = json::parse(s_object, [](int, json::parse_event_t e, const json&) + { + static bool first = true; + if (e == json::parse_event_t::object_end and first) + { + first = false; + return false; + } + else + { + return true; + } + }); + + // the first completed object will be discarded + CHECK (j_object == json({{"foo", 2}})); + } + + { + json j_array = json::parse(s_array, [](int, json::parse_event_t e, const json&) + { + static bool first = true; + if (e == json::parse_event_t::array_end and first) + { + first = false; + return false; + } + else + { + return true; + } + }); + + // the first completed array will be discarded + CHECK (j_array == json({1, 2, 4, 5})); + } + } + } + + SECTION("special cases") + { + // the following test cases cover the situation in which an empty + // object and array is discarded only after the closing character + // has been read + + json j_empty_object = json::parse("{}", [](int, json::parse_event_t e, const json&) + { + if (e == json::parse_event_t::object_end) + { + return false; + } + else + { + return true; + } + }); + CHECK(j_empty_object == json()); + + json j_empty_array = json::parse("[]", [](int, json::parse_event_t e, const json&) + { + if (e == json::parse_event_t::array_end) + { + return false; + } + else + { + return true; + } + }); + CHECK(j_empty_array == json()); + } + } + + SECTION("copy constructor") + { + json::string_t* s = new json::string_t("[1,2,3,4]"); + json::parser p(*s); + delete s; + CHECK(p.parse() == json({1, 2, 3, 4})); + } +} diff --git a/test/src/unit-comparison.cpp b/test/src/unit-comparison.cpp new file mode 100644 index 00000000..a8ce4c8a --- /dev/null +++ b/test/src/unit-comparison.cpp @@ -0,0 +1,246 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("lexicographical comparison operators") +{ + SECTION("types") + { + std::vector j_types = + { + json::value_t::null, + json::value_t::boolean, + json::value_t::number_integer, + json::value_t::number_unsigned, + json::value_t::number_float, + json::value_t::object, + json::value_t::array, + json::value_t::string + }; + + SECTION("comparison: less") + { + std::vector> expected = + { + {false, true, true, true, true, true, true, true}, + {false, false, true, true, true, true, true, true}, + {false, false, false, false, false, true, true, true}, + {false, false, false, false, false, true, true, true}, + {false, false, false, false, false, true, true, true}, + {false, false, false, false, false, false, true, true}, + {false, false, false, false, false, false, false, true}, + {false, false, false, false, false, false, false, false} + }; + + for (size_t i = 0; i < j_types.size(); ++i) + { + for (size_t j = 0; j < j_types.size(); ++j) + { + CAPTURE(i); + CAPTURE(j); + // check precomputed values + CHECK(operator<(j_types[i], j_types[j]) == expected[i][j]); + } + } + } + } + + SECTION("values") + { + json j_values = + { + nullptr, nullptr, + 17, 42, + 8u, 13u, + 3.14159, 23.42, + "foo", "bar", + true, false, + {1, 2, 3}, {"one", "two", "three"}, + {{"first", 1}, {"second", 2}}, {{"a", "A"}, {"b", {"B"}}} + }; + + SECTION("comparison: equal") + { + std::vector> expected = + { + {true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true} + }; + + for (size_t i = 0; i < j_values.size(); ++i) + { + for (size_t j = 0; j < j_values.size(); ++j) + { + CAPTURE(i); + CAPTURE(j); + // check precomputed values + CHECK( (j_values[i] == j_values[j]) == expected[i][j] ); + } + } + + // comparison with discarded elements + json j_discarded(json::value_t::discarded); + for (size_t i = 0; i < j_values.size(); ++i) + { + CHECK( (j_values[i] == j_discarded) == false); + CHECK( (j_discarded == j_values[i]) == false); + CHECK( (j_discarded == j_discarded) == false); + } + + // compare with null pointer + json j_null; + CHECK(j_null == nullptr); + CHECK(nullptr == j_null); + } + + SECTION("comparison: not equal") + { + for (size_t i = 0; i < j_values.size(); ++i) + { + for (size_t j = 0; j < j_values.size(); ++j) + { + CAPTURE(i); + CAPTURE(j); + // check definition + CHECK( (j_values[i] != j_values[j]) == not(j_values[i] == j_values[j]) ); + } + } + + // compare with null pointer + json j_null; + CHECK( (j_null != nullptr) == false); + CHECK( (nullptr != j_null) == false); + CHECK( (j_null != nullptr) == not(j_null == nullptr)); + CHECK( (nullptr != j_null) == not(nullptr == j_null)); + } + + SECTION("comparison: less") + { + std::vector> expected = + { + {false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true}, + {false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true}, + {false, false, false, true, false, false, false, true, true, true, false, false, true, true, true, true}, + {false, false, false, false, false, false, false, false, true, true, false, false, true, true, true, true}, + {false, false, true, true, false, true, false, true, true, true, false, false, true, true, true, true}, + {false, false, true, true, false, false, false, true, true, true, false, false, true, true, true, true}, + {false, false, true, true, true, true, false, true, true, true, false, false, true, true, true, true}, + {false, false, false, true, false, false, false, false, true, true, false, false, true, true, true, true}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false}, + {false, false, true, true, true, true, true, true, true, true, false, false, true, true, true, true}, + {false, false, true, true, true, true, true, true, true, true, true, false, true, true, true, true}, + {false, false, false, false, false, false, false, false, true, true, false, false, false, true, false, false}, + {false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, true, true, false, false, true, true, false, false}, + {false, false, false, false, false, false, false, false, true, true, false, false, true, true, true, false} + }; + + for (size_t i = 0; i < j_values.size(); ++i) + { + for (size_t j = 0; j < j_values.size(); ++j) + { + CAPTURE(i); + CAPTURE(j); + // check precomputed values + CHECK( (j_values[i] < j_values[j]) == expected[i][j] ); + } + } + + // comparison with discarded elements + json j_discarded(json::value_t::discarded); + for (size_t i = 0; i < j_values.size(); ++i) + { + CAPTURE(i); + CHECK( (j_values[i] < j_discarded) == false); + CHECK( (j_discarded < j_values[i]) == false); + CHECK( (j_discarded < j_discarded) == false); + } + } + + SECTION("comparison: less than or equal equal") + { + for (size_t i = 0; i < j_values.size(); ++i) + { + for (size_t j = 0; j < j_values.size(); ++j) + { + CAPTURE(i); + CAPTURE(j); + // check definition + CHECK( (j_values[i] <= j_values[j]) == not(j_values[j] < j_values[i]) ); + } + } + } + + SECTION("comparison: greater than") + { + for (size_t i = 0; i < j_values.size(); ++i) + { + for (size_t j = 0; j < j_values.size(); ++j) + { + CAPTURE(i); + CAPTURE(j); + // check definition + CHECK( (j_values[i] > j_values[j]) == (j_values[j] < j_values[i]) ); + } + } + } + + SECTION("comparison: greater than or equal") + { + for (size_t i = 0; i < j_values.size(); ++i) + { + for (size_t j = 0; j < j_values.size(); ++j) + { + CAPTURE(i); + CAPTURE(j); + // check definition + CHECK( (j_values[i] >= j_values[j]) == not(j_values[i] < j_values[j]) ); + } + } + } + } +} diff --git a/test/src/unit-concepts.cpp b/test/src/unit-concepts.cpp new file mode 100644 index 00000000..9cbdd8e9 --- /dev/null +++ b/test/src/unit-concepts.cpp @@ -0,0 +1,169 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("concepts") +{ + SECTION("container requirements for json") + { + // X: container class: json + // T: type of objects: json + // a, b: values of type X: json + + // TABLE 96 - Container Requirements + + // X::value_type must return T + CHECK((std::is_same::value)); + + // X::reference must return lvalue of T + CHECK((std::is_same::value)); + + // X::const_reference must return const lvalue of T + CHECK((std::is_same::value)); + + // X::iterator must return iterator whose value_type is T + CHECK((std::is_same::value)); + // X::iterator must meet the forward iterator requirements + CHECK((std::is_base_of::iterator_category>::value)); + // X::iterator must be convertible to X::const_iterator + CHECK((std::is_convertible::value)); + + // X::const_iterator must return iterator whose value_type is T + CHECK((std::is_same::value)); + // X::const_iterator must meet the forward iterator requirements + CHECK((std::is_base_of::iterator_category>::value)); + + // X::difference_type must return a signed integer + CHECK((std::is_signed::value)); + // X::difference_type must be identical to X::iterator::difference_type + CHECK((std::is_same::value)); + // X::difference_type must be identical to X::const_iterator::difference_type + CHECK((std::is_same::value)); + + // X::size_type must return an unsigned integer + CHECK((std::is_unsigned::value)); + // X::size_type can represent any non-negative value of X::difference_type + CHECK(std::numeric_limits::max() <= + std::numeric_limits::max()); + + // the expression "X u" has the post-condition "u.empty()" + { + json u; + CHECK(u.empty()); + } + + // the expression "X()" has the post-condition "X().empty()" + CHECK(json().empty()); + } + + SECTION("class json") + { + SECTION("DefaultConstructible") + { + CHECK(std::is_nothrow_default_constructible::value); + } + + SECTION("MoveConstructible") + { + CHECK(std::is_nothrow_move_constructible::value); + } + + SECTION("CopyConstructible") + { + CHECK(std::is_copy_constructible::value); + } + + SECTION("MoveAssignable") + { + CHECK(std::is_nothrow_move_assignable::value); + } + + SECTION("CopyAssignable") + { + CHECK(std::is_copy_assignable::value); + } + + SECTION("Destructible") + { + CHECK(std::is_nothrow_destructible::value); + } + + SECTION("StandardLayoutType") + { + CHECK(std::is_standard_layout::value); + } + } + + SECTION("class iterator") + { + SECTION("CopyConstructible") + { + CHECK(std::is_nothrow_copy_constructible::value); + CHECK(std::is_nothrow_copy_constructible::value); + } + + SECTION("CopyAssignable") + { + // STL iterators used by json::iterator don't pass this test in Debug mode +#if !defined(_MSC_VER) || (_ITERATOR_DEBUG_LEVEL == 0) + CHECK(std::is_nothrow_copy_assignable::value); + CHECK(std::is_nothrow_copy_assignable::value); +#endif + } + + SECTION("Destructible") + { + CHECK(std::is_nothrow_destructible::value); + CHECK(std::is_nothrow_destructible::value); + } + + SECTION("Swappable") + { + { + json j {1, 2, 3}; + json::iterator it1 = j.begin(); + json::iterator it2 = j.end(); + std::swap(it1, it2); + CHECK(it1 == j.end()); + CHECK(it2 == j.begin()); + } + { + json j {1, 2, 3}; + json::const_iterator it1 = j.cbegin(); + json::const_iterator it2 = j.cend(); + std::swap(it1, it2); + CHECK(it1 == j.end()); + CHECK(it2 == j.begin()); + } + } + } +} diff --git a/test/src/unit-constructor.cpp b/test/src/unit-constructor.cpp new file mode 100644 index 00000000..e4842a58 --- /dev/null +++ b/test/src/unit-constructor.cpp @@ -0,0 +1,1470 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#define private public +#include "json.hpp" +using nlohmann::json; + +#include +#include +#include +#include +#include +#include + +TEST_CASE("constructors") +{ + SECTION("create an empty value with a given type") + { + SECTION("null") + { + auto t = json::value_t::null; + json j(t); + CHECK(j.type() == t); + } + + SECTION("discarded") + { + auto t = json::value_t::discarded; + json j(t); + CHECK(j.type() == t); + } + + SECTION("object") + { + auto t = json::value_t::object; + json j(t); + CHECK(j.type() == t); + } + + SECTION("array") + { + auto t = json::value_t::array; + json j(t); + CHECK(j.type() == t); + } + + SECTION("boolean") + { + auto t = json::value_t::boolean; + json j(t); + CHECK(j.type() == t); + } + + SECTION("string") + { + auto t = json::value_t::string; + json j(t); + CHECK(j.type() == t); + } + + SECTION("number_integer") + { + auto t = json::value_t::number_integer; + json j(t); + CHECK(j.type() == t); + } + + SECTION("number_unsigned") + { + auto t = json::value_t::number_unsigned; + json j(t); + CHECK(j.type() == t); + } + + SECTION("number_float") + { + auto t = json::value_t::number_float; + json j(t); + CHECK(j.type() == t); + } + } + + SECTION("create a null object (implicitly)") + { + SECTION("no parameter") + { + json j{}; + CHECK(j.type() == json::value_t::null); + } + } + + SECTION("create a null object (explicitly)") + { + SECTION("parameter") + { + json j(nullptr); + CHECK(j.type() == json::value_t::null); + } + } + + SECTION("create an object (explicit)") + { + SECTION("empty object") + { + json::object_t o; + json j(o); + CHECK(j.type() == json::value_t::object); + } + + SECTION("filled object") + { + json::object_t o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; + json j(o); + CHECK(j.type() == json::value_t::object); + } + } + + SECTION("create an object (implicit)") + { + // reference object + json::object_t o_reference {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; + json j_reference(o_reference); + + SECTION("std::map") + { + std::map o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; + json j(o); + CHECK(j.type() == json::value_t::object); + CHECK(j == j_reference); + } + + SECTION("std::map") + { + std::map o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; + json j(o); + CHECK(j.type() == json::value_t::object); + CHECK(j == j_reference); + } + + SECTION("std::multimap") + { + std::multimap o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; + json j(o); + CHECK(j.type() == json::value_t::object); + CHECK(j == j_reference); + } + + SECTION("std::unordered_map") + { + std::unordered_map o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; + json j(o); + CHECK(j.type() == json::value_t::object); + CHECK(j == j_reference); + } + + SECTION("std::unordered_multimap") + { + std::unordered_multimap o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; + json j(o); + CHECK(j.type() == json::value_t::object); + CHECK(j == j_reference); + } + + SECTION("associative container literal") + { + json j({{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}); + CHECK(j.type() == json::value_t::object); + CHECK(j == j_reference); + } + } + + SECTION("create an array (explicit)") + { + SECTION("empty array") + { + json::array_t a; + json j(a); + CHECK(j.type() == json::value_t::array); + } + + SECTION("filled array") + { + json::array_t a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; + json j(a); + CHECK(j.type() == json::value_t::array); + } + } + + SECTION("create an array (implicit)") + { + // reference array + json::array_t a_reference {json(1), json(1u), json(2.2), json(false), json("string"), json()}; + json j_reference(a_reference); + + SECTION("std::list") + { + std::list a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; + json j(a); + CHECK(j.type() == json::value_t::array); + CHECK(j == j_reference); + } + + SECTION("std::forward_list") + { + std::forward_list a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; + json j(a); + CHECK(j.type() == json::value_t::array); + CHECK(j == j_reference); + } + + SECTION("std::array") + { + std::array a {{json(1), json(1u), json(2.2), json(false), json("string"), json()}}; + json j(a); + CHECK(j.type() == json::value_t::array); + CHECK(j == j_reference); + } + + SECTION("std::vector") + { + std::vector a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; + json j(a); + CHECK(j.type() == json::value_t::array); + CHECK(j == j_reference); + } + + SECTION("std::deque") + { + std::deque a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; + json j(a); + CHECK(j.type() == json::value_t::array); + CHECK(j == j_reference); + } + + SECTION("std::set") + { + std::set a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; + json j(a); + CHECK(j.type() == json::value_t::array); + // we cannot really check for equality here + } + + SECTION("std::unordered_set") + { + std::unordered_set a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; + json j(a); + CHECK(j.type() == json::value_t::array); + // we cannot really check for equality here + } + + SECTION("sequence container literal") + { + json j({json(1), json(1u), json(2.2), json(false), json("string"), json()}); + CHECK(j.type() == json::value_t::array); + CHECK(j == j_reference); + } + } + + SECTION("create a string (explicit)") + { + SECTION("empty string") + { + json::string_t s; + json j(s); + CHECK(j.type() == json::value_t::string); + } + + SECTION("filled string") + { + json::string_t s {"Hello world"}; + json j(s); + CHECK(j.type() == json::value_t::string); + } + } + + SECTION("create a string (implicit)") + { + // reference string + json::string_t s_reference {"Hello world"}; + json j_reference(s_reference); + + SECTION("std::string") + { + std::string s {"Hello world"}; + json j(s); + CHECK(j.type() == json::value_t::string); + CHECK(j == j_reference); + } + + SECTION("char[]") + { + char s[] {"Hello world"}; + json j(s); + CHECK(j.type() == json::value_t::string); + CHECK(j == j_reference); + } + + SECTION("const char*") + { + const char* s {"Hello world"}; + json j(s); + CHECK(j.type() == json::value_t::string); + CHECK(j == j_reference); + } + + SECTION("string literal") + { + json j("Hello world"); + CHECK(j.type() == json::value_t::string); + CHECK(j == j_reference); + } + } + + SECTION("create a boolean (explicit)") + { + SECTION("empty boolean") + { + json::boolean_t b{}; + json j(b); + CHECK(j.type() == json::value_t::boolean); + } + + SECTION("filled boolean (true)") + { + json j(true); + CHECK(j.type() == json::value_t::boolean); + } + + SECTION("filled boolean (false)") + { + json j(false); + CHECK(j.type() == json::value_t::boolean); + } + } + + SECTION("create an integer number (explicit)") + { + SECTION("uninitialized value") + { + json::number_integer_t n{}; + json j(n); + CHECK(j.type() == json::value_t::number_integer); + } + + SECTION("initialized value") + { + json::number_integer_t n(42); + json j(n); + CHECK(j.type() == json::value_t::number_integer); + } + } + + SECTION("create an integer number (implicit)") + { + // reference objects + json::number_integer_t n_reference = 42; + json j_reference(n_reference); + json::number_unsigned_t n_unsigned_reference = 42; + json j_unsigned_reference(n_unsigned_reference); + + SECTION("short") + { + short n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("unsigned short") + { + unsigned short n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("int") + { + int n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("unsigned int") + { + unsigned int n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("long") + { + long n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("unsigned long") + { + unsigned long n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("long long") + { + long long n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("unsigned long long") + { + unsigned long long n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("int8_t") + { + int8_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("int16_t") + { + int16_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("int32_t") + { + int32_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("int64_t") + { + int64_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("int_fast8_t") + { + int_fast8_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("int_fast16_t") + { + int_fast16_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("int_fast32_t") + { + int_fast32_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("int_fast64_t") + { + int_fast64_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("int_least8_t") + { + int_least8_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("int_least16_t") + { + int_least16_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("int_least32_t") + { + int_least32_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("int_least64_t") + { + int_least64_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("uint8_t") + { + uint8_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("uint16_t") + { + uint16_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("uint32_t") + { + uint32_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("uint64_t") + { + uint64_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("uint_fast8_t") + { + uint_fast8_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("uint_fast16_t") + { + uint_fast16_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("uint_fast32_t") + { + uint_fast32_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("uint_fast64_t") + { + uint_fast64_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("uint_least8_t") + { + uint_least8_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("uint_least16_t") + { + uint_least16_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("uint_least32_t") + { + uint_least32_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("uint_least64_t") + { + uint_least64_t n = 42; + json j(n); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("integer literal without suffix") + { + json j(42); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("integer literal with u suffix") + { + json j(42u); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("integer literal with l suffix") + { + json j(42l); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("integer literal with ul suffix") + { + json j(42ul); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + + SECTION("integer literal with ll suffix") + { + json j(42ll); + CHECK(j.type() == json::value_t::number_integer); + CHECK(j == j_reference); + } + + SECTION("integer literal with ull suffix") + { + json j(42ull); + CHECK(j.type() == json::value_t::number_unsigned); + CHECK(j == j_unsigned_reference); + } + } + + SECTION("create a floating-point number (explicit)") + { + SECTION("uninitialized value") + { + json::number_float_t n{}; + json j(n); + CHECK(j.type() == json::value_t::number_float); + } + + SECTION("initialized value") + { + json::number_float_t n(42.23); + json j(n); + CHECK(j.type() == json::value_t::number_float); + } + } + + SECTION("create a floating-point number (implicit)") + { + // reference object + json::number_float_t n_reference = 42.23; + json j_reference(n_reference); + + SECTION("float") + { + float n = 42.23; + json j(n); + CHECK(j.type() == json::value_t::number_float); + CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float)); + } + + SECTION("double") + { + double n = 42.23; + json j(n); + CHECK(j.type() == json::value_t::number_float); + CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float)); + } + + SECTION("long double") + { + long double n = 42.23; + json j(n); + CHECK(j.type() == json::value_t::number_float); + CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float)); + } + + SECTION("floating-point literal without suffix") + { + json j(42.23); + CHECK(j.type() == json::value_t::number_float); + CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float)); + } + + SECTION("integer literal with f suffix") + { + json j(42.23f); + CHECK(j.type() == json::value_t::number_float); + CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float)); + } + + SECTION("integer literal with l suffix") + { + json j(42.23l); + CHECK(j.type() == json::value_t::number_float); + CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float)); + } + } + + SECTION("create a container (array or object) from an initializer list") + { + SECTION("empty initializer list") + { + SECTION("explicit") + { + std::initializer_list l; + json j(l); + CHECK(j.type() == json::value_t::object); + } + + SECTION("implicit") + { + json j {}; + CHECK(j.type() == json::value_t::null); + } + } + + SECTION("one element") + { + SECTION("array") + { + SECTION("explicit") + { + std::initializer_list l = {json(json::array_t())}; + json j(l); + CHECK(j.type() == json::value_t::array); + } + + SECTION("implicit") + { + json j {json::array_t()}; + CHECK(j.type() == json::value_t::array); + } + } + + SECTION("object") + { + SECTION("explicit") + { + std::initializer_list l = {json(json::object_t())}; + json j(l); + CHECK(j.type() == json::value_t::array); + } + + SECTION("implicit") + { + json j {json::object_t()}; + CHECK(j.type() == json::value_t::array); + } + } + + SECTION("string") + { + SECTION("explicit") + { + std::initializer_list l = {json("Hello world")}; + json j(l); + CHECK(j.type() == json::value_t::array); + } + + SECTION("implicit") + { + json j {"Hello world"}; + CHECK(j.type() == json::value_t::array); + } + } + + SECTION("boolean") + { + SECTION("explicit") + { + std::initializer_list l = {json(true)}; + json j(l); + CHECK(j.type() == json::value_t::array); + } + + SECTION("implicit") + { + json j {true}; + CHECK(j.type() == json::value_t::array); + } + } + + SECTION("number (integer)") + { + SECTION("explicit") + { + std::initializer_list l = {json(1)}; + json j(l); + CHECK(j.type() == json::value_t::array); + } + + SECTION("implicit") + { + json j {1}; + CHECK(j.type() == json::value_t::array); + } + } + + SECTION("number (unsigned)") + { + SECTION("explicit") + { + std::initializer_list l = {json(1u)}; + json j(l); + CHECK(j.type() == json::value_t::array); + } + + SECTION("implicit") + { + json j {1u}; + CHECK(j.type() == json::value_t::array); + } + } + + SECTION("number (floating-point)") + { + SECTION("explicit") + { + std::initializer_list l = {json(42.23)}; + json j(l); + CHECK(j.type() == json::value_t::array); + } + + SECTION("implicit") + { + json j {42.23}; + CHECK(j.type() == json::value_t::array); + } + } + } + + SECTION("more elements") + { + SECTION("explicit") + { + std::initializer_list l = {1, 1u, 42.23, true, nullptr, json::object_t(), json::array_t()}; + json j(l); + CHECK(j.type() == json::value_t::array); + } + + SECTION("implicit") + { + json j {1, 1u, 42.23, true, nullptr, json::object_t(), json::array_t()}; + CHECK(j.type() == json::value_t::array); + } + } + + SECTION("implicit type deduction") + { + SECTION("object") + { + json j { {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} }; + CHECK(j.type() == json::value_t::object); + } + + SECTION("array") + { + json j { {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} , 13 }; + CHECK(j.type() == json::value_t::array); + } + } + + SECTION("explicit type deduction") + { + SECTION("empty object") + { + json j = json::object(); + CHECK(j.type() == json::value_t::object); + } + + SECTION("object") + { + json j = json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} }); + CHECK(j.type() == json::value_t::object); + } + + SECTION("object with error") + { + CHECK_THROWS_AS(json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false}, 13 }), + std::logic_error); + CHECK_THROWS_WITH(json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false}, 13 }), + "cannot create object from initializer list"); + } + + SECTION("empty array") + { + json j = json::array(); + CHECK(j.type() == json::value_t::array); + } + + SECTION("array") + { + json j = json::array({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} }); + CHECK(j.type() == json::value_t::array); + } + } + } + + SECTION("create an array of n copies of a given value") + { + json v = {1, "foo", 34.23, {1, 2, 3}, {{"A", 1}, {"B", 2u}}}; + json arr(3, v); + CHECK(arr.size() == 3); + for (auto& x : arr) + { + CHECK(x == v); + } + } + + SECTION("create a JSON container from an iterator range") + { + SECTION("object") + { + SECTION("json(begin(), end())") + { + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; + json j_new(jobject.begin(), jobject.end()); + CHECK(j_new == jobject); + } + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; + json j_new(jobject.cbegin(), jobject.cend()); + CHECK(j_new == jobject); + } + } + + SECTION("json(begin(), begin())") + { + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; + json j_new(jobject.begin(), jobject.begin()); + CHECK(j_new == json::object()); + } + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; + json j_new(jobject.cbegin(), jobject.cbegin()); + CHECK(j_new == json::object()); + } + } + + SECTION("construct from subrange") + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; + json j_new(jobject.find("b"), jobject.find("e")); + CHECK(j_new == json({{"b", 1}, {"c", 17u}, {"d", false}})); + } + + SECTION("incompatible iterators") + { + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; + json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}}; + CHECK_THROWS_AS(json(jobject.begin(), jobject2.end()), std::domain_error); + CHECK_THROWS_AS(json(jobject2.begin(), jobject.end()), std::domain_error); + CHECK_THROWS_WITH(json(jobject.begin(), jobject2.end()), "iterators are not compatible"); + CHECK_THROWS_WITH(json(jobject2.begin(), jobject.end()), "iterators are not compatible"); + } + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; + json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}}; + CHECK_THROWS_AS(json(jobject.cbegin(), jobject2.cend()), std::domain_error); + CHECK_THROWS_AS(json(jobject2.cbegin(), jobject.cend()), std::domain_error); + CHECK_THROWS_WITH(json(jobject.cbegin(), jobject2.cend()), "iterators are not compatible"); + CHECK_THROWS_WITH(json(jobject2.cbegin(), jobject.cend()), "iterators are not compatible"); + } + } + } + + SECTION("array") + { + SECTION("json(begin(), end())") + { + { + json jarray = {1, 2, 3, 4, 5}; + json j_new(jarray.begin(), jarray.end()); + CHECK(j_new == jarray); + } + { + json jarray = {1, 2, 3, 4, 5}; + json j_new(jarray.cbegin(), jarray.cend()); + CHECK(j_new == jarray); + } + } + + SECTION("json(begin(), begin())") + { + { + json jarray = {1, 2, 3, 4, 5}; + json j_new(jarray.begin(), jarray.begin()); + CHECK(j_new == json::array()); + } + { + json jarray = {1, 2, 3, 4, 5}; + json j_new(jarray.cbegin(), jarray.cbegin()); + CHECK(j_new == json::array()); + } + } + + SECTION("construct from subrange") + { + { + json jarray = {1, 2, 3, 4, 5}; + json j_new(jarray.begin() + 1, jarray.begin() + 3); + CHECK(j_new == json({2, 3})); + } + { + json jarray = {1, 2, 3, 4, 5}; + json j_new(jarray.cbegin() + 1, jarray.cbegin() + 3); + CHECK(j_new == json({2, 3})); + } + } + + SECTION("incompatible iterators") + { + { + json jarray = {1, 2, 3, 4}; + json jarray2 = {2, 3, 4, 5}; + CHECK_THROWS_AS(json(jarray.begin(), jarray2.end()), std::domain_error); + CHECK_THROWS_AS(json(jarray2.begin(), jarray.end()), std::domain_error); + CHECK_THROWS_WITH(json(jarray.begin(), jarray2.end()), "iterators are not compatible"); + CHECK_THROWS_WITH(json(jarray2.begin(), jarray.end()), "iterators are not compatible"); + } + { + json jarray = {1, 2, 3, 4}; + json jarray2 = {2, 3, 4, 5}; + CHECK_THROWS_AS(json(jarray.cbegin(), jarray2.cend()), std::domain_error); + CHECK_THROWS_AS(json(jarray2.cbegin(), jarray.cend()), std::domain_error); + CHECK_THROWS_WITH(json(jarray.cbegin(), jarray2.cend()), "iterators are not compatible"); + CHECK_THROWS_WITH(json(jarray2.cbegin(), jarray.cend()), "iterators are not compatible"); + } + } + } + + SECTION("other values") + { + SECTION("construct with two valid iterators") + { + SECTION("null") + { + { + json j; + CHECK_THROWS_AS(json(j.begin(), j.end()), std::domain_error); + CHECK_THROWS_WITH(json(j.begin(), j.end()), "cannot use construct with iterators from null"); + } + { + json j; + CHECK_THROWS_AS(json(j.cbegin(), j.cend()), std::domain_error); + CHECK_THROWS_WITH(json(j.cbegin(), j.cend()), "cannot use construct with iterators from null"); + } + } + + SECTION("string") + { + { + json j = "foo"; + json j_new(j.begin(), j.end()); + CHECK(j == j_new); + } + { + json j = "bar"; + json j_new(j.cbegin(), j.cend()); + CHECK(j == j_new); + } + } + + SECTION("number (boolean)") + { + { + json j = false; + json j_new(j.begin(), j.end()); + CHECK(j == j_new); + } + { + json j = true; + json j_new(j.cbegin(), j.cend()); + CHECK(j == j_new); + } + } + + SECTION("number (integer)") + { + { + json j = 17; + json j_new(j.begin(), j.end()); + CHECK(j == j_new); + } + { + json j = 17; + json j_new(j.cbegin(), j.cend()); + CHECK(j == j_new); + } + } + + SECTION("number (unsigned)") + { + { + json j = 17u; + json j_new(j.begin(), j.end()); + CHECK(j == j_new); + } + { + json j = 17u; + json j_new(j.cbegin(), j.cend()); + CHECK(j == j_new); + } + } + + SECTION("number (floating point)") + { + { + json j = 23.42; + json j_new(j.begin(), j.end()); + CHECK(j == j_new); + } + { + json j = 23.42; + json j_new(j.cbegin(), j.cend()); + CHECK(j == j_new); + } + } + } + + SECTION("construct with two invalid iterators") + { + SECTION("string") + { + { + json j = "foo"; + CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); + CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); + CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range"); + CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range"); + } + { + json j = "bar"; + CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); + CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); + CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range"); + CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range"); + } + } + + SECTION("number (boolean)") + { + { + json j = false; + CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); + CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); + CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range"); + CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range"); + } + { + json j = true; + CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); + CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); + CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range"); + CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range"); + } + } + + SECTION("number (integer)") + { + { + json j = 17; + CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); + CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); + CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range"); + CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range"); + } + { + json j = 17; + CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); + CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); + CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range"); + CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range"); + } + } + + SECTION("number (integer)") + { + { + json j = 17u; + CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); + CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); + CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range"); + CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range"); + } + { + json j = 17u; + CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); + CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); + CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range"); + CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range"); + } + } + + SECTION("number (floating point)") + { + { + json j = 23.42; + CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); + CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); + CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range"); + CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range"); + } + { + json j = 23.42; + CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); + CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); + CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range"); + CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range"); + } + } + } + } + } + + SECTION("create a JSON value from an input stream") + { + SECTION("std::stringstream") + { + std::stringstream ss; + ss << "[\"foo\",1,2,3,false,{\"one\":1}]"; + json j(ss); + CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); + } + + SECTION("with callback function") + { + std::stringstream ss; + ss << "[\"foo\",1,2,3,false,{\"one\":1}]"; + json j(ss, [](int, json::parse_event_t, const json & val) + { + // filter all number(2) elements + if (val == json(2)) + { + return false; + } + else + { + return true; + } + }); + CHECK(j == json({"foo", 1, 3, false, {{"one", 1}}})); + } + + SECTION("std::ifstream") + { + std::ifstream f("test/data/json_tests/pass1.json"); + json j(f); + } + } +} + +TEST_CASE("other constructors and destructor") +{ + SECTION("copy constructor") + { + SECTION("object") + { + json j {{"foo", 1}, {"bar", false}}; + json k(j); + CHECK(j == k); + } + + SECTION("array") + { + json j {"foo", 1, 42.23, false}; + json k(j); + CHECK(j == k); + } + + SECTION("null") + { + json j(nullptr); + json k(j); + CHECK(j == k); + } + + SECTION("boolean") + { + json j(true); + json k(j); + CHECK(j == k); + } + + SECTION("string") + { + json j("Hello world"); + json k(j); + CHECK(j == k); + } + + SECTION("number (integer)") + { + json j(42); + json k(j); + CHECK(j == k); + } + + SECTION("number (unsigned)") + { + json j(42u); + json k(j); + CHECK(j == k); + } + + SECTION("number (floating-point)") + { + json j(42.23); + json k(j); + CHECK(j == k); + } + } + + SECTION("move constructor") + { + json j {{"foo", "bar"}, {"baz", {1, 2, 3, 4}}, {"a", 42u}, {"b", 42.23}, {"c", nullptr}}; + CHECK(j.type() == json::value_t::object); + json k(std::move(j)); + CHECK(k.type() == json::value_t::object); + CHECK(j.type() == json::value_t::null); + } + + SECTION("copy assignment") + { + SECTION("object") + { + json j {{"foo", 1}, {"bar", false}}; + json k; + k = j; + CHECK(j == k); + } + + SECTION("array") + { + json j {"foo", 1, 42.23, false}; + json k; + k = j; + CHECK(j == k); + } + + SECTION("null") + { + json j(nullptr); + json k; + k = j; + CHECK(j == k); + } + + SECTION("boolean") + { + json j(true); + json k; + k = j; + CHECK(j == k); + } + + SECTION("string") + { + json j("Hello world"); + json k; + k = j; + CHECK(j == k); + } + + SECTION("number (integer)") + { + json j(42); + json k; + k = j; + CHECK(j == k); + } + + SECTION("number (unsigned)") + { + json j(42u); + json k; + k = j; + CHECK(j == k); + } + + SECTION("number (floating-point)") + { + json j(42.23); + json k; + k = j; + CHECK(j == k); + } + } + + SECTION("destructor") + { + SECTION("object") + { + auto j = new json {{"foo", 1}, {"bar", false}}; + delete j; + } + + SECTION("array") + { + auto j = new json {"foo", 1, 1u, false, 23.42}; + delete j; + } + + SECTION("string") + { + auto j = new json("Hello world"); + delete j; + } + } +} diff --git a/test/src/unit-convenience.cpp b/test/src/unit-convenience.cpp new file mode 100644 index 00000000..4d3c9b7c --- /dev/null +++ b/test/src/unit-convenience.cpp @@ -0,0 +1,92 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#define private public +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("convenience functions") +{ + SECTION("type name as string") + { + CHECK(json(json::value_t::null).type_name() == "null"); + CHECK(json(json::value_t::object).type_name() == "object"); + CHECK(json(json::value_t::array).type_name() == "array"); + CHECK(json(json::value_t::number_integer).type_name() == "number"); + CHECK(json(json::value_t::number_unsigned).type_name() == "number"); + CHECK(json(json::value_t::number_float).type_name() == "number"); + CHECK(json(json::value_t::boolean).type_name() == "boolean"); + CHECK(json(json::value_t::string).type_name() == "string"); + CHECK(json(json::value_t::discarded).type_name() == "discarded"); + } + + SECTION("string escape") + { + CHECK(json::escape_string("\"") == "\\\""); + CHECK(json::escape_string("\\") == "\\\\"); + CHECK(json::escape_string("\b") == "\\b"); + CHECK(json::escape_string("\f") == "\\f"); + CHECK(json::escape_string("\n") == "\\n"); + CHECK(json::escape_string("\r") == "\\r"); + CHECK(json::escape_string("\t") == "\\t"); + + CHECK(json::escape_string("\x01") == "\\u0001"); + CHECK(json::escape_string("\x02") == "\\u0002"); + CHECK(json::escape_string("\x03") == "\\u0003"); + CHECK(json::escape_string("\x04") == "\\u0004"); + CHECK(json::escape_string("\x05") == "\\u0005"); + CHECK(json::escape_string("\x06") == "\\u0006"); + CHECK(json::escape_string("\x07") == "\\u0007"); + CHECK(json::escape_string("\x08") == "\\b"); + CHECK(json::escape_string("\x09") == "\\t"); + CHECK(json::escape_string("\x0a") == "\\n"); + CHECK(json::escape_string("\x0b") == "\\u000b"); + CHECK(json::escape_string("\x0c") == "\\f"); + CHECK(json::escape_string("\x0d") == "\\r"); + CHECK(json::escape_string("\x0e") == "\\u000e"); + CHECK(json::escape_string("\x0f") == "\\u000f"); + CHECK(json::escape_string("\x10") == "\\u0010"); + CHECK(json::escape_string("\x11") == "\\u0011"); + CHECK(json::escape_string("\x12") == "\\u0012"); + CHECK(json::escape_string("\x13") == "\\u0013"); + CHECK(json::escape_string("\x14") == "\\u0014"); + CHECK(json::escape_string("\x15") == "\\u0015"); + CHECK(json::escape_string("\x16") == "\\u0016"); + CHECK(json::escape_string("\x17") == "\\u0017"); + CHECK(json::escape_string("\x18") == "\\u0018"); + CHECK(json::escape_string("\x19") == "\\u0019"); + CHECK(json::escape_string("\x1a") == "\\u001a"); + CHECK(json::escape_string("\x1b") == "\\u001b"); + CHECK(json::escape_string("\x1c") == "\\u001c"); + CHECK(json::escape_string("\x1d") == "\\u001d"); + CHECK(json::escape_string("\x1e") == "\\u001e"); + CHECK(json::escape_string("\x1f") == "\\u001f"); + } +} diff --git a/test/src/unit-conversions.cpp b/test/src/unit-conversions.cpp new file mode 100644 index 00000000..982efe42 --- /dev/null +++ b/test/src/unit-conversions.cpp @@ -0,0 +1,1014 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#define private public +#include "json.hpp" +using nlohmann::json; + +#include +#include +#include +#include +#include + +TEST_CASE("value conversion") +{ + SECTION("get an object (explicit)") + { + json::object_t o_reference = {{"object", json::object()}, {"array", {1, 2, 3, 4}}, {"number", 42}, {"boolean", false}, {"null", nullptr}, {"string", "Hello world"} }; + json j(o_reference); + + SECTION("json::object_t") + { + json::object_t o = j.get(); + CHECK(json(o) == j); + } + + SECTION("std::map") + { + std::map o = j.get>(); + CHECK(json(o) == j); + } + + SECTION("std::multimap") + { + std::multimap o = j.get>(); + CHECK(json(o) == j); + } + + SECTION("std::unordered_map") + { + std::unordered_map o = j.get>(); + CHECK(json(o) == j); + } + + SECTION("std::unordered_multimap") + { + std::unordered_multimap o = + j.get>(); + CHECK(json(o) == j); + } + + SECTION("exception in case of a non-object type") + { + CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::array).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::string).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::boolean).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::number_integer).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::number_float).get(), std::logic_error); + + CHECK_THROWS_WITH(json(json::value_t::null).get(), + "type must be object, but is null"); + CHECK_THROWS_WITH(json(json::value_t::array).get(), + "type must be object, but is array"); + CHECK_THROWS_WITH(json(json::value_t::string).get(), + "type must be object, but is string"); + CHECK_THROWS_WITH(json(json::value_t::boolean).get(), + "type must be object, but is boolean"); + CHECK_THROWS_WITH(json(json::value_t::number_integer).get(), + "type must be object, but is number"); + CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get(), + "type must be object, but is number"); + CHECK_THROWS_WITH(json(json::value_t::number_float).get(), + "type must be object, but is number"); + } + } + + SECTION("get an object (implicit)") + { + json::object_t o_reference = {{"object", json::object()}, {"array", {1, 2, 3, 4}}, {"number", 42}, {"boolean", false}, {"null", nullptr}, {"string", "Hello world"} }; + json j(o_reference); + + SECTION("json::object_t") + { + json::object_t o = j; + CHECK(json(o) == j); + } + + SECTION("std::map") + { + std::map o = j; + CHECK(json(o) == j); + } + + SECTION("std::multimap") + { + std::multimap o = j; + CHECK(json(o) == j); + } + + SECTION("std::unordered_map") + { + std::unordered_map o = j; + CHECK(json(o) == j); + } + + SECTION("std::unordered_multimap") + { + std::unordered_multimap o = j; + CHECK(json(o) == j); + } + } + + SECTION("get an array (explicit)") + { + json::array_t a_reference {json(1), json(1u), json(2.2), json(false), json("string"), json()}; + json j(a_reference); + + SECTION("json::array_t") + { + json::array_t a = j.get(); + CHECK(json(a) == j); + } + + SECTION("std::list") + { + std::list a = j.get>(); + CHECK(json(a) == j); + } + + SECTION("std::forward_list") + { + std::forward_list a = j.get>(); + CHECK(json(a) == j); + } + + SECTION("std::vector") + { + std::vector a = j.get>(); + CHECK(json(a) == j); + } + + SECTION("std::deque") + { + std::deque a = j.get>(); + CHECK(json(a) == j); + } + + SECTION("exception in case of a non-array type") + { + CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::object).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::string).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::boolean).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::number_integer).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::number_float).get(), std::logic_error); + + CHECK_THROWS_WITH(json(json::value_t::null).get(), + "type must be array, but is null"); + CHECK_THROWS_WITH(json(json::value_t::object).get(), + "type must be array, but is object"); + CHECK_THROWS_WITH(json(json::value_t::string).get(), + "type must be array, but is string"); + CHECK_THROWS_WITH(json(json::value_t::boolean).get(), + "type must be array, but is boolean"); + CHECK_THROWS_WITH(json(json::value_t::number_integer).get(), + "type must be array, but is number"); + CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get(), + "type must be array, but is number"); + CHECK_THROWS_WITH(json(json::value_t::number_float).get(), + "type must be array, but is number"); + } + } + + SECTION("get an array (implicit)") + { + json::array_t a_reference {json(1), json(1u), json(2.2), json(false), json("string"), json()}; + json j(a_reference); + + SECTION("json::array_t") + { + json::array_t a = j; + CHECK(json(a) == j); + } + + SECTION("std::list") + { + std::list a = j; + CHECK(json(a) == j); + } + + SECTION("std::forward_list") + { + std::forward_list a = j; + CHECK(json(a) == j); + } + + SECTION("std::vector") + { + std::vector a = j; + CHECK(json(a) == j); + } + + SECTION("std::deque") + { + std::deque a = j; + CHECK(json(a) == j); + } + } + + SECTION("get a string (explicit)") + { + json::string_t s_reference {"Hello world"}; + json j(s_reference); + + SECTION("string_t") + { + json::string_t s = j.get(); + CHECK(json(s) == j); + } + + SECTION("std::string") + { + std::string s = j.get(); + CHECK(json(s) == j); + } + + SECTION("exception in case of a non-string type") + { + CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::object).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::array).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::boolean).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::number_integer).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::number_float).get(), std::logic_error); + + CHECK_THROWS_WITH(json(json::value_t::null).get(), + "type must be string, but is null"); + CHECK_THROWS_WITH(json(json::value_t::object).get(), + "type must be string, but is object"); + CHECK_THROWS_WITH(json(json::value_t::array).get(), + "type must be string, but is array"); + CHECK_THROWS_WITH(json(json::value_t::boolean).get(), + "type must be string, but is boolean"); + CHECK_THROWS_WITH(json(json::value_t::number_integer).get(), + "type must be string, but is number"); + CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get(), + "type must be string, but is number"); + CHECK_THROWS_WITH(json(json::value_t::number_float).get(), + "type must be string, but is number"); + } + } + + SECTION("get a string (implicit)") + { + json::string_t s_reference {"Hello world"}; + json j(s_reference); + + SECTION("string_t") + { + json::string_t s = j; + CHECK(json(s) == j); + } + + SECTION("std::string") + { + std::string s = j; + CHECK(json(s) == j); + } + } + + SECTION("get a boolean (explicit)") + { + json::boolean_t b_reference {true}; + json j(b_reference); + + SECTION("boolean_t") + { + json::boolean_t b = j.get(); + CHECK(json(b) == j); + } + + SECTION("bool") + { + bool b = j.get(); + CHECK(json(b) == j); + } + + SECTION("exception in case of a non-string type") + { + CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::object).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::array).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::string).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::number_integer).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::number_float).get(), std::logic_error); + + CHECK_THROWS_WITH(json(json::value_t::null).get(), + "type must be boolean, but is null"); + CHECK_THROWS_WITH(json(json::value_t::object).get(), + "type must be boolean, but is object"); + CHECK_THROWS_WITH(json(json::value_t::array).get(), + "type must be boolean, but is array"); + CHECK_THROWS_WITH(json(json::value_t::string).get(), + "type must be boolean, but is string"); + CHECK_THROWS_WITH(json(json::value_t::number_integer).get(), + "type must be boolean, but is number"); + CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get(), + "type must be boolean, but is number"); + CHECK_THROWS_WITH(json(json::value_t::number_float).get(), + "type must be boolean, but is number"); + } + } + + SECTION("get a boolean (implicit)") + { + json::boolean_t b_reference {true}; + json j(b_reference); + + SECTION("boolean_t") + { + json::boolean_t b = j; + CHECK(json(b) == j); + } + + SECTION("bool") + { + bool b = j; + CHECK(json(b) == j); + } + } + + SECTION("get an integer number (explicit)") + { + json::number_integer_t n_reference {42}; + json j(n_reference); + json::number_unsigned_t n_unsigned_reference {42u}; + json j_unsigned(n_unsigned_reference); + + SECTION("number_integer_t") + { + json::number_integer_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("number_unsigned_t") + { + json::number_unsigned_t n = j_unsigned.get(); + CHECK(json(n) == j_unsigned); + } + + SECTION("short") + { + short n = j.get(); + CHECK(json(n) == j); + } + + SECTION("unsigned short") + { + unsigned short n = j.get(); + CHECK(json(n) == j); + } + + SECTION("int") + { + int n = j.get(); + CHECK(json(n) == j); + } + + SECTION("unsigned int") + { + unsigned int n = j.get(); + CHECK(json(n) == j); + } + + SECTION("long") + { + long n = j.get(); + CHECK(json(n) == j); + } + + SECTION("unsigned long") + { + unsigned long n = j.get(); + CHECK(json(n) == j); + } + + SECTION("long long") + { + long long n = j.get(); + CHECK(json(n) == j); + } + + SECTION("unsigned long long") + { + unsigned long long n = j.get(); + CHECK(json(n) == j); + } + + SECTION("int8_t") + { + int8_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("int16_t") + { + int16_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("int32_t") + { + int32_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("int64_t") + { + int64_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("int8_fast_t") + { + int_fast8_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("int16_fast_t") + { + int_fast16_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("int32_fast_t") + { + int_fast32_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("int64_fast_t") + { + int_fast64_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("int8_least_t") + { + int_least8_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("int16_least_t") + { + int_least16_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("int32_least_t") + { + int_least32_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("int64_least_t") + { + int_least64_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("uint8_t") + { + uint8_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("uint16_t") + { + uint16_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("uint32_t") + { + uint32_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("uint64_t") + { + uint64_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("uint8_fast_t") + { + uint_fast8_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("uint16_fast_t") + { + uint_fast16_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("uint32_fast_t") + { + uint_fast32_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("uint64_fast_t") + { + uint_fast64_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("uint8_least_t") + { + uint_least8_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("uint16_least_t") + { + uint_least16_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("uint32_least_t") + { + uint_least32_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("uint64_least_t") + { + uint_least64_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("exception in case of a non-number type") + { + CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::object).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::array).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::string).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::boolean).get(), std::logic_error); + + CHECK_THROWS_WITH(json(json::value_t::null).get(), + "type must be number, but is null"); + CHECK_THROWS_WITH(json(json::value_t::object).get(), + "type must be number, but is object"); + CHECK_THROWS_WITH(json(json::value_t::array).get(), + "type must be number, but is array"); + CHECK_THROWS_WITH(json(json::value_t::string).get(), + "type must be number, but is string"); + CHECK_THROWS_WITH(json(json::value_t::boolean).get(), + "type must be number, but is boolean"); + + CHECK_NOTHROW(json(json::value_t::number_float).get()); + CHECK_NOTHROW(json(json::value_t::number_float).get()); + } + } + + SECTION("get an integer number (implicit)") + { + json::number_integer_t n_reference {42}; + json j(n_reference); + json::number_unsigned_t n_unsigned_reference {42u}; + json j_unsigned(n_unsigned_reference); + + SECTION("number_integer_t") + { + json::number_integer_t n = j.get(); + CHECK(json(n) == j); + } + + SECTION("number_unsigned_t") + { + json::number_unsigned_t n = j_unsigned.get(); + CHECK(json(n) == j_unsigned); + } + + SECTION("short") + { + short n = j; + CHECK(json(n) == j); + } + + SECTION("unsigned short") + { + unsigned short n = j_unsigned; + CHECK(json(n) == j_unsigned); + } + + SECTION("int") + { + int n = j; + CHECK(json(n) == j); + } + + SECTION("unsigned int") + { + unsigned int n = j_unsigned; + CHECK(json(n) == j_unsigned); + } + + SECTION("long") + { + long n = j; + CHECK(json(n) == j); + } + + SECTION("unsigned long") + { + unsigned long n = j_unsigned; + CHECK(json(n) == j_unsigned); + } + + SECTION("long long") + { + long long n = j; + CHECK(json(n) == j); + } + + SECTION("unsigned long long") + { + unsigned long long n = j_unsigned; + CHECK(json(n) == j_unsigned); + } + + SECTION("int8_t") + { + int8_t n = j; + CHECK(json(n) == j); + } + + SECTION("int16_t") + { + int16_t n = j; + CHECK(json(n) == j); + } + + SECTION("int32_t") + { + int32_t n = j; + CHECK(json(n) == j); + } + + SECTION("int64_t") + { + int64_t n = j; + CHECK(json(n) == j); + } + + SECTION("int8_fast_t") + { + int_fast8_t n = j; + CHECK(json(n) == j); + } + + SECTION("int16_fast_t") + { + int_fast16_t n = j; + CHECK(json(n) == j); + } + + SECTION("int32_fast_t") + { + int_fast32_t n = j; + CHECK(json(n) == j); + } + + SECTION("int64_fast_t") + { + int_fast64_t n = j; + CHECK(json(n) == j); + } + + SECTION("int8_least_t") + { + int_least8_t n = j; + CHECK(json(n) == j); + } + + SECTION("int16_least_t") + { + int_least16_t n = j; + CHECK(json(n) == j); + } + + SECTION("int32_least_t") + { + int_least32_t n = j; + CHECK(json(n) == j); + } + + SECTION("int64_least_t") + { + int_least64_t n = j; + CHECK(json(n) == j); + } + + SECTION("uint8_t") + { + uint8_t n = j_unsigned; + CHECK(json(n) == j_unsigned); + } + + SECTION("uint16_t") + { + uint16_t n = j_unsigned; + CHECK(json(n) == j_unsigned); + } + + SECTION("uint32_t") + { + uint32_t n = j_unsigned; + CHECK(json(n) == j_unsigned); + } + + SECTION("uint64_t") + { + uint64_t n = j_unsigned; + CHECK(json(n) == j_unsigned); + } + + SECTION("uint8_fast_t") + { + uint_fast8_t n = j_unsigned; + CHECK(json(n) == j_unsigned); + } + + SECTION("uint16_fast_t") + { + uint_fast16_t n = j_unsigned; + CHECK(json(n) == j_unsigned); + } + + SECTION("uint32_fast_t") + { + uint_fast32_t n = j_unsigned; + CHECK(json(n) == j_unsigned); + } + + SECTION("uint64_fast_t") + { + uint_fast64_t n = j_unsigned; + CHECK(json(n) == j_unsigned); + } + + SECTION("uint8_least_t") + { + uint_least8_t n = j_unsigned; + CHECK(json(n) == j_unsigned); + } + + SECTION("uint16_least_t") + { + uint_least16_t n = j_unsigned; + CHECK(json(n) == j_unsigned); + } + + SECTION("uint32_least_t") + { + uint_least32_t n = j_unsigned; + CHECK(json(n) == j_unsigned); + } + + SECTION("uint64_least_t") + { + uint_least64_t n = j_unsigned; + CHECK(json(n) == j_unsigned); + } + } + + SECTION("get a floating-point number (explicit)") + { + json::number_float_t n_reference {42.23}; + json j(n_reference); + + SECTION("number_float_t") + { + json::number_float_t n = j.get(); + CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float)); + } + + SECTION("float") + { + float n = j.get(); + CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float)); + } + + SECTION("double") + { + double n = j.get(); + CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float)); + } + + SECTION("exception in case of a non-string type") + { + CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::object).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::array).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::string).get(), std::logic_error); + CHECK_THROWS_AS(json(json::value_t::boolean).get(), std::logic_error); + + CHECK_THROWS_WITH(json(json::value_t::null).get(), + "type must be number, but is null"); + CHECK_THROWS_WITH(json(json::value_t::object).get(), + "type must be number, but is object"); + CHECK_THROWS_WITH(json(json::value_t::array).get(), + "type must be number, but is array"); + CHECK_THROWS_WITH(json(json::value_t::string).get(), + "type must be number, but is string"); + CHECK_THROWS_WITH(json(json::value_t::boolean).get(), + "type must be number, but is boolean"); + + CHECK_NOTHROW(json(json::value_t::number_integer).get()); + CHECK_NOTHROW(json(json::value_t::number_unsigned).get()); + } + } + + SECTION("get a floating-point number (implicit)") + { + json::number_float_t n_reference {42.23}; + json j(n_reference); + + SECTION("number_float_t") + { + json::number_float_t n = j; + CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float)); + } + + SECTION("float") + { + float n = j; + CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float)); + } + + SECTION("double") + { + double n = j; + CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float)); + } + } + + SECTION("more involved conversions") + { + SECTION("object-like STL containers") + { + json j1 = {{"one", 1}, {"two", 2}, {"three", 3}}; + json j2 = {{"one", 1u}, {"two", 2u}, {"three", 3u}}; + json j3 = {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}}; + json j4 = {{"one", true}, {"two", false}, {"three", true}}; + json j5 = {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}}; + + SECTION("std::map") + { + auto m1 = j1.get>(); + auto m2 = j2.get>(); + auto m3 = j3.get>(); + auto m4 = j4.get>(); + //auto m5 = j5.get>(); + } + + SECTION("std::unordered_map") + { + auto m1 = j1.get>(); + auto m2 = j2.get>(); + auto m3 = j3.get>(); + auto m4 = j4.get>(); + //auto m5 = j5.get>(); + //CHECK(m5["one"] == "eins"); + } + + SECTION("std::multimap") + { + auto m1 = j1.get>(); + auto m2 = j2.get>(); + auto m3 = j3.get>(); + auto m4 = j4.get>(); + //auto m5 = j5.get>(); + //CHECK(m5["one"] == "eins"); + } + + SECTION("std::unordered_multimap") + { + auto m1 = j1.get>(); + auto m2 = j2.get>(); + auto m3 = j3.get>(); + auto m4 = j4.get>(); + //auto m5 = j5.get>(); + //CHECK(m5["one"] == "eins"); + } + + SECTION("exception in case of a non-object type") + { + CHECK_THROWS_AS((json().get>()), std::logic_error); + CHECK_THROWS_WITH((json().get>()), "type must be object, but is null"); + } + } + + SECTION("array-like STL containers") + { + json j1 = {1, 2, 3, 4}; + json j2 = {1u, 2u, 3u, 4u}; + json j3 = {1.2, 2.3, 3.4, 4.5}; + json j4 = {true, false, true}; + json j5 = {"one", "two", "three"}; + + SECTION("std::list") + { + auto m1 = j1.get>(); + auto m2 = j2.get>(); + auto m3 = j3.get>(); + auto m4 = j4.get>(); + auto m5 = j5.get>(); + } + + //SECTION("std::forward_list") + //{ + // auto m1 = j1.get>(); + // auto m2 = j2.get>(); + // auto m3 = j3.get>(); + // auto m4 = j4.get>(); + // auto m5 = j5.get>(); + //} + + SECTION("std::vector") + { + auto m1 = j1.get>(); + auto m2 = j2.get>(); + auto m3 = j3.get>(); + auto m4 = j4.get>(); + auto m5 = j5.get>(); + } + + SECTION("std::deque") + { + auto m1 = j1.get>(); + auto m2 = j2.get>(); + auto m3 = j2.get>(); + auto m4 = j4.get>(); + auto m5 = j5.get>(); + } + + SECTION("std::set") + { + auto m1 = j1.get>(); + auto m2 = j2.get>(); + auto m3 = j3.get>(); + auto m4 = j4.get>(); + auto m5 = j5.get>(); + } + + SECTION("std::unordered_set") + { + auto m1 = j1.get>(); + auto m2 = j2.get>(); + auto m3 = j3.get>(); + auto m4 = j4.get>(); + auto m5 = j5.get>(); + } + + SECTION("exception in case of a non-object type") + { + CHECK_THROWS_AS((json().get>()), std::logic_error); + CHECK_THROWS_AS((json().get>()), std::logic_error); + CHECK_THROWS_AS((json().get>()), std::logic_error); + CHECK_THROWS_AS((json().get>()), std::logic_error); + + CHECK_THROWS_WITH((json().get>()), "type must be array, but is null"); + CHECK_THROWS_WITH((json().get>()), "type must be array, but is null"); + CHECK_THROWS_WITH((json().get>()), "type must be array, but is null"); + CHECK_THROWS_WITH((json().get>()), "type must be array, but is null"); + } + } + } +} diff --git a/test/src/unit-deserialization.cpp b/test/src/unit-deserialization.cpp new file mode 100644 index 00000000..78188574 --- /dev/null +++ b/test/src/unit-deserialization.cpp @@ -0,0 +1,73 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("deserialization") +{ + SECTION("stream") + { + std::stringstream ss; + ss << "[\"foo\",1,2,3,false,{\"one\":1}]"; + json j = json::parse(ss); + CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); + } + + SECTION("string") + { + auto s = "[\"foo\",1,2,3,false,{\"one\":1}]"; + json j = json::parse(s); + CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); + } + + SECTION("operator<<") + { + std::stringstream ss; + ss << "[\"foo\",1,2,3,false,{\"one\":1}]"; + json j; + j << ss; + CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); + } + + SECTION("operator>>") + { + std::stringstream ss; + ss << "[\"foo\",1,2,3,false,{\"one\":1}]"; + json j; + ss >> j; + CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); + } + + SECTION("user-defined string literal") + { + CHECK("[\"foo\",1,2,3,false,{\"one\":1}]"_json == json({"foo", 1, 2, 3, false, {{"one", 1}}})); + } +} diff --git a/test/src/unit-element_access.cpp b/test/src/unit-element_access.cpp new file mode 100644 index 00000000..bd33e1e0 --- /dev/null +++ b/test/src/unit-element_access.cpp @@ -0,0 +1,1871 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("element access") +{ + SECTION("array") + { + json j = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + const json j_const = j; + + SECTION("access specified element with bounds checking") + { + SECTION("access within bounds") + { + CHECK(j.at(0) == json(1)); + CHECK(j.at(1) == json(1u)); + CHECK(j.at(2) == json(true)); + CHECK(j.at(3) == json(nullptr)); + CHECK(j.at(4) == json("string")); + CHECK(j.at(5) == json(42.23)); + CHECK(j.at(6) == json(json::object())); + CHECK(j.at(7) == json({1, 2, 3})); + + CHECK(j_const.at(0) == json(1)); + CHECK(j_const.at(1) == json(1u)); + CHECK(j_const.at(2) == json(true)); + CHECK(j_const.at(3) == json(nullptr)); + CHECK(j_const.at(4) == json("string")); + CHECK(j_const.at(5) == json(42.23)); + CHECK(j_const.at(6) == json(json::object())); + CHECK(j_const.at(7) == json({1, 2, 3})); + } + + SECTION("access outside bounds") + { + CHECK_THROWS_AS(j.at(8), std::out_of_range); + CHECK_THROWS_AS(j_const.at(8), std::out_of_range); + + CHECK_THROWS_WITH(j.at(8), "array index 8 is out of range"); + CHECK_THROWS_WITH(j_const.at(8), "array index 8 is out of range"); + } + + SECTION("access on non-array type") + { + SECTION("null") + { + json j_nonarray(json::value_t::null); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); + CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); + + CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with null"); + CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with null"); + } + + SECTION("boolean") + { + json j_nonarray(json::value_t::boolean); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); + CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); + + CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with boolean"); + CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with boolean"); + } + + SECTION("string") + { + json j_nonarray(json::value_t::string); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); + CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); + + CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with string"); + CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with string"); + } + + SECTION("object") + { + json j_nonarray(json::value_t::object); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); + CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); + + CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with object"); + CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with object"); + } + + SECTION("number (integer)") + { + json j_nonarray(json::value_t::number_integer); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); + CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); + + CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with number"); + CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with number"); + } + + SECTION("number (unsigned)") + { + json j_nonarray(json::value_t::number_unsigned); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); + CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); + + CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with number"); + CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with number"); + } + + SECTION("number (floating-point)") + { + json j_nonarray(json::value_t::number_float); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); + CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); + + CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with number"); + CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with number"); + } + } + } + + SECTION("front and back") + { + CHECK(j.front() == json(1)); + CHECK(j_const.front() == json(1)); + CHECK(j.back() == json({1, 2, 3})); + CHECK(j_const.back() == json({1, 2, 3})); + } + + SECTION("access specified element") + { + SECTION("access within bounds") + { + CHECK(j[0] == json(1)); + CHECK(j[1] == json(1u)); + CHECK(j[2] == json(true)); + CHECK(j[3] == json(nullptr)); + CHECK(j[4] == json("string")); + CHECK(j[5] == json(42.23)); + CHECK(j[6] == json(json::object())); + CHECK(j[7] == json({1, 2, 3})); + + CHECK(j_const[0] == json(1)); + CHECK(j_const[1] == json(1u)); + CHECK(j_const[2] == json(true)); + CHECK(j_const[3] == json(nullptr)); + CHECK(j_const[4] == json("string")); + CHECK(j_const[5] == json(42.23)); + CHECK(j_const[6] == json(json::object())); + CHECK(j_const[7] == json({1, 2, 3})); + } + + SECTION("access on non-array type") + { + SECTION("null") + { + SECTION("standard tests") + { + json j_nonarray(json::value_t::null); + const json j_nonarray_const(j_nonarray); + CHECK_NOTHROW(j_nonarray[0]); + CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); + CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with null"); + } + + SECTION("implicit transformation to properly filled array") + { + json j_nonarray; + j_nonarray[3] = 42; + CHECK(j_nonarray == json({nullptr, nullptr, nullptr, 42})); + } + } + + SECTION("boolean") + { + json j_nonarray(json::value_t::boolean); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray[0], std::domain_error); + CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); + CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with boolean"); + CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with boolean"); + } + + SECTION("string") + { + json j_nonarray(json::value_t::string); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray[0], std::domain_error); + CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); + CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with string"); + CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with string"); + } + + SECTION("object") + { + json j_nonarray(json::value_t::object); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray[0], std::domain_error); + CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); + CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with object"); + CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with object"); + } + + SECTION("number (integer)") + { + json j_nonarray(json::value_t::number_integer); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray[0], std::domain_error); + CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); + CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with number"); + CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with number"); + } + + SECTION("number (unsigned)") + { + json j_nonarray(json::value_t::number_unsigned); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray[0], std::domain_error); + CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); + CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with number"); + CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with number"); + } + + SECTION("number (floating-point)") + { + json j_nonarray(json::value_t::number_float); + const json j_nonarray_const(j_nonarray); + CHECK_THROWS_AS(j_nonarray[0], std::domain_error); + CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); + CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with number"); + CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with number"); + } + } + } + + SECTION("remove specified element") + { + SECTION("remove element by index") + { + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + jarray.erase(0); + CHECK(jarray == json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + jarray.erase(1); + CHECK(jarray == json({1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + jarray.erase(2); + CHECK(jarray == json({1, 1u, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + jarray.erase(3); + CHECK(jarray == json({1, 1u, true, "string", 42.23, json::object(), {1, 2, 3}})); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + jarray.erase(4); + CHECK(jarray == json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}})); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + jarray.erase(5); + CHECK(jarray == json({1, 1u, true, nullptr, "string", json::object(), {1, 2, 3}})); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + jarray.erase(6); + CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, {1, 2, 3}})); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + jarray.erase(7); + CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, json::object()})); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + CHECK_THROWS_AS(jarray.erase(8), std::out_of_range); + CHECK_THROWS_WITH(jarray.erase(8), "array index 8 is out of range"); + } + } + + SECTION("remove element by iterator") + { + SECTION("erase(begin())") + { + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json::iterator it2 = jarray.erase(jarray.begin()); + CHECK(jarray == json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); + CHECK(*it2 == json(1u)); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json::const_iterator it2 = jarray.erase(jarray.cbegin()); + CHECK(jarray == json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); + CHECK(*it2 == json(1u)); + } + } + + SECTION("erase(begin(), end())") + { + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json::iterator it2 = jarray.erase(jarray.begin(), jarray.end()); + CHECK(jarray == json::array()); + CHECK(it2 == jarray.end()); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json::const_iterator it2 = jarray.erase(jarray.cbegin(), jarray.cend()); + CHECK(jarray == json::array()); + CHECK(it2 == jarray.cend()); + } + } + + SECTION("erase(begin(), begin())") + { + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json::iterator it2 = jarray.erase(jarray.begin(), jarray.begin()); + CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); + CHECK(*it2 == json(1)); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json::const_iterator it2 = jarray.erase(jarray.cbegin(), jarray.cbegin()); + CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); + CHECK(*it2 == json(1)); + } + } + + SECTION("erase at offset") + { + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json::iterator it = jarray.begin() + 4; + json::iterator it2 = jarray.erase(it); + CHECK(jarray == json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}})); + CHECK(*it2 == json(42.23)); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json::const_iterator it = jarray.cbegin() + 4; + json::const_iterator it2 = jarray.erase(it); + CHECK(jarray == json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}})); + CHECK(*it2 == json(42.23)); + } + } + + SECTION("erase subrange") + { + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json::iterator it2 = jarray.erase(jarray.begin() + 3, jarray.begin() + 6); + CHECK(jarray == json({1, 1u, true, json::object(), {1, 2, 3}})); + CHECK(*it2 == json::object()); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json::const_iterator it2 = jarray.erase(jarray.cbegin() + 3, jarray.cbegin() + 6); + CHECK(jarray == json({1, 1u, true, json::object(), {1, 2, 3}})); + CHECK(*it2 == json::object()); + } + } + + SECTION("different arrays") + { + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json jarray2 = {"foo", "bar"}; + CHECK_THROWS_AS(jarray.erase(jarray2.begin()), std::domain_error); + CHECK_THROWS_AS(jarray.erase(jarray.begin(), jarray2.end()), std::domain_error); + CHECK_THROWS_AS(jarray.erase(jarray2.begin(), jarray.end()), std::domain_error); + CHECK_THROWS_AS(jarray.erase(jarray2.begin(), jarray2.end()), std::domain_error); + + CHECK_THROWS_WITH(jarray.erase(jarray2.begin()), "iterator does not fit current value"); + CHECK_THROWS_WITH(jarray.erase(jarray.begin(), jarray2.end()), + "iterators do not fit current value"); + CHECK_THROWS_WITH(jarray.erase(jarray2.begin(), jarray.end()), + "iterators do not fit current value"); + CHECK_THROWS_WITH(jarray.erase(jarray2.begin(), jarray2.end()), + "iterators do not fit current value"); + } + { + json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; + json jarray2 = {"foo", "bar"}; + CHECK_THROWS_AS(jarray.erase(jarray2.cbegin()), std::domain_error); + CHECK_THROWS_AS(jarray.erase(jarray.cbegin(), jarray2.cend()), std::domain_error); + CHECK_THROWS_AS(jarray.erase(jarray2.cbegin(), jarray.cend()), std::domain_error); + CHECK_THROWS_AS(jarray.erase(jarray2.cbegin(), jarray2.cend()), std::domain_error); + + CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin()), "iterator does not fit current value"); + CHECK_THROWS_WITH(jarray.erase(jarray.cbegin(), jarray2.cend()), + "iterators do not fit current value"); + CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin(), jarray.cend()), + "iterators do not fit current value"); + CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin(), jarray2.cend()), + "iterators do not fit current value"); + } + } + } + + SECTION("remove element by index in non-array type") + { + SECTION("null") + { + json j_nonobject(json::value_t::null); + CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with null"); + } + + SECTION("boolean") + { + json j_nonobject(json::value_t::boolean); + CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with boolean"); + } + + SECTION("string") + { + json j_nonobject(json::value_t::string); + CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with string"); + } + + SECTION("object") + { + json j_nonobject(json::value_t::object); + CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with object"); + } + + SECTION("number (integer)") + { + json j_nonobject(json::value_t::number_integer); + CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with number"); + } + + SECTION("number (unsigned)") + { + json j_nonobject(json::value_t::number_unsigned); + CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with number"); + } + + SECTION("number (floating-point)") + { + json j_nonobject(json::value_t::number_float); + CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with number"); + } + } + } + } + + SECTION("object") + { + json j = {{"integer", 1}, {"unsigned", 1u}, {"floating", 42.23}, {"null", nullptr}, {"string", "hello world"}, {"boolean", true}, {"object", json::object()}, {"array", {1, 2, 3}}}; + const json j_const = j; + + SECTION("access specified element with bounds checking") + { + SECTION("access within bounds") + { + CHECK(j.at("integer") == json(1)); + CHECK(j.at("unsigned") == json(1u)); + CHECK(j.at("boolean") == json(true)); + CHECK(j.at("null") == json(nullptr)); + CHECK(j.at("string") == json("hello world")); + CHECK(j.at("floating") == json(42.23)); + CHECK(j.at("object") == json(json::object())); + CHECK(j.at("array") == json({1, 2, 3})); + + CHECK(j_const.at("integer") == json(1)); + CHECK(j_const.at("unsigned") == json(1u)); + CHECK(j_const.at("boolean") == json(true)); + CHECK(j_const.at("null") == json(nullptr)); + CHECK(j_const.at("string") == json("hello world")); + CHECK(j_const.at("floating") == json(42.23)); + CHECK(j_const.at("object") == json(json::object())); + CHECK(j_const.at("array") == json({1, 2, 3})); + } + + SECTION("access outside bounds") + { + CHECK_THROWS_AS(j.at("foo"), std::out_of_range); + CHECK_THROWS_AS(j_const.at("foo"), std::out_of_range); + CHECK_THROWS_WITH(j.at("foo"), "key 'foo' not found"); + CHECK_THROWS_WITH(j_const.at("foo"), "key 'foo' not found"); + } + + SECTION("access on non-object type") + { + SECTION("null") + { + json j_nonobject(json::value_t::null); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with null"); + CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with null"); + } + + SECTION("boolean") + { + json j_nonobject(json::value_t::boolean); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with boolean"); + CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with boolean"); + } + + SECTION("string") + { + json j_nonobject(json::value_t::string); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with string"); + CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with string"); + } + + SECTION("array") + { + json j_nonobject(json::value_t::array); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with array"); + CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with array"); + } + + SECTION("number (integer)") + { + json j_nonobject(json::value_t::number_integer); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with number"); + CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with number"); + } + + SECTION("number (unsigned)") + { + json j_nonobject(json::value_t::number_unsigned); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with number"); + CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with number"); + } + + SECTION("number (floating-point)") + { + json j_nonobject(json::value_t::number_float); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with number"); + CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with number"); + } + } + } + + SECTION("access specified element with default value") + { + SECTION("given a key") + { + SECTION("access existing value") + { + CHECK(j.value("integer", 2) == 1); + CHECK(j.value("integer", 1.0) == Approx(1)); + CHECK(j.value("unsigned", 2) == 1u); + CHECK(j.value("unsigned", 1.0) == Approx(1u)); + CHECK(j.value("null", json(1)) == json()); + CHECK(j.value("boolean", false) == true); + CHECK(j.value("string", "bar") == "hello world"); + CHECK(j.value("string", std::string("bar")) == "hello world"); + CHECK(j.value("floating", 12.34) == Approx(42.23)); + CHECK(j.value("floating", 12) == 42); + CHECK(j.value("object", json({{"foo", "bar"}})) == json(json::object())); + CHECK(j.value("array", json({10, 100})) == json({1, 2, 3})); + + CHECK(j_const.value("integer", 2) == 1); + CHECK(j_const.value("integer", 1.0) == Approx(1)); + CHECK(j_const.value("unsigned", 2) == 1u); + CHECK(j_const.value("unsigned", 1.0) == Approx(1u)); + CHECK(j_const.value("boolean", false) == true); + CHECK(j_const.value("string", "bar") == "hello world"); + CHECK(j_const.value("string", std::string("bar")) == "hello world"); + CHECK(j_const.value("floating", 12.34) == Approx(42.23)); + CHECK(j_const.value("floating", 12) == 42); + CHECK(j_const.value("object", json({{"foo", "bar"}})) == json(json::object())); + CHECK(j_const.value("array", json({10, 100})) == json({1, 2, 3})); + } + + SECTION("access non-existing value") + { + CHECK(j.value("_", 2) == 2); + CHECK(j.value("_", 2u) == 2u); + CHECK(j.value("_", false) == false); + CHECK(j.value("_", "bar") == "bar"); + CHECK(j.value("_", 12.34) == Approx(12.34)); + CHECK(j.value("_", json({{"foo", "bar"}})) == json({{"foo", "bar"}})); + CHECK(j.value("_", json({10, 100})) == json({10, 100})); + + CHECK(j_const.value("_", 2) == 2); + CHECK(j_const.value("_", 2u) == 2u); + CHECK(j_const.value("_", false) == false); + CHECK(j_const.value("_", "bar") == "bar"); + CHECK(j_const.value("_", 12.34) == Approx(12.34)); + CHECK(j_const.value("_", json({{"foo", "bar"}})) == json({{"foo", "bar"}})); + CHECK(j_const.value("_", json({10, 100})) == json({10, 100})); + } + + SECTION("access on non-object type") + { + SECTION("null") + { + json j_nonobject(json::value_t::null); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with null"); + CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with null"); + } + + SECTION("boolean") + { + json j_nonobject(json::value_t::boolean); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with boolean"); + CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with boolean"); + } + + SECTION("string") + { + json j_nonobject(json::value_t::string); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with string"); + CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with string"); + } + + SECTION("array") + { + json j_nonobject(json::value_t::array); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with array"); + CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with array"); + } + + SECTION("number (integer)") + { + json j_nonobject(json::value_t::number_integer); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number"); + CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number"); + } + + SECTION("number (unsigned)") + { + json j_nonobject(json::value_t::number_unsigned); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number"); + CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number"); + } + + SECTION("number (floating-point)") + { + json j_nonobject(json::value_t::number_float); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number"); + CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number"); + } + } + } + + SECTION("given a JSON pointer") + { + SECTION("access existing value") + { + CHECK(j.value("/integer"_json_pointer, 2) == 1); + CHECK(j.value("/integer"_json_pointer, 1.0) == Approx(1)); + CHECK(j.value("/unsigned"_json_pointer, 2) == 1u); + CHECK(j.value("/unsigned"_json_pointer, 1.0) == Approx(1u)); + CHECK(j.value("/null"_json_pointer, json(1)) == json()); + CHECK(j.value("/boolean"_json_pointer, false) == true); + CHECK(j.value("/string"_json_pointer, "bar") == "hello world"); + CHECK(j.value("/string"_json_pointer, std::string("bar")) == "hello world"); + CHECK(j.value("/floating"_json_pointer, 12.34) == Approx(42.23)); + CHECK(j.value("/floating"_json_pointer, 12) == 42); + CHECK(j.value("/object"_json_pointer, json({{"foo", "bar"}})) == json(json::object())); + CHECK(j.value("/array"_json_pointer, json({10, 100})) == json({1, 2, 3})); + + CHECK(j_const.value("/integer"_json_pointer, 2) == 1); + CHECK(j_const.value("/integer"_json_pointer, 1.0) == Approx(1)); + CHECK(j_const.value("/unsigned"_json_pointer, 2) == 1u); + CHECK(j_const.value("/unsigned"_json_pointer, 1.0) == Approx(1u)); + CHECK(j_const.value("/boolean"_json_pointer, false) == true); + CHECK(j_const.value("/string"_json_pointer, "bar") == "hello world"); + CHECK(j_const.value("/string"_json_pointer, std::string("bar")) == "hello world"); + CHECK(j_const.value("/floating"_json_pointer, 12.34) == Approx(42.23)); + CHECK(j_const.value("/floating"_json_pointer, 12) == 42); + CHECK(j_const.value("/object"_json_pointer, json({{"foo", "bar"}})) == json(json::object())); + CHECK(j_const.value("/array"_json_pointer, json({10, 100})) == json({1, 2, 3})); + } + + SECTION("access non-existing value") + { + CHECK(j.value("/not/existing"_json_pointer, 2) == 2); + CHECK(j.value("/not/existing"_json_pointer, 2u) == 2u); + CHECK(j.value("/not/existing"_json_pointer, false) == false); + CHECK(j.value("/not/existing"_json_pointer, "bar") == "bar"); + CHECK(j.value("/not/existing"_json_pointer, 12.34) == Approx(12.34)); + CHECK(j.value("/not/existing"_json_pointer, json({{"foo", "bar"}})) == json({{"foo", "bar"}})); + CHECK(j.value("/not/existing"_json_pointer, json({10, 100})) == json({10, 100})); + + CHECK(j_const.value("/not/existing"_json_pointer, 2) == 2); + CHECK(j_const.value("/not/existing"_json_pointer, 2u) == 2u); + CHECK(j_const.value("/not/existing"_json_pointer, false) == false); + CHECK(j_const.value("/not/existing"_json_pointer, "bar") == "bar"); + CHECK(j_const.value("/not/existing"_json_pointer, 12.34) == Approx(12.34)); + CHECK(j_const.value("/not/existing"_json_pointer, json({{"foo", "bar"}})) == json({{"foo", "bar"}})); + CHECK(j_const.value("/not/existing"_json_pointer, json({10, 100})) == json({10, 100})); + } + + SECTION("access on non-object type") + { + SECTION("null") + { + json j_nonobject(json::value_t::null); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with null"); + CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), "cannot use value() with null"); + } + + SECTION("boolean") + { + json j_nonobject(json::value_t::boolean); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with boolean"); + CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), + "cannot use value() with boolean"); + } + + SECTION("string") + { + json j_nonobject(json::value_t::string); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with string"); + CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), + "cannot use value() with string"); + } + + SECTION("array") + { + json j_nonobject(json::value_t::array); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with array"); + CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), "cannot use value() with array"); + } + + SECTION("number (integer)") + { + json j_nonobject(json::value_t::number_integer); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with number"); + CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), + "cannot use value() with number"); + } + + SECTION("number (unsigned)") + { + json j_nonobject(json::value_t::number_unsigned); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with number"); + CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), + "cannot use value() with number"); + } + + SECTION("number (floating-point)") + { + json j_nonobject(json::value_t::number_float); + const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); + CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with number"); + CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), + "cannot use value() with number"); + } + } + } + } + + SECTION("front and back") + { + // "array" is the smallest key + CHECK(j.front() == json({1, 2, 3})); + CHECK(j_const.front() == json({1, 2, 3})); + // "unsigned" is the largest key + CHECK(j.back() == json(1u)); + CHECK(j_const.back() == json(1u)); + } + + SECTION("access specified element") + { + SECTION("access within bounds") + { + CHECK(j["integer"] == json(1)); + CHECK(j[json::object_t::key_type("integer")] == j["integer"]); + + CHECK(j["unsigned"] == json(1u)); + CHECK(j[json::object_t::key_type("unsigned")] == j["unsigned"]); + + CHECK(j["boolean"] == json(true)); + CHECK(j[json::object_t::key_type("boolean")] == j["boolean"]); + + CHECK(j["null"] == json(nullptr)); + CHECK(j[json::object_t::key_type("null")] == j["null"]); + + CHECK(j["string"] == json("hello world")); + CHECK(j[json::object_t::key_type("string")] == j["string"]); + + CHECK(j["floating"] == json(42.23)); + CHECK(j[json::object_t::key_type("floating")] == j["floating"]); + + CHECK(j["object"] == json(json::object())); + CHECK(j[json::object_t::key_type("object")] == j["object"]); + + CHECK(j["array"] == json({1, 2, 3})); + CHECK(j[json::object_t::key_type("array")] == j["array"]); + + CHECK(j_const["integer"] == json(1)); + CHECK(j_const[json::object_t::key_type("integer")] == j["integer"]); + + CHECK(j_const["boolean"] == json(true)); + CHECK(j_const[json::object_t::key_type("boolean")] == j["boolean"]); + + CHECK(j_const["null"] == json(nullptr)); + CHECK(j_const[json::object_t::key_type("null")] == j["null"]); + + CHECK(j_const["string"] == json("hello world")); + CHECK(j_const[json::object_t::key_type("string")] == j["string"]); + + CHECK(j_const["floating"] == json(42.23)); + CHECK(j_const[json::object_t::key_type("floating")] == j["floating"]); + + CHECK(j_const["object"] == json(json::object())); + CHECK(j_const[json::object_t::key_type("object")] == j["object"]); + + CHECK(j_const["array"] == json({1, 2, 3})); + CHECK(j_const[json::object_t::key_type("array")] == j["array"]); + } + + SECTION("access on non-object type") + { + SECTION("null") + { + json j_nonobject(json::value_t::null); + json j_nonobject2(json::value_t::null); + const json j_const_nonobject(j_nonobject); + CHECK_NOTHROW(j_nonobject["foo"]); + CHECK_NOTHROW(j_nonobject2[json::object_t::key_type("foo")]); + CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); + CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); + CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with null"); + CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], + "cannot use operator[] with null"); + } + + SECTION("boolean") + { + json j_nonobject(json::value_t::boolean); + const json j_const_nonobject(j_nonobject); + CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); + CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); + CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); + CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); + CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with boolean"); + CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], + "cannot use operator[] with boolean"); + CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with boolean"); + CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], + "cannot use operator[] with boolean"); + } + + SECTION("string") + { + json j_nonobject(json::value_t::string); + const json j_const_nonobject(j_nonobject); + CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); + CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); + CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); + CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); + CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with string"); + CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], + "cannot use operator[] with string"); + CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with string"); + CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], + "cannot use operator[] with string"); + } + + SECTION("array") + { + json j_nonobject(json::value_t::array); + const json j_const_nonobject(j_nonobject); + CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); + CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); + CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); + CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); + CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with array"); + CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], "cannot use operator[] with array"); + CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with array"); + CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], + "cannot use operator[] with array"); + } + + SECTION("number (integer)") + { + json j_nonobject(json::value_t::number_integer); + const json j_const_nonobject(j_nonobject); + CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); + CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); + CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); + CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); + CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with number"); + CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], + "cannot use operator[] with number"); + CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with number"); + CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], + "cannot use operator[] with number"); + } + + SECTION("number (unsigned)") + { + json j_nonobject(json::value_t::number_unsigned); + const json j_const_nonobject(j_nonobject); + CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); + CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); + CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); + CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); + CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with number"); + CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], + "cannot use operator[] with number"); + CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with number"); + CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], + "cannot use operator[] with number"); + } + + SECTION("number (floating-point)") + { + json j_nonobject(json::value_t::number_float); + const json j_const_nonobject(j_nonobject); + CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); + CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); + CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); + CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); + CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with number"); + CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], + "cannot use operator[] with number"); + CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with number"); + CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], + "cannot use operator[] with number"); + } + } + } + + SECTION("remove specified element") + { + SECTION("remove element by key") + { + CHECK(j.find("integer") != j.end()); + CHECK(j.erase("integer") == 1); + CHECK(j.find("integer") == j.end()); + CHECK(j.erase("integer") == 0); + + CHECK(j.find("unsigned") != j.end()); + CHECK(j.erase("unsigned") == 1); + CHECK(j.find("unsigned") == j.end()); + CHECK(j.erase("unsigned") == 0); + + CHECK(j.find("boolean") != j.end()); + CHECK(j.erase("boolean") == 1); + CHECK(j.find("boolean") == j.end()); + CHECK(j.erase("boolean") == 0); + + CHECK(j.find("null") != j.end()); + CHECK(j.erase("null") == 1); + CHECK(j.find("null") == j.end()); + CHECK(j.erase("null") == 0); + + CHECK(j.find("string") != j.end()); + CHECK(j.erase("string") == 1); + CHECK(j.find("string") == j.end()); + CHECK(j.erase("string") == 0); + + CHECK(j.find("floating") != j.end()); + CHECK(j.erase("floating") == 1); + CHECK(j.find("floating") == j.end()); + CHECK(j.erase("floating") == 0); + + CHECK(j.find("object") != j.end()); + CHECK(j.erase("object") == 1); + CHECK(j.find("object") == j.end()); + CHECK(j.erase("object") == 0); + + CHECK(j.find("array") != j.end()); + CHECK(j.erase("array") == 1); + CHECK(j.find("array") == j.end()); + CHECK(j.erase("array") == 0); + } + + SECTION("remove element by iterator") + { + SECTION("erase(begin())") + { + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; + json::iterator it2 = jobject.erase(jobject.begin()); + CHECK(jobject == json({{"b", 1}, {"c", 17u}})); + CHECK(*it2 == json(1)); + } + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; + json::const_iterator it2 = jobject.erase(jobject.cbegin()); + CHECK(jobject == json({{"b", 1}, {"c", 17u}})); + CHECK(*it2 == json(1)); + } + } + + SECTION("erase(begin(), end())") + { + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; + json::iterator it2 = jobject.erase(jobject.begin(), jobject.end()); + CHECK(jobject == json::object()); + CHECK(it2 == jobject.end()); + } + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; + json::const_iterator it2 = jobject.erase(jobject.cbegin(), jobject.cend()); + CHECK(jobject == json::object()); + CHECK(it2 == jobject.cend()); + } + } + + SECTION("erase(begin(), begin())") + { + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; + json::iterator it2 = jobject.erase(jobject.begin(), jobject.begin()); + CHECK(jobject == json({{"a", "a"}, {"b", 1}, {"c", 17u}})); + CHECK(*it2 == json("a")); + } + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; + json::const_iterator it2 = jobject.erase(jobject.cbegin(), jobject.cbegin()); + CHECK(jobject == json({{"a", "a"}, {"b", 1}, {"c", 17u}})); + CHECK(*it2 == json("a")); + } + } + + SECTION("erase at offset") + { + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; + json::iterator it = jobject.find("b"); + json::iterator it2 = jobject.erase(it); + CHECK(jobject == json({{"a", "a"}, {"c", 17u}})); + CHECK(*it2 == json(17)); + } + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; + json::const_iterator it = jobject.find("b"); + json::const_iterator it2 = jobject.erase(it); + CHECK(jobject == json({{"a", "a"}, {"c", 17u}})); + CHECK(*it2 == json(17)); + } + } + + SECTION("erase subrange") + { + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; + json::iterator it2 = jobject.erase(jobject.find("b"), jobject.find("e")); + CHECK(jobject == json({{"a", "a"}, {"e", true}})); + CHECK(*it2 == json(true)); + } + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; + json::const_iterator it2 = jobject.erase(jobject.find("b"), jobject.find("e")); + CHECK(jobject == json({{"a", "a"}, {"e", true}})); + CHECK(*it2 == json(true)); + } + } + + SECTION("different objects") + { + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; + json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}}; + CHECK_THROWS_AS(jobject.erase(jobject2.begin()), std::domain_error); + CHECK_THROWS_AS(jobject.erase(jobject.begin(), jobject2.end()), std::domain_error); + CHECK_THROWS_AS(jobject.erase(jobject2.begin(), jobject.end()), std::domain_error); + CHECK_THROWS_AS(jobject.erase(jobject2.begin(), jobject2.end()), std::domain_error); + CHECK_THROWS_WITH(jobject.erase(jobject2.begin()), "iterator does not fit current value"); + CHECK_THROWS_WITH(jobject.erase(jobject.begin(), jobject2.end()), + "iterators do not fit current value"); + CHECK_THROWS_WITH(jobject.erase(jobject2.begin(), jobject.end()), + "iterators do not fit current value"); + CHECK_THROWS_WITH(jobject.erase(jobject2.begin(), jobject2.end()), + "iterators do not fit current value"); + } + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; + json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}}; + CHECK_THROWS_AS(jobject.erase(jobject2.cbegin()), std::domain_error); + CHECK_THROWS_AS(jobject.erase(jobject.cbegin(), jobject2.cend()), std::domain_error); + CHECK_THROWS_AS(jobject.erase(jobject2.cbegin(), jobject.cend()), std::domain_error); + CHECK_THROWS_AS(jobject.erase(jobject2.cbegin(), jobject2.cend()), std::domain_error); + CHECK_THROWS_WITH(jobject.erase(jobject2.cbegin()), "iterator does not fit current value"); + CHECK_THROWS_WITH(jobject.erase(jobject.cbegin(), jobject2.cend()), + "iterators do not fit current value"); + CHECK_THROWS_WITH(jobject.erase(jobject2.cbegin(), jobject.cend()), + "iterators do not fit current value"); + CHECK_THROWS_WITH(jobject.erase(jobject2.cbegin(), jobject2.cend()), + "iterators do not fit current value"); + } + } + } + + SECTION("remove element by key in non-object type") + { + SECTION("null") + { + json j_nonobject(json::value_t::null); + CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with null"); + } + + SECTION("boolean") + { + json j_nonobject(json::value_t::boolean); + CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with boolean"); + } + + SECTION("string") + { + json j_nonobject(json::value_t::string); + CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with string"); + } + + SECTION("array") + { + json j_nonobject(json::value_t::array); + CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with array"); + } + + SECTION("number (integer)") + { + json j_nonobject(json::value_t::number_integer); + CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with number"); + } + + SECTION("number (floating-point)") + { + json j_nonobject(json::value_t::number_float); + CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); + CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with number"); + } + } + } + + SECTION("find an element in an object") + { + SECTION("existing element") + { + for (auto key : + {"integer", "unsigned", "floating", "null", "string", "boolean", "object", "array" + }) + { + CHECK(j.find(key) != j.end()); + CHECK(*j.find(key) == j.at(key)); + CHECK(j_const.find(key) != j_const.end()); + CHECK(*j_const.find(key) == j_const.at(key)); + } + } + + SECTION("nonexisting element") + { + CHECK(j.find("foo") == j.end()); + CHECK(j_const.find("foo") == j_const.end()); + } + + SECTION("all types") + { + SECTION("null") + { + json j_nonarray(json::value_t::null); + const json j_nonarray_const(j_nonarray); + CHECK(j_nonarray.find("foo") == j_nonarray.end()); + CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); + } + + SECTION("string") + { + json j_nonarray(json::value_t::string); + const json j_nonarray_const(j_nonarray); + CHECK(j_nonarray.find("foo") == j_nonarray.end()); + CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); + } + + SECTION("object") + { + json j_nonarray(json::value_t::object); + const json j_nonarray_const(j_nonarray); + CHECK(j_nonarray.find("foo") == j_nonarray.end()); + CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); + } + + SECTION("array") + { + json j_nonarray(json::value_t::array); + const json j_nonarray_const(j_nonarray); + CHECK(j_nonarray.find("foo") == j_nonarray.end()); + CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); + } + + SECTION("boolean") + { + json j_nonarray(json::value_t::boolean); + const json j_nonarray_const(j_nonarray); + CHECK(j_nonarray.find("foo") == j_nonarray.end()); + CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); + } + + SECTION("number (integer)") + { + json j_nonarray(json::value_t::number_integer); + const json j_nonarray_const(j_nonarray); + CHECK(j_nonarray.find("foo") == j_nonarray.end()); + CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); + } + + SECTION("number (unsigned)") + { + json j_nonarray(json::value_t::number_unsigned); + const json j_nonarray_const(j_nonarray); + CHECK(j_nonarray.find("foo") == j_nonarray.end()); + CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); + } + + SECTION("number (floating-point)") + { + json j_nonarray(json::value_t::number_float); + const json j_nonarray_const(j_nonarray); + CHECK(j_nonarray.find("foo") == j_nonarray.end()); + CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); + } + } + } + + SECTION("count keys in an object") + { + SECTION("existing element") + { + for (auto key : + {"integer", "unsigned", "floating", "null", "string", "boolean", "object", "array" + }) + { + CHECK(j.count(key) == 1); + CHECK(j_const.count(key) == 1); + } + } + + SECTION("nonexisting element") + { + CHECK(j.count("foo") == 0); + CHECK(j_const.count("foo") == 0); + } + + SECTION("all types") + { + SECTION("null") + { + json j_nonobject(json::value_t::null); + const json j_nonobject_const(j_nonobject); + CHECK(j_nonobject.count("foo") == 0); + CHECK(j_nonobject_const.count("foo") == 0); + } + + SECTION("string") + { + json j_nonobject(json::value_t::string); + const json j_nonobject_const(j_nonobject); + CHECK(j_nonobject.count("foo") == 0); + CHECK(j_nonobject_const.count("foo") == 0); + } + + SECTION("object") + { + json j_nonobject(json::value_t::object); + const json j_nonobject_const(j_nonobject); + CHECK(j_nonobject.count("foo") == 0); + CHECK(j_nonobject_const.count("foo") == 0); + } + + SECTION("array") + { + json j_nonobject(json::value_t::array); + const json j_nonobject_const(j_nonobject); + CHECK(j_nonobject.count("foo") == 0); + CHECK(j_nonobject_const.count("foo") == 0); + } + + SECTION("boolean") + { + json j_nonobject(json::value_t::boolean); + const json j_nonobject_const(j_nonobject); + CHECK(j_nonobject.count("foo") == 0); + CHECK(j_nonobject_const.count("foo") == 0); + } + + SECTION("number (integer)") + { + json j_nonobject(json::value_t::number_integer); + const json j_nonobject_const(j_nonobject); + CHECK(j_nonobject.count("foo") == 0); + CHECK(j_nonobject_const.count("foo") == 0); + } + + SECTION("number (unsigned)") + { + json j_nonobject(json::value_t::number_unsigned); + const json j_nonobject_const(j_nonobject); + CHECK(j_nonobject.count("foo") == 0); + CHECK(j_nonobject_const.count("foo") == 0); + } + + SECTION("number (floating-point)") + { + json j_nonobject(json::value_t::number_float); + const json j_nonobject_const(j_nonobject); + CHECK(j_nonobject.count("foo") == 0); + CHECK(j_nonobject_const.count("foo") == 0); + } + } + } + } + + SECTION("other values") + { + SECTION("front and back") + { + SECTION("null") + { + { + json j; + CHECK_THROWS_AS(j.front(), std::out_of_range); + CHECK_THROWS_AS(j.back(), std::out_of_range); + CHECK_THROWS_WITH(j.front(), "cannot get value"); + CHECK_THROWS_WITH(j.back(), "cannot get value"); + } + { + const json j{}; + CHECK_THROWS_AS(j.front(), std::out_of_range); + CHECK_THROWS_AS(j.back(), std::out_of_range); + CHECK_THROWS_WITH(j.front(), "cannot get value"); + CHECK_THROWS_WITH(j.back(), "cannot get value"); + } + } + + SECTION("string") + { + { + json j = "foo"; + CHECK(j.front() == j); + CHECK(j.back() == j); + } + { + const json j = "bar"; + CHECK(j.front() == j); + CHECK(j.back() == j); + } + } + + SECTION("number (boolean)") + { + { + json j = false; + CHECK(j.front() == j); + CHECK(j.back() == j); + } + { + const json j = true; + CHECK(j.front() == j); + CHECK(j.back() == j); + } + } + + SECTION("number (integer)") + { + { + json j = 17; + CHECK(j.front() == j); + CHECK(j.back() == j); + } + { + const json j = 17; + CHECK(j.front() == j); + CHECK(j.back() == j); + } + } + + SECTION("number (unsigned)") + { + { + json j = 17u; + CHECK(j.front() == j); + CHECK(j.back() == j); + } + { + const json j = 17u; + CHECK(j.front() == j); + CHECK(j.back() == j); + } + } + + SECTION("number (floating point)") + { + { + json j = 23.42; + CHECK(j.front() == j); + CHECK(j.back() == j); + } + { + const json j = 23.42; + CHECK(j.front() == j); + CHECK(j.back() == j); + } + } + } + + SECTION("erase with one valid iterator") + { + SECTION("null") + { + { + json j; + CHECK_THROWS_AS(j.erase(j.begin()), std::domain_error); + CHECK_THROWS_WITH(j.erase(j.begin()), "cannot use erase() with null"); + } + { + json j; + CHECK_THROWS_AS(j.erase(j.cbegin()), std::domain_error); + CHECK_THROWS_WITH(j.erase(j.begin()), "cannot use erase() with null"); + } + } + + SECTION("string") + { + { + json j = "foo"; + json::iterator it = j.erase(j.begin()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + { + json j = "bar"; + json::const_iterator it = j.erase(j.cbegin()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + } + + SECTION("number (boolean)") + { + { + json j = false; + json::iterator it = j.erase(j.begin()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + { + json j = true; + json::const_iterator it = j.erase(j.cbegin()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + } + + SECTION("number (integer)") + { + { + json j = 17; + json::iterator it = j.erase(j.begin()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + { + json j = 17; + json::const_iterator it = j.erase(j.cbegin()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + } + + SECTION("number (unsigned)") + { + { + json j = 17u; + json::iterator it = j.erase(j.begin()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + { + json j = 17u; + json::const_iterator it = j.erase(j.cbegin()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + } + + SECTION("number (floating point)") + { + { + json j = 23.42; + json::iterator it = j.erase(j.begin()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + { + json j = 23.42; + json::const_iterator it = j.erase(j.cbegin()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + } + } + + SECTION("erase with one invalid iterator") + { + SECTION("string") + { + { + json j = "foo"; + CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); + } + { + json j = "bar"; + CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); + } + } + + SECTION("number (boolean)") + { + { + json j = false; + CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); + } + { + json j = true; + CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); + } + } + + SECTION("number (integer)") + { + { + json j = 17; + CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); + } + { + json j = 17; + CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); + } + } + + SECTION("number (unsigned)") + { + { + json j = 17u; + CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); + } + { + json j = 17u; + CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); + } + } + + SECTION("number (floating point)") + { + { + json j = 23.42; + CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); + } + { + json j = 23.42; + CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); + } + } + } + + SECTION("erase with two valid iterators") + { + SECTION("null") + { + { + json j; + CHECK_THROWS_AS(j.erase(j.begin(), j.end()), std::domain_error); + CHECK_THROWS_WITH(j.erase(j.begin(), j.end()), "cannot use erase() with null"); + } + { + json j; + CHECK_THROWS_AS(j.erase(j.cbegin(), j.cend()), std::domain_error); + CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cend()), "cannot use erase() with null"); + } + } + + SECTION("string") + { + { + json j = "foo"; + json::iterator it = j.erase(j.begin(), j.end()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + { + json j = "bar"; + json::const_iterator it = j.erase(j.cbegin(), j.cend()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + } + + SECTION("number (boolean)") + { + { + json j = false; + json::iterator it = j.erase(j.begin(), j.end()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + { + json j = true; + json::const_iterator it = j.erase(j.cbegin(), j.cend()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + } + + SECTION("number (integer)") + { + { + json j = 17; + json::iterator it = j.erase(j.begin(), j.end()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + { + json j = 17; + json::const_iterator it = j.erase(j.cbegin(), j.cend()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + } + + SECTION("number (unsigned)") + { + { + json j = 17u; + json::iterator it = j.erase(j.begin(), j.end()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + { + json j = 17u; + json::const_iterator it = j.erase(j.cbegin(), j.cend()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + } + + SECTION("number (floating point)") + { + { + json j = 23.42; + json::iterator it = j.erase(j.begin(), j.end()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + { + json j = 23.42; + json::const_iterator it = j.erase(j.cbegin(), j.cend()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + } + } + + SECTION("erase with two invalid iterators") + { + SECTION("string") + { + { + json j = "foo"; + CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); + CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); + } + { + json j = "bar"; + CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); + CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); + } + } + + SECTION("number (boolean)") + { + { + json j = false; + CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); + CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); + } + { + json j = true; + CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); + CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); + } + } + + SECTION("number (integer)") + { + { + json j = 17; + CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); + CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); + } + { + json j = 17; + CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); + CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); + } + } + + SECTION("number (unsigned)") + { + { + json j = 17u; + CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); + CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); + } + { + json j = 17u; + CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); + CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); + } + } + + SECTION("number (floating point)") + { + { + json j = 23.42; + CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); + CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); + } + { + json j = 23.42; + CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); + CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); + CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); + CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); + } + } + } + } +} diff --git a/test/src/unit-inspection.cpp b/test/src/unit-inspection.cpp new file mode 100644 index 00000000..25fcb535 --- /dev/null +++ b/test/src/unit-inspection.cpp @@ -0,0 +1,373 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("object inspection") +{ + SECTION("convenience type checker") + { + SECTION("object") + { + json j {{"foo", 1}, {"bar", false}}; + CHECK(not j.is_null()); + CHECK(not j.is_boolean()); + CHECK(not j.is_number()); + CHECK(not j.is_number_integer()); + CHECK(not j.is_number_unsigned()); + CHECK(not j.is_number_float()); + CHECK(j.is_object()); + CHECK(not j.is_array()); + CHECK(not j.is_string()); + CHECK(not j.is_discarded()); + CHECK(not j.is_primitive()); + CHECK(j.is_structured()); + } + + SECTION("array") + { + json j {"foo", 1, 1u, 42.23, false}; + CHECK(not j.is_null()); + CHECK(not j.is_boolean()); + CHECK(not j.is_number()); + CHECK(not j.is_number_integer()); + CHECK(not j.is_number_unsigned()); + CHECK(not j.is_number_float()); + CHECK(not j.is_object()); + CHECK(j.is_array()); + CHECK(not j.is_string()); + CHECK(not j.is_discarded()); + CHECK(not j.is_primitive()); + CHECK(j.is_structured()); + } + + SECTION("null") + { + json j(nullptr); + CHECK(j.is_null()); + CHECK(not j.is_boolean()); + CHECK(not j.is_number()); + CHECK(not j.is_number_integer()); + CHECK(not j.is_number_unsigned()); + CHECK(not j.is_number_float()); + CHECK(not j.is_object()); + CHECK(not j.is_array()); + CHECK(not j.is_string()); + CHECK(not j.is_discarded()); + CHECK(j.is_primitive()); + CHECK(not j.is_structured()); + } + + SECTION("boolean") + { + json j(true); + CHECK(not j.is_null()); + CHECK(j.is_boolean()); + CHECK(not j.is_number()); + CHECK(not j.is_number_integer()); + CHECK(not j.is_number_unsigned()); + CHECK(not j.is_number_float()); + CHECK(not j.is_object()); + CHECK(not j.is_array()); + CHECK(not j.is_string()); + CHECK(not j.is_discarded()); + CHECK(j.is_primitive()); + CHECK(not j.is_structured()); + } + + SECTION("string") + { + json j("Hello world"); + CHECK(not j.is_null()); + CHECK(not j.is_boolean()); + CHECK(not j.is_number()); + CHECK(not j.is_number_integer()); + CHECK(not j.is_number_unsigned()); + CHECK(not j.is_number_float()); + CHECK(not j.is_object()); + CHECK(not j.is_array()); + CHECK(j.is_string()); + CHECK(not j.is_discarded()); + CHECK(j.is_primitive()); + CHECK(not j.is_structured()); + } + + SECTION("number (integer)") + { + json j(42); + CHECK(not j.is_null()); + CHECK(not j.is_boolean()); + CHECK(j.is_number()); + CHECK(j.is_number_integer()); + CHECK(not j.is_number_unsigned()); + CHECK(not j.is_number_float()); + CHECK(not j.is_object()); + CHECK(not j.is_array()); + CHECK(not j.is_string()); + CHECK(not j.is_discarded()); + CHECK(j.is_primitive()); + CHECK(not j.is_structured()); + } + + SECTION("number (unsigned)") + { + json j(42u); + CHECK(not j.is_null()); + CHECK(not j.is_boolean()); + CHECK(j.is_number()); + CHECK(j.is_number_integer()); + CHECK(j.is_number_unsigned()); + CHECK(not j.is_number_float()); + CHECK(not j.is_object()); + CHECK(not j.is_array()); + CHECK(not j.is_string()); + CHECK(not j.is_discarded()); + CHECK(j.is_primitive()); + CHECK(not j.is_structured()); + } + + SECTION("number (floating-point)") + { + json j(42.23); + CHECK(not j.is_null()); + CHECK(not j.is_boolean()); + CHECK(j.is_number()); + CHECK(not j.is_number_integer()); + CHECK(not j.is_number_unsigned()); + CHECK(j.is_number_float()); + CHECK(not j.is_object()); + CHECK(not j.is_array()); + CHECK(not j.is_string()); + CHECK(not j.is_discarded()); + CHECK(j.is_primitive()); + CHECK(not j.is_structured()); + } + + SECTION("discarded") + { + json j(json::value_t::discarded); + CHECK(not j.is_null()); + CHECK(not j.is_boolean()); + CHECK(not j.is_number()); + CHECK(not j.is_number_integer()); + CHECK(not j.is_number_unsigned()); + CHECK(not j.is_number_float()); + CHECK(not j.is_object()); + CHECK(not j.is_array()); + CHECK(not j.is_string()); + CHECK(j.is_discarded()); + CHECK(not j.is_primitive()); + CHECK(not j.is_structured()); + } + } + + SECTION("serialization") + { + json j {{"object", json::object()}, {"array", {1, 2, 3, 4}}, {"number", 42}, {"boolean", false}, {"null", nullptr}, {"string", "Hello world"} }; + + SECTION("no indent / indent=-1") + { + CHECK(j.dump() == + "{\"array\":[1,2,3,4],\"boolean\":false,\"null\":null,\"number\":42,\"object\":{},\"string\":\"Hello world\"}"); + + CHECK(j.dump() == j.dump(-1)); + } + + SECTION("indent=0") + { + CHECK(j.dump(0) == + "{\n\"array\": [\n1,\n2,\n3,\n4\n],\n\"boolean\": false,\n\"null\": null,\n\"number\": 42,\n\"object\": {},\n\"string\": \"Hello world\"\n}"); + } + + SECTION("indent=4") + { + CHECK(j.dump(4) == + "{\n \"array\": [\n 1,\n 2,\n 3,\n 4\n ],\n \"boolean\": false,\n \"null\": null,\n \"number\": 42,\n \"object\": {},\n \"string\": \"Hello world\"\n}"); + } + + SECTION("dump and floating-point numbers") + { + auto s = json(42.23).dump(); + CHECK(s.find("42.23") != std::string::npos); + } + + SECTION("dump and small floating-point numbers") + { + auto s = json(1.23456e-78).dump(); + CHECK(s.find("1.23456e-78") != std::string::npos); + } + + SECTION("dump and non-ASCII characters") + { + CHECK(json("ä").dump() == "\"ä\""); + CHECK(json("Ö").dump() == "\"Ö\""); + CHECK(json("❤️").dump() == "\"❤️\""); + } + + SECTION("serialization of discarded element") + { + json j_discarded(json::value_t::discarded); + CHECK(j_discarded.dump() == ""); + } + + SECTION("check that precision is reset after serialization") + { + // create stringstream and set precision + std::stringstream ss; + ss.precision(3); + ss << 3.141592653589793 << std::fixed; + CHECK(ss.str() == "3.14"); + + // reset stringstream + ss.str(std::string()); + + // use stringstream for JSON serialization + json j_number = 3.141592653589793; + ss << j_number; + + // check that precision has been overridden during serialization + CHECK(ss.str() == "3.141592653589793"); + + // check that precision has been restored + CHECK(ss.precision() == 3); + } + } + + SECTION("return the type of the object (explicit)") + { + SECTION("null") + { + json j = nullptr; + CHECK(j.type() == json::value_t::null); + } + + SECTION("object") + { + json j = {{"foo", "bar"}}; + CHECK(j.type() == json::value_t::object); + } + + SECTION("array") + { + json j = {1, 2, 3, 4}; + CHECK(j.type() == json::value_t::array); + } + + SECTION("boolean") + { + json j = true; + CHECK(j.type() == json::value_t::boolean); + } + + SECTION("string") + { + json j = "Hello world"; + CHECK(j.type() == json::value_t::string); + } + + SECTION("number (integer)") + { + json j = 23; + CHECK(j.type() == json::value_t::number_integer); + } + + SECTION("number (unsigned)") + { + json j = 23u; + CHECK(j.type() == json::value_t::number_unsigned); + } + + SECTION("number (floating-point)") + { + json j = 42.23; + CHECK(j.type() == json::value_t::number_float); + } + } + + SECTION("return the type of the object (implicit)") + { + SECTION("null") + { + json j = nullptr; + json::value_t t = j; + CHECK(t == j.type()); + } + + SECTION("object") + { + json j = {{"foo", "bar"}}; + json::value_t t = j; + CHECK(t == j.type()); + } + + SECTION("array") + { + json j = {1, 2, 3, 4}; + json::value_t t = j; + CHECK(t == j.type()); + } + + SECTION("boolean") + { + json j = true; + json::value_t t = j; + CHECK(t == j.type()); + } + + SECTION("string") + { + json j = "Hello world"; + json::value_t t = j; + CHECK(t == j.type()); + } + + SECTION("number (integer)") + { + json j = 23; + json::value_t t = j; + CHECK(t == j.type()); + } + + SECTION("number (unsigned)") + { + json j = 23u; + json::value_t t = j; + CHECK(t == j.type()); + } + + SECTION("number (floating-point)") + { + json j = 42.23; + json::value_t t = j; + CHECK(t == j.type()); + } + } +} diff --git a/test/src/unit-iterator_wrapper.cpp b/test/src/unit-iterator_wrapper.cpp new file mode 100644 index 00000000..5d676414 --- /dev/null +++ b/test/src/unit-iterator_wrapper.cpp @@ -0,0 +1,729 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("iterator_wrapper") +{ + SECTION("object") + { + SECTION("value") + { + json j = {{"A", 1}, {"B", 2}}; + int counter = 1; + + for (auto i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "A"); + CHECK(i.value() == json(1)); + break; + } + + case 2: + { + CHECK(i.key() == "B"); + CHECK(i.value() == json(2)); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("reference") + { + json j = {{"A", 1}, {"B", 2}}; + int counter = 1; + + for (auto& i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "A"); + CHECK(i.value() == json(1)); + + // change the value + i.value() = json(11); + CHECK(i.value() == json(11)); + break; + } + + case 2: + { + CHECK(i.key() == "B"); + CHECK(i.value() == json(2)); + + // change the value + i.value() = json(22); + CHECK(i.value() == json(22)); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + + // check if values where changed + CHECK(j == json({{"A", 11}, {"B", 22}})); + } + + SECTION("const value") + { + json j = {{"A", 1}, {"B", 2}}; + int counter = 1; + + for (const auto i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "A"); + CHECK(i.value() == json(1)); + break; + } + + case 2: + { + CHECK(i.key() == "B"); + CHECK(i.value() == json(2)); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("const reference") + { + json j = {{"A", 1}, {"B", 2}}; + int counter = 1; + + for (const auto& i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "A"); + CHECK(i.value() == json(1)); + break; + } + + case 2: + { + CHECK(i.key() == "B"); + CHECK(i.value() == json(2)); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + } + + SECTION("const object") + { + SECTION("value") + { + const json j = {{"A", 1}, {"B", 2}}; + int counter = 1; + + for (auto i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "A"); + CHECK(i.value() == json(1)); + break; + } + + case 2: + { + CHECK(i.key() == "B"); + CHECK(i.value() == json(2)); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("reference") + { + const json j = {{"A", 1}, {"B", 2}}; + int counter = 1; + + for (auto& i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "A"); + CHECK(i.value() == json(1)); + break; + } + + case 2: + { + CHECK(i.key() == "B"); + CHECK(i.value() == json(2)); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("const value") + { + const json j = {{"A", 1}, {"B", 2}}; + int counter = 1; + + for (const auto i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "A"); + CHECK(i.value() == json(1)); + break; + } + + case 2: + { + CHECK(i.key() == "B"); + CHECK(i.value() == json(2)); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("const reference") + { + const json j = {{"A", 1}, {"B", 2}}; + int counter = 1; + + for (const auto& i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "A"); + CHECK(i.value() == json(1)); + break; + } + + case 2: + { + CHECK(i.key() == "B"); + CHECK(i.value() == json(2)); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + } + + SECTION("array") + { + SECTION("value") + { + json j = {"A", "B"}; + int counter = 1; + + for (auto i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "0"); + CHECK(i.value() == "A"); + break; + } + + case 2: + { + CHECK(i.key() == "1"); + CHECK(i.value() == "B"); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("reference") + { + json j = {"A", "B"}; + int counter = 1; + + for (auto& i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "0"); + CHECK(i.value() == "A"); + + // change the value + i.value() = "AA"; + CHECK(i.value() == "AA"); + break; + } + + case 2: + { + CHECK(i.key() == "1"); + CHECK(i.value() == "B"); + + // change the value + i.value() = "BB"; + CHECK(i.value() == "BB"); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + + // check if values where changed + CHECK(j == json({"AA", "BB"})); + } + + SECTION("const value") + { + json j = {"A", "B"}; + int counter = 1; + + for (const auto i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "0"); + CHECK(i.value() == "A"); + break; + } + + case 2: + { + CHECK(i.key() == "1"); + CHECK(i.value() == "B"); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("const reference") + { + json j = {"A", "B"}; + int counter = 1; + + for (const auto& i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "0"); + CHECK(i.value() == "A"); + break; + } + + case 2: + { + CHECK(i.key() == "1"); + CHECK(i.value() == "B"); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + } + + SECTION("const array") + { + SECTION("value") + { + const json j = {"A", "B"}; + int counter = 1; + + for (auto i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "0"); + CHECK(i.value() == "A"); + break; + } + + case 2: + { + CHECK(i.key() == "1"); + CHECK(i.value() == "B"); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("reference") + { + const json j = {"A", "B"}; + int counter = 1; + + for (auto& i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "0"); + CHECK(i.value() == "A"); + break; + } + + case 2: + { + CHECK(i.key() == "1"); + CHECK(i.value() == "B"); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("const value") + { + const json j = {"A", "B"}; + int counter = 1; + + for (const auto i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "0"); + CHECK(i.value() == "A"); + break; + } + + case 2: + { + CHECK(i.key() == "1"); + CHECK(i.value() == "B"); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("const reference") + { + const json j = {"A", "B"}; + int counter = 1; + + for (const auto& i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "0"); + CHECK(i.value() == "A"); + break; + } + + case 2: + { + CHECK(i.key() == "1"); + CHECK(i.value() == "B"); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + } + + SECTION("primitive") + { + SECTION("value") + { + json j = 1; + int counter = 1; + + for (auto i : json::iterator_wrapper(j)) + { + ++counter; + CHECK(i.key() == ""); + CHECK(i.value() == json(1)); + } + + CHECK(counter == 2); + } + + SECTION("reference") + { + json j = 1; + int counter = 1; + + for (auto& i : json::iterator_wrapper(j)) + { + ++counter; + CHECK(i.key() == ""); + CHECK(i.value() == json(1)); + + // change value + i.value() = json(2); + } + + CHECK(counter == 2); + + // check if value has changed + CHECK(j == json(2)); + } + + SECTION("const value") + { + json j = 1; + int counter = 1; + + for (const auto i : json::iterator_wrapper(j)) + { + ++counter; + CHECK(i.key() == ""); + CHECK(i.value() == json(1)); + } + + CHECK(counter == 2); + } + + SECTION("const reference") + { + json j = 1; + int counter = 1; + + for (const auto& i : json::iterator_wrapper(j)) + { + ++counter; + CHECK(i.key() == ""); + CHECK(i.value() == json(1)); + } + + CHECK(counter == 2); + } + } + + SECTION("const primitive") + { + SECTION("value") + { + const json j = 1; + int counter = 1; + + for (auto i : json::iterator_wrapper(j)) + { + ++counter; + CHECK(i.key() == ""); + CHECK(i.value() == json(1)); + } + + CHECK(counter == 2); + } + + SECTION("reference") + { + const json j = 1; + int counter = 1; + + for (auto& i : json::iterator_wrapper(j)) + { + ++counter; + CHECK(i.key() == ""); + CHECK(i.value() == json(1)); + } + + CHECK(counter == 2); + } + + SECTION("const value") + { + const json j = 1; + int counter = 1; + + for (const auto i : json::iterator_wrapper(j)) + { + ++counter; + CHECK(i.key() == ""); + CHECK(i.value() == json(1)); + } + + CHECK(counter == 2); + } + + SECTION("const reference") + { + const json j = 1; + int counter = 1; + + for (const auto& i : json::iterator_wrapper(j)) + { + ++counter; + CHECK(i.key() == ""); + CHECK(i.value() == json(1)); + } + + CHECK(counter == 2); + } + } +} diff --git a/test/src/unit-iterators.cpp b/test/src/unit-iterators.cpp new file mode 100644 index 00000000..06dcb6aa --- /dev/null +++ b/test/src/unit-iterators.cpp @@ -0,0 +1,2352 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#define private public +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("iterators") +{ + SECTION("basic behavior") + { + SECTION("uninitialized") + { + json::iterator it; + CHECK(it.m_object == nullptr); + + json::const_iterator cit; + CHECK(cit.m_object == nullptr); + } + + SECTION("boolean") + { + json j = true; + json j_const(j); + + SECTION("json + begin/end") + { + json::iterator it = j.begin(); + CHECK(it != j.end()); + CHECK(*it == j); + + it++; + CHECK(it != j.begin()); + CHECK(it == j.end()); + + it--; + CHECK(it == j.begin()); + CHECK(it != j.end()); + CHECK(*it == j); + + ++it; + CHECK(it != j.begin()); + CHECK(it == j.end()); + + --it; + CHECK(it == j.begin()); + CHECK(it != j.end()); + CHECK(*it == j); + } + + SECTION("const json + begin/end") + { + json::const_iterator it = j_const.begin(); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.begin()); + CHECK(it == j_const.end()); + + it--; + CHECK(it == j_const.begin()); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.begin()); + CHECK(it == j_const.end()); + + --it; + CHECK(it == j_const.begin()); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + } + + SECTION("json + cbegin/cend") + { + json::const_iterator it = j.cbegin(); + CHECK(it != j.cend()); + CHECK(*it == j); + + it++; + CHECK(it != j.cbegin()); + CHECK(it == j.cend()); + + it--; + CHECK(it == j.cbegin()); + CHECK(it != j.cend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.cbegin()); + CHECK(it == j.cend()); + + --it; + CHECK(it == j.cbegin()); + CHECK(it != j.cend()); + CHECK(*it == j); + } + + SECTION("const json + cbegin/cend") + { + json::const_iterator it = j_const.cbegin(); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.cbegin()); + CHECK(it == j_const.cend()); + + it--; + CHECK(it == j_const.cbegin()); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.cbegin()); + CHECK(it == j_const.cend()); + + --it; + CHECK(it == j_const.cbegin()); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + } + + SECTION("json + rbegin/rend") + { + json::reverse_iterator it = j.rbegin(); + CHECK(it != j.rend()); + CHECK(*it == j); + + it++; + CHECK(it != j.rbegin()); + CHECK(it == j.rend()); + + it--; + CHECK(it == j.rbegin()); + CHECK(it != j.rend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.rbegin()); + CHECK(it == j.rend()); + + --it; + CHECK(it == j.rbegin()); + CHECK(it != j.rend()); + CHECK(*it == j); + } + + SECTION("json + crbegin/crend") + { + json::const_reverse_iterator it = j.crbegin(); + CHECK(it != j.crend()); + CHECK(*it == j); + + it++; + CHECK(it != j.crbegin()); + CHECK(it == j.crend()); + + it--; + CHECK(it == j.crbegin()); + CHECK(it != j.crend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.crbegin()); + CHECK(it == j.crend()); + + --it; + CHECK(it == j.crbegin()); + CHECK(it != j.crend()); + CHECK(*it == j); + } + + SECTION("const json + crbegin/crend") + { + json::const_reverse_iterator it = j_const.crbegin(); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.crbegin()); + CHECK(it == j_const.crend()); + + it--; + CHECK(it == j_const.crbegin()); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.crbegin()); + CHECK(it == j_const.crend()); + + --it; + CHECK(it == j_const.crbegin()); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + } + + SECTION("key/value") + { + auto it = j.begin(); + auto cit = j_const.cbegin(); + CHECK_THROWS_AS(it.key(), std::domain_error); + CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); + CHECK(it.value() == json(true)); + CHECK_THROWS_AS(cit.key(), std::domain_error); + CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); + CHECK(cit.value() == json(true)); + + auto rit = j.rend(); + auto crit = j.crend(); + CHECK_THROWS_AS(rit.key(), std::domain_error); + CHECK_THROWS_AS(rit.value(), std::out_of_range); + CHECK_THROWS_AS(crit.key(), std::domain_error); + CHECK_THROWS_AS(crit.value(), std::out_of_range); + CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(rit.value(), "cannot get value"); + CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(crit.value(), "cannot get value"); + } + } + + SECTION("string") + { + json j = "hello world"; + json j_const(j); + + SECTION("json + begin/end") + { + json::iterator it = j.begin(); + CHECK(it != j.end()); + CHECK(*it == j); + + it++; + CHECK(it != j.begin()); + CHECK(it == j.end()); + + it--; + CHECK(it == j.begin()); + CHECK(it != j.end()); + CHECK(*it == j); + + ++it; + CHECK(it != j.begin()); + CHECK(it == j.end()); + + --it; + CHECK(it == j.begin()); + CHECK(it != j.end()); + CHECK(*it == j); + } + + SECTION("const json + begin/end") + { + json::const_iterator it = j_const.begin(); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.begin()); + CHECK(it == j_const.end()); + + it--; + CHECK(it == j_const.begin()); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.begin()); + CHECK(it == j_const.end()); + + --it; + CHECK(it == j_const.begin()); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + } + + SECTION("json + cbegin/cend") + { + json::const_iterator it = j.cbegin(); + CHECK(it != j.cend()); + CHECK(*it == j); + + it++; + CHECK(it != j.cbegin()); + CHECK(it == j.cend()); + + it--; + CHECK(it == j.cbegin()); + CHECK(it != j.cend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.cbegin()); + CHECK(it == j.cend()); + + --it; + CHECK(it == j.cbegin()); + CHECK(it != j.cend()); + CHECK(*it == j); + } + + SECTION("const json + cbegin/cend") + { + json::const_iterator it = j_const.cbegin(); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.cbegin()); + CHECK(it == j_const.cend()); + + it--; + CHECK(it == j_const.cbegin()); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.cbegin()); + CHECK(it == j_const.cend()); + + --it; + CHECK(it == j_const.cbegin()); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + } + + SECTION("json + rbegin/rend") + { + json::reverse_iterator it = j.rbegin(); + CHECK(it != j.rend()); + CHECK(*it == j); + + it++; + CHECK(it != j.rbegin()); + CHECK(it == j.rend()); + + it--; + CHECK(it == j.rbegin()); + CHECK(it != j.rend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.rbegin()); + CHECK(it == j.rend()); + + --it; + CHECK(it == j.rbegin()); + CHECK(it != j.rend()); + CHECK(*it == j); + } + + SECTION("json + crbegin/crend") + { + json::const_reverse_iterator it = j.crbegin(); + CHECK(it != j.crend()); + CHECK(*it == j); + + it++; + CHECK(it != j.crbegin()); + CHECK(it == j.crend()); + + it--; + CHECK(it == j.crbegin()); + CHECK(it != j.crend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.crbegin()); + CHECK(it == j.crend()); + + --it; + CHECK(it == j.crbegin()); + CHECK(it != j.crend()); + CHECK(*it == j); + } + + SECTION("const json + crbegin/crend") + { + json::const_reverse_iterator it = j_const.crbegin(); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.crbegin()); + CHECK(it == j_const.crend()); + + it--; + CHECK(it == j_const.crbegin()); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.crbegin()); + CHECK(it == j_const.crend()); + + --it; + CHECK(it == j_const.crbegin()); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + } + + SECTION("key/value") + { + auto it = j.begin(); + auto cit = j_const.cbegin(); + CHECK_THROWS_AS(it.key(), std::domain_error); + CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); + CHECK(it.value() == json("hello world")); + CHECK_THROWS_AS(cit.key(), std::domain_error); + CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); + CHECK(cit.value() == json("hello world")); + + auto rit = j.rend(); + auto crit = j.crend(); + CHECK_THROWS_AS(rit.key(), std::domain_error); + CHECK_THROWS_AS(rit.value(), std::out_of_range); + CHECK_THROWS_AS(crit.key(), std::domain_error); + CHECK_THROWS_AS(crit.value(), std::out_of_range); + CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(rit.value(), "cannot get value"); + CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(crit.value(), "cannot get value"); + } + } + + SECTION("array") + { + json j = {1, 2, 3}; + json j_const(j); + + SECTION("json + begin/end") + { + json::iterator it_begin = j.begin(); + json::iterator it_end = j.end(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j[0]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[1]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[2]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("const json + begin/end") + { + json::const_iterator it_begin = j_const.begin(); + json::const_iterator it_end = j_const.end(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j_const[0]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j_const[1]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j_const[2]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("json + cbegin/cend") + { + json::const_iterator it_begin = j.cbegin(); + json::const_iterator it_end = j.cend(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j[0]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[1]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[2]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("const json + cbegin/cend") + { + json::const_iterator it_begin = j_const.cbegin(); + json::const_iterator it_end = j_const.cend(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j[0]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[1]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[2]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("json + rbegin/rend") + { + json::reverse_iterator it_begin = j.rbegin(); + json::reverse_iterator it_end = j.rend(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j[2]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[1]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[0]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("json + crbegin/crend") + { + json::const_reverse_iterator it_begin = j.crbegin(); + json::const_reverse_iterator it_end = j.crend(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j[2]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[1]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[0]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("const json + crbegin/crend") + { + json::const_reverse_iterator it_begin = j_const.crbegin(); + json::const_reverse_iterator it_end = j_const.crend(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j[2]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[1]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j[0]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("key/value") + { + auto it = j.begin(); + auto cit = j_const.cbegin(); + CHECK_THROWS_AS(it.key(), std::domain_error); + CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); + CHECK(it.value() == json(1)); + CHECK_THROWS_AS(cit.key(), std::domain_error); + CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); + CHECK(cit.value() == json(1)); + } + } + + SECTION("object") + { + json j = {{"A", 1}, {"B", 2}, {"C", 3}}; + json j_const(j); + + SECTION("json + begin/end") + { + json::iterator it_begin = j.begin(); + json::iterator it_end = j.end(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j["A"]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j["B"]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j["C"]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("const json + begin/end") + { + json::const_iterator it_begin = j_const.begin(); + json::const_iterator it_end = j_const.end(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j_const["A"]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j_const["B"]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j_const["C"]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("json + cbegin/cend") + { + json::const_iterator it_begin = j.cbegin(); + json::const_iterator it_end = j.cend(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j["A"]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j["B"]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j["C"]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("const json + cbegin/cend") + { + json::const_iterator it_begin = j_const.cbegin(); + json::const_iterator it_end = j_const.cend(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j_const["A"]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j_const["B"]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j_const["C"]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("json + rbegin/rend") + { + json::reverse_iterator it_begin = j.rbegin(); + json::reverse_iterator it_end = j.rend(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j["C"]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j["B"]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j["A"]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("json + crbegin/crend") + { + json::const_reverse_iterator it_begin = j.crbegin(); + json::const_reverse_iterator it_end = j.crend(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j["C"]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j["B"]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j["A"]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("const json + crbegin/crend") + { + json::const_reverse_iterator it_begin = j_const.crbegin(); + json::const_reverse_iterator it_end = j_const.crend(); + + auto it = it_begin; + CHECK(it != it_end); + CHECK(*it == j["C"]); + + it++; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j["B"]); + + ++it; + CHECK(it != it_begin); + CHECK(it != it_end); + CHECK(*it == j["A"]); + + ++it; + CHECK(it != it_begin); + CHECK(it == it_end); + } + + SECTION("key/value") + { + auto it = j.begin(); + auto cit = j_const.cbegin(); + CHECK(it.key() == "A"); + CHECK(it.value() == json(1)); + CHECK(cit.key() == "A"); + CHECK(cit.value() == json(1)); + } + } + + SECTION("number (integer)") + { + json j = 23; + json j_const(j); + + SECTION("json + begin/end") + { + json::iterator it = j.begin(); + CHECK(it != j.end()); + CHECK(*it == j); + + it++; + CHECK(it != j.begin()); + CHECK(it == j.end()); + + it--; + CHECK(it == j.begin()); + CHECK(it != j.end()); + CHECK(*it == j); + + ++it; + CHECK(it != j.begin()); + CHECK(it == j.end()); + + --it; + CHECK(it == j.begin()); + CHECK(it != j.end()); + CHECK(*it == j); + } + + SECTION("const json + begin/end") + { + json::const_iterator it = j_const.begin(); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.begin()); + CHECK(it == j_const.end()); + + it--; + CHECK(it == j_const.begin()); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.begin()); + CHECK(it == j_const.end()); + + --it; + CHECK(it == j_const.begin()); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + } + + SECTION("json + cbegin/cend") + { + json::const_iterator it = j.cbegin(); + CHECK(it != j.cend()); + CHECK(*it == j); + + it++; + CHECK(it != j.cbegin()); + CHECK(it == j.cend()); + + it--; + CHECK(it == j.cbegin()); + CHECK(it != j.cend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.cbegin()); + CHECK(it == j.cend()); + + --it; + CHECK(it == j.cbegin()); + CHECK(it != j.cend()); + CHECK(*it == j); + } + + SECTION("const json + cbegin/cend") + { + json::const_iterator it = j_const.cbegin(); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.cbegin()); + CHECK(it == j_const.cend()); + + it--; + CHECK(it == j_const.cbegin()); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.cbegin()); + CHECK(it == j_const.cend()); + + --it; + CHECK(it == j_const.cbegin()); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + } + + SECTION("json + rbegin/rend") + { + json::reverse_iterator it = j.rbegin(); + CHECK(it != j.rend()); + CHECK(*it == j); + + it++; + CHECK(it != j.rbegin()); + CHECK(it == j.rend()); + + it--; + CHECK(it == j.rbegin()); + CHECK(it != j.rend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.rbegin()); + CHECK(it == j.rend()); + + --it; + CHECK(it == j.rbegin()); + CHECK(it != j.rend()); + CHECK(*it == j); + } + + SECTION("json + crbegin/crend") + { + json::const_reverse_iterator it = j.crbegin(); + CHECK(it != j.crend()); + CHECK(*it == j); + + it++; + CHECK(it != j.crbegin()); + CHECK(it == j.crend()); + + it--; + CHECK(it == j.crbegin()); + CHECK(it != j.crend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.crbegin()); + CHECK(it == j.crend()); + + --it; + CHECK(it == j.crbegin()); + CHECK(it != j.crend()); + CHECK(*it == j); + } + + SECTION("const json + crbegin/crend") + { + json::const_reverse_iterator it = j_const.crbegin(); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.crbegin()); + CHECK(it == j_const.crend()); + + it--; + CHECK(it == j_const.crbegin()); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.crbegin()); + CHECK(it == j_const.crend()); + + --it; + CHECK(it == j_const.crbegin()); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + } + + SECTION("key/value") + { + auto it = j.begin(); + auto cit = j_const.cbegin(); + CHECK_THROWS_AS(it.key(), std::domain_error); + CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); + CHECK(it.value() == json(23)); + CHECK_THROWS_AS(cit.key(), std::domain_error); + CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); + CHECK(cit.value() == json(23)); + + auto rit = j.rend(); + auto crit = j.crend(); + CHECK_THROWS_AS(rit.key(), std::domain_error); + CHECK_THROWS_AS(rit.value(), std::out_of_range); + CHECK_THROWS_AS(crit.key(), std::domain_error); + CHECK_THROWS_AS(crit.value(), std::out_of_range); + CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(rit.value(), "cannot get value"); + CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(crit.value(), "cannot get value"); + } + } + + SECTION("number (unsigned)") + { + json j = 23u; + json j_const(j); + + SECTION("json + begin/end") + { + json::iterator it = j.begin(); + CHECK(it != j.end()); + CHECK(*it == j); + + it++; + CHECK(it != j.begin()); + CHECK(it == j.end()); + + it--; + CHECK(it == j.begin()); + CHECK(it != j.end()); + CHECK(*it == j); + + ++it; + CHECK(it != j.begin()); + CHECK(it == j.end()); + + --it; + CHECK(it == j.begin()); + CHECK(it != j.end()); + CHECK(*it == j); + } + + SECTION("const json + begin/end") + { + json::const_iterator it = j_const.begin(); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.begin()); + CHECK(it == j_const.end()); + + it--; + CHECK(it == j_const.begin()); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.begin()); + CHECK(it == j_const.end()); + + --it; + CHECK(it == j_const.begin()); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + } + + SECTION("json + cbegin/cend") + { + json::const_iterator it = j.cbegin(); + CHECK(it != j.cend()); + CHECK(*it == j); + + it++; + CHECK(it != j.cbegin()); + CHECK(it == j.cend()); + + it--; + CHECK(it == j.cbegin()); + CHECK(it != j.cend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.cbegin()); + CHECK(it == j.cend()); + + --it; + CHECK(it == j.cbegin()); + CHECK(it != j.cend()); + CHECK(*it == j); + } + + SECTION("const json + cbegin/cend") + { + json::const_iterator it = j_const.cbegin(); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.cbegin()); + CHECK(it == j_const.cend()); + + it--; + CHECK(it == j_const.cbegin()); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.cbegin()); + CHECK(it == j_const.cend()); + + --it; + CHECK(it == j_const.cbegin()); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + } + + SECTION("json + rbegin/rend") + { + json::reverse_iterator it = j.rbegin(); + CHECK(it != j.rend()); + CHECK(*it == j); + + it++; + CHECK(it != j.rbegin()); + CHECK(it == j.rend()); + + it--; + CHECK(it == j.rbegin()); + CHECK(it != j.rend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.rbegin()); + CHECK(it == j.rend()); + + --it; + CHECK(it == j.rbegin()); + CHECK(it != j.rend()); + CHECK(*it == j); + } + + SECTION("json + crbegin/crend") + { + json::const_reverse_iterator it = j.crbegin(); + CHECK(it != j.crend()); + CHECK(*it == j); + + it++; + CHECK(it != j.crbegin()); + CHECK(it == j.crend()); + + it--; + CHECK(it == j.crbegin()); + CHECK(it != j.crend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.crbegin()); + CHECK(it == j.crend()); + + --it; + CHECK(it == j.crbegin()); + CHECK(it != j.crend()); + CHECK(*it == j); + } + + SECTION("const json + crbegin/crend") + { + json::const_reverse_iterator it = j_const.crbegin(); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.crbegin()); + CHECK(it == j_const.crend()); + + it--; + CHECK(it == j_const.crbegin()); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.crbegin()); + CHECK(it == j_const.crend()); + + --it; + CHECK(it == j_const.crbegin()); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + } + + SECTION("key/value") + { + auto it = j.begin(); + auto cit = j_const.cbegin(); + CHECK_THROWS_AS(it.key(), std::domain_error); + CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); + CHECK(it.value() == json(23)); + CHECK_THROWS_AS(cit.key(), std::domain_error); + CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); + CHECK(cit.value() == json(23)); + + auto rit = j.rend(); + auto crit = j.crend(); + CHECK_THROWS_AS(rit.key(), std::domain_error); + CHECK_THROWS_AS(rit.value(), std::out_of_range); + CHECK_THROWS_AS(crit.key(), std::domain_error); + CHECK_THROWS_AS(crit.value(), std::out_of_range); + CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(rit.value(), "cannot get value"); + CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(crit.value(), "cannot get value"); + } + } + + SECTION("number (float)") + { + json j = 23.42; + json j_const(j); + + SECTION("json + begin/end") + { + json::iterator it = j.begin(); + CHECK(it != j.end()); + CHECK(*it == j); + + it++; + CHECK(it != j.begin()); + CHECK(it == j.end()); + + it--; + CHECK(it == j.begin()); + CHECK(it != j.end()); + CHECK(*it == j); + + ++it; + CHECK(it != j.begin()); + CHECK(it == j.end()); + + --it; + CHECK(it == j.begin()); + CHECK(it != j.end()); + CHECK(*it == j); + } + + SECTION("const json + begin/end") + { + json::const_iterator it = j_const.begin(); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.begin()); + CHECK(it == j_const.end()); + + it--; + CHECK(it == j_const.begin()); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.begin()); + CHECK(it == j_const.end()); + + --it; + CHECK(it == j_const.begin()); + CHECK(it != j_const.end()); + CHECK(*it == j_const); + } + + SECTION("json + cbegin/cend") + { + json::const_iterator it = j.cbegin(); + CHECK(it != j.cend()); + CHECK(*it == j); + + it++; + CHECK(it != j.cbegin()); + CHECK(it == j.cend()); + + it--; + CHECK(it == j.cbegin()); + CHECK(it != j.cend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.cbegin()); + CHECK(it == j.cend()); + + --it; + CHECK(it == j.cbegin()); + CHECK(it != j.cend()); + CHECK(*it == j); + } + + SECTION("const json + cbegin/cend") + { + json::const_iterator it = j_const.cbegin(); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.cbegin()); + CHECK(it == j_const.cend()); + + it--; + CHECK(it == j_const.cbegin()); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.cbegin()); + CHECK(it == j_const.cend()); + + --it; + CHECK(it == j_const.cbegin()); + CHECK(it != j_const.cend()); + CHECK(*it == j_const); + } + + SECTION("json + rbegin/rend") + { + json::reverse_iterator it = j.rbegin(); + CHECK(it != j.rend()); + CHECK(*it == j); + + it++; + CHECK(it != j.rbegin()); + CHECK(it == j.rend()); + + it--; + CHECK(it == j.rbegin()); + CHECK(it != j.rend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.rbegin()); + CHECK(it == j.rend()); + + --it; + CHECK(it == j.rbegin()); + CHECK(it != j.rend()); + CHECK(*it == j); + } + + SECTION("json + crbegin/crend") + { + json::const_reverse_iterator it = j.crbegin(); + CHECK(it != j.crend()); + CHECK(*it == j); + + it++; + CHECK(it != j.crbegin()); + CHECK(it == j.crend()); + + it--; + CHECK(it == j.crbegin()); + CHECK(it != j.crend()); + CHECK(*it == j); + + ++it; + CHECK(it != j.crbegin()); + CHECK(it == j.crend()); + + --it; + CHECK(it == j.crbegin()); + CHECK(it != j.crend()); + CHECK(*it == j); + } + + SECTION("const json + crbegin/crend") + { + json::const_reverse_iterator it = j_const.crbegin(); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + + it++; + CHECK(it != j_const.crbegin()); + CHECK(it == j_const.crend()); + + it--; + CHECK(it == j_const.crbegin()); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + + ++it; + CHECK(it != j_const.crbegin()); + CHECK(it == j_const.crend()); + + --it; + CHECK(it == j_const.crbegin()); + CHECK(it != j_const.crend()); + CHECK(*it == j_const); + } + + SECTION("key/value") + { + auto it = j.begin(); + auto cit = j_const.cbegin(); + CHECK_THROWS_AS(it.key(), std::domain_error); + CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); + CHECK(it.value() == json(23.42)); + CHECK_THROWS_AS(cit.key(), std::domain_error); + CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); + CHECK(cit.value() == json(23.42)); + + auto rit = j.rend(); + auto crit = j.crend(); + CHECK_THROWS_AS(rit.key(), std::domain_error); + CHECK_THROWS_AS(rit.value(), std::out_of_range); + CHECK_THROWS_AS(crit.key(), std::domain_error); + CHECK_THROWS_AS(crit.value(), std::out_of_range); + CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(rit.value(), "cannot get value"); + CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(crit.value(), "cannot get value"); + } + } + + SECTION("null") + { + json j = nullptr; + json j_const(j); + + SECTION("json + begin/end") + { + json::iterator it = j.begin(); + CHECK(it == j.end()); + } + + SECTION("const json + begin/end") + { + json::const_iterator it_begin = j_const.begin(); + json::const_iterator it_end = j_const.end(); + CHECK(it_begin == it_end); + } + + SECTION("json + cbegin/cend") + { + json::const_iterator it_begin = j.cbegin(); + json::const_iterator it_end = j.cend(); + CHECK(it_begin == it_end); + } + + SECTION("const json + cbegin/cend") + { + json::const_iterator it_begin = j_const.cbegin(); + json::const_iterator it_end = j_const.cend(); + CHECK(it_begin == it_end); + } + + SECTION("json + rbegin/rend") + { + json::reverse_iterator it = j.rbegin(); + CHECK(it == j.rend()); + } + + SECTION("json + crbegin/crend") + { + json::const_reverse_iterator it = j.crbegin(); + CHECK(it == j.crend()); + } + + SECTION("const json + crbegin/crend") + { + json::const_reverse_iterator it = j_const.crbegin(); + CHECK(it == j_const.crend()); + } + + SECTION("key/value") + { + auto it = j.begin(); + auto cit = j_const.cbegin(); + CHECK_THROWS_AS(it.key(), std::domain_error); + CHECK_THROWS_AS(it.value(), std::out_of_range); + CHECK_THROWS_AS(cit.key(), std::domain_error); + CHECK_THROWS_AS(cit.value(), std::out_of_range); + CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(it.value(), "cannot get value"); + CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(cit.value(), "cannot get value"); + + auto rit = j.rend(); + auto crit = j.crend(); + CHECK_THROWS_AS(rit.key(), std::domain_error); + CHECK_THROWS_AS(rit.value(), std::out_of_range); + CHECK_THROWS_AS(crit.key(), std::domain_error); + CHECK_THROWS_AS(crit.value(), std::out_of_range); + CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(rit.value(), "cannot get value"); + CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); + CHECK_THROWS_WITH(crit.value(), "cannot get value"); + } + } + } + + SECTION("iterator comparisons") + { + json j_values = {nullptr, true, 42, 42u, 23.23, {{"one", 1}, {"two", 2}}, {1, 2, 3, 4, 5}, "Hello, world"}; + + for (json& j : j_values) + { + auto it1 = j.begin(); + auto it2 = j.begin(); + auto it3 = j.begin(); + ++it2; + ++it3; + ++it3; + auto it1_c = j.cbegin(); + auto it2_c = j.cbegin(); + auto it3_c = j.cbegin(); + ++it2_c; + ++it3_c; + ++it3_c; + + // comparison: equal + { + CHECK(it1 == it1); + CHECK(not (it1 == it2)); + CHECK(not (it1 == it3)); + CHECK(not (it2 == it3)); + CHECK(it1_c == it1_c); + CHECK(not (it1_c == it2_c)); + CHECK(not (it1_c == it3_c)); + CHECK(not (it2_c == it3_c)); + } + + // comparison: not equal + { + // check definition + CHECK( (it1 != it1) == not(it1 == it1) ); + CHECK( (it1 != it2) == not(it1 == it2) ); + CHECK( (it1 != it3) == not(it1 == it3) ); + CHECK( (it2 != it3) == not(it2 == it3) ); + CHECK( (it1_c != it1_c) == not(it1_c == it1_c) ); + CHECK( (it1_c != it2_c) == not(it1_c == it2_c) ); + CHECK( (it1_c != it3_c) == not(it1_c == it3_c) ); + CHECK( (it2_c != it3_c) == not(it2_c == it3_c) ); + } + + // comparison: smaller + { + if (j.type() == json::value_t::object) + { + CHECK_THROWS_AS(it1 < it1, std::domain_error); + CHECK_THROWS_AS(it1 < it2, std::domain_error); + CHECK_THROWS_AS(it2 < it3, std::domain_error); + CHECK_THROWS_AS(it1 < it3, std::domain_error); + CHECK_THROWS_AS(it1_c < it1_c, std::domain_error); + CHECK_THROWS_AS(it1_c < it2_c, std::domain_error); + CHECK_THROWS_AS(it2_c < it3_c, std::domain_error); + CHECK_THROWS_AS(it1_c < it3_c, std::domain_error); + CHECK_THROWS_WITH(it1 < it1, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 < it2, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 < it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 < it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c < it1_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c < it2_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c < it3_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c < it3_c, "cannot compare order of object iterators"); + } + else + { + CHECK(not (it1 < it1)); + CHECK(it1 < it2); + CHECK(it1 < it3); + CHECK(it2 < it3); + CHECK(not (it1_c < it1_c)); + CHECK(it1_c < it2_c); + CHECK(it1_c < it3_c); + CHECK(it2_c < it3_c); + } + } + + // comparison: less than or equal + { + if (j.type() == json::value_t::object) + { + CHECK_THROWS_AS(it1 <= it1, std::domain_error); + CHECK_THROWS_AS(it1 <= it2, std::domain_error); + CHECK_THROWS_AS(it2 <= it3, std::domain_error); + CHECK_THROWS_AS(it1 <= it3, std::domain_error); + CHECK_THROWS_AS(it1_c <= it1_c, std::domain_error); + CHECK_THROWS_AS(it1_c <= it2_c, std::domain_error); + CHECK_THROWS_AS(it2_c <= it3_c, std::domain_error); + CHECK_THROWS_AS(it1_c <= it3_c, std::domain_error); + CHECK_THROWS_WITH(it1 <= it1, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 <= it2, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 <= it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 <= it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c <= it1_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c <= it2_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c <= it3_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c <= it3_c, "cannot compare order of object iterators"); + } + else + { + // check definition + CHECK( (it1 <= it1) == not(it1 < it1) ); + CHECK( (it1 <= it2) == not(it2 < it1) ); + CHECK( (it1 <= it3) == not(it3 < it1) ); + CHECK( (it2 <= it3) == not(it3 < it2) ); + CHECK( (it1_c <= it1_c) == not(it1_c < it1_c) ); + CHECK( (it1_c <= it2_c) == not(it2_c < it1_c) ); + CHECK( (it1_c <= it3_c) == not(it3_c < it1_c) ); + CHECK( (it2_c <= it3_c) == not(it3_c < it2_c) ); + } + } + + // comparison: greater than + { + if (j.type() == json::value_t::object) + { + CHECK_THROWS_AS(it1 > it1, std::domain_error); + CHECK_THROWS_AS(it1 > it2, std::domain_error); + CHECK_THROWS_AS(it2 > it3, std::domain_error); + CHECK_THROWS_AS(it1 > it3, std::domain_error); + CHECK_THROWS_AS(it1_c > it1_c, std::domain_error); + CHECK_THROWS_AS(it1_c > it2_c, std::domain_error); + CHECK_THROWS_AS(it2_c > it3_c, std::domain_error); + CHECK_THROWS_AS(it1_c > it3_c, std::domain_error); + CHECK_THROWS_WITH(it1 > it1, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 > it2, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 > it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 > it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c > it1_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c > it2_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c > it3_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c > it3_c, "cannot compare order of object iterators"); + } + else + { + // check definition + CHECK( (it1 > it1) == (it1 < it1) ); + CHECK( (it1 > it2) == (it2 < it1) ); + CHECK( (it1 > it3) == (it3 < it1) ); + CHECK( (it2 > it3) == (it3 < it2) ); + CHECK( (it1_c > it1_c) == (it1_c < it1_c) ); + CHECK( (it1_c > it2_c) == (it2_c < it1_c) ); + CHECK( (it1_c > it3_c) == (it3_c < it1_c) ); + CHECK( (it2_c > it3_c) == (it3_c < it2_c) ); + } + } + + // comparison: greater than or equal + { + if (j.type() == json::value_t::object) + { + CHECK_THROWS_AS(it1 >= it1, std::domain_error); + CHECK_THROWS_AS(it1 >= it2, std::domain_error); + CHECK_THROWS_AS(it2 >= it3, std::domain_error); + CHECK_THROWS_AS(it1 >= it3, std::domain_error); + CHECK_THROWS_AS(it1_c >= it1_c, std::domain_error); + CHECK_THROWS_AS(it1_c >= it2_c, std::domain_error); + CHECK_THROWS_AS(it2_c >= it3_c, std::domain_error); + CHECK_THROWS_AS(it1_c >= it3_c, std::domain_error); + CHECK_THROWS_WITH(it1 >= it1, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 >= it2, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 >= it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 >= it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c >= it1_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c >= it2_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c >= it3_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c >= it3_c, "cannot compare order of object iterators"); + } + else + { + // check definition + CHECK( (it1 >= it1) == not(it1 < it1) ); + CHECK( (it1 >= it2) == not(it1 < it2) ); + CHECK( (it1 >= it3) == not(it1 < it3) ); + CHECK( (it2 >= it3) == not(it2 < it3) ); + CHECK( (it1_c >= it1_c) == not(it1_c < it1_c) ); + CHECK( (it1_c >= it2_c) == not(it1_c < it2_c) ); + CHECK( (it1_c >= it3_c) == not(it1_c < it3_c) ); + CHECK( (it2_c >= it3_c) == not(it2_c < it3_c) ); + } + } + } + + // check exceptions if different objects are compared + for (auto j : j_values) + { + for (auto k : j_values) + { + if (j != k) + { + CHECK_THROWS_AS(j.begin() == k.begin(), std::domain_error); + CHECK_THROWS_AS(j.cbegin() == k.cbegin(), std::domain_error); + CHECK_THROWS_WITH(j.begin() == k.begin(), "cannot compare iterators of different containers"); + CHECK_THROWS_WITH(j.cbegin() == k.cbegin(), "cannot compare iterators of different containers"); + + CHECK_THROWS_AS(j.begin() < k.begin(), std::domain_error); + CHECK_THROWS_AS(j.cbegin() < k.cbegin(), std::domain_error); + CHECK_THROWS_WITH(j.begin() < k.begin(), "cannot compare iterators of different containers"); + CHECK_THROWS_WITH(j.cbegin() < k.cbegin(), "cannot compare iterators of different containers"); + } + } + } + } + + SECTION("iterator arithmetic") + { + json j_object = {{"one", 1}, {"two", 2}, {"three", 3}}; + json j_array = {1, 2, 3, 4, 5, 6}; + json j_null = nullptr; + json j_value = 42; + + SECTION("addition and subtraction") + { + SECTION("object") + { + { + auto it = j_object.begin(); + CHECK_THROWS_AS(it += 1, std::domain_error); + CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.cbegin(); + CHECK_THROWS_AS(it += 1, std::domain_error); + CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.begin(); + CHECK_THROWS_AS(it + 1, std::domain_error); + CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.cbegin(); + CHECK_THROWS_AS(it + 1, std::domain_error); + CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.begin(); + CHECK_THROWS_AS(it -= 1, std::domain_error); + CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.cbegin(); + CHECK_THROWS_AS(it -= 1, std::domain_error); + CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.begin(); + CHECK_THROWS_AS(it - 1, std::domain_error); + CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.cbegin(); + CHECK_THROWS_AS(it - 1, std::domain_error); + CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.begin(); + CHECK_THROWS_AS(it - it, std::domain_error); + CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators"); + } + { + auto it = j_object.cbegin(); + CHECK_THROWS_AS(it - it, std::domain_error); + CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators"); + } + } + + SECTION("array") + { + { + auto it = j_array.begin(); + it += 3; + CHECK((j_array.begin() + 3) == it); + CHECK((it - 3) == j_array.begin()); + CHECK((it - j_array.begin()) == 3); + CHECK(*it == json(4)); + it -= 2; + CHECK(*it == json(2)); + } + { + auto it = j_array.cbegin(); + it += 3; + CHECK((j_array.cbegin() + 3) == it); + CHECK((it - 3) == j_array.cbegin()); + CHECK((it - j_array.cbegin()) == 3); + CHECK(*it == json(4)); + it -= 2; + CHECK(*it == json(2)); + } + } + + SECTION("null") + { + { + auto it = j_null.begin(); + it += 3; + CHECK((j_null.begin() + 3) == it); + CHECK((it - 3) == j_null.begin()); + CHECK((it - j_null.begin()) == 3); + CHECK(it != j_null.end()); + it -= 3; + CHECK(it == j_null.end()); + } + { + auto it = j_null.cbegin(); + it += 3; + CHECK((j_null.cbegin() + 3) == it); + CHECK((it - 3) == j_null.cbegin()); + CHECK((it - j_null.cbegin()) == 3); + CHECK(it != j_null.cend()); + it -= 3; + CHECK(it == j_null.cend()); + } + } + + SECTION("value") + { + { + auto it = j_value.begin(); + it += 3; + CHECK((j_value.begin() + 3) == it); + CHECK((it - 3) == j_value.begin()); + CHECK((it - j_value.begin()) == 3); + CHECK(it != j_value.end()); + it -= 3; + CHECK(*it == json(42)); + } + { + auto it = j_value.cbegin(); + it += 3; + CHECK((j_value.cbegin() + 3) == it); + CHECK((it - 3) == j_value.cbegin()); + CHECK((it - j_value.cbegin()) == 3); + CHECK(it != j_value.cend()); + it -= 3; + CHECK(*it == json(42)); + } + } + } + + SECTION("subscript operator") + { + SECTION("object") + { + { + auto it = j_object.begin(); + CHECK_THROWS_AS(it[0], std::domain_error); + CHECK_THROWS_AS(it[1], std::domain_error); + CHECK_THROWS_WITH(it[0], "cannot use operator[] for object iterators"); + CHECK_THROWS_WITH(it[1], "cannot use operator[] for object iterators"); + } + { + auto it = j_object.cbegin(); + CHECK_THROWS_AS(it[0], std::domain_error); + CHECK_THROWS_AS(it[1], std::domain_error); + CHECK_THROWS_WITH(it[0], "cannot use operator[] for object iterators"); + CHECK_THROWS_WITH(it[1], "cannot use operator[] for object iterators"); + } + } + + SECTION("array") + { + { + auto it = j_array.begin(); + CHECK(it[0] == json(1)); + CHECK(it[1] == json(2)); + CHECK(it[2] == json(3)); + CHECK(it[3] == json(4)); + CHECK(it[4] == json(5)); + CHECK(it[5] == json(6)); + } + { + auto it = j_array.cbegin(); + CHECK(it[0] == json(1)); + CHECK(it[1] == json(2)); + CHECK(it[2] == json(3)); + CHECK(it[3] == json(4)); + CHECK(it[4] == json(5)); + CHECK(it[5] == json(6)); + } + } + + SECTION("null") + { + { + auto it = j_null.begin(); + CHECK_THROWS_AS(it[0], std::out_of_range); + CHECK_THROWS_AS(it[1], std::out_of_range); + CHECK_THROWS_WITH(it[0], "cannot get value"); + CHECK_THROWS_WITH(it[1], "cannot get value"); + } + { + auto it = j_null.cbegin(); + CHECK_THROWS_AS(it[0], std::out_of_range); + CHECK_THROWS_AS(it[1], std::out_of_range); + CHECK_THROWS_WITH(it[0], "cannot get value"); + CHECK_THROWS_WITH(it[1], "cannot get value"); + } + } + + SECTION("value") + { + { + auto it = j_value.begin(); + CHECK(it[0] == json(42)); + CHECK_THROWS_AS(it[1], std::out_of_range); + CHECK_THROWS_WITH(it[1], "cannot get value"); + } + { + auto it = j_value.cbegin(); + CHECK(it[0] == json(42)); + CHECK_THROWS_AS(it[1], std::out_of_range); + CHECK_THROWS_WITH(it[1], "cannot get value"); + } + } + } + } + + SECTION("reverse iterator comparisons") + { + json j_values = {nullptr, true, 42, 42u, 23.23, {{"one", 1}, {"two", 2}}, {1, 2, 3, 4, 5}, "Hello, world"}; + + for (json& j : j_values) + { + auto it1 = j.rbegin(); + auto it2 = j.rbegin(); + auto it3 = j.rbegin(); + ++it2; + ++it3; + ++it3; + auto it1_c = j.crbegin(); + auto it2_c = j.crbegin(); + auto it3_c = j.crbegin(); + ++it2_c; + ++it3_c; + ++it3_c; + + // comparison: equal + { + CHECK(it1 == it1); + CHECK(not (it1 == it2)); + CHECK(not (it1 == it3)); + CHECK(not (it2 == it3)); + CHECK(it1_c == it1_c); + CHECK(not (it1_c == it2_c)); + CHECK(not (it1_c == it3_c)); + CHECK(not (it2_c == it3_c)); + } + + // comparison: not equal + { + // check definition + CHECK( (it1 != it1) == not(it1 == it1) ); + CHECK( (it1 != it2) == not(it1 == it2) ); + CHECK( (it1 != it3) == not(it1 == it3) ); + CHECK( (it2 != it3) == not(it2 == it3) ); + CHECK( (it1_c != it1_c) == not(it1_c == it1_c) ); + CHECK( (it1_c != it2_c) == not(it1_c == it2_c) ); + CHECK( (it1_c != it3_c) == not(it1_c == it3_c) ); + CHECK( (it2_c != it3_c) == not(it2_c == it3_c) ); + } + + // comparison: smaller + { + if (j.type() == json::value_t::object) + { + CHECK_THROWS_AS(it1 < it1, std::domain_error); + CHECK_THROWS_AS(it1 < it2, std::domain_error); + CHECK_THROWS_AS(it2 < it3, std::domain_error); + CHECK_THROWS_AS(it1 < it3, std::domain_error); + CHECK_THROWS_AS(it1_c < it1_c, std::domain_error); + CHECK_THROWS_AS(it1_c < it2_c, std::domain_error); + CHECK_THROWS_AS(it2_c < it3_c, std::domain_error); + CHECK_THROWS_AS(it1_c < it3_c, std::domain_error); + CHECK_THROWS_WITH(it1 < it1, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 < it2, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 < it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 < it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c < it1_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c < it2_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c < it3_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c < it3_c, "cannot compare order of object iterators"); + } + else + { + CHECK(not (it1 < it1)); + CHECK(it1 < it2); + CHECK(it1 < it3); + CHECK(it2 < it3); + CHECK(not (it1_c < it1_c)); + CHECK(it1_c < it2_c); + CHECK(it1_c < it3_c); + CHECK(it2_c < it3_c); + } + } + + // comparison: less than or equal + { + if (j.type() == json::value_t::object) + { + CHECK_THROWS_AS(it1 <= it1, std::domain_error); + CHECK_THROWS_AS(it1 <= it2, std::domain_error); + CHECK_THROWS_AS(it2 <= it3, std::domain_error); + CHECK_THROWS_AS(it1 <= it3, std::domain_error); + CHECK_THROWS_AS(it1_c <= it1_c, std::domain_error); + CHECK_THROWS_AS(it1_c <= it2_c, std::domain_error); + CHECK_THROWS_AS(it2_c <= it3_c, std::domain_error); + CHECK_THROWS_AS(it1_c <= it3_c, std::domain_error); + CHECK_THROWS_WITH(it1 <= it1, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 <= it2, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 <= it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 <= it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c <= it1_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c <= it2_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c <= it3_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c <= it3_c, "cannot compare order of object iterators"); + } + else + { + // check definition + CHECK( (it1 <= it1) == not(it1 < it1) ); + CHECK( (it1 <= it2) == not(it2 < it1) ); + CHECK( (it1 <= it3) == not(it3 < it1) ); + CHECK( (it2 <= it3) == not(it3 < it2) ); + CHECK( (it1_c <= it1_c) == not(it1_c < it1_c) ); + CHECK( (it1_c <= it2_c) == not(it2_c < it1_c) ); + CHECK( (it1_c <= it3_c) == not(it3_c < it1_c) ); + CHECK( (it2_c <= it3_c) == not(it3_c < it2_c) ); + } + } + + // comparison: greater than + { + if (j.type() == json::value_t::object) + { + CHECK_THROWS_AS(it1 > it1, std::domain_error); + CHECK_THROWS_AS(it1 > it2, std::domain_error); + CHECK_THROWS_AS(it2 > it3, std::domain_error); + CHECK_THROWS_AS(it1 > it3, std::domain_error); + CHECK_THROWS_AS(it1_c > it1_c, std::domain_error); + CHECK_THROWS_AS(it1_c > it2_c, std::domain_error); + CHECK_THROWS_AS(it2_c > it3_c, std::domain_error); + CHECK_THROWS_AS(it1_c > it3_c, std::domain_error); + CHECK_THROWS_WITH(it1 > it1, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 > it2, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 > it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 > it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c > it1_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c > it2_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c > it3_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c > it3_c, "cannot compare order of object iterators"); + } + else + { + // check definition + CHECK( (it1 > it1) == (it1 < it1) ); + CHECK( (it1 > it2) == (it2 < it1) ); + CHECK( (it1 > it3) == (it3 < it1) ); + CHECK( (it2 > it3) == (it3 < it2) ); + CHECK( (it1_c > it1_c) == (it1_c < it1_c) ); + CHECK( (it1_c > it2_c) == (it2_c < it1_c) ); + CHECK( (it1_c > it3_c) == (it3_c < it1_c) ); + CHECK( (it2_c > it3_c) == (it3_c < it2_c) ); + } + } + + // comparison: greater than or equal + { + if (j.type() == json::value_t::object) + { + CHECK_THROWS_AS(it1 >= it1, std::domain_error); + CHECK_THROWS_AS(it1 >= it2, std::domain_error); + CHECK_THROWS_AS(it2 >= it3, std::domain_error); + CHECK_THROWS_AS(it1 >= it3, std::domain_error); + CHECK_THROWS_AS(it1_c >= it1_c, std::domain_error); + CHECK_THROWS_AS(it1_c >= it2_c, std::domain_error); + CHECK_THROWS_AS(it2_c >= it3_c, std::domain_error); + CHECK_THROWS_AS(it1_c >= it3_c, std::domain_error); + CHECK_THROWS_WITH(it1 >= it1, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 >= it2, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2 >= it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1 >= it3, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c >= it1_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c >= it2_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it2_c >= it3_c, "cannot compare order of object iterators"); + CHECK_THROWS_WITH(it1_c >= it3_c, "cannot compare order of object iterators"); + } + else + { + // check definition + CHECK( (it1 >= it1) == not(it1 < it1) ); + CHECK( (it1 >= it2) == not(it1 < it2) ); + CHECK( (it1 >= it3) == not(it1 < it3) ); + CHECK( (it2 >= it3) == not(it2 < it3) ); + CHECK( (it1_c >= it1_c) == not(it1_c < it1_c) ); + CHECK( (it1_c >= it2_c) == not(it1_c < it2_c) ); + CHECK( (it1_c >= it3_c) == not(it1_c < it3_c) ); + CHECK( (it2_c >= it3_c) == not(it2_c < it3_c) ); + } + } + } + + // check exceptions if different objects are compared + for (auto j : j_values) + { + for (auto k : j_values) + { + if (j != k) + { + CHECK_THROWS_AS(j.rbegin() == k.rbegin(), std::domain_error); + CHECK_THROWS_AS(j.crbegin() == k.crbegin(), std::domain_error); + CHECK_THROWS_WITH(j.rbegin() == k.rbegin(), "cannot compare iterators of different containers"); + CHECK_THROWS_WITH(j.crbegin() == k.crbegin(), "cannot compare iterators of different containers"); + + CHECK_THROWS_AS(j.rbegin() < k.rbegin(), std::domain_error); + CHECK_THROWS_AS(j.crbegin() < k.crbegin(), std::domain_error); + CHECK_THROWS_WITH(j.rbegin() < k.rbegin(), "cannot compare iterators of different containers"); + CHECK_THROWS_WITH(j.crbegin() < k.crbegin(), "cannot compare iterators of different containers"); + } + } + } + } + + SECTION("reverse iterator arithmetic") + { + json j_object = {{"one", 1}, {"two", 2}, {"three", 3}}; + json j_array = {1, 2, 3, 4, 5, 6}; + json j_null = nullptr; + json j_value = 42; + + SECTION("addition and subtraction") + { + SECTION("object") + { + { + auto it = j_object.rbegin(); + CHECK_THROWS_AS(it += 1, std::domain_error); + CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.crbegin(); + CHECK_THROWS_AS(it += 1, std::domain_error); + CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.rbegin(); + CHECK_THROWS_AS(it + 1, std::domain_error); + CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.crbegin(); + CHECK_THROWS_AS(it + 1, std::domain_error); + CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.rbegin(); + CHECK_THROWS_AS(it -= 1, std::domain_error); + CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.crbegin(); + CHECK_THROWS_AS(it -= 1, std::domain_error); + CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.rbegin(); + CHECK_THROWS_AS(it - 1, std::domain_error); + CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.crbegin(); + CHECK_THROWS_AS(it - 1, std::domain_error); + CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators"); + } + { + auto it = j_object.rbegin(); + CHECK_THROWS_AS(it - it, std::domain_error); + CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators"); + } + { + auto it = j_object.crbegin(); + CHECK_THROWS_AS(it - it, std::domain_error); + CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators"); + } + } + + SECTION("array") + { + { + auto it = j_array.rbegin(); + it += 3; + CHECK((j_array.rbegin() + 3) == it); + CHECK((it - 3) == j_array.rbegin()); + CHECK((j_array.rbegin() - it) == 3); + CHECK(*it == json(3)); + it -= 2; + CHECK(*it == json(5)); + } + { + auto it = j_array.crbegin(); + it += 3; + CHECK((j_array.crbegin() + 3) == it); + CHECK((it - 3) == j_array.crbegin()); + CHECK((j_array.crbegin() - it) == 3); + CHECK(*it == json(3)); + it -= 2; + CHECK(*it == json(5)); + } + } + + SECTION("null") + { + { + auto it = j_null.rbegin(); + it += 3; + CHECK((j_null.rbegin() + 3) == it); + CHECK((it - 3) == j_null.rbegin()); + CHECK((j_null.rbegin() - it) == 3); + CHECK(it != j_null.rend()); + it -= 3; + CHECK(it == j_null.rend()); + } + { + auto it = j_null.crbegin(); + it += 3; + CHECK((j_null.crbegin() + 3) == it); + CHECK((it - 3) == j_null.crbegin()); + CHECK((j_null.crbegin() - it) == 3); + CHECK(it != j_null.crend()); + it -= 3; + CHECK(it == j_null.crend()); + } + } + + SECTION("value") + { + { + auto it = j_value.rbegin(); + it += 3; + CHECK((j_value.rbegin() + 3) == it); + CHECK((it - 3) == j_value.rbegin()); + CHECK((j_value.rbegin() - it) == 3); + CHECK(it != j_value.rend()); + it -= 3; + CHECK(*it == json(42)); + } + { + auto it = j_value.crbegin(); + it += 3; + CHECK((j_value.crbegin() + 3) == it); + CHECK((it - 3) == j_value.crbegin()); + CHECK((j_value.crbegin() - it) == 3); + CHECK(it != j_value.crend()); + it -= 3; + CHECK(*it == json(42)); + } + } + } + + SECTION("subscript operator") + { + SECTION("object") + { + { + auto it = j_object.rbegin(); + CHECK_THROWS_AS(it[0], std::domain_error); + CHECK_THROWS_AS(it[1], std::domain_error); + CHECK_THROWS_WITH(it[0], "cannot use offsets with object iterators"); + CHECK_THROWS_WITH(it[1], "cannot use offsets with object iterators"); + } + { + auto it = j_object.crbegin(); + CHECK_THROWS_AS(it[0], std::domain_error); + CHECK_THROWS_AS(it[1], std::domain_error); + CHECK_THROWS_WITH(it[0], "cannot use offsets with object iterators"); + CHECK_THROWS_WITH(it[1], "cannot use offsets with object iterators"); + } + } + + SECTION("array") + { + { + auto it = j_array.rbegin(); + CHECK(it[0] == json(6)); + CHECK(it[1] == json(5)); + CHECK(it[2] == json(4)); + CHECK(it[3] == json(3)); + CHECK(it[4] == json(2)); + CHECK(it[5] == json(1)); + } + { + auto it = j_array.crbegin(); + CHECK(it[0] == json(6)); + CHECK(it[1] == json(5)); + CHECK(it[2] == json(4)); + CHECK(it[3] == json(3)); + CHECK(it[4] == json(2)); + CHECK(it[5] == json(1)); + } + } + + SECTION("null") + { + { + auto it = j_null.rbegin(); + CHECK_THROWS_AS(it[0], std::out_of_range); + CHECK_THROWS_AS(it[1], std::out_of_range); + CHECK_THROWS_WITH(it[0], "cannot get value"); + CHECK_THROWS_WITH(it[1], "cannot get value"); + } + { + auto it = j_null.crbegin(); + CHECK_THROWS_AS(it[0], std::out_of_range); + CHECK_THROWS_AS(it[1], std::out_of_range); + CHECK_THROWS_WITH(it[0], "cannot get value"); + CHECK_THROWS_WITH(it[1], "cannot get value"); + } + } + + SECTION("value") + { + { + auto it = j_value.rbegin(); + CHECK(it[0] == json(42)); + CHECK_THROWS_AS(it[1], std::out_of_range); + CHECK_THROWS_WITH(it[1], "cannot get value"); + } + { + auto it = j_value.crbegin(); + CHECK(it[0] == json(42)); + CHECK_THROWS_AS(it[1], std::out_of_range); + CHECK_THROWS_WITH(it[1], "cannot get value"); + } + } + } + } +} diff --git a/test/src/unit-json_patch.cpp b/test/src/unit-json_patch.cpp new file mode 100644 index 00000000..af4d4547 --- /dev/null +++ b/test/src/unit-json_patch.cpp @@ -0,0 +1,1217 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("JSON patch") +{ + SECTION("examples from RFC 6902") + { + SECTION("4. Operations") + { + // the ordering of members in JSON objects is not significant: + json op1 = R"({ "op": "add", "path": "/a/b/c", "value": "foo" })"_json; + json op2 = R"({ "path": "/a/b/c", "op": "add", "value": "foo" })"_json; + json op3 = R"({ "value": "foo", "path": "/a/b/c", "op": "add" })"_json; + + // check if the operation objects are equivalent + CHECK(op1 == op2); + CHECK(op1 == op3); + } + + SECTION("4.1 add") + { + json patch = R"([{ "op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ] }])"_json; + + // However, the object itself or an array containing it does need + // to exist, and it remains an error for that not to be the case. + // For example, an "add" with a target location of "/a/b" starting + // with this document + json doc1 = R"({ "a": { "foo": 1 } })"_json; + + // is not an error, because "a" exists, and "b" will be added to + // its value. + CHECK_NOTHROW(doc1.patch(patch)); + CHECK(doc1.patch(patch) == R"( + { + "a": { + "foo": 1, + "b": { + "c": [ "foo", "bar" ] + } + } + } + )"_json); + + // It is an error in this document: + json doc2 = R"({ "q": { "bar": 2 } })"_json; + + // because "a" does not exist. + CHECK_THROWS_AS(doc2.patch(patch), std::out_of_range); + CHECK_THROWS_WITH(doc2.patch(patch), "key 'a' not found"); + } + + SECTION("4.2 remove") + { + // If removing an element from an array, any elements above the + // specified index are shifted one position to the left. + json doc = {1, 2, 3, 4}; + json patch = {{{"op", "remove"}, {"path", "/1"}}}; + CHECK(doc.patch(patch) == json({1, 3, 4})); + } + + SECTION("A.1. Adding an Object Member") + { + // An example target JSON document: + json doc = R"( + { "foo": "bar"} + )"_json; + + // A JSON Patch document: + json patch = R"( + [ + { "op": "add", "path": "/baz", "value": "qux" } + ] + )"_json; + + // The resulting JSON document: + json expected = R"( + { + "baz": "qux", + "foo": "bar" + } + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == expected); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, expected)) == expected); + } + + SECTION("A.2. Adding an Array Element") + { + // An example target JSON document: + json doc = R"( + { "foo": [ "bar", "baz" ] } + )"_json; + + // A JSON Patch document: + json patch = R"( + [ + { "op": "add", "path": "/foo/1", "value": "qux" } + ] + )"_json; + + // The resulting JSON document: + json expected = R"( + { "foo": [ "bar", "qux", "baz" ] } + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == expected); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, expected)) == expected); + } + + SECTION("A.3. Removing an Object Member") + { + // An example target JSON document: + json doc = R"( + { + "baz": "qux", + "foo": "bar" + } + )"_json; + + // A JSON Patch document: + json patch = R"( + [ + { "op": "remove", "path": "/baz" } + ] + )"_json; + + // The resulting JSON document: + json expected = R"( + { "foo": "bar" } + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == expected); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, expected)) == expected); + } + + SECTION("A.4. Removing an Array Element") + { + // An example target JSON document: + json doc = R"( + { "foo": [ "bar", "qux", "baz" ] } + )"_json; + + // A JSON Patch document: + json patch = R"( + [ + { "op": "remove", "path": "/foo/1" } + ] + )"_json; + + // The resulting JSON document: + json expected = R"( + { "foo": [ "bar", "baz" ] } + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == expected); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, expected)) == expected); + } + + SECTION("A.5. Replacing a Value") + { + // An example target JSON document: + json doc = R"( + { + "baz": "qux", + "foo": "bar" + } + )"_json; + + // A JSON Patch document: + json patch = R"( + [ + { "op": "replace", "path": "/baz", "value": "boo" } + ] + )"_json; + + json expected = R"( + { + "baz": "boo", + "foo": "bar" + } + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == expected); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, expected)) == expected); + } + + SECTION("A.6. Moving a Value") + { + // An example target JSON document: + json doc = R"( + { + "foo": { + "bar": "baz", + "waldo": "fred" + }, + "qux": { + "corge": "grault" + } + } + )"_json; + + // A JSON Patch document: + json patch = R"( + [ + { "op": "move", "from": "/foo/waldo", "path": "/qux/thud" } + ] + )"_json; + + // The resulting JSON document: + json expected = R"( + { + "foo": { + "bar": "baz" + }, + "qux": { + "corge": "grault", + "thud": "fred" + } + } + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == expected); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, expected)) == expected); + } + + SECTION("A.7. Moving a Value") + { + // An example target JSON document: + json doc = R"( + { "foo": [ "all", "grass", "cows", "eat" ] } + )"_json; + + // A JSON Patch document: + json patch = R"( + [ + { "op": "move", "from": "/foo/1", "path": "/foo/3" } + ] + )"_json; + + // The resulting JSON document: + json expected = R"( + { "foo": [ "all", "cows", "eat", "grass" ] } + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == expected); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, expected)) == expected); + } + + SECTION("A.8. Testing a Value: Success") + { + // An example target JSON document: + json doc = R"( + { + "baz": "qux", + "foo": [ "a", 2, "c" ] + } + )"_json; + + // A JSON Patch document that will result in successful evaluation: + json patch = R"( + [ + { "op": "test", "path": "/baz", "value": "qux" }, + { "op": "test", "path": "/foo/1", "value": 2 } + ] + )"_json; + + // check if evaluation does not throw + CHECK_NOTHROW(doc.patch(patch)); + // check if patched document is unchanged + CHECK(doc.patch(patch) == doc); + } + + SECTION("A.9. Testing a Value: Error") + { + // An example target JSON document: + json doc = R"( + { "baz": "qux" } + )"_json; + + // A JSON Patch document that will result in an error condition: + json patch = R"( + [ + { "op": "test", "path": "/baz", "value": "bar" } + ] + )"_json; + + // check that evaluation throws + CHECK_THROWS_AS(doc.patch(patch), std::domain_error); + CHECK_THROWS_WITH(doc.patch(patch), "unsuccessful: " + patch[0].dump()); + } + + SECTION("A.10. Adding a Nested Member Object") + { + // An example target JSON document: + json doc = R"( + { "foo": "bar" } + )"_json; + + // A JSON Patch document: + json patch = R"( + [ + { "op": "add", "path": "/child", "value": { "grandchild": { } } } + ] + )"_json; + + // The resulting JSON document: + json expected = R"( + { + "foo": "bar", + "child": { + "grandchild": { + } + } + } + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == expected); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, expected)) == expected); + } + + SECTION("A.11. Ignoring Unrecognized Elements") + { + // An example target JSON document: + json doc = R"( + { "foo": "bar" } + )"_json; + + // A JSON Patch document: + json patch = R"( + [ + { "op": "add", "path": "/baz", "value": "qux", "xyz": 123 } + ] + )"_json; + + json expected = R"( + { + "foo": "bar", + "baz": "qux" + } + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == expected); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, expected)) == expected); + } + + SECTION("A.12. Adding to a Nonexistent Target") + { + // An example target JSON document: + json doc = R"( + { "foo": "bar" } + )"_json; + + // A JSON Patch document: + json patch = R"( + [ + { "op": "add", "path": "/baz/bat", "value": "qux" } + ] + )"_json; + + // This JSON Patch document, applied to the target JSON document + // above, would result in an error (therefore, it would not be + // applied), because the "add" operation's target location that + // references neither the root of the document, nor a member of + // an existing object, nor a member of an existing array. + + CHECK_THROWS_AS(doc.patch(patch), std::out_of_range); + CHECK_THROWS_WITH(doc.patch(patch), "key 'baz' not found"); + } + + // A.13. Invalid JSON Patch Document + // not applicable + + SECTION("A.14. Escape Ordering") + { + // An example target JSON document: + json doc = R"( + { + "/": 9, + "~1": 10 + } + )"_json; + + // A JSON Patch document: + json patch = R"( + [ + {"op": "test", "path": "/~01", "value": 10} + ] + )"_json; + + json expected = R"( + { + "/": 9, + "~1": 10 + } + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == expected); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, expected)) == expected); + } + + SECTION("A.15. Comparing Strings and Numbers") + { + // An example target JSON document: + json doc = R"( + { + "/": 9, + "~1": 10 + } + )"_json; + + // A JSON Patch document that will result in an error condition: + json patch = R"( + [ + {"op": "test", "path": "/~01", "value": "10"} + ] + )"_json; + + // check that evaluation throws + CHECK_THROWS_AS(doc.patch(patch), std::domain_error); + CHECK_THROWS_WITH(doc.patch(patch), "unsuccessful: " + patch[0].dump()); + } + + SECTION("A.16. Adding an Array Value") + { + // An example target JSON document: + json doc = R"( + { "foo": ["bar"] } + )"_json; + + // A JSON Patch document: + json patch = R"( + [ + { "op": "add", "path": "/foo/-", "value": ["abc", "def"] } + ] + )"_json; + + // The resulting JSON document: + json expected = R"( + { "foo": ["bar", ["abc", "def"]] } + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == expected); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, expected)) == expected); + } + } + + SECTION("own examples") + { + SECTION("add") + { + SECTION("add to the root element") + { + // If the path is the root of the target document - the + // specified value becomes the entire content of the target + // document. + + // An example target JSON document: + json doc = 17; + + // A JSON Patch document: + json patch = R"( + [ + { "op": "add", "path": "", "value": [1,2,3] } + ] + )"_json; + + // The resulting JSON document: + json expected = {1, 2, 3}; + + // check if patched value is as expected + CHECK(doc.patch(patch) == expected); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, expected)) == expected); + } + + SECTION("add to end of the array") + { + // The specified index MUST NOT be greater than the number of + // elements in the array. The example below uses and index of + // exactly the number of elements in the array which is legal. + + // An example target JSON document: + json doc = {0, 1, 2}; + + // A JSON Patch document: + json patch = R"( + [ + { "op": "add", "path": "/3", "value": 3 } + ] + )"_json; + + // The resulting JSON document: + json expected = {0, 1, 2, 3}; + + // check if patched value is as expected + CHECK(doc.patch(patch) == expected); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, expected)) == expected); + } + } + + SECTION("copy") + { + // An example target JSON document: + json doc = R"( + { + "foo": { + "bar": "baz", + "waldo": "fred" + }, + "qux": { + "corge": "grault" + } + } + )"_json; + + // A JSON Patch document: + json patch = R"( + [ + { "op": "copy", "from": "/foo/waldo", "path": "/qux/thud" } + ] + )"_json; + + // The resulting JSON document: + json expected = R"( + { + "foo": { + "bar": "baz", + "waldo": "fred" + }, + "qux": { + "corge": "grault", + "thud": "fred" + } + } + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == expected); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, expected)) == expected); + } + + SECTION("replace") + { + json j = "string"; + json patch = {{{"op", "replace"}, {"path", ""}, {"value", 1}}}; + CHECK(j.patch(patch) == json(1)); + } + + SECTION("documentation GIF") + { + { + // a JSON patch + json p1 = R"( + [{"op": "add", "path": "/GB", "value": "London"}] + )"_json; + + // a JSON value + json source = R"( + {"D": "Berlin", "F": "Paris"} + )"_json; + + // apply the patch + json target = source.patch(p1); + // target = { "D": "Berlin", "F": "Paris", "GB": "London" } + CHECK(target == R"({ "D": "Berlin", "F": "Paris", "GB": "London" })"_json); + + // create a diff from two JSONs + json p2 = json::diff(target, source); + // p2 = [{"op": "delete", "path": "/GB"}] + CHECK(p2 == R"([{"op":"remove","path":"/GB"}])"_json); + } + { + // a JSON value + json j = {"good", "bad", "ugly"}; + + // a JSON pointer + auto ptr = json::json_pointer("/2"); + + // use to access elements + j[ptr] = {{"it", "cattivo"}}; + CHECK(j == R"(["good","bad",{"it":"cattivo"}])"_json); + + // use user-defined string literal + j["/2/en"_json_pointer] = "ugly"; + CHECK(j == R"(["good","bad",{"en":"ugly","it":"cattivo"}])"_json); + + json flat = j.flatten(); + CHECK(flat == R"({"/0":"good","/1":"bad","/2/en":"ugly","/2/it":"cattivo"})"_json); + } + } + } + + SECTION("errors") + { + SECTION("unknown operation") + { + SECTION("not an array") + { + json j; + json patch = {{"op", "add"}, {"path", ""}, {"value", 1}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "JSON patch must be an array of objects"); + } + + SECTION("not an array of objects") + { + json j; + json patch = {"op", "add", "path", "", "value", 1}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "JSON patch must be an array of objects"); + } + + SECTION("missing 'op'") + { + json j; + json patch = {{{"foo", "bar"}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation must have member 'op'"); + } + + SECTION("non-string 'op'") + { + json j; + json patch = {{{"op", 1}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation must have string member 'op'"); + } + + SECTION("invalid operation") + { + json j; + json patch = {{{"op", "foo"}, {"path", ""}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation value 'foo' is invalid"); + } + } + + SECTION("add") + { + SECTION("missing 'path'") + { + json j; + json patch = {{{"op", "add"}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'add' must have member 'path'"); + } + + SECTION("non-string 'path'") + { + json j; + json patch = {{{"op", "add"}, {"path", 1}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'add' must have string member 'path'"); + } + + SECTION("missing 'value'") + { + json j; + json patch = {{{"op", "add"}, {"path", ""}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'add' must have member 'value'"); + } + + SECTION("invalid array index") + { + json j = {1, 2}; + json patch = {{{"op", "add"}, {"path", "/4"}, {"value", 4}}}; + CHECK_THROWS_AS(j.patch(patch), std::out_of_range); + CHECK_THROWS_WITH(j.patch(patch), "array index 4 is out of range"); + } + } + + SECTION("remove") + { + SECTION("missing 'path'") + { + json j; + json patch = {{{"op", "remove"}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'remove' must have member 'path'"); + } + + SECTION("non-string 'path'") + { + json j; + json patch = {{{"op", "remove"}, {"path", 1}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'remove' must have string member 'path'"); + } + + SECTION("nonexisting target location (array)") + { + json j = {1, 2, 3}; + json patch = {{{"op", "remove"}, {"path", "/17"}}}; + CHECK_THROWS_AS(j.patch(patch), std::out_of_range); + CHECK_THROWS_WITH(j.patch(patch), "array index 17 is out of range"); + } + + SECTION("nonexisting target location (object)") + { + json j = {{"foo", 1}, {"bar", 2}}; + json patch = {{{"op", "remove"}, {"path", "/baz"}}}; + CHECK_THROWS_AS(j.patch(patch), std::out_of_range); + CHECK_THROWS_WITH(j.patch(patch), "key 'baz' not found"); + } + + SECTION("root element as target location") + { + json j = "string"; + json patch = {{{"op", "remove"}, {"path", ""}}}; + CHECK_THROWS_AS(j.patch(patch), std::domain_error); + CHECK_THROWS_WITH(j.patch(patch), "JSON pointer has no parent"); + } + } + + SECTION("replace") + { + SECTION("missing 'path'") + { + json j; + json patch = {{{"op", "replace"}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'replace' must have member 'path'"); + } + + SECTION("non-string 'path'") + { + json j; + json patch = {{{"op", "replace"}, {"path", 1}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'replace' must have string member 'path'"); + } + + SECTION("missing 'value'") + { + json j; + json patch = {{{"op", "replace"}, {"path", ""}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'replace' must have member 'value'"); + } + + SECTION("nonexisting target location (array)") + { + json j = {1, 2, 3}; + json patch = {{{"op", "replace"}, {"path", "/17"}, {"value", 19}}}; + CHECK_THROWS_AS(j.patch(patch), std::out_of_range); + CHECK_THROWS_WITH(j.patch(patch), "array index 17 is out of range"); + } + + SECTION("nonexisting target location (object)") + { + json j = {{"foo", 1}, {"bar", 2}}; + json patch = {{{"op", "replace"}, {"path", "/baz"}, {"value", 3}}}; + CHECK_THROWS_AS(j.patch(patch), std::out_of_range); + CHECK_THROWS_WITH(j.patch(patch), "key 'baz' not found"); + } + } + + SECTION("move") + { + SECTION("missing 'path'") + { + json j; + json patch = {{{"op", "move"}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have member 'path'"); + } + + SECTION("non-string 'path'") + { + json j; + json patch = {{{"op", "move"}, {"path", 1}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have string member 'path'"); + } + + SECTION("missing 'from'") + { + json j; + json patch = {{{"op", "move"}, {"path", ""}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have member 'from'"); + } + + SECTION("non-string 'from'") + { + json j; + json patch = {{{"op", "move"}, {"path", ""}, {"from", 1}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have string member 'from'"); + } + + SECTION("nonexisting from location (array)") + { + json j = {1, 2, 3}; + json patch = {{{"op", "move"}, {"path", "/0"}, {"from", "/5"}}}; + CHECK_THROWS_AS(j.patch(patch), std::out_of_range); + CHECK_THROWS_WITH(j.patch(patch), "array index 5 is out of range"); + } + + SECTION("nonexisting from location (object)") + { + json j = {{"foo", 1}, {"bar", 2}}; + json patch = {{{"op", "move"}, {"path", "/baz"}, {"from", "/baz"}}}; + CHECK_THROWS_AS(j.patch(patch), std::out_of_range); + CHECK_THROWS_WITH(j.patch(patch), "key 'baz' not found"); + } + } + + SECTION("copy") + { + SECTION("missing 'path'") + { + json j; + json patch = {{{"op", "copy"}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have member 'path'"); + } + + SECTION("non-string 'path'") + { + json j; + json patch = {{{"op", "copy"}, {"path", 1}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have string member 'path'"); + } + + SECTION("missing 'from'") + { + json j; + json patch = {{{"op", "copy"}, {"path", ""}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have member 'from'"); + } + + SECTION("non-string 'from'") + { + json j; + json patch = {{{"op", "copy"}, {"path", ""}, {"from", 1}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have string member 'from'"); + } + + SECTION("nonexisting from location (array)") + { + json j = {1, 2, 3}; + json patch = {{{"op", "copy"}, {"path", "/0"}, {"from", "/5"}}}; + CHECK_THROWS_AS(j.patch(patch), std::out_of_range); + CHECK_THROWS_WITH(j.patch(patch), "array index 5 is out of range"); + } + + SECTION("nonexisting from location (object)") + { + json j = {{"foo", 1}, {"bar", 2}}; + json patch = {{{"op", "copy"}, {"path", "/fob"}, {"from", "/baz"}}}; + CHECK_THROWS_AS(j.patch(patch), std::out_of_range); + CHECK_THROWS_WITH(j.patch(patch), "key 'baz' not found"); + } + } + + SECTION("test") + { + SECTION("missing 'path'") + { + json j; + json patch = {{{"op", "test"}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'test' must have member 'path'"); + } + + SECTION("non-string 'path'") + { + json j; + json patch = {{{"op", "test"}, {"path", 1}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'test' must have string member 'path'"); + } + + SECTION("missing 'value'") + { + json j; + json patch = {{{"op", "test"}, {"path", ""}}}; + CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); + CHECK_THROWS_WITH(j.patch(patch), "operation 'test' must have member 'value'"); + } + } + } + + SECTION("Examples from jsonpatch.com") + { + SECTION("Simple Example") + { + // The original document + json doc = R"( + { + "baz": "qux", + "foo": "bar" + } + )"_json; + + // The patch + json patch = R"( + [ + { "op": "replace", "path": "/baz", "value": "boo" }, + { "op": "add", "path": "/hello", "value": ["world"] }, + { "op": "remove", "path": "/foo"} + ] + )"_json; + + // The result + json result = R"( + { + "baz": "boo", + "hello": ["world"] + } + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == result); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, result)) == result); + } + + SECTION("Operations") + { + // The original document + json doc = R"( + { + "biscuits": [ + {"name":"Digestive"}, + {"name": "Choco Liebniz"} + ] + } + )"_json; + + SECTION("add") + { + // The patch + json patch = R"( + [ + {"op": "add", "path": "/biscuits/1", "value": {"name": "Ginger Nut"}} + ] + )"_json; + + // The result + json result = R"( + { + "biscuits": [ + {"name": "Digestive"}, + {"name": "Ginger Nut"}, + {"name": "Choco Liebniz"} + ] + } + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == result); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, result)) == result); + } + + SECTION("remove") + { + // The patch + json patch = R"( + [ + {"op": "remove", "path": "/biscuits"} + ] + )"_json; + + // The result + json result = R"( + {} + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == result); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, result)) == result); + } + + SECTION("replace") + { + // The patch + json patch = R"( + [ + {"op": "replace", "path": "/biscuits/0/name", "value": "Chocolate Digestive"} + ] + )"_json; + + // The result + json result = R"( + { + "biscuits": [ + {"name": "Chocolate Digestive"}, + {"name": "Choco Liebniz"} + ] + } + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == result); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, result)) == result); + } + + SECTION("copy") + { + // The patch + json patch = R"( + [ + {"op": "copy", "from": "/biscuits/0", "path": "/best_biscuit"} + ] + )"_json; + + // The result + json result = R"( + { + "biscuits": [ + {"name": "Digestive"}, + {"name": "Choco Liebniz"} + ], + "best_biscuit": { + "name": "Digestive" + } + } + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == result); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, result)) == result); + } + + SECTION("move") + { + // The patch + json patch = R"( + [ + {"op": "move", "from": "/biscuits", "path": "/cookies"} + ] + )"_json; + + // The result + json result = R"( + { + "cookies": [ + {"name": "Digestive"}, + {"name": "Choco Liebniz"} + ] + } + )"_json; + + // check if patched value is as expected + CHECK(doc.patch(patch) == result); + + // check roundtrip + CHECK(doc.patch(json::diff(doc, result)) == result); + } + + SECTION("test") + { + // The patch + json patch = R"( + [ + {"op": "test", "path": "/best_biscuit/name", "value": "Choco Liebniz"} + ] + )"_json; + + // the test will fail + CHECK_THROWS_AS(doc.patch(patch), std::domain_error); + CHECK_THROWS_WITH(doc.patch(patch), "unsuccessful: " + patch[0].dump()); + } + } + } + + SECTION("Examples from bruth.github.io/jsonpatch-js") + { + SECTION("add") + { + CHECK(R"( {} )"_json.patch( + R"( [{"op": "add", "path": "/foo", "value": "bar"}] )"_json + ) == R"( {"foo": "bar"} )"_json); + + CHECK(R"( {"foo": [1, 3]} )"_json.patch( + R"( [{"op": "add", "path": "/foo", "value": "bar"}] )"_json + ) == R"( {"foo": "bar"} )"_json); + + CHECK(R"( {"foo": [{}]} )"_json.patch( + R"( [{"op": "add", "path": "/foo/0/bar", "value": "baz"}] )"_json + ) == R"( {"foo": [{"bar": "baz"}]} )"_json); + } + + SECTION("remove") + { + CHECK(R"( {"foo": "bar"} )"_json.patch( + R"( [{"op": "remove", "path": "/foo"}] )"_json + ) == R"( {} )"_json); + + CHECK(R"( {"foo": [1, 2, 3]} )"_json.patch( + R"( [{"op": "remove", "path": "/foo/1"}] )"_json + ) == R"( {"foo": [1, 3]} )"_json); + + CHECK(R"( {"foo": [{"bar": "baz"}]} )"_json.patch( + R"( [{"op": "remove", "path": "/foo/0/bar"}] )"_json + ) == R"( {"foo": [{}]} )"_json); + } + + SECTION("replace") + { + CHECK(R"( {"foo": "bar"} )"_json.patch( + R"( [{"op": "replace", "path": "/foo", "value": 1}] )"_json + ) == R"( {"foo": 1} )"_json); + + CHECK(R"( {"foo": [1, 2, 3]} )"_json.patch( + R"( [{"op": "replace", "path": "/foo/1", "value": 4}] )"_json + ) == R"( {"foo": [1, 4, 3]} )"_json); + + CHECK(R"( {"foo": [{"bar": "baz"}]} )"_json.patch( + R"( [{"op": "replace", "path": "/foo/0/bar", "value": 1}] )"_json + ) == R"( {"foo": [{"bar": 1}]} )"_json); + } + + SECTION("move") + { + CHECK(R"( {"foo": [1, 2, 3]} )"_json.patch( + R"( [{"op": "move", "from": "/foo", "path": "/bar"}] )"_json + ) == R"( {"bar": [1, 2, 3]} )"_json); + } + + SECTION("copy") + { + CHECK(R"( {"foo": [1, 2, 3]} )"_json.patch( + R"( [{"op": "copy", "from": "/foo/1", "path": "/bar"}] )"_json + ) == R"( {"foo": [1, 2, 3], "bar": 2} )"_json); + } + + SECTION("copy") + { + CHECK_NOTHROW(R"( {"foo": "bar"} )"_json.patch( + R"( [{"op": "test", "path": "/foo", "value": "bar"}] )"_json)); + } + } +} diff --git a/test/src/unit-json_pointer.cpp b/test/src/unit-json_pointer.cpp new file mode 100644 index 00000000..b14a4593 --- /dev/null +++ b/test/src/unit-json_pointer.cpp @@ -0,0 +1,388 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#define private public +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("JSON pointers") +{ + SECTION("errors") + { + CHECK_THROWS_AS(json::json_pointer("foo"), std::domain_error); + CHECK_THROWS_WITH(json::json_pointer("foo"), "JSON pointer must be empty or begin with '/'"); + + CHECK_THROWS_AS(json::json_pointer("/~~"), std::domain_error); + CHECK_THROWS_WITH(json::json_pointer("/~~"), "escape error: '~' must be followed with '0' or '1'"); + + CHECK_THROWS_AS(json::json_pointer("/~"), std::domain_error); + CHECK_THROWS_WITH(json::json_pointer("/~"), "escape error: '~' must be followed with '0' or '1'"); + + json::json_pointer p; + CHECK_THROWS_AS(p.top(), std::domain_error); + CHECK_THROWS_WITH(p.top(), "JSON pointer has no parent"); + CHECK_THROWS_AS(p.pop_back(), std::domain_error); + CHECK_THROWS_WITH(p.pop_back(), "JSON pointer has no parent"); + } + + SECTION("examples from RFC 6901") + { + SECTION("nonconst access") + { + json j = R"( + { + "foo": ["bar", "baz"], + "": 0, + "a/b": 1, + "c%d": 2, + "e^f": 3, + "g|h": 4, + "i\\j": 5, + "k\"l": 6, + " ": 7, + "m~n": 8 + } + )"_json; + + // the whole document + CHECK(j[json::json_pointer()] == j); + CHECK(j[json::json_pointer("")] == j); + + // array access + CHECK(j[json::json_pointer("/foo")] == j["foo"]); + CHECK(j[json::json_pointer("/foo/0")] == j["foo"][0]); + CHECK(j[json::json_pointer("/foo/1")] == j["foo"][1]); + CHECK(j["/foo/1"_json_pointer] == j["foo"][1]); + + // checked array access + CHECK(j.at(json::json_pointer("/foo/0")) == j["foo"][0]); + CHECK(j.at(json::json_pointer("/foo/1")) == j["foo"][1]); + + // empty string access + CHECK(j[json::json_pointer("/")] == j[""]); + + // other cases + CHECK(j[json::json_pointer("/ ")] == j[" "]); + CHECK(j[json::json_pointer("/c%d")] == j["c%d"]); + CHECK(j[json::json_pointer("/e^f")] == j["e^f"]); + CHECK(j[json::json_pointer("/g|h")] == j["g|h"]); + CHECK(j[json::json_pointer("/i\\j")] == j["i\\j"]); + CHECK(j[json::json_pointer("/k\"l")] == j["k\"l"]); + + // checked access + CHECK(j.at(json::json_pointer("/ ")) == j[" "]); + CHECK(j.at(json::json_pointer("/c%d")) == j["c%d"]); + CHECK(j.at(json::json_pointer("/e^f")) == j["e^f"]); + CHECK(j.at(json::json_pointer("/g|h")) == j["g|h"]); + CHECK(j.at(json::json_pointer("/i\\j")) == j["i\\j"]); + CHECK(j.at(json::json_pointer("/k\"l")) == j["k\"l"]); + + // escaped access + CHECK(j[json::json_pointer("/a~1b")] == j["a/b"]); + CHECK(j[json::json_pointer("/m~0n")] == j["m~n"]); + + // unescaped access + CHECK_THROWS_AS(j[json::json_pointer("/a/b")], std::out_of_range); + CHECK_THROWS_WITH(j[json::json_pointer("/a/b")], "unresolved reference token 'b'"); + // "/a/b" works for JSON {"a": {"b": 42}} + CHECK(json({{"a", {{"b", 42}}}})[json::json_pointer("/a/b")] == json(42)); + + // unresolved access + json j_primitive = 1; + CHECK_THROWS_AS(j_primitive["/foo"_json_pointer], std::out_of_range); + CHECK_THROWS_WITH(j_primitive["/foo"_json_pointer], "unresolved reference token 'foo'"); + CHECK_THROWS_AS(j_primitive.at("/foo"_json_pointer), std::out_of_range); + CHECK_THROWS_WITH(j_primitive.at("/foo"_json_pointer), "unresolved reference token 'foo'"); + } + + SECTION("const access") + { + const json j = R"( + { + "foo": ["bar", "baz"], + "": 0, + "a/b": 1, + "c%d": 2, + "e^f": 3, + "g|h": 4, + "i\\j": 5, + "k\"l": 6, + " ": 7, + "m~n": 8 + } + )"_json; + + // the whole document + CHECK(j[json::json_pointer()] == j); + CHECK(j[json::json_pointer("")] == j); + + // array access + CHECK(j[json::json_pointer("/foo")] == j["foo"]); + CHECK(j[json::json_pointer("/foo/0")] == j["foo"][0]); + CHECK(j[json::json_pointer("/foo/1")] == j["foo"][1]); + CHECK(j["/foo/1"_json_pointer] == j["foo"][1]); + + // checked array access + CHECK(j.at(json::json_pointer("/foo/0")) == j["foo"][0]); + CHECK(j.at(json::json_pointer("/foo/1")) == j["foo"][1]); + + // empty string access + CHECK(j[json::json_pointer("/")] == j[""]); + + // other cases + CHECK(j[json::json_pointer("/ ")] == j[" "]); + CHECK(j[json::json_pointer("/c%d")] == j["c%d"]); + CHECK(j[json::json_pointer("/e^f")] == j["e^f"]); + CHECK(j[json::json_pointer("/g|h")] == j["g|h"]); + CHECK(j[json::json_pointer("/i\\j")] == j["i\\j"]); + CHECK(j[json::json_pointer("/k\"l")] == j["k\"l"]); + + // checked access + CHECK(j.at(json::json_pointer("/ ")) == j[" "]); + CHECK(j.at(json::json_pointer("/c%d")) == j["c%d"]); + CHECK(j.at(json::json_pointer("/e^f")) == j["e^f"]); + CHECK(j.at(json::json_pointer("/g|h")) == j["g|h"]); + CHECK(j.at(json::json_pointer("/i\\j")) == j["i\\j"]); + CHECK(j.at(json::json_pointer("/k\"l")) == j["k\"l"]); + + // escaped access + CHECK(j[json::json_pointer("/a~1b")] == j["a/b"]); + CHECK(j[json::json_pointer("/m~0n")] == j["m~n"]); + + // unescaped access + CHECK_THROWS_AS(j.at(json::json_pointer("/a/b")), std::out_of_range); + CHECK_THROWS_WITH(j.at(json::json_pointer("/a/b")), "key 'a' not found"); + + // unresolved access + const json j_primitive = 1; + CHECK_THROWS_AS(j_primitive["/foo"_json_pointer], std::out_of_range); + CHECK_THROWS_WITH(j_primitive["/foo"_json_pointer], "unresolved reference token 'foo'"); + CHECK_THROWS_AS(j_primitive.at("/foo"_json_pointer), std::out_of_range); + CHECK_THROWS_WITH(j_primitive.at("/foo"_json_pointer), "unresolved reference token 'foo'"); + } + + SECTION("user-defined string literal") + { + json j = R"( + { + "foo": ["bar", "baz"], + "": 0, + "a/b": 1, + "c%d": 2, + "e^f": 3, + "g|h": 4, + "i\\j": 5, + "k\"l": 6, + " ": 7, + "m~n": 8 + } + )"_json; + + // the whole document + CHECK(j[""_json_pointer] == j); + + // array access + CHECK(j["/foo"_json_pointer] == j["foo"]); + CHECK(j["/foo/0"_json_pointer] == j["foo"][0]); + CHECK(j["/foo/1"_json_pointer] == j["foo"][1]); + } + } + + SECTION("array access") + { + SECTION("nonconst access") + { + json j = {1, 2, 3}; + const json j_const = j; + + // check reading access + CHECK(j["/0"_json_pointer] == j[0]); + CHECK(j["/1"_json_pointer] == j[1]); + CHECK(j["/2"_json_pointer] == j[2]); + + // assign to existing index + j["/1"_json_pointer] = 13; + CHECK(j[1] == json(13)); + + // assign to nonexisting index + j["/3"_json_pointer] = 33; + CHECK(j[3] == json(33)); + + // assign to nonexisting index (with gap) + j["/5"_json_pointer] = 55; + CHECK(j == json({1, 13, 3, 33, nullptr, 55})); + + // error with leading 0 + CHECK_THROWS_AS(j["/01"_json_pointer], std::domain_error); + CHECK_THROWS_WITH(j["/01"_json_pointer], "array index must not begin with '0'"); + CHECK_THROWS_AS(j_const["/01"_json_pointer], std::domain_error); + CHECK_THROWS_WITH(j_const["/01"_json_pointer], "array index must not begin with '0'"); + CHECK_THROWS_AS(j.at("/01"_json_pointer), std::domain_error); + CHECK_THROWS_WITH(j.at("/01"_json_pointer), "array index must not begin with '0'"); + CHECK_THROWS_AS(j_const.at("/01"_json_pointer), std::domain_error); + CHECK_THROWS_WITH(j_const.at("/01"_json_pointer), "array index must not begin with '0'"); + + // error with incorrect numbers + CHECK_THROWS_AS(j["/one"_json_pointer] = 1, std::invalid_argument); + + // assign to "-" + j["/-"_json_pointer] = 99; + CHECK(j == json({1, 13, 3, 33, nullptr, 55, 99})); + + // error when using "-" in const object + CHECK_THROWS_AS(j_const["/-"_json_pointer], std::out_of_range); + CHECK_THROWS_WITH(j_const["/-"_json_pointer], "array index '-' (3) is out of range"); + + // error when using "-" with at + CHECK_THROWS_AS(j.at("/-"_json_pointer), std::out_of_range); + CHECK_THROWS_WITH(j.at("/-"_json_pointer), "array index '-' (7) is out of range"); + CHECK_THROWS_AS(j_const.at("/-"_json_pointer), std::out_of_range); + CHECK_THROWS_WITH(j_const.at("/-"_json_pointer), "array index '-' (3) is out of range"); + } + + SECTION("const access") + { + const json j = {1, 2, 3}; + + // check reading access + CHECK(j["/0"_json_pointer] == j[0]); + CHECK(j["/1"_json_pointer] == j[1]); + CHECK(j["/2"_json_pointer] == j[2]); + + // assign to nonexisting index + CHECK_THROWS_AS(j.at("/3"_json_pointer), std::out_of_range); + CHECK_THROWS_WITH(j.at("/3"_json_pointer), "array index 3 is out of range"); + + // assign to nonexisting index (with gap) + CHECK_THROWS_AS(j.at("/5"_json_pointer), std::out_of_range); + CHECK_THROWS_WITH(j.at("/5"_json_pointer), "array index 5 is out of range"); + + // assign to "-" + CHECK_THROWS_AS(j["/-"_json_pointer], std::out_of_range); + CHECK_THROWS_WITH(j["/-"_json_pointer], "array index '-' (3) is out of range"); + CHECK_THROWS_AS(j.at("/-"_json_pointer), std::out_of_range); + CHECK_THROWS_WITH(j.at("/-"_json_pointer), "array index '-' (3) is out of range"); + } + + } + + SECTION("flatten") + { + json j = + { + {"pi", 3.141}, + {"happy", true}, + {"name", "Niels"}, + {"nothing", nullptr}, + { + "answer", { + {"everything", 42} + } + }, + {"list", {1, 0, 2}}, + { + "object", { + {"currency", "USD"}, + {"value", 42.99}, + {"", "empty string"}, + {"/", "slash"}, + {"~", "tilde"}, + {"~1", "tilde1"} + } + } + }; + + json j_flatten = + { + {"/pi", 3.141}, + {"/happy", true}, + {"/name", "Niels"}, + {"/nothing", nullptr}, + {"/answer/everything", 42}, + {"/list/0", 1}, + {"/list/1", 0}, + {"/list/2", 2}, + {"/object/currency", "USD"}, + {"/object/value", 42.99}, + {"/object/", "empty string"}, + {"/object/~1", "slash"}, + {"/object/~0", "tilde"}, + {"/object/~01", "tilde1"} + }; + + // check if flattened result is as expected + CHECK(j.flatten() == j_flatten); + + // check if unflattened result is as expected + CHECK(j_flatten.unflatten() == j); + + // error for nonobjects + CHECK_THROWS_AS(json(1).unflatten(), std::domain_error); + CHECK_THROWS_WITH(json(1).unflatten(), "only objects can be unflattened"); + + // error for nonprimitve values + CHECK_THROWS_AS(json({{"/1", {1, 2, 3}}}).unflatten(), std::domain_error); + CHECK_THROWS_WITH(json({{"/1", {1, 2, 3}}}).unflatten(), "values in object must be primitive"); + + // error for conflicting values + json j_error = {{"", 42}, {"/foo", 17}}; + CHECK_THROWS_AS(j_error.unflatten(), std::domain_error); + CHECK_THROWS_WITH(j_error.unflatten(), "invalid value to unflatten"); + + // explicit roundtrip check + CHECK(j.flatten().unflatten() == j); + + // roundtrip for primitive values + json j_null; + CHECK(j_null.flatten().unflatten() == j_null); + json j_number = 42; + CHECK(j_number.flatten().unflatten() == j_number); + json j_boolean = false; + CHECK(j_boolean.flatten().unflatten() == j_boolean); + json j_string = "foo"; + CHECK(j_string.flatten().unflatten() == j_string); + + // roundtrip for empty structured values (will be unflattened to null) + json j_array(json::value_t::array); + CHECK(j_array.flatten().unflatten() == json()); + json j_object(json::value_t::object); + CHECK(j_object.flatten().unflatten() == json()); + } + + SECTION("string representation") + { + for (auto ptr : + {"", "/foo", "/foo/0", "/", "/a~1b", "/c%d", "/e^f", "/g|h", "/i\\j", "/k\"l", "/ ", "/m~0n" + }) + { + CHECK(json::json_pointer(ptr).to_string() == ptr); + } + } +} diff --git a/test/src/unit-modifiers.cpp b/test/src/unit-modifiers.cpp new file mode 100644 index 00000000..9cb33684 --- /dev/null +++ b/test/src/unit-modifiers.cpp @@ -0,0 +1,713 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("modifiers") +{ + SECTION("clear()") + { + SECTION("boolean") + { + json j = true; + + j.clear(); + CHECK(j == json(json::value_t::boolean)); + } + + SECTION("string") + { + json j = "hello world"; + + j.clear(); + CHECK(j == json(json::value_t::string)); + } + + SECTION("array") + { + SECTION("empty array") + { + json j = json::array(); + + j.clear(); + CHECK(j.empty()); + CHECK(j == json(json::value_t::array)); + } + + SECTION("filled array") + { + json j = {1, 2, 3}; + + j.clear(); + CHECK(j.empty()); + CHECK(j == json(json::value_t::array)); + } + } + + SECTION("object") + { + SECTION("empty object") + { + json j = json::object(); + + j.clear(); + CHECK(j.empty()); + CHECK(j == json(json::value_t::object)); + } + + SECTION("filled object") + { + json j = {{"one", 1}, {"two", 2}, {"three", 3}}; + + j.clear(); + CHECK(j.empty()); + CHECK(j == json(json::value_t::object)); + } + } + + SECTION("number (integer)") + { + json j = 23; + + j.clear(); + CHECK(j == json(json::value_t::number_integer)); + } + + SECTION("number (unsigned)") + { + json j = 23u; + + j.clear(); + CHECK(j == json(json::value_t::number_integer)); + } + + SECTION("number (float)") + { + json j = 23.42; + + j.clear(); + CHECK(j == json(json::value_t::number_float)); + } + + SECTION("null") + { + json j = nullptr; + + j.clear(); + CHECK(j == json(json::value_t::null)); + } + } + + SECTION("push_back()") + { + SECTION("to array") + { + SECTION("json&&") + { + SECTION("null") + { + json j; + j.push_back(1); + j.push_back(2); + CHECK(j.type() == json::value_t::array); + CHECK(j == json({1, 2})); + } + + SECTION("array") + { + json j = {1, 2, 3}; + j.push_back("Hello"); + CHECK(j.type() == json::value_t::array); + CHECK(j == json({1, 2, 3, "Hello"})); + } + + SECTION("other type") + { + json j = 1; + CHECK_THROWS_AS(j.push_back("Hello"), std::domain_error); + CHECK_THROWS_WITH(j.push_back("Hello"), "cannot use push_back() with number"); + } + } + + SECTION("const json&") + { + SECTION("null") + { + json j; + json k(1); + j.push_back(k); + j.push_back(k); + CHECK(j.type() == json::value_t::array); + CHECK(j == json({1, 1})); + } + + SECTION("array") + { + json j = {1, 2, 3}; + json k("Hello"); + j.push_back(k); + CHECK(j.type() == json::value_t::array); + CHECK(j == json({1, 2, 3, "Hello"})); + } + + SECTION("other type") + { + json j = 1; + json k("Hello"); + CHECK_THROWS_AS(j.push_back(k), std::domain_error); + CHECK_THROWS_WITH(j.push_back(k), "cannot use push_back() with number"); + } + } + } + + SECTION("to object") + { + SECTION("null") + { + json j; + j.push_back(json::object_t::value_type({"one", 1})); + j.push_back(json::object_t::value_type({"two", 2})); + CHECK(j.type() == json::value_t::object); + CHECK(j.size() == 2); + CHECK(j["one"] == json(1)); + CHECK(j["two"] == json(2)); + } + + SECTION("object") + { + json j(json::value_t::object); + j.push_back(json::object_t::value_type({"one", 1})); + j.push_back(json::object_t::value_type({"two", 2})); + CHECK(j.size() == 2); + CHECK(j["one"] == json(1)); + CHECK(j["two"] == json(2)); + } + + SECTION("other type") + { + json j = 1; + json k("Hello"); + CHECK_THROWS_AS(j.push_back(json::object_t::value_type({"one", 1})), std::domain_error); + CHECK_THROWS_WITH(j.push_back(json::object_t::value_type({"one", 1})), + "cannot use push_back() with number"); + } + } + + SECTION("with initializer_list") + { + SECTION("null") + { + json j; + j.push_back({"foo", "bar"}); + CHECK(j == json::array({{"foo", "bar"}})); + + json k; + k.push_back({1, 2, 3}); + CHECK(k == json::array({{1, 2, 3}})); + } + + SECTION("array") + { + json j = {1, 2, 3}; + j.push_back({"foo", "bar"}); + CHECK(j == json({1, 2, 3, {"foo", "bar"}})); + + json k = {1, 2, 3}; + k.push_back({1, 2, 3}); + CHECK(k == json({1, 2, 3, {1, 2, 3}})); + } + + SECTION("object") + { + json j = {{"key1", 1}}; + j.push_back({"key2", "bar"}); + CHECK(j == json({{"key1", 1}, {"key2", "bar"}})); + + json k = {{"key1", 1}}; + CHECK_THROWS_AS(k.push_back({1, 2, 3, 4}), std::domain_error); + CHECK_THROWS_WITH(k.push_back({1, 2, 3, 4}), "cannot use push_back() with object"); + } + } + } + + SECTION("operator+=") + { + SECTION("to array") + { + SECTION("json&&") + { + SECTION("null") + { + json j; + j += 1; + j += 2; + CHECK(j.type() == json::value_t::array); + CHECK(j == json({1, 2})); + } + + SECTION("array") + { + json j = {1, 2, 3}; + j += "Hello"; + CHECK(j.type() == json::value_t::array); + CHECK(j == json({1, 2, 3, "Hello"})); + } + + SECTION("other type") + { + json j = 1; + CHECK_THROWS_AS(j += "Hello", std::domain_error); + CHECK_THROWS_WITH(j += "Hello", "cannot use push_back() with number"); + } + } + + SECTION("const json&") + { + SECTION("null") + { + json j; + json k(1); + j += k; + j += k; + CHECK(j.type() == json::value_t::array); + CHECK(j == json({1, 1})); + } + + SECTION("array") + { + json j = {1, 2, 3}; + json k("Hello"); + j += k; + CHECK(j.type() == json::value_t::array); + CHECK(j == json({1, 2, 3, "Hello"})); + } + + SECTION("other type") + { + json j = 1; + json k("Hello"); + CHECK_THROWS_AS(j += k, std::domain_error); + CHECK_THROWS_WITH(j += k, "cannot use push_back() with number"); + } + } + } + + SECTION("to object") + { + SECTION("null") + { + json j; + j += json::object_t::value_type({"one", 1}); + j += json::object_t::value_type({"two", 2}); + CHECK(j.type() == json::value_t::object); + CHECK(j.size() == 2); + CHECK(j["one"] == json(1)); + CHECK(j["two"] == json(2)); + } + + SECTION("object") + { + json j(json::value_t::object); + j += json::object_t::value_type({"one", 1}); + j += json::object_t::value_type({"two", 2}); + CHECK(j.size() == 2); + CHECK(j["one"] == json(1)); + CHECK(j["two"] == json(2)); + } + + SECTION("other type") + { + json j = 1; + json k("Hello"); + CHECK_THROWS_AS(j += json::object_t::value_type({"one", 1}), std::domain_error); + CHECK_THROWS_WITH(j += json::object_t::value_type({"one", 1}), + "cannot use push_back() with number"); + } + } + + SECTION("with initializer_list") + { + SECTION("null") + { + json j; + j += {"foo", "bar"}; + CHECK(j == json::array({{"foo", "bar"}})); + + json k; + k += {1, 2, 3}; + CHECK(k == json::array({{1, 2, 3}})); + } + + SECTION("array") + { + json j = {1, 2, 3}; + j += {"foo", "bar"}; + CHECK(j == json({1, 2, 3, {"foo", "bar"}})); + + json k = {1, 2, 3}; + k += {1, 2, 3}; + CHECK(k == json({1, 2, 3, {1, 2, 3}})); + } + + SECTION("object") + { + json j = {{"key1", 1}}; + j += {"key2", "bar"}; + CHECK(j == json({{"key1", 1}, {"key2", "bar"}})); + + json k = {{"key1", 1}}; + CHECK_THROWS_AS((k += {1, 2, 3, 4}), std::domain_error); + CHECK_THROWS_WITH((k += {1, 2, 3, 4}), "cannot use push_back() with object"); + } + } + } + + SECTION("insert") + { + json j_array = {1, 2, 3, 4}; + json j_value = 5; + + SECTION("value at position") + { + SECTION("insert before begin()") + { + auto it = j_array.insert(j_array.begin(), j_value); + CHECK(j_array.size() == 5); + CHECK(*it == j_value); + CHECK(j_array.begin() == it); + CHECK(j_array == json({5, 1, 2, 3, 4})); + } + + SECTION("insert in the middle") + { + auto it = j_array.insert(j_array.begin() + 2, j_value); + CHECK(j_array.size() == 5); + CHECK(*it == j_value); + CHECK((it - j_array.begin()) == 2); + CHECK(j_array == json({1, 2, 5, 3, 4})); + } + + SECTION("insert before end()") + { + auto it = j_array.insert(j_array.end(), j_value); + CHECK(j_array.size() == 5); + CHECK(*it == j_value); + CHECK((j_array.end() - it) == 1); + CHECK(j_array == json({1, 2, 3, 4, 5})); + } + } + + SECTION("rvalue at position") + { + SECTION("insert before begin()") + { + auto it = j_array.insert(j_array.begin(), 5); + CHECK(j_array.size() == 5); + CHECK(*it == j_value); + CHECK(j_array.begin() == it); + CHECK(j_array == json({5, 1, 2, 3, 4})); + } + + SECTION("insert in the middle") + { + auto it = j_array.insert(j_array.begin() + 2, 5); + CHECK(j_array.size() == 5); + CHECK(*it == j_value); + CHECK((it - j_array.begin()) == 2); + CHECK(j_array == json({1, 2, 5, 3, 4})); + } + + SECTION("insert before end()") + { + auto it = j_array.insert(j_array.end(), 5); + CHECK(j_array.size() == 5); + CHECK(*it == j_value); + CHECK((j_array.end() - it) == 1); + CHECK(j_array == json({1, 2, 3, 4, 5})); + } + } + + SECTION("copies at position") + { + SECTION("insert before begin()") + { + auto it = j_array.insert(j_array.begin(), 3, 5); + CHECK(j_array.size() == 7); + CHECK(*it == j_value); + CHECK(j_array.begin() == it); + CHECK(j_array == json({5, 5, 5, 1, 2, 3, 4})); + } + + SECTION("insert in the middle") + { + auto it = j_array.insert(j_array.begin() + 2, 3, 5); + CHECK(j_array.size() == 7); + CHECK(*it == j_value); + CHECK((it - j_array.begin()) == 2); + CHECK(j_array == json({1, 2, 5, 5, 5, 3, 4})); + } + + SECTION("insert before end()") + { + auto it = j_array.insert(j_array.end(), 3, 5); + CHECK(j_array.size() == 7); + CHECK(*it == j_value); + CHECK((j_array.end() - it) == 3); + CHECK(j_array == json({1, 2, 3, 4, 5, 5, 5})); + } + + SECTION("insert nothing (count = 0)") + { + auto pos = j_array.end(); + auto it = j_array.insert(j_array.end(), 0, 5); + CHECK(j_array.size() == 4); + CHECK(it == pos); + CHECK(j_array == json({1, 2, 3, 4})); + } + } + + SECTION("range") + { + json j_other_array = {"first", "second"}; + + SECTION("proper usage") + { + auto it = j_array.insert(j_array.end(), j_other_array.begin(), j_other_array.end()); + CHECK(j_array.size() == 6); + CHECK(*it == *j_other_array.begin()); + CHECK((j_array.end() - it) == 2); + CHECK(j_array == json({1, 2, 3, 4, "first", "second"})); + } + + SECTION("empty range") + { + auto it = j_array.insert(j_array.end(), j_other_array.begin(), j_other_array.begin()); + CHECK(j_array.size() == 4); + CHECK(it == j_array.end()); + CHECK(j_array == json({1, 2, 3, 4})); + } + + SECTION("invalid iterators") + { + json j_other_array2 = {"first", "second"}; + + CHECK_THROWS_AS(j_array.insert(j_array.end(), j_array.begin(), j_array.end()), std::domain_error); + CHECK_THROWS_AS(j_array.insert(j_array.end(), j_other_array.begin(), j_other_array2.end()), + std::domain_error); + + CHECK_THROWS_WITH(j_array.insert(j_array.end(), j_array.begin(), j_array.end()), + "passed iterators may not belong to container"); + CHECK_THROWS_WITH(j_array.insert(j_array.end(), j_other_array.begin(), j_other_array2.end()), + "iterators do not fit"); + } + } + + SECTION("initializer list at position") + { + SECTION("insert before begin()") + { + auto it = j_array.insert(j_array.begin(), {7, 8, 9}); + CHECK(j_array.size() == 7); + CHECK(*it == json(7)); + CHECK(j_array.begin() == it); + CHECK(j_array == json({7, 8, 9, 1, 2, 3, 4})); + } + + SECTION("insert in the middle") + { + auto it = j_array.insert(j_array.begin() + 2, {7, 8, 9}); + CHECK(j_array.size() == 7); + CHECK(*it == json(7)); + CHECK((it - j_array.begin()) == 2); + CHECK(j_array == json({1, 2, 7, 8, 9, 3, 4})); + } + + SECTION("insert before end()") + { + auto it = j_array.insert(j_array.end(), {7, 8, 9}); + CHECK(j_array.size() == 7); + CHECK(*it == json(7)); + CHECK((j_array.end() - it) == 3); + CHECK(j_array == json({1, 2, 3, 4, 7, 8, 9})); + } + } + + SECTION("invalid iterator") + { + // pass iterator to a different array + json j_another_array = {1, 2}; + json j_yet_another_array = {"first", "second"}; + CHECK_THROWS_AS(j_array.insert(j_another_array.end(), 10), std::domain_error); + CHECK_THROWS_AS(j_array.insert(j_another_array.end(), j_value), std::domain_error); + CHECK_THROWS_AS(j_array.insert(j_another_array.end(), 10, 11), std::domain_error); + CHECK_THROWS_AS(j_array.insert(j_another_array.end(), j_yet_another_array.begin(), + j_yet_another_array.end()), std::domain_error); + CHECK_THROWS_AS(j_array.insert(j_another_array.end(), {1, 2, 3, 4}), std::domain_error); + + CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), 10), "iterator does not fit current value"); + CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), j_value), + "iterator does not fit current value"); + CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), 10, 11), + "iterator does not fit current value"); + CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), j_yet_another_array.begin(), + j_yet_another_array.end()), "iterator does not fit current value"); + CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), {1, 2, 3, 4}), + "iterator does not fit current value"); + } + + SECTION("non-array type") + { + // call insert on a non-array type + json j_nonarray = 3; + json j_yet_another_array = {"first", "second"}; + CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), 10), std::domain_error); + CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), j_value), std::domain_error); + CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), 10, 11), std::domain_error); + CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), j_yet_another_array.begin(), + j_yet_another_array.end()), std::domain_error); + CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), {1, 2, 3, 4}), std::domain_error); + + CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), 10), "cannot use insert() with number"); + CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), j_value), "cannot use insert() with number"); + CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), 10, 11), "cannot use insert() with number"); + CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), j_yet_another_array.begin(), + j_yet_another_array.end()), "cannot use insert() with number"); + CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), {1, 2, 3, 4}), + "cannot use insert() with number"); + } + } + + SECTION("swap()") + { + SECTION("json") + { + SECTION("member swap") + { + json j("hello world"); + json k(42.23); + + j.swap(k); + + CHECK(j == json(42.23)); + CHECK(k == json("hello world")); + } + + SECTION("nonmember swap") + { + json j("hello world"); + json k(42.23); + + std::swap(j, k); + + CHECK(j == json(42.23)); + CHECK(k == json("hello world")); + } + } + + SECTION("array_t") + { + SECTION("array_t type") + { + json j = {1, 2, 3, 4}; + json::array_t a = {"foo", "bar", "baz"}; + + j.swap(a); + + CHECK(j == json({"foo", "bar", "baz"})); + + j.swap(a); + + CHECK(j == json({1, 2, 3, 4})); + } + + SECTION("non-array_t type") + { + json j = 17; + json::array_t a = {"foo", "bar", "baz"}; + + CHECK_THROWS_AS(j.swap(a), std::domain_error); + CHECK_THROWS_WITH(j.swap(a), "cannot use swap() with number"); + } + } + + SECTION("object_t") + { + SECTION("object_t type") + { + json j = {{"one", 1}, {"two", 2}}; + json::object_t o = {{"cow", "Kuh"}, {"chicken", "Huhn"}}; + + j.swap(o); + + CHECK(j == json({{"cow", "Kuh"}, {"chicken", "Huhn"}})); + + j.swap(o); + + CHECK(j == json({{"one", 1}, {"two", 2}})); + } + + SECTION("non-object_t type") + { + json j = 17; + json::object_t o = {{"cow", "Kuh"}, {"chicken", "Huhn"}}; + + CHECK_THROWS_AS(j.swap(o), std::domain_error); + CHECK_THROWS_WITH(j.swap(o), "cannot use swap() with number"); + } + } + + SECTION("string_t") + { + SECTION("string_t type") + { + json j = "Hello world"; + json::string_t s = "Hallo Welt"; + + j.swap(s); + + CHECK(j == json("Hallo Welt")); + + j.swap(s); + + CHECK(j == json("Hello world")); + } + + SECTION("non-string_t type") + { + json j = 17; + json::string_t s = "Hallo Welt"; + + CHECK_THROWS_AS(j.swap(s), std::domain_error); + CHECK_THROWS_WITH(j.swap(s), "cannot use swap() with number"); + } + } + } +} diff --git a/test/src/unit-pointer_access.cpp b/test/src/unit-pointer_access.cpp new file mode 100644 index 00000000..bae9ee4c --- /dev/null +++ b/test/src/unit-pointer_access.cpp @@ -0,0 +1,263 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("pointer access") +{ + // create a JSON value with different types + json json_types = + { + {"boolean", true}, + { + "number", { + {"integer", 42}, + {"unsigned", 42u}, + {"floating-point", 17.23} + } + }, + {"string", "Hello, world!"}, + {"array", {1, 2, 3, 4, 5}}, + {"null", nullptr} + }; + + SECTION("pointer access to object_t") + { + using test_type = json::object_t; + json value = {{"one", 1}, {"two", 2}}; + + // check if pointers are returned correctly + test_type* p1 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p1 == value.get()); + + const test_type* p2 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p2 == value.get()); + + const test_type* const p3 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p3 == value.get()); + + // check if null pointers are returned correctly + CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + } + + SECTION("pointer access to const object_t") + { + using test_type = json::object_t; + const json value = {{"one", 1}, {"two", 2}}; + + // this should not compile + // test_type* p1 = value.get_ptr(); + + // check if pointers are returned correctly + const test_type* p2 = value.get_ptr(); + CHECK(*p2 == value.get()); + + const test_type* const p3 = value.get_ptr(); + CHECK(p2 == p3); + } + + SECTION("pointer access to array_t") + { + using test_type = json::array_t; + json value = {1, 2, 3, 4}; + + // check if pointers are returned correctly + test_type* p1 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p1 == value.get()); + + const test_type* p2 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p2 == value.get()); + + const test_type* const p3 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p3 == value.get()); + + // check if null pointers are returned correctly + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + } + + SECTION("pointer access to string_t") + { + using test_type = json::string_t; + json value = "hello"; + + // check if pointers are returned correctly + test_type* p1 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p1 == value.get()); + + const test_type* p2 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p2 == value.get()); + + const test_type* const p3 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p3 == value.get()); + + // check if null pointers are returned correctly + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + } + + SECTION("pointer access to boolean_t") + { + using test_type = json::boolean_t; + json value = false; + + // check if pointers are returned correctly + test_type* p1 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p1 == value.get()); + + const test_type* p2 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p2 == value.get()); + + const test_type* const p3 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p3 == value.get()); + + // check if null pointers are returned correctly + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + } + + SECTION("pointer access to number_integer_t") + { + using test_type = json::number_integer_t; + json value = 23; + + // check if pointers are returned correctly + test_type* p1 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p1 == value.get()); + + const test_type* p2 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p2 == value.get()); + + const test_type* const p3 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p3 == value.get()); + + // check if null pointers are returned correctly + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + } + + SECTION("pointer access to number_unsigned_t") + { + using test_type = json::number_unsigned_t; + json value = 23u; + + // check if pointers are returned correctly + test_type* p1 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p1 == value.get()); + + const test_type* p2 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p2 == value.get()); + + const test_type* const p3 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p3 == value.get()); + + // check if null pointers are returned correctly + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() == nullptr); + } + + SECTION("pointer access to number_float_t") + { + using test_type = json::number_float_t; + json value = 42.23; + + // check if pointers are returned correctly + test_type* p1 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p1 == Approx(value.get())); + + const test_type* p2 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p2 == Approx(value.get())); + + const test_type* const p3 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p3 == Approx(value.get())); + + // check if null pointers are returned correctly + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() != nullptr); + } +} diff --git a/test/src/unit-readme.cpp b/test/src/unit-readme.cpp new file mode 100644 index 00000000..7e7ef6b2 --- /dev/null +++ b/test/src/unit-readme.cpp @@ -0,0 +1,299 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +#include +#include +#include +#include +#include + +TEST_CASE("README", "[hide]") +{ + { + // redirect std::cout for the README file + auto old_cout_buffer = std::cout.rdbuf(); + std::ostringstream new_stream; + std::cout.rdbuf(new_stream.rdbuf()); + { + // create an empty structure (null) + json j; + + // add a number that is stored as double (note the implicit conversion of j to an object) + j["pi"] = 3.141; + + // add a Boolean that is stored as bool + j["happy"] = true; + + // add a string that is stored as std::string + j["name"] = "Niels"; + + // add another null object by passing nullptr + j["nothing"] = nullptr; + + // add an object inside the object + j["answer"]["everything"] = 42; + + // add an array that is stored as std::vector (using an initializer list) + j["list"] = { 1, 0, 2 }; + + // add another object (using an initializer list of pairs) + j["object"] = { {"currency", "USD"}, {"value", 42.99} }; + + // instead, you could also write (which looks very similar to the JSON above) + json j2 = + { + {"pi", 3.141}, + {"happy", true}, + {"name", "Niels"}, + {"nothing", nullptr}, + { + "answer", { + {"everything", 42} + } + }, + {"list", {1, 0, 2}}, + { + "object", { + {"currency", "USD"}, + {"value", 42.99} + } + } + }; + } + + { + // ways to express the empty array [] + json empty_array_implicit = {{}}; + json empty_array_explicit = json::array(); + + // a way to express the empty object {} + json empty_object_explicit = json::object(); + + // a way to express an _array_ of key/value pairs [["currency", "USD"], ["value", 42.99]] + json array_not_object = { json::array({"currency", "USD"}), json::array({"value", 42.99}) }; + } + + { + // create object from string literal + json j = "{ \"happy\": true, \"pi\": 3.141 }"_json; + + // or even nicer with a raw string literal + auto j2 = R"( + { + "happy": true, + "pi": 3.141 + } + )"_json; + + // or explicitly + auto j3 = json::parse("{ \"happy\": true, \"pi\": 3.141 }"); + + // explicit conversion to string + std::string s = j.dump(); // {\"happy\":true,\"pi\":3.141} + + // serialization with pretty printing + // pass in the amount of spaces to indent + std::cout << j.dump(4) << std::endl; + // { + // "happy": true, + // "pi": 3.141 + // } + + std::cout << std::setw(2) << j << std::endl; + } + + { + // create an array using push_back + json j; + j.push_back("foo"); + j.push_back(1); + j.push_back(true); + + // iterate the array + for (json::iterator it = j.begin(); it != j.end(); ++it) + { + std::cout << *it << '\n'; + } + + // range-based for + for (auto element : j) + { + std::cout << element << '\n'; + } + + // getter/setter + const std::string tmp = j[0]; + j[1] = 42; + bool foo = j.at(2); + + // other stuff + j.size(); // 3 entries + j.empty(); // false + j.type(); // json::value_t::array + j.clear(); // the array is empty again + + // comparison + j == "[\"foo\", 1, true]"_json; // true + + // create an object + json o; + o["foo"] = 23; + o["bar"] = false; + o["baz"] = 3.141; + + // find an entry + if (o.find("foo") != o.end()) + { + // there is an entry with key "foo" + } + } + + { + std::vector c_vector {1, 2, 3, 4}; + json j_vec(c_vector); + // [1, 2, 3, 4] + + std::deque c_deque {1.2f, 2.3f, 3.4f, 5.6f}; + json j_deque(c_deque); + // [1.2, 2.3, 3.4, 5.6] + + std::list c_list {true, true, false, true}; + json j_list(c_list); + // [true, true, false, true] + + std::forward_list c_flist {12345678909876, 23456789098765, 34567890987654, 45678909876543}; + json j_flist(c_flist); + // [12345678909876, 23456789098765, 34567890987654, 45678909876543] + + std::array c_array {{1, 2, 3, 4}}; + json j_array(c_array); + // [1, 2, 3, 4] + + std::set c_set {"one", "two", "three", "four", "one"}; + json j_set(c_set); // only one entry for "one" is used + // ["four", "one", "three", "two"] + + std::unordered_set c_uset {"one", "two", "three", "four", "one"}; + json j_uset(c_uset); // only one entry for "one" is used + // maybe ["two", "three", "four", "one"] + + std::multiset c_mset {"one", "two", "one", "four"}; + json j_mset(c_mset); // only one entry for "one" is used + // maybe ["one", "two", "four"] + + std::unordered_multiset c_umset {"one", "two", "one", "four"}; + json j_umset(c_umset); // both entries for "one" are used + // maybe ["one", "two", "one", "four"] + } + + { + std::map c_map { {"one", 1}, {"two", 2}, {"three", 3} }; + json j_map(c_map); + // {"one": 1, "two": 2, "three": 3} + + std::unordered_map c_umap { {"one", 1.2f}, {"two", 2.3f}, {"three", 3.4f} }; + json j_umap(c_umap); + // {"one": 1.2, "two": 2.3, "three": 3.4} + + std::multimap c_mmap { {"one", true}, {"two", true}, {"three", false}, {"three", true} }; + json j_mmap(c_mmap); // only one entry for key "three" is used + // maybe {"one": true, "two": true, "three": true} + + std::unordered_multimap c_ummap { {"one", true}, {"two", true}, {"three", false}, {"three", true} }; + json j_ummap(c_ummap); // only one entry for key "three" is used + // maybe {"one": true, "two": true, "three": true} + } + + { + // strings + std::string s1 = "Hello, world!"; + json js = s1; + std::string s2 = js; + + // Booleans + bool b1 = true; + json jb = b1; + bool b2 = jb; + + // numbers + int i = 42; + json jn = i; + double f = jn; + + // etc. + + std::string vs = js.get(); + bool vb = jb.get(); + int vi = jn.get(); + + // etc. + } + + { + // a JSON value + json j_original = R"({ + "baz": ["one", "two", "three"], + "foo": "bar" + })"_json; + + // access members with a JSON pointer (RFC 6901) + j_original["/baz/1"_json_pointer]; + // "two" + + // a JSON patch (RFC 6902) + json j_patch = R"([ + { "op": "replace", "path": "/baz", "value": "boo" }, + { "op": "add", "path": "/hello", "value": ["world"] }, + { "op": "remove", "path": "/foo"} + ])"_json; + + // apply the patch + json j_result = j_original.patch(j_patch); + // { + // "baz": "boo", + // "hello": ["world"] + // } + + // calculate a JSON patch from two JSON values + json::diff(j_result, j_original); + // [ + // { "op":" replace", "path": "/baz", "value": ["one", "two", "three"] }, + // { "op":"remove","path":"/hello" }, + // { "op":"add","path":"/foo","value":"bar" } + // ] + } + + // restore old std::cout + std::cout.rdbuf(old_cout_buffer); + } +} diff --git a/test/src/unit-reference_access.cpp b/test/src/unit-reference_access.cpp new file mode 100644 index 00000000..922c4825 --- /dev/null +++ b/test/src/unit-reference_access.cpp @@ -0,0 +1,202 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("reference access") +{ + // create a JSON value with different types + json json_types = + { + {"boolean", true}, + { + "number", { + {"integer", 42}, + {"floating-point", 17.23} + } + }, + {"string", "Hello, world!"}, + {"array", {1, 2, 3, 4, 5}}, + {"null", nullptr} + }; + + SECTION("reference access to object_t") + { + using test_type = json::object_t; + json value = {{"one", 1}, {"two", 2}}; + + // check if references are returned correctly + test_type& p1 = value.get_ref(); + CHECK(&p1 == value.get_ptr()); + CHECK(p1 == value.get()); + + const test_type& p2 = value.get_ref(); + CHECK(&p2 == value.get_ptr()); + CHECK(p2 == value.get()); + + // check if mismatching references throw correctly + CHECK_NOTHROW(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + } + + SECTION("const reference access to const object_t") + { + using test_type = json::object_t; + const json value = {{"one", 1}, {"two", 2}}; + + // this should not compile + // test_type& p1 = value.get_ref(); + + // check if references are returned correctly + const test_type& p2 = value.get_ref(); + CHECK(&p2 == value.get_ptr()); + CHECK(p2 == value.get()); + } + + SECTION("reference access to array_t") + { + using test_type = json::array_t; + json value = {1, 2, 3, 4}; + + // check if references are returned correctly + test_type& p1 = value.get_ref(); + CHECK(&p1 == value.get_ptr()); + CHECK(p1 == value.get()); + + const test_type& p2 = value.get_ref(); + CHECK(&p2 == value.get_ptr()); + CHECK(p2 == value.get()); + + // check if mismatching references throw correctly + CHECK_THROWS(value.get_ref()); + CHECK_NOTHROW(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + } + + SECTION("reference access to string_t") + { + using test_type = json::string_t; + json value = "hello"; + + // check if references are returned correctly + test_type& p1 = value.get_ref(); + CHECK(&p1 == value.get_ptr()); + CHECK(p1 == value.get()); + + const test_type& p2 = value.get_ref(); + CHECK(&p2 == value.get_ptr()); + CHECK(p2 == value.get()); + + // check if mismatching references throw correctly + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_NOTHROW(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + } + + SECTION("reference access to boolean_t") + { + using test_type = json::boolean_t; + json value = false; + + // check if references are returned correctly + test_type& p1 = value.get_ref(); + CHECK(&p1 == value.get_ptr()); + CHECK(p1 == value.get()); + + const test_type& p2 = value.get_ref(); + CHECK(&p2 == value.get_ptr()); + CHECK(p2 == value.get()); + + // check if mismatching references throw correctly + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_NOTHROW(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + } + + SECTION("reference access to number_integer_t") + { + using test_type = json::number_integer_t; + json value = 23; + + // check if references are returned correctly + test_type& p1 = value.get_ref(); + CHECK(&p1 == value.get_ptr()); + CHECK(p1 == value.get()); + + const test_type& p2 = value.get_ref(); + CHECK(&p2 == value.get_ptr()); + CHECK(p2 == value.get()); + + // check if mismatching references throw correctly + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_NOTHROW(value.get_ref()); + CHECK_THROWS(value.get_ref()); + } + + SECTION("reference access to number_float_t") + { + using test_type = json::number_float_t; + json value = 42.23; + + // check if references are returned correctly + test_type& p1 = value.get_ref(); + CHECK(&p1 == value.get_ptr()); + CHECK(p1 == value.get()); + + const test_type& p2 = value.get_ref(); + CHECK(&p2 == value.get_ptr()); + CHECK(p2 == value.get()); + + // check if mismatching references throw correctly + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_THROWS(value.get_ref()); + CHECK_NOTHROW(value.get_ref()); + } +} diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp new file mode 100644 index 00000000..da4c3d23 --- /dev/null +++ b/test/src/unit-regression.cpp @@ -0,0 +1,443 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("regression tests") +{ + SECTION("issue #60 - Double quotation mark is not parsed correctly") + { + SECTION("escape_dobulequote") + { + auto s = "[\"\\\"foo\\\"\"]"; + json j = json::parse(s); + auto expected = R"(["\"foo\""])"_json; + CHECK(j == expected); + } + } + + SECTION("issue #70 - Handle infinity and NaN cases") + { + SECTION("NAN value") + { + CHECK(json(NAN) == json()); + CHECK(json(json::number_float_t(NAN)) == json()); + } + + SECTION("infinity") + { + CHECK(json(INFINITY) == json()); + CHECK(json(json::number_float_t(INFINITY)) == json()); + } + } + + SECTION("pull request #71 - handle enum type") + { + enum { t = 0 }; + json j = json::array(); + j.push_back(t); + + j.push_back(json::object( + { + {"game_type", t} + })); + } + + SECTION("issue #76 - dump() / parse() not idempotent") + { + // create JSON object + json fields; + fields["one"] = std::string("one"); + fields["two"] = std::string("two three"); + fields["three"] = std::string("three \"four\""); + + // create another JSON object by deserializing the serialization + std::string payload = fields.dump(); + json parsed_fields = json::parse(payload); + + // check individual fields to match both objects + CHECK(parsed_fields["one"] == fields["one"]); + CHECK(parsed_fields["two"] == fields["two"]); + CHECK(parsed_fields["three"] == fields["three"]); + + // check individual fields to match original input + CHECK(parsed_fields["one"] == std::string("one")); + CHECK(parsed_fields["two"] == std::string("two three")); + CHECK(parsed_fields["three"] == std::string("three \"four\"")); + + // check equality of the objects + CHECK(parsed_fields == fields); + + // check equality of the serialized objects + CHECK(fields.dump() == parsed_fields.dump()); + + // check everything in one line + CHECK(fields == json::parse(fields.dump())); + } + + SECTION("issue #82 - lexer::get_number return NAN") + { + const auto content = R"( + { + "Test":"Test1", + "Number":100, + "Foo":42.42 + })"; + + std::stringstream ss; + ss << content; + json j; + ss >> j; + + std::string test = j["Test"]; + CHECK(test == "Test1"); + int number = j["Number"]; + CHECK(number == 100); + float foo = j["Foo"]; + CHECK(foo == Approx(42.42)); + } + + SECTION("issue #89 - nonstandard integer type") + { + // create JSON class with nonstandard integer number type + using custom_json = + nlohmann::basic_json; + custom_json j; + j["int_1"] = 1; + // we need to cast to int to compile with Catch - the value is int32_t + CHECK(static_cast(j["int_1"]) == 1); + + // tests for correct handling of non-standard integers that overflow the type selected by the user + + // unsigned integer object creation - expected to wrap and still be stored as an integer + j = 4294967296U; // 2^32 + CHECK(static_cast(j.type()) == static_cast(custom_json::value_t::number_unsigned)); + CHECK(j.get() == 0); // Wrap + + // unsigned integer parsing - expected to overflow and be stored as a float + j = custom_json::parse("4294967296"); // 2^32 + CHECK(static_cast(j.type()) == static_cast(custom_json::value_t::number_float)); + CHECK(j.get() == 4294967296.0f); + + // integer object creation - expected to wrap and still be stored as an integer + j = -2147483649LL; // -2^31-1 + CHECK(static_cast(j.type()) == static_cast(custom_json::value_t::number_integer)); + CHECK(j.get() == 2147483647); // Wrap + + // integer parsing - expected to overflow and be stored as a float with rounding + j = custom_json::parse("-2147483649"); // -2^31 + CHECK(static_cast(j.type()) == static_cast(custom_json::value_t::number_float)); + CHECK(j.get() == -2147483650.0f); + } + + SECTION("issue #93 reverse_iterator operator inheritance problem") + { + { + json a = {1, 2, 3}; + json::reverse_iterator rit = a.rbegin(); + ++rit; + CHECK(*rit == json(2)); + CHECK(rit.value() == json(2)); + } + { + json a = {1, 2, 3}; + json::reverse_iterator rit = ++a.rbegin(); + } + { + json a = {1, 2, 3}; + json::reverse_iterator rit = a.rbegin(); + ++rit; + json b = {0, 0, 0}; + std::transform(rit, a.rend(), b.rbegin(), [](json el) + { + return el; + }); + CHECK(b == json({0, 1, 2})); + } + { + json a = {1, 2, 3}; + json b = {0, 0, 0}; + std::transform(++a.rbegin(), a.rend(), b.rbegin(), [](json el) + { + return el; + }); + CHECK(b == json({0, 1, 2})); + } + } + + SECTION("issue #100 - failed to iterator json object with reverse_iterator") + { + json config = + { + { "111", 111 }, + { "112", 112 }, + { "113", 113 } + }; + + std::stringstream ss; + + for (auto it = config.begin(); it != config.end(); ++it) + { + ss << it.key() << ": " << it.value() << '\n'; + } + + for (auto it = config.rbegin(); it != config.rend(); ++it) + { + ss << it.key() << ": " << it.value() << '\n'; + } + + CHECK(ss.str() == "111: 111\n112: 112\n113: 113\n113: 113\n112: 112\n111: 111\n"); + } + + SECTION("issue #101 - binary string causes numbers to be dumped as hex") + { + int64_t number = 10; + std::string bytes{"\x00" "asdf\n", 6}; + json j; + j["int64"] = number; + j["binary string"] = bytes; + // make sure the number is really printed as decimal "10" and not as + // hexadecimal "a" + CHECK(j.dump() == "{\"binary string\":\"\\u0000asdf\\n\",\"int64\":10}"); + } + + SECTION("issue #111 - subsequent unicode chars") + { + std::string bytes{0x7, 0x7}; + json j; + j["string"] = bytes; + CHECK(j["string"] == "\u0007\u0007"); + } + + SECTION("issue #144 - implicit assignment to std::string fails") + { + json o = {{"name", "value"}}; + + std::string s1 = o["name"]; + CHECK(s1 == "value"); + + std::string s2; + s2 = o["name"]; + + CHECK(s2 == "value"); + } + + SECTION("issue #146 - character following a surrogate pair is skipped") + { + CHECK(json::parse("\"\\ud80c\\udc60abc\"").get() == u8"\U00013060abc"); + } + + SECTION("issue #171 - Cannot index by key of type static constexpr const char*") + { + json j; + + // Non-const access with key as "char []" + char array_key[] = "Key1"; + CHECK_NOTHROW(j[array_key] = 1); + CHECK(j[array_key] == json(1)); + + // Non-const access with key as "const char[]" + const char const_array_key[] = "Key2"; + CHECK_NOTHROW(j[const_array_key] = 2); + CHECK(j[const_array_key] == json(2)); + + // Non-const access with key as "char *" + char _ptr_key[] = "Key3"; + char* ptr_key = &_ptr_key[0]; + CHECK_NOTHROW(j[ptr_key] = 3); + CHECK(j[ptr_key] == json(3)); + + // Non-const access with key as "const char *" + const char* const_ptr_key = "Key4"; + CHECK_NOTHROW(j[const_ptr_key] = 4); + CHECK(j[const_ptr_key] == json(4)); + + // Non-const access with key as "static constexpr const char *" + static constexpr const char* constexpr_ptr_key = "Key5"; + CHECK_NOTHROW(j[constexpr_ptr_key] = 5); + CHECK(j[constexpr_ptr_key] == json(5)); + + const json j_const = j; + + // Const access with key as "char []" + CHECK(j_const[array_key] == json(1)); + + // Const access with key as "const char[]" + CHECK(j_const[const_array_key] == json(2)); + + // Const access with key as "char *" + CHECK(j_const[ptr_key] == json(3)); + + // Const access with key as "const char *" + CHECK(j_const[const_ptr_key] == json(4)); + + // Const access with key as "static constexpr const char *" + CHECK(j_const[constexpr_ptr_key] == json(5)); + } + + SECTION("issue #186 miloyip/nativejson-benchmark: floating-point parsing") + { + json j; + + j = json::parse("-0.0"); + CHECK(j.get() == -0.0); + + j = json::parse("2.22507385850720113605740979670913197593481954635164564e-308"); + CHECK(j.get() == 2.2250738585072009e-308); + + j = json::parse("0.999999999999999944488848768742172978818416595458984374"); + CHECK(j.get() == 0.99999999999999989); + + j = json::parse("1.00000000000000011102230246251565404236316680908203126"); + CHECK(j.get() == 1.00000000000000022); + + j = json::parse("7205759403792793199999e-5"); + CHECK(j.get() == 72057594037927928.0); + + j = json::parse("922337203685477529599999e-5"); + CHECK(j.get() == 9223372036854774784.0); + + j = json::parse("1014120480182583464902367222169599999e-5"); + CHECK(j.get() == 10141204801825834086073718800384.0); + + j = json::parse("5708990770823839207320493820740630171355185151999e-3"); + CHECK(j.get() == 5708990770823838890407843763683279797179383808.0); + + // create JSON class with nonstandard float number type + + // float + nlohmann::basic_json j_float = + 1.23e25f; + CHECK(j_float.get() == 1.23e25f); + + // double + nlohmann::basic_json j_double = + 1.23e35f; + CHECK(j_double.get() == 1.23e35f); + + // long double + nlohmann::basic_json + j_long_double = 1.23e45L; + CHECK(j_long_double.get() == 1.23e45L); + } + + SECTION("issue #228 - double values are serialized with commas as decimal points") + { + json j1a = 23.42; + json j1b = json::parse("23.42"); + + json j2a = 2342e-2; + //issue #230 + //json j2b = json::parse("2342e-2"); + + json j3a = 10E3; + json j3b = json::parse("10E3"); + json j3c = json::parse("10e3"); + + // class to create a locale that would use a comma for decimals + class CommaDecimalSeparator : public std::numpunct + { + protected: + char do_decimal_point() const + { + return ','; + } + }; + + // change locale to mess with decimal points + std::locale::global(std::locale(std::locale(), new CommaDecimalSeparator)); + + CHECK(j1a.dump() == "23.42"); + CHECK(j1b.dump() == "23.42"); + + // check if locale is properly reset + std::stringstream ss; + ss.imbue(std::locale(std::locale(), new CommaDecimalSeparator)); + ss << 47.11; + CHECK(ss.str() == "47,11"); + ss << j1a; + CHECK(ss.str() == "47,1123.42"); + ss << 47.11; + CHECK(ss.str() == "47,1123.4247,11"); + + CHECK(j2a.dump() == "23.42"); + //issue #230 + //CHECK(j2b.dump() == "23.42"); + + CHECK(j3a.dump() == "10000"); + CHECK(j3b.dump() == "10000"); + CHECK(j3c.dump() == "10000"); + //CHECK(j3b.dump() == "1E04"); // roundtrip error + //CHECK(j3c.dump() == "1e04"); // roundtrip error + } + + SECTION("issue #233 - Can't use basic_json::iterator as a base iterator for std::move_iterator") + { + json source = {"a", "b", "c"}; + json expected = {"a", "b"}; + json dest; + + std::copy_n(std::make_move_iterator(source.begin()), 2, std::back_inserter(dest)); + + CHECK(dest == expected); + } + + SECTION("issue #235 - ambiguous overload for 'push_back' and 'operator+='") + { + json data = {{"key", "value"}}; + data.push_back({"key2", "value2"}); + data += {"key3", "value3"}; + + CHECK(data == json({{"key", "value"}, {"key2", "value2"}, {"key3", "value3"}})); + } + + SECTION("issue #269 - diff generates incorrect patch when removing multiple array elements") + { + json doc = R"( { "arr1": [1, 2, 3, 4] } )"_json; + json expected = R"( { "arr1": [1, 2] } )"_json; + + // check roundtrip + CHECK(doc.patch(json::diff(doc, expected)) == expected); + } + + SECTION("issue #283 - value() does not work with _json_pointer types") + { + json j = + { + {"object", {{"key1", 1}, {"key2", 2}}}, + }; + + int at_integer = j.at("/object/key2"_json_pointer); + int val_integer = j.value("/object/key2"_json_pointer, 0); + + CHECK(at_integer == val_integer); + } +} diff --git a/test/src/unit-serialization.cpp b/test/src/unit-serialization.cpp new file mode 100644 index 00000000..13333ff2 --- /dev/null +++ b/test/src/unit-serialization.cpp @@ -0,0 +1,76 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +TEST_CASE("serialization") +{ + SECTION("operator<<") + { + SECTION("no given width") + { + std::stringstream ss; + json j = {"foo", 1, 2, 3, false, {{"one", 1}}}; + ss << j; + CHECK(ss.str() == "[\"foo\",1,2,3,false,{\"one\":1}]"); + } + + SECTION("given width") + { + std::stringstream ss; + json j = {"foo", 1, 2, 3, false, {{"one", 1}}}; + ss << std::setw(4) << j; + CHECK(ss.str() == + "[\n \"foo\",\n 1,\n 2,\n 3,\n false,\n {\n \"one\": 1\n }\n]"); + } + } + + SECTION("operator>>") + { + SECTION("no given width") + { + std::stringstream ss; + json j = {"foo", 1, 2, 3, false, {{"one", 1}}}; + j >> ss; + CHECK(ss.str() == "[\"foo\",1,2,3,false,{\"one\":1}]"); + } + + SECTION("given width") + { + std::stringstream ss; + json j = {"foo", 1, 2, 3, false, {{"one", 1}}}; + ss.width(4); + j >> ss; + CHECK(ss.str() == + "[\n \"foo\",\n 1,\n 2,\n 3,\n false,\n {\n \"one\": 1\n }\n]"); + } + } +} diff --git a/test/src/unit-testsuites.cpp b/test/src/unit-testsuites.cpp new file mode 100644 index 00000000..54f09479 --- /dev/null +++ b/test/src/unit-testsuites.cpp @@ -0,0 +1,436 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#include "json.hpp" +using nlohmann::json; + +#include + +TEST_CASE("compliance tests from json.org") +{ + // test cases are from http://json.org/JSON_checker/ + + SECTION("expected failures") + { + for (auto filename : + { + //"test/data/json_tests/fail1.json", + "test/data/json_tests/fail2.json", + "test/data/json_tests/fail3.json", + "test/data/json_tests/fail4.json", + "test/data/json_tests/fail5.json", + "test/data/json_tests/fail6.json", + "test/data/json_tests/fail7.json", + "test/data/json_tests/fail8.json", + "test/data/json_tests/fail9.json", + "test/data/json_tests/fail10.json", + "test/data/json_tests/fail11.json", + "test/data/json_tests/fail12.json", + "test/data/json_tests/fail13.json", + "test/data/json_tests/fail14.json", + "test/data/json_tests/fail15.json", + "test/data/json_tests/fail16.json", + "test/data/json_tests/fail17.json", + //"test/data/json_tests/fail18.json", + "test/data/json_tests/fail19.json", + "test/data/json_tests/fail20.json", + "test/data/json_tests/fail21.json", + "test/data/json_tests/fail22.json", + "test/data/json_tests/fail23.json", + "test/data/json_tests/fail24.json", + "test/data/json_tests/fail25.json", + "test/data/json_tests/fail26.json", + "test/data/json_tests/fail27.json", + "test/data/json_tests/fail28.json", + "test/data/json_tests/fail29.json", + "test/data/json_tests/fail30.json", + "test/data/json_tests/fail31.json", + "test/data/json_tests/fail32.json", + "test/data/json_tests/fail33.json" + }) + { + CAPTURE(filename); + json j; + std::ifstream f(filename); + CHECK_THROWS_AS(j << f, std::invalid_argument); + } + } + + SECTION("expected passes") + { + for (auto filename : + { + "test/data/json_tests/pass1.json", + "test/data/json_tests/pass2.json", + "test/data/json_tests/pass3.json" + }) + { + CAPTURE(filename); + json j; + std::ifstream f(filename); + CHECK_NOTHROW(j << f); + } + } +} + +TEST_CASE("compliance tests from nativejson-benchmark") +{ + // test cases from https://github.com/miloyip/nativejson-benchmark/blob/master/src/main.cpp + + SECTION("doubles") + { + auto TEST_DOUBLE = [](const std::string & json_string, const double expected) + { + CAPTURE(json_string); + CAPTURE(expected); + CHECK(json::parse(json_string)[0].get() == Approx(expected)); + }; + + TEST_DOUBLE("[0.0]", 0.0); + TEST_DOUBLE("[-0.0]", -0.0); + TEST_DOUBLE("[1.0]", 1.0); + TEST_DOUBLE("[-1.0]", -1.0); + TEST_DOUBLE("[1.5]", 1.5); + TEST_DOUBLE("[-1.5]", -1.5); + TEST_DOUBLE("[3.1416]", 3.1416); + TEST_DOUBLE("[1E10]", 1E10); + TEST_DOUBLE("[1e10]", 1e10); + TEST_DOUBLE("[1E+10]", 1E+10); + TEST_DOUBLE("[1E-10]", 1E-10); + TEST_DOUBLE("[-1E10]", -1E10); + TEST_DOUBLE("[-1e10]", -1e10); + TEST_DOUBLE("[-1E+10]", -1E+10); + TEST_DOUBLE("[-1E-10]", -1E-10); + TEST_DOUBLE("[1.234E+10]", 1.234E+10); + TEST_DOUBLE("[1.234E-10]", 1.234E-10); + TEST_DOUBLE("[1.79769e+308]", 1.79769e+308); + TEST_DOUBLE("[2.22507e-308]", 2.22507e-308); + TEST_DOUBLE("[-1.79769e+308]", -1.79769e+308); + TEST_DOUBLE("[-2.22507e-308]", -2.22507e-308); + TEST_DOUBLE("[4.9406564584124654e-324]", 4.9406564584124654e-324); // minimum denormal + TEST_DOUBLE("[2.2250738585072009e-308]", 2.2250738585072009e-308); // Max subnormal double + TEST_DOUBLE("[2.2250738585072014e-308]", 2.2250738585072014e-308); // Min normal positive double + TEST_DOUBLE("[1.7976931348623157e+308]", 1.7976931348623157e+308); // Max double + TEST_DOUBLE("[1e-10000]", 0.0); // must underflow + TEST_DOUBLE("[18446744073709551616]", + 18446744073709551616.0); // 2^64 (max of uint64_t + 1, force to use double) + TEST_DOUBLE("[-9223372036854775809]", + -9223372036854775809.0); // -2^63 - 1(min of int64_t + 1, force to use double) + TEST_DOUBLE("[0.9868011474609375]", + 0.9868011474609375); // https://github.com/miloyip/rapidjson/issues/120 + TEST_DOUBLE("[123e34]", 123e34); // Fast Path Cases In Disguise + TEST_DOUBLE("[45913141877270640000.0]", 45913141877270640000.0); + TEST_DOUBLE("[2.2250738585072011e-308]", + 2.2250738585072011e-308); + //TEST_DOUBLE("[1e-00011111111111]", 0.0); + //TEST_DOUBLE("[-1e-00011111111111]", -0.0); + TEST_DOUBLE("[1e-214748363]", 0.0); + TEST_DOUBLE("[1e-214748364]", 0.0); + //TEST_DOUBLE("[1e-21474836311]", 0.0); + TEST_DOUBLE("[0.017976931348623157e+310]", 1.7976931348623157e+308); // Max double in another form + + // Since + // abs((2^-1022 - 2^-1074) - 2.2250738585072012e-308) = 3.109754131239141401123495768877590405345064751974375599... ¡Á 10^-324 + // abs((2^-1022) - 2.2250738585072012e-308) = 1.830902327173324040642192159804623318305533274168872044... ¡Á 10 ^ -324 + // So 2.2250738585072012e-308 should round to 2^-1022 = 2.2250738585072014e-308 + TEST_DOUBLE("[2.2250738585072012e-308]", + 2.2250738585072014e-308); + + // More closer to normal/subnormal boundary + // boundary = 2^-1022 - 2^-1075 = 2.225073858507201136057409796709131975934819546351645648... ¡Á 10^-308 + TEST_DOUBLE("[2.22507385850720113605740979670913197593481954635164564e-308]", + 2.2250738585072009e-308); + TEST_DOUBLE("[2.22507385850720113605740979670913197593481954635164565e-308]", + 2.2250738585072014e-308); + + // 1.0 is in (1.0 - 2^-54, 1.0 + 2^-53) + // 1.0 - 2^-54 = 0.999999999999999944488848768742172978818416595458984375 + TEST_DOUBLE("[0.999999999999999944488848768742172978818416595458984375]", 1.0); // round to even + TEST_DOUBLE("[0.999999999999999944488848768742172978818416595458984374]", + 0.99999999999999989); // previous double + TEST_DOUBLE("[0.999999999999999944488848768742172978818416595458984376]", 1.0); // next double + // 1.0 + 2^-53 = 1.00000000000000011102230246251565404236316680908203125 + TEST_DOUBLE("[1.00000000000000011102230246251565404236316680908203125]", 1.0); // round to even + TEST_DOUBLE("[1.00000000000000011102230246251565404236316680908203124]", 1.0); // previous double + TEST_DOUBLE("[1.00000000000000011102230246251565404236316680908203126]", + 1.00000000000000022); // next double + + // Numbers from https://github.com/floitsch/double-conversion/blob/master/test/cctest/test-strtod.cc + + TEST_DOUBLE("[72057594037927928.0]", 72057594037927928.0); + TEST_DOUBLE("[72057594037927936.0]", 72057594037927936.0); + TEST_DOUBLE("[72057594037927932.0]", 72057594037927936.0); + TEST_DOUBLE("[7205759403792793199999e-5]", 72057594037927928.0); + TEST_DOUBLE("[7205759403792793200001e-5]", 72057594037927936.0); + + TEST_DOUBLE("[9223372036854774784.0]", 9223372036854774784.0); + TEST_DOUBLE("[9223372036854775808.0]", 9223372036854775808.0); + TEST_DOUBLE("[9223372036854775296.0]", 9223372036854775808.0); + TEST_DOUBLE("[922337203685477529599999e-5]", 9223372036854774784.0); + TEST_DOUBLE("[922337203685477529600001e-5]", 9223372036854775808.0); + + TEST_DOUBLE("[10141204801825834086073718800384]", 10141204801825834086073718800384.0); + TEST_DOUBLE("[10141204801825835211973625643008]", 10141204801825835211973625643008.0); + TEST_DOUBLE("[10141204801825834649023672221696]", 10141204801825835211973625643008.0); + TEST_DOUBLE("[1014120480182583464902367222169599999e-5]", 10141204801825834086073718800384.0); + TEST_DOUBLE("[1014120480182583464902367222169600001e-5]", 10141204801825835211973625643008.0); + + TEST_DOUBLE("[5708990770823838890407843763683279797179383808]", + 5708990770823838890407843763683279797179383808.0); + TEST_DOUBLE("[5708990770823839524233143877797980545530986496]", + 5708990770823839524233143877797980545530986496.0); + TEST_DOUBLE("[5708990770823839207320493820740630171355185152]", + 5708990770823839524233143877797980545530986496.0); + TEST_DOUBLE("[5708990770823839207320493820740630171355185151999e-3]", + 5708990770823838890407843763683279797179383808.0); + TEST_DOUBLE("[5708990770823839207320493820740630171355185152001e-3]", + 5708990770823839524233143877797980545530986496.0); + + { + char n1e308[312]; // '1' followed by 308 '0' + n1e308[0] = '['; + n1e308[1] = '1'; + for (int j = 2; j < 310; j++) + { + n1e308[j] = '0'; + } + n1e308[310] = ']'; + n1e308[311] = '\0'; + TEST_DOUBLE(n1e308, 1E308); + } + + // Cover trimming + TEST_DOUBLE( + "[2.22507385850720113605740979670913197593481954635164564802342610972482222202107694551652952390813508" + "7914149158913039621106870086438694594645527657207407820621743379988141063267329253552286881372149012" + "9811224514518898490572223072852551331557550159143974763979834118019993239625482890171070818506906306" + "6665599493827577257201576306269066333264756530000924588831643303777979186961204949739037782970490505" + "1080609940730262937128958950003583799967207254304360284078895771796150945516748243471030702609144621" + "5722898802581825451803257070188608721131280795122334262883686223215037756666225039825343359745688844" + "2390026549819838548794829220689472168983109969836584681402285424333066033985088644580400103493397042" + "7567186443383770486037861622771738545623065874679014086723327636718751234567890123456789012345678901" + "e-308]", + 2.2250738585072014e-308); + } + + SECTION("strings") + { + auto TEST_STRING = [](const std::string & json_string, const std::string & expected) + { + CAPTURE(json_string); + CAPTURE(expected); + CHECK(json::parse(json_string)[0].get() == expected); + }; + + TEST_STRING("[\"\"]", ""); + TEST_STRING("[\"Hello\"]", "Hello"); + TEST_STRING("[\"Hello\\nWorld\"]", "Hello\nWorld"); + //TEST_STRING("[\"Hello\\u0000World\"]", "Hello\0World"); + TEST_STRING("[\"\\\"\\\\/\\b\\f\\n\\r\\t\"]", "\"\\/\b\f\n\r\t"); + TEST_STRING("[\"\\u0024\"]", "\x24"); // Dollar sign U+0024 + TEST_STRING("[\"\\u00A2\"]", "\xC2\xA2"); // Cents sign U+00A2 + TEST_STRING("[\"\\u20AC\"]", "\xE2\x82\xAC"); // Euro sign U+20AC + TEST_STRING("[\"\\uD834\\uDD1E\"]", "\xF0\x9D\x84\x9E"); // G clef sign U+1D11E + } + + SECTION("roundtrip") + { + // test cases are from https://github.com/miloyip/nativejson-benchmark/tree/master/test/data/roundtrip + + for (auto filename : + { + "test/data/json_roundtrip/roundtrip01.json", + "test/data/json_roundtrip/roundtrip02.json", + "test/data/json_roundtrip/roundtrip03.json", + "test/data/json_roundtrip/roundtrip04.json", + "test/data/json_roundtrip/roundtrip05.json", + "test/data/json_roundtrip/roundtrip06.json", + "test/data/json_roundtrip/roundtrip07.json", + "test/data/json_roundtrip/roundtrip08.json", + "test/data/json_roundtrip/roundtrip09.json", + "test/data/json_roundtrip/roundtrip10.json", + "test/data/json_roundtrip/roundtrip11.json", + "test/data/json_roundtrip/roundtrip12.json", + "test/data/json_roundtrip/roundtrip13.json", + "test/data/json_roundtrip/roundtrip14.json", + "test/data/json_roundtrip/roundtrip15.json", + "test/data/json_roundtrip/roundtrip16.json", + "test/data/json_roundtrip/roundtrip17.json", + "test/data/json_roundtrip/roundtrip18.json", + "test/data/json_roundtrip/roundtrip19.json", + "test/data/json_roundtrip/roundtrip20.json", + "test/data/json_roundtrip/roundtrip21.json", + "test/data/json_roundtrip/roundtrip22.json", + "test/data/json_roundtrip/roundtrip23.json", + //"test/data/json_roundtrip/roundtrip24.json", // roundtrip error + //"test/data/json_roundtrip/roundtrip25.json", // roundtrip error + //"test/data/json_roundtrip/roundtrip26.json", // roundtrip error + //"test/data/json_roundtrip/roundtrip27.json", // roundtrip error + //"test/data/json_roundtrip/roundtrip28.json", // roundtrip error + "test/data/json_roundtrip/roundtrip29.json", + //"test/data/json_roundtrip/roundtrip30.json", // roundtrip error + //"test/data/json_roundtrip/roundtrip31.json", // roundtrip error + "test/data/json_roundtrip/roundtrip32.json" + }) + { + CAPTURE(filename); + std::ifstream f(filename); + std::string json_string( (std::istreambuf_iterator(f) ), + (std::istreambuf_iterator()) ); + + json j = json::parse(json_string); + CHECK(j.dump() == json_string); + } + } +} + +TEST_CASE("test suite from json-test-suite") +{ + SECTION("read all sample.json") + { + // read a file with all unicode characters stored as single-character + // strings in a JSON array + std::ifstream f("test/data/json_testsuite/sample.json"); + json j; + CHECK_NOTHROW(j << f); + + // the array has 3 elements + CHECK(j.size() == 3); + } +} + +TEST_CASE("json.org examples") +{ + // here, we list all JSON values from http://json.org/example + + SECTION("1.json") + { + std::ifstream f("test/data/json.org/1.json"); + json j; + CHECK_NOTHROW(j << f); + } + + SECTION("2.json") + { + std::ifstream f("test/data/json.org/2.json"); + json j; + CHECK_NOTHROW(j << f); + } + + SECTION("3.json") + { + std::ifstream f("test/data/json.org/3.json"); + json j; + CHECK_NOTHROW(j << f); + } + + SECTION("4.json") + { + std::ifstream f("test/data/json.org/4.json"); + json j; + CHECK_NOTHROW(j << f); + } + + SECTION("5.json") + { + std::ifstream f("test/data/json.org/5.json"); + json j; + CHECK_NOTHROW(j << f); + } +} + +TEST_CASE("RFC 7159 examples") +{ + // here, we list all JSON values from the RFC 7159 document + + SECTION("7. Strings") + { + CHECK(json::parse("\"\\u005C\"") == json("\\")); + CHECK(json::parse("\"\\uD834\\uDD1E\"") == json("𝄞")); + CHECK(json::parse("\"𝄞\"") == json("𝄞")); + } + + SECTION("8.3 String Comparison") + { + CHECK(json::parse("\"a\\b\"") == json::parse("\"a\u005Cb\"")); + } + + SECTION("13 Examples") + { + { + CHECK_NOTHROW(json(R"( + { + "Image": { + "Width": 800, + "Height": 600, + "Title": "View from 15th Floor", + "Thumbnail": { + "Url": "http://www.example.com/image/481989943", + "Height": 125, + "Width": 100 + }, + "Animated" : false, + "IDs": [116, 943, 234, 38793] + } + } + )")); + } + + { + CHECK_NOTHROW(json(R"( + [ + { + "precision": "zip", + "Latitude": 37.7668, + "Longitude": -122.3959, + "Address": "", + "City": "SAN FRANCISCO", + "State": "CA", + "Zip": "94107", + "Country": "US" + }, + { + "precision": "zip", + "Latitude": 37.371991, + "Longitude": -122.026020, + "Address": "", + "City": "SUNNYVALE", + "State": "CA", + "Zip": "94085", + "Country": "US" + } + ])")); + } + + CHECK(json::parse("\"Hello world!\"") == json("Hello world!")); + CHECK(json::parse("42") == json(42)); + CHECK(json::parse("true") == json(true)); + } +} diff --git a/test/src/unit-unicode.cpp b/test/src/unit-unicode.cpp new file mode 100644 index 00000000..8efb6154 --- /dev/null +++ b/test/src/unit-unicode.cpp @@ -0,0 +1,176 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 2.0.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2016 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "catch.hpp" + +#define private public +#include "json.hpp" +using nlohmann::json; + +#include + +TEST_CASE("Unicode", "[hide]") +{ + SECTION("full enumeration of Unicode code points") + { + // create an escaped string from a code point + const auto codepoint_to_unicode = [](std::size_t cp) + { + // copd points are represented as a six-character sequence: a + // reverse solidus, followed by the lowercase letter u, followed + // by four hexadecimal digits that encode the character's code + // point + std::stringstream ss; + ss << "\\u" << std::setw(4) << std::setfill('0') << std::hex << cp; + return ss.str(); + }; + + // generate all UTF-8 code points; in total, 1112064 code points are + // generated: 0x1FFFFF code points - 2048 invalid values between + // 0xD800 and 0xDFFF. + for (std::size_t cp = 0; cp <= 0x10FFFFu; ++cp) + { + // The Unicode standard permanently reserves these code point + // values for UTF-16 encoding of the high and low surrogates, and + // they will never be assigned a character, so there should be no + // reason to encode them. The official Unicode standard says that + // no UTF forms, including UTF-16, can encode these code points. + if (cp >= 0xD800u and cp <= 0xDFFFu) + { + // if we would not skip these code points, we would get a + // "missing low surrogate" exception + continue; + } + + // string to store the code point as in \uxxxx format + std::string escaped_string; + // string to store the code point as unescaped character sequence + std::string unescaped_string; + + if (cp < 0x10000u) + { + // code points in the Basic Multilingual Plane can be + // represented with one \\uxxxx sequence + escaped_string = codepoint_to_unicode(cp); + + // All Unicode characters may be placed within the quotation + // marks, except for the characters that must be escaped: + // quotation mark, reverse solidus, and the control characters + // (U+0000 through U+001F); we ignore these code points as + // they are checked with codepoint_to_unicode. + if (cp > 0x1f and cp != 0x22 and cp != 0x5c) + { + unescaped_string = json::lexer::to_unicode(cp); + } + } + else + { + // To escape an extended character that is not in the Basic + // Multilingual Plane, the character is represented as a + // 12-character sequence, encoding the UTF-16 surrogate pair + const auto codepoint1 = 0xd800u + (((cp - 0x10000u) >> 10) & 0x3ffu); + const auto codepoint2 = 0xdc00u + ((cp - 0x10000u) & 0x3ffu); + escaped_string = codepoint_to_unicode(codepoint1); + escaped_string += codepoint_to_unicode(codepoint2); + unescaped_string += json::lexer::to_unicode(codepoint1, codepoint2); + } + + // all other code points are valid and must not yield parse errors + CAPTURE(cp); + CAPTURE(escaped_string); + CAPTURE(unescaped_string); + + json j1, j2, j3, j4; + CHECK_NOTHROW(j1 = json::parse("\"" + escaped_string + "\"")); + CHECK_NOTHROW(j2 = json::parse(j1.dump())); + CHECK(j1 == j2); + + CHECK_NOTHROW(j3 = json::parse("\"" + unescaped_string + "\"")); + CHECK_NOTHROW(j4 = json::parse(j3.dump())); + CHECK(j3 == j4); + } + } + + SECTION("read all unicode characters") + { + // read a file with all unicode characters stored as single-character + // strings in a JSON array + std::ifstream f("test/data/json_nlohmann_tests/all_unicode.json"); + json j; + CHECK_NOTHROW(j << f); + + // the array has 1112064 + 1 elemnts (a terminating "null" value) + // Note: 1112064 = 0x1FFFFF code points - 2048 invalid values between + // 0xD800 and 0xDFFF. + CHECK(j.size() == 1112065); + + SECTION("check JSON Pointers") + { + for (auto s : j) + { + // skip non-string JSON values + if (not s.is_string()) + { + continue; + } + + std::string ptr = s; + + // tilde must be followed by 0 or 1 + if (ptr == "~") + { + ptr += "0"; + } + + // JSON Pointers must begin with "/" + ptr = "/" + ptr; + + CHECK_NOTHROW(json::json_pointer("/" + ptr)); + + // check escape/unescape roundtrip + auto escaped = json::json_pointer::escape(ptr); + json::json_pointer::unescape(escaped); + CHECK(escaped == ptr); + } + } + } + + SECTION("ignore byte-order-mark") + { + // read a file with a UTF-8 BOM + std::ifstream f("test/data/json_nlohmann_tests/bom.json"); + json j; + CHECK_NOTHROW(j << f); + } + + SECTION("error for incomplete/wrong BOM") + { + CHECK_THROWS_AS(json::parse("\xef\xbb"), std::invalid_argument); + CHECK_THROWS_AS(json::parse("\xef\xbb\xbb"), std::invalid_argument); + } +} diff --git a/test/src/unit.cpp b/test/src/unit.cpp index a5c348bb..ec957b7a 100644 --- a/test/src/unit.cpp +++ b/test/src/unit.cpp @@ -26,14382 +26,5 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#define CATCH_CONFIG_MAIN #include "catch.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define private public -#include "json.hpp" -using nlohmann::json; - -// disable float-equal warnings on GCC/clang -#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) - #pragma GCC diagnostic ignored "-Wfloat-equal" -#endif - -TEST_CASE("constructors") -{ - SECTION("create an empty value with a given type") - { - SECTION("null") - { - auto t = json::value_t::null; - json j(t); - CHECK(j.type() == t); - } - - SECTION("discarded") - { - auto t = json::value_t::discarded; - json j(t); - CHECK(j.type() == t); - } - - SECTION("object") - { - auto t = json::value_t::object; - json j(t); - CHECK(j.type() == t); - } - - SECTION("array") - { - auto t = json::value_t::array; - json j(t); - CHECK(j.type() == t); - } - - SECTION("boolean") - { - auto t = json::value_t::boolean; - json j(t); - CHECK(j.type() == t); - } - - SECTION("string") - { - auto t = json::value_t::string; - json j(t); - CHECK(j.type() == t); - } - - SECTION("number_integer") - { - auto t = json::value_t::number_integer; - json j(t); - CHECK(j.type() == t); - } - - SECTION("number_unsigned") - { - auto t = json::value_t::number_unsigned; - json j(t); - CHECK(j.type() == t); - } - - SECTION("number_float") - { - auto t = json::value_t::number_float; - json j(t); - CHECK(j.type() == t); - } - } - - SECTION("create a null object (implicitly)") - { - SECTION("no parameter") - { - json j{}; - CHECK(j.type() == json::value_t::null); - } - } - - SECTION("create a null object (explicitly)") - { - SECTION("parameter") - { - json j(nullptr); - CHECK(j.type() == json::value_t::null); - } - } - - SECTION("create an object (explicit)") - { - SECTION("empty object") - { - json::object_t o; - json j(o); - CHECK(j.type() == json::value_t::object); - } - - SECTION("filled object") - { - json::object_t o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; - json j(o); - CHECK(j.type() == json::value_t::object); - } - } - - SECTION("create an object (implicit)") - { - // reference object - json::object_t o_reference {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; - json j_reference(o_reference); - - SECTION("std::map") - { - std::map o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; - json j(o); - CHECK(j.type() == json::value_t::object); - CHECK(j == j_reference); - } - - SECTION("std::map") - { - std::map o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; - json j(o); - CHECK(j.type() == json::value_t::object); - CHECK(j == j_reference); - } - - SECTION("std::multimap") - { - std::multimap o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; - json j(o); - CHECK(j.type() == json::value_t::object); - CHECK(j == j_reference); - } - - SECTION("std::unordered_map") - { - std::unordered_map o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; - json j(o); - CHECK(j.type() == json::value_t::object); - CHECK(j == j_reference); - } - - SECTION("std::unordered_multimap") - { - std::unordered_multimap o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; - json j(o); - CHECK(j.type() == json::value_t::object); - CHECK(j == j_reference); - } - - SECTION("associative container literal") - { - json j({{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}); - CHECK(j.type() == json::value_t::object); - CHECK(j == j_reference); - } - } - - SECTION("create an array (explicit)") - { - SECTION("empty array") - { - json::array_t a; - json j(a); - CHECK(j.type() == json::value_t::array); - } - - SECTION("filled array") - { - json::array_t a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; - json j(a); - CHECK(j.type() == json::value_t::array); - } - } - - SECTION("create an array (implicit)") - { - // reference array - json::array_t a_reference {json(1), json(1u), json(2.2), json(false), json("string"), json()}; - json j_reference(a_reference); - - SECTION("std::list") - { - std::list a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; - json j(a); - CHECK(j.type() == json::value_t::array); - CHECK(j == j_reference); - } - - SECTION("std::forward_list") - { - std::forward_list a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; - json j(a); - CHECK(j.type() == json::value_t::array); - CHECK(j == j_reference); - } - - SECTION("std::array") - { - std::array a {{json(1), json(1u), json(2.2), json(false), json("string"), json()}}; - json j(a); - CHECK(j.type() == json::value_t::array); - CHECK(j == j_reference); - } - - SECTION("std::vector") - { - std::vector a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; - json j(a); - CHECK(j.type() == json::value_t::array); - CHECK(j == j_reference); - } - - SECTION("std::deque") - { - std::deque a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; - json j(a); - CHECK(j.type() == json::value_t::array); - CHECK(j == j_reference); - } - - SECTION("std::set") - { - std::set a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; - json j(a); - CHECK(j.type() == json::value_t::array); - // we cannot really check for equality here - } - - SECTION("std::unordered_set") - { - std::unordered_set a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; - json j(a); - CHECK(j.type() == json::value_t::array); - // we cannot really check for equality here - } - - SECTION("sequence container literal") - { - json j({json(1), json(1u), json(2.2), json(false), json("string"), json()}); - CHECK(j.type() == json::value_t::array); - CHECK(j == j_reference); - } - } - - SECTION("create a string (explicit)") - { - SECTION("empty string") - { - json::string_t s; - json j(s); - CHECK(j.type() == json::value_t::string); - } - - SECTION("filled string") - { - json::string_t s {"Hello world"}; - json j(s); - CHECK(j.type() == json::value_t::string); - } - } - - SECTION("create a string (implicit)") - { - // reference string - json::string_t s_reference {"Hello world"}; - json j_reference(s_reference); - - SECTION("std::string") - { - std::string s {"Hello world"}; - json j(s); - CHECK(j.type() == json::value_t::string); - CHECK(j == j_reference); - } - - SECTION("char[]") - { - char s[] {"Hello world"}; - json j(s); - CHECK(j.type() == json::value_t::string); - CHECK(j == j_reference); - } - - SECTION("const char*") - { - const char* s {"Hello world"}; - json j(s); - CHECK(j.type() == json::value_t::string); - CHECK(j == j_reference); - } - - SECTION("string literal") - { - json j("Hello world"); - CHECK(j.type() == json::value_t::string); - CHECK(j == j_reference); - } - } - - SECTION("create a boolean (explicit)") - { - SECTION("empty boolean") - { - json::boolean_t b{}; - json j(b); - CHECK(j.type() == json::value_t::boolean); - } - - SECTION("filled boolean (true)") - { - json j(true); - CHECK(j.type() == json::value_t::boolean); - } - - SECTION("filled boolean (false)") - { - json j(false); - CHECK(j.type() == json::value_t::boolean); - } - } - - SECTION("create an integer number (explicit)") - { - SECTION("uninitialized value") - { - json::number_integer_t n{}; - json j(n); - CHECK(j.type() == json::value_t::number_integer); - } - - SECTION("initialized value") - { - json::number_integer_t n(42); - json j(n); - CHECK(j.type() == json::value_t::number_integer); - } - } - - SECTION("create an integer number (implicit)") - { - // reference objects - json::number_integer_t n_reference = 42; - json j_reference(n_reference); - json::number_unsigned_t n_unsigned_reference = 42; - json j_unsigned_reference(n_unsigned_reference); - - SECTION("short") - { - short n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("unsigned short") - { - unsigned short n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("int") - { - int n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("unsigned int") - { - unsigned int n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("long") - { - long n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("unsigned long") - { - unsigned long n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("long long") - { - long long n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("unsigned long long") - { - unsigned long long n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("int8_t") - { - int8_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("int16_t") - { - int16_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("int32_t") - { - int32_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("int64_t") - { - int64_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("int_fast8_t") - { - int_fast8_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("int_fast16_t") - { - int_fast16_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("int_fast32_t") - { - int_fast32_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("int_fast64_t") - { - int_fast64_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("int_least8_t") - { - int_least8_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("int_least16_t") - { - int_least16_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("int_least32_t") - { - int_least32_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("int_least64_t") - { - int_least64_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("uint8_t") - { - uint8_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("uint16_t") - { - uint16_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("uint32_t") - { - uint32_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("uint64_t") - { - uint64_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("uint_fast8_t") - { - uint_fast8_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("uint_fast16_t") - { - uint_fast16_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("uint_fast32_t") - { - uint_fast32_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("uint_fast64_t") - { - uint_fast64_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("uint_least8_t") - { - uint_least8_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("uint_least16_t") - { - uint_least16_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("uint_least32_t") - { - uint_least32_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("uint_least64_t") - { - uint_least64_t n = 42; - json j(n); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("integer literal without suffix") - { - json j(42); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("integer literal with u suffix") - { - json j(42u); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("integer literal with l suffix") - { - json j(42l); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("integer literal with ul suffix") - { - json j(42ul); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - - SECTION("integer literal with ll suffix") - { - json j(42ll); - CHECK(j.type() == json::value_t::number_integer); - CHECK(j == j_reference); - } - - SECTION("integer literal with ull suffix") - { - json j(42ull); - CHECK(j.type() == json::value_t::number_unsigned); - CHECK(j == j_unsigned_reference); - } - } - - SECTION("create a floating-point number (explicit)") - { - SECTION("uninitialized value") - { - json::number_float_t n{}; - json j(n); - CHECK(j.type() == json::value_t::number_float); - } - - SECTION("initialized value") - { - json::number_float_t n(42.23); - json j(n); - CHECK(j.type() == json::value_t::number_float); - } - } - - SECTION("create a floating-point number (implicit)") - { - // reference object - json::number_float_t n_reference = 42.23; - json j_reference(n_reference); - - SECTION("float") - { - float n = 42.23; - json j(n); - CHECK(j.type() == json::value_t::number_float); - CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float)); - } - - SECTION("double") - { - double n = 42.23; - json j(n); - CHECK(j.type() == json::value_t::number_float); - CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float)); - } - - SECTION("long double") - { - long double n = 42.23; - json j(n); - CHECK(j.type() == json::value_t::number_float); - CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float)); - } - - SECTION("floating-point literal without suffix") - { - json j(42.23); - CHECK(j.type() == json::value_t::number_float); - CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float)); - } - - SECTION("integer literal with f suffix") - { - json j(42.23f); - CHECK(j.type() == json::value_t::number_float); - CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float)); - } - - SECTION("integer literal with l suffix") - { - json j(42.23l); - CHECK(j.type() == json::value_t::number_float); - CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float)); - } - } - - SECTION("create a container (array or object) from an initializer list") - { - SECTION("empty initializer list") - { - SECTION("explicit") - { - std::initializer_list l; - json j(l); - CHECK(j.type() == json::value_t::object); - } - - SECTION("implicit") - { - json j {}; - CHECK(j.type() == json::value_t::null); - } - } - - SECTION("one element") - { - SECTION("array") - { - SECTION("explicit") - { - std::initializer_list l = {json(json::array_t())}; - json j(l); - CHECK(j.type() == json::value_t::array); - } - - SECTION("implicit") - { - json j {json::array_t()}; - CHECK(j.type() == json::value_t::array); - } - } - - SECTION("object") - { - SECTION("explicit") - { - std::initializer_list l = {json(json::object_t())}; - json j(l); - CHECK(j.type() == json::value_t::array); - } - - SECTION("implicit") - { - json j {json::object_t()}; - CHECK(j.type() == json::value_t::array); - } - } - - SECTION("string") - { - SECTION("explicit") - { - std::initializer_list l = {json("Hello world")}; - json j(l); - CHECK(j.type() == json::value_t::array); - } - - SECTION("implicit") - { - json j {"Hello world"}; - CHECK(j.type() == json::value_t::array); - } - } - - SECTION("boolean") - { - SECTION("explicit") - { - std::initializer_list l = {json(true)}; - json j(l); - CHECK(j.type() == json::value_t::array); - } - - SECTION("implicit") - { - json j {true}; - CHECK(j.type() == json::value_t::array); - } - } - - SECTION("number (integer)") - { - SECTION("explicit") - { - std::initializer_list l = {json(1)}; - json j(l); - CHECK(j.type() == json::value_t::array); - } - - SECTION("implicit") - { - json j {1}; - CHECK(j.type() == json::value_t::array); - } - } - - SECTION("number (unsigned)") - { - SECTION("explicit") - { - std::initializer_list l = {json(1u)}; - json j(l); - CHECK(j.type() == json::value_t::array); - } - - SECTION("implicit") - { - json j {1u}; - CHECK(j.type() == json::value_t::array); - } - } - - SECTION("number (floating-point)") - { - SECTION("explicit") - { - std::initializer_list l = {json(42.23)}; - json j(l); - CHECK(j.type() == json::value_t::array); - } - - SECTION("implicit") - { - json j {42.23}; - CHECK(j.type() == json::value_t::array); - } - } - } - - SECTION("more elements") - { - SECTION("explicit") - { - std::initializer_list l = {1, 1u, 42.23, true, nullptr, json::object_t(), json::array_t()}; - json j(l); - CHECK(j.type() == json::value_t::array); - } - - SECTION("implicit") - { - json j {1, 1u, 42.23, true, nullptr, json::object_t(), json::array_t()}; - CHECK(j.type() == json::value_t::array); - } - } - - SECTION("implicit type deduction") - { - SECTION("object") - { - json j { {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} }; - CHECK(j.type() == json::value_t::object); - } - - SECTION("array") - { - json j { {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} , 13 }; - CHECK(j.type() == json::value_t::array); - } - } - - SECTION("explicit type deduction") - { - SECTION("empty object") - { - json j = json::object(); - CHECK(j.type() == json::value_t::object); - } - - SECTION("object") - { - json j = json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} }); - CHECK(j.type() == json::value_t::object); - } - - SECTION("object with error") - { - CHECK_THROWS_AS(json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false}, 13 }), - std::logic_error); - CHECK_THROWS_WITH(json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false}, 13 }), - "cannot create object from initializer list"); - } - - SECTION("empty array") - { - json j = json::array(); - CHECK(j.type() == json::value_t::array); - } - - SECTION("array") - { - json j = json::array({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} }); - CHECK(j.type() == json::value_t::array); - } - } - } - - SECTION("create an array of n copies of a given value") - { - json v = {1, "foo", 34.23, {1, 2, 3}, {{"A", 1}, {"B", 2u}}}; - json arr(3, v); - CHECK(arr.size() == 3); - for (auto& x : arr) - { - CHECK(x == v); - } - } - - SECTION("create a JSON container from an iterator range") - { - SECTION("object") - { - SECTION("json(begin(), end())") - { - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - json j_new(jobject.begin(), jobject.end()); - CHECK(j_new == jobject); - } - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - json j_new(jobject.cbegin(), jobject.cend()); - CHECK(j_new == jobject); - } - } - - SECTION("json(begin(), begin())") - { - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - json j_new(jobject.begin(), jobject.begin()); - CHECK(j_new == json::object()); - } - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - json j_new(jobject.cbegin(), jobject.cbegin()); - CHECK(j_new == json::object()); - } - } - - SECTION("construct from subrange") - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; - json j_new(jobject.find("b"), jobject.find("e")); - CHECK(j_new == json({{"b", 1}, {"c", 17u}, {"d", false}})); - } - - SECTION("incompatible iterators") - { - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; - json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - CHECK_THROWS_AS(json(jobject.begin(), jobject2.end()), std::domain_error); - CHECK_THROWS_AS(json(jobject2.begin(), jobject.end()), std::domain_error); - CHECK_THROWS_WITH(json(jobject.begin(), jobject2.end()), "iterators are not compatible"); - CHECK_THROWS_WITH(json(jobject2.begin(), jobject.end()), "iterators are not compatible"); - } - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; - json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - CHECK_THROWS_AS(json(jobject.cbegin(), jobject2.cend()), std::domain_error); - CHECK_THROWS_AS(json(jobject2.cbegin(), jobject.cend()), std::domain_error); - CHECK_THROWS_WITH(json(jobject.cbegin(), jobject2.cend()), "iterators are not compatible"); - CHECK_THROWS_WITH(json(jobject2.cbegin(), jobject.cend()), "iterators are not compatible"); - } - } - } - - SECTION("array") - { - SECTION("json(begin(), end())") - { - { - json jarray = {1, 2, 3, 4, 5}; - json j_new(jarray.begin(), jarray.end()); - CHECK(j_new == jarray); - } - { - json jarray = {1, 2, 3, 4, 5}; - json j_new(jarray.cbegin(), jarray.cend()); - CHECK(j_new == jarray); - } - } - - SECTION("json(begin(), begin())") - { - { - json jarray = {1, 2, 3, 4, 5}; - json j_new(jarray.begin(), jarray.begin()); - CHECK(j_new == json::array()); - } - { - json jarray = {1, 2, 3, 4, 5}; - json j_new(jarray.cbegin(), jarray.cbegin()); - CHECK(j_new == json::array()); - } - } - - SECTION("construct from subrange") - { - { - json jarray = {1, 2, 3, 4, 5}; - json j_new(jarray.begin() + 1, jarray.begin() + 3); - CHECK(j_new == json({2, 3})); - } - { - json jarray = {1, 2, 3, 4, 5}; - json j_new(jarray.cbegin() + 1, jarray.cbegin() + 3); - CHECK(j_new == json({2, 3})); - } - } - - SECTION("incompatible iterators") - { - { - json jarray = {1, 2, 3, 4}; - json jarray2 = {2, 3, 4, 5}; - CHECK_THROWS_AS(json(jarray.begin(), jarray2.end()), std::domain_error); - CHECK_THROWS_AS(json(jarray2.begin(), jarray.end()), std::domain_error); - CHECK_THROWS_WITH(json(jarray.begin(), jarray2.end()), "iterators are not compatible"); - CHECK_THROWS_WITH(json(jarray2.begin(), jarray.end()), "iterators are not compatible"); - } - { - json jarray = {1, 2, 3, 4}; - json jarray2 = {2, 3, 4, 5}; - CHECK_THROWS_AS(json(jarray.cbegin(), jarray2.cend()), std::domain_error); - CHECK_THROWS_AS(json(jarray2.cbegin(), jarray.cend()), std::domain_error); - CHECK_THROWS_WITH(json(jarray.cbegin(), jarray2.cend()), "iterators are not compatible"); - CHECK_THROWS_WITH(json(jarray2.cbegin(), jarray.cend()), "iterators are not compatible"); - } - } - } - - SECTION("other values") - { - SECTION("construct with two valid iterators") - { - SECTION("null") - { - { - json j; - CHECK_THROWS_AS(json(j.begin(), j.end()), std::domain_error); - CHECK_THROWS_WITH(json(j.begin(), j.end()), "cannot use construct with iterators from null"); - } - { - json j; - CHECK_THROWS_AS(json(j.cbegin(), j.cend()), std::domain_error); - CHECK_THROWS_WITH(json(j.cbegin(), j.cend()), "cannot use construct with iterators from null"); - } - } - - SECTION("string") - { - { - json j = "foo"; - json j_new(j.begin(), j.end()); - CHECK(j == j_new); - } - { - json j = "bar"; - json j_new(j.cbegin(), j.cend()); - CHECK(j == j_new); - } - } - - SECTION("number (boolean)") - { - { - json j = false; - json j_new(j.begin(), j.end()); - CHECK(j == j_new); - } - { - json j = true; - json j_new(j.cbegin(), j.cend()); - CHECK(j == j_new); - } - } - - SECTION("number (integer)") - { - { - json j = 17; - json j_new(j.begin(), j.end()); - CHECK(j == j_new); - } - { - json j = 17; - json j_new(j.cbegin(), j.cend()); - CHECK(j == j_new); - } - } - - SECTION("number (unsigned)") - { - { - json j = 17u; - json j_new(j.begin(), j.end()); - CHECK(j == j_new); - } - { - json j = 17u; - json j_new(j.cbegin(), j.cend()); - CHECK(j == j_new); - } - } - - SECTION("number (floating point)") - { - { - json j = 23.42; - json j_new(j.begin(), j.end()); - CHECK(j == j_new); - } - { - json j = 23.42; - json j_new(j.cbegin(), j.cend()); - CHECK(j == j_new); - } - } - } - - SECTION("construct with two invalid iterators") - { - SECTION("string") - { - { - json j = "foo"; - CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range"); - } - { - json j = "bar"; - CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range"); - } - } - - SECTION("number (boolean)") - { - { - json j = false; - CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range"); - } - { - json j = true; - CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range"); - } - } - - SECTION("number (integer)") - { - { - json j = 17; - CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range"); - } - { - json j = 17; - CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range"); - } - } - - SECTION("number (integer)") - { - { - json j = 17u; - CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range"); - } - { - json j = 17u; - CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range"); - } - } - - SECTION("number (floating point)") - { - { - json j = 23.42; - CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range"); - } - { - json j = 23.42; - CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range"); - } - } - } - } - } - - SECTION("create a JSON value from an input stream") - { - SECTION("std::stringstream") - { - std::stringstream ss; - ss << "[\"foo\",1,2,3,false,{\"one\":1}]"; - json j(ss); - CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); - } - - SECTION("with callback function") - { - std::stringstream ss; - ss << "[\"foo\",1,2,3,false,{\"one\":1}]"; - json j(ss, [](int, json::parse_event_t, const json & val) - { - // filter all number(2) elements - if (val == json(2)) - { - return false; - } - else - { - return true; - } - }); - CHECK(j == json({"foo", 1, 3, false, {{"one", 1}}})); - } - - SECTION("std::ifstream") - { - std::ifstream f("test/data/json_tests/pass1.json"); - json j(f); - } - } -} - -TEST_CASE("other constructors and destructor") -{ - SECTION("copy constructor") - { - SECTION("object") - { - json j {{"foo", 1}, {"bar", false}}; - json k(j); - CHECK(j == k); - } - - SECTION("array") - { - json j {"foo", 1, 42.23, false}; - json k(j); - CHECK(j == k); - } - - SECTION("null") - { - json j(nullptr); - json k(j); - CHECK(j == k); - } - - SECTION("boolean") - { - json j(true); - json k(j); - CHECK(j == k); - } - - SECTION("string") - { - json j("Hello world"); - json k(j); - CHECK(j == k); - } - - SECTION("number (integer)") - { - json j(42); - json k(j); - CHECK(j == k); - } - - SECTION("number (unsigned)") - { - json j(42u); - json k(j); - CHECK(j == k); - } - - SECTION("number (floating-point)") - { - json j(42.23); - json k(j); - CHECK(j == k); - } - } - - SECTION("move constructor") - { - json j {{"foo", "bar"}, {"baz", {1, 2, 3, 4}}, {"a", 42u}, {"b", 42.23}, {"c", nullptr}}; - CHECK(j.type() == json::value_t::object); - json k(std::move(j)); - CHECK(k.type() == json::value_t::object); - CHECK(j.type() == json::value_t::null); - } - - SECTION("copy assignment") - { - SECTION("object") - { - json j {{"foo", 1}, {"bar", false}}; - json k; - k = j; - CHECK(j == k); - } - - SECTION("array") - { - json j {"foo", 1, 42.23, false}; - json k; - k = j; - CHECK(j == k); - } - - SECTION("null") - { - json j(nullptr); - json k; - k = j; - CHECK(j == k); - } - - SECTION("boolean") - { - json j(true); - json k; - k = j; - CHECK(j == k); - } - - SECTION("string") - { - json j("Hello world"); - json k; - k = j; - CHECK(j == k); - } - - SECTION("number (integer)") - { - json j(42); - json k; - k = j; - CHECK(j == k); - } - - SECTION("number (unsigned)") - { - json j(42u); - json k; - k = j; - CHECK(j == k); - } - - SECTION("number (floating-point)") - { - json j(42.23); - json k; - k = j; - CHECK(j == k); - } - } - - SECTION("destructor") - { - SECTION("object") - { - auto j = new json {{"foo", 1}, {"bar", false}}; - delete j; - } - - SECTION("array") - { - auto j = new json {"foo", 1, 1u, false, 23.42}; - delete j; - } - - SECTION("string") - { - auto j = new json("Hello world"); - delete j; - } - } -} - -TEST_CASE("object inspection") -{ - SECTION("convenience type checker") - { - SECTION("object") - { - json j {{"foo", 1}, {"bar", false}}; - CHECK(not j.is_null()); - CHECK(not j.is_boolean()); - CHECK(not j.is_number()); - CHECK(not j.is_number_integer()); - CHECK(not j.is_number_unsigned()); - CHECK(not j.is_number_float()); - CHECK(j.is_object()); - CHECK(not j.is_array()); - CHECK(not j.is_string()); - CHECK(not j.is_discarded()); - CHECK(not j.is_primitive()); - CHECK(j.is_structured()); - } - - SECTION("array") - { - json j {"foo", 1, 1u, 42.23, false}; - CHECK(not j.is_null()); - CHECK(not j.is_boolean()); - CHECK(not j.is_number()); - CHECK(not j.is_number_integer()); - CHECK(not j.is_number_unsigned()); - CHECK(not j.is_number_float()); - CHECK(not j.is_object()); - CHECK(j.is_array()); - CHECK(not j.is_string()); - CHECK(not j.is_discarded()); - CHECK(not j.is_primitive()); - CHECK(j.is_structured()); - } - - SECTION("null") - { - json j(nullptr); - CHECK(j.is_null()); - CHECK(not j.is_boolean()); - CHECK(not j.is_number()); - CHECK(not j.is_number_integer()); - CHECK(not j.is_number_unsigned()); - CHECK(not j.is_number_float()); - CHECK(not j.is_object()); - CHECK(not j.is_array()); - CHECK(not j.is_string()); - CHECK(not j.is_discarded()); - CHECK(j.is_primitive()); - CHECK(not j.is_structured()); - } - - SECTION("boolean") - { - json j(true); - CHECK(not j.is_null()); - CHECK(j.is_boolean()); - CHECK(not j.is_number()); - CHECK(not j.is_number_integer()); - CHECK(not j.is_number_unsigned()); - CHECK(not j.is_number_float()); - CHECK(not j.is_object()); - CHECK(not j.is_array()); - CHECK(not j.is_string()); - CHECK(not j.is_discarded()); - CHECK(j.is_primitive()); - CHECK(not j.is_structured()); - } - - SECTION("string") - { - json j("Hello world"); - CHECK(not j.is_null()); - CHECK(not j.is_boolean()); - CHECK(not j.is_number()); - CHECK(not j.is_number_integer()); - CHECK(not j.is_number_unsigned()); - CHECK(not j.is_number_float()); - CHECK(not j.is_object()); - CHECK(not j.is_array()); - CHECK(j.is_string()); - CHECK(not j.is_discarded()); - CHECK(j.is_primitive()); - CHECK(not j.is_structured()); - } - - SECTION("number (integer)") - { - json j(42); - CHECK(not j.is_null()); - CHECK(not j.is_boolean()); - CHECK(j.is_number()); - CHECK(j.is_number_integer()); - CHECK(not j.is_number_unsigned()); - CHECK(not j.is_number_float()); - CHECK(not j.is_object()); - CHECK(not j.is_array()); - CHECK(not j.is_string()); - CHECK(not j.is_discarded()); - CHECK(j.is_primitive()); - CHECK(not j.is_structured()); - } - - SECTION("number (unsigned)") - { - json j(42u); - CHECK(not j.is_null()); - CHECK(not j.is_boolean()); - CHECK(j.is_number()); - CHECK(j.is_number_integer()); - CHECK(j.is_number_unsigned()); - CHECK(not j.is_number_float()); - CHECK(not j.is_object()); - CHECK(not j.is_array()); - CHECK(not j.is_string()); - CHECK(not j.is_discarded()); - CHECK(j.is_primitive()); - CHECK(not j.is_structured()); - } - - SECTION("number (floating-point)") - { - json j(42.23); - CHECK(not j.is_null()); - CHECK(not j.is_boolean()); - CHECK(j.is_number()); - CHECK(not j.is_number_integer()); - CHECK(not j.is_number_unsigned()); - CHECK(j.is_number_float()); - CHECK(not j.is_object()); - CHECK(not j.is_array()); - CHECK(not j.is_string()); - CHECK(not j.is_discarded()); - CHECK(j.is_primitive()); - CHECK(not j.is_structured()); - } - - SECTION("discarded") - { - json j(json::value_t::discarded); - CHECK(not j.is_null()); - CHECK(not j.is_boolean()); - CHECK(not j.is_number()); - CHECK(not j.is_number_integer()); - CHECK(not j.is_number_unsigned()); - CHECK(not j.is_number_float()); - CHECK(not j.is_object()); - CHECK(not j.is_array()); - CHECK(not j.is_string()); - CHECK(j.is_discarded()); - CHECK(not j.is_primitive()); - CHECK(not j.is_structured()); - } - } - - SECTION("serialization") - { - json j {{"object", json::object()}, {"array", {1, 2, 3, 4}}, {"number", 42}, {"boolean", false}, {"null", nullptr}, {"string", "Hello world"} }; - - SECTION("no indent / indent=-1") - { - CHECK(j.dump() == - "{\"array\":[1,2,3,4],\"boolean\":false,\"null\":null,\"number\":42,\"object\":{},\"string\":\"Hello world\"}"); - - CHECK(j.dump() == j.dump(-1)); - } - - SECTION("indent=0") - { - CHECK(j.dump(0) == - "{\n\"array\": [\n1,\n2,\n3,\n4\n],\n\"boolean\": false,\n\"null\": null,\n\"number\": 42,\n\"object\": {},\n\"string\": \"Hello world\"\n}"); - } - - SECTION("indent=4") - { - CHECK(j.dump(4) == - "{\n \"array\": [\n 1,\n 2,\n 3,\n 4\n ],\n \"boolean\": false,\n \"null\": null,\n \"number\": 42,\n \"object\": {},\n \"string\": \"Hello world\"\n}"); - } - - SECTION("dump and floating-point numbers") - { - auto s = json(42.23).dump(); - CHECK(s.find("42.23") != std::string::npos); - } - - SECTION("dump and small floating-point numbers") - { - auto s = json(1.23456e-78).dump(); - CHECK(s.find("1.23456e-78") != std::string::npos); - } - - SECTION("dump and non-ASCII characters") - { - CHECK(json("ä").dump() == "\"ä\""); - CHECK(json("Ö").dump() == "\"Ö\""); - CHECK(json("❤️").dump() == "\"❤️\""); - } - - SECTION("serialization of discarded element") - { - json j_discarded(json::value_t::discarded); - CHECK(j_discarded.dump() == ""); - } - - SECTION("check that precision is reset after serialization") - { - // create stringstream and set precision - std::stringstream ss; - ss.precision(3); - ss << 3.141592653589793 << std::fixed; - CHECK(ss.str() == "3.14"); - - // reset stringstream - ss.str(std::string()); - - // use stringstream for JSON serialization - json j_number = 3.141592653589793; - ss << j_number; - - // check that precision has been overridden during serialization - CHECK(ss.str() == "3.141592653589793"); - - // check that precision has been restored - CHECK(ss.precision() == 3); - } - } - - SECTION("return the type of the object (explicit)") - { - SECTION("null") - { - json j = nullptr; - CHECK(j.type() == json::value_t::null); - } - - SECTION("object") - { - json j = {{"foo", "bar"}}; - CHECK(j.type() == json::value_t::object); - } - - SECTION("array") - { - json j = {1, 2, 3, 4}; - CHECK(j.type() == json::value_t::array); - } - - SECTION("boolean") - { - json j = true; - CHECK(j.type() == json::value_t::boolean); - } - - SECTION("string") - { - json j = "Hello world"; - CHECK(j.type() == json::value_t::string); - } - - SECTION("number (integer)") - { - json j = 23; - CHECK(j.type() == json::value_t::number_integer); - } - - SECTION("number (unsigned)") - { - json j = 23u; - CHECK(j.type() == json::value_t::number_unsigned); - } - - SECTION("number (floating-point)") - { - json j = 42.23; - CHECK(j.type() == json::value_t::number_float); - } - } - - SECTION("return the type of the object (implicit)") - { - SECTION("null") - { - json j = nullptr; - json::value_t t = j; - CHECK(t == j.type()); - } - - SECTION("object") - { - json j = {{"foo", "bar"}}; - json::value_t t = j; - CHECK(t == j.type()); - } - - SECTION("array") - { - json j = {1, 2, 3, 4}; - json::value_t t = j; - CHECK(t == j.type()); - } - - SECTION("boolean") - { - json j = true; - json::value_t t = j; - CHECK(t == j.type()); - } - - SECTION("string") - { - json j = "Hello world"; - json::value_t t = j; - CHECK(t == j.type()); - } - - SECTION("number (integer)") - { - json j = 23; - json::value_t t = j; - CHECK(t == j.type()); - } - - SECTION("number (unsigned)") - { - json j = 23u; - json::value_t t = j; - CHECK(t == j.type()); - } - - SECTION("number (floating-point)") - { - json j = 42.23; - json::value_t t = j; - CHECK(t == j.type()); - } - } -} - -TEST_CASE("value conversion") -{ - SECTION("get an object (explicit)") - { - json::object_t o_reference = {{"object", json::object()}, {"array", {1, 2, 3, 4}}, {"number", 42}, {"boolean", false}, {"null", nullptr}, {"string", "Hello world"} }; - json j(o_reference); - - SECTION("json::object_t") - { - json::object_t o = j.get(); - CHECK(json(o) == j); - } - - SECTION("std::map") - { - std::map o = j.get>(); - CHECK(json(o) == j); - } - - SECTION("std::multimap") - { - std::multimap o = j.get>(); - CHECK(json(o) == j); - } - - SECTION("std::unordered_map") - { - std::unordered_map o = j.get>(); - CHECK(json(o) == j); - } - - SECTION("std::unordered_multimap") - { - std::unordered_multimap o = - j.get>(); - CHECK(json(o) == j); - } - - SECTION("exception in case of a non-object type") - { - CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::array).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::string).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::boolean).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_integer).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_float).get(), std::logic_error); - - CHECK_THROWS_WITH(json(json::value_t::null).get(), - "type must be object, but is null"); - CHECK_THROWS_WITH(json(json::value_t::array).get(), - "type must be object, but is array"); - CHECK_THROWS_WITH(json(json::value_t::string).get(), - "type must be object, but is string"); - CHECK_THROWS_WITH(json(json::value_t::boolean).get(), - "type must be object, but is boolean"); - CHECK_THROWS_WITH(json(json::value_t::number_integer).get(), - "type must be object, but is number"); - CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get(), - "type must be object, but is number"); - CHECK_THROWS_WITH(json(json::value_t::number_float).get(), - "type must be object, but is number"); - } - } - - SECTION("get an object (implicit)") - { - json::object_t o_reference = {{"object", json::object()}, {"array", {1, 2, 3, 4}}, {"number", 42}, {"boolean", false}, {"null", nullptr}, {"string", "Hello world"} }; - json j(o_reference); - - SECTION("json::object_t") - { - json::object_t o = j; - CHECK(json(o) == j); - } - - SECTION("std::map") - { - std::map o = j; - CHECK(json(o) == j); - } - - SECTION("std::multimap") - { - std::multimap o = j; - CHECK(json(o) == j); - } - - SECTION("std::unordered_map") - { - std::unordered_map o = j; - CHECK(json(o) == j); - } - - SECTION("std::unordered_multimap") - { - std::unordered_multimap o = j; - CHECK(json(o) == j); - } - } - - SECTION("get an array (explicit)") - { - json::array_t a_reference {json(1), json(1u), json(2.2), json(false), json("string"), json()}; - json j(a_reference); - - SECTION("json::array_t") - { - json::array_t a = j.get(); - CHECK(json(a) == j); - } - - SECTION("std::list") - { - std::list a = j.get>(); - CHECK(json(a) == j); - } - - SECTION("std::forward_list") - { - std::forward_list a = j.get>(); - CHECK(json(a) == j); - } - - SECTION("std::vector") - { - std::vector a = j.get>(); - CHECK(json(a) == j); - } - - SECTION("std::deque") - { - std::deque a = j.get>(); - CHECK(json(a) == j); - } - - SECTION("exception in case of a non-array type") - { - CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::object).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::string).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::boolean).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_integer).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_float).get(), std::logic_error); - - CHECK_THROWS_WITH(json(json::value_t::null).get(), - "type must be array, but is null"); - CHECK_THROWS_WITH(json(json::value_t::object).get(), - "type must be array, but is object"); - CHECK_THROWS_WITH(json(json::value_t::string).get(), - "type must be array, but is string"); - CHECK_THROWS_WITH(json(json::value_t::boolean).get(), - "type must be array, but is boolean"); - CHECK_THROWS_WITH(json(json::value_t::number_integer).get(), - "type must be array, but is number"); - CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get(), - "type must be array, but is number"); - CHECK_THROWS_WITH(json(json::value_t::number_float).get(), - "type must be array, but is number"); - } - } - - SECTION("get an array (implicit)") - { - json::array_t a_reference {json(1), json(1u), json(2.2), json(false), json("string"), json()}; - json j(a_reference); - - SECTION("json::array_t") - { - json::array_t a = j; - CHECK(json(a) == j); - } - - SECTION("std::list") - { - std::list a = j; - CHECK(json(a) == j); - } - - SECTION("std::forward_list") - { - std::forward_list a = j; - CHECK(json(a) == j); - } - - SECTION("std::vector") - { - std::vector a = j; - CHECK(json(a) == j); - } - - SECTION("std::deque") - { - std::deque a = j; - CHECK(json(a) == j); - } - } - - SECTION("get a string (explicit)") - { - json::string_t s_reference {"Hello world"}; - json j(s_reference); - - SECTION("string_t") - { - json::string_t s = j.get(); - CHECK(json(s) == j); - } - - SECTION("std::string") - { - std::string s = j.get(); - CHECK(json(s) == j); - } - - SECTION("exception in case of a non-string type") - { - CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::object).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::array).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::boolean).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_integer).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_float).get(), std::logic_error); - - CHECK_THROWS_WITH(json(json::value_t::null).get(), - "type must be string, but is null"); - CHECK_THROWS_WITH(json(json::value_t::object).get(), - "type must be string, but is object"); - CHECK_THROWS_WITH(json(json::value_t::array).get(), - "type must be string, but is array"); - CHECK_THROWS_WITH(json(json::value_t::boolean).get(), - "type must be string, but is boolean"); - CHECK_THROWS_WITH(json(json::value_t::number_integer).get(), - "type must be string, but is number"); - CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get(), - "type must be string, but is number"); - CHECK_THROWS_WITH(json(json::value_t::number_float).get(), - "type must be string, but is number"); - } - } - - SECTION("get a string (implicit)") - { - json::string_t s_reference {"Hello world"}; - json j(s_reference); - - SECTION("string_t") - { - json::string_t s = j; - CHECK(json(s) == j); - } - - SECTION("std::string") - { - std::string s = j; - CHECK(json(s) == j); - } - } - - SECTION("get a boolean (explicit)") - { - json::boolean_t b_reference {true}; - json j(b_reference); - - SECTION("boolean_t") - { - json::boolean_t b = j.get(); - CHECK(json(b) == j); - } - - SECTION("bool") - { - bool b = j.get(); - CHECK(json(b) == j); - } - - SECTION("exception in case of a non-string type") - { - CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::object).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::array).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::string).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_integer).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::number_float).get(), std::logic_error); - - CHECK_THROWS_WITH(json(json::value_t::null).get(), - "type must be boolean, but is null"); - CHECK_THROWS_WITH(json(json::value_t::object).get(), - "type must be boolean, but is object"); - CHECK_THROWS_WITH(json(json::value_t::array).get(), - "type must be boolean, but is array"); - CHECK_THROWS_WITH(json(json::value_t::string).get(), - "type must be boolean, but is string"); - CHECK_THROWS_WITH(json(json::value_t::number_integer).get(), - "type must be boolean, but is number"); - CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get(), - "type must be boolean, but is number"); - CHECK_THROWS_WITH(json(json::value_t::number_float).get(), - "type must be boolean, but is number"); - } - } - - SECTION("get a boolean (implicit)") - { - json::boolean_t b_reference {true}; - json j(b_reference); - - SECTION("boolean_t") - { - json::boolean_t b = j; - CHECK(json(b) == j); - } - - SECTION("bool") - { - bool b = j; - CHECK(json(b) == j); - } - } - - SECTION("get an integer number (explicit)") - { - json::number_integer_t n_reference {42}; - json j(n_reference); - json::number_unsigned_t n_unsigned_reference {42u}; - json j_unsigned(n_unsigned_reference); - - SECTION("number_integer_t") - { - json::number_integer_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("number_unsigned_t") - { - json::number_unsigned_t n = j_unsigned.get(); - CHECK(json(n) == j_unsigned); - } - - SECTION("short") - { - short n = j.get(); - CHECK(json(n) == j); - } - - SECTION("unsigned short") - { - unsigned short n = j.get(); - CHECK(json(n) == j); - } - - SECTION("int") - { - int n = j.get(); - CHECK(json(n) == j); - } - - SECTION("unsigned int") - { - unsigned int n = j.get(); - CHECK(json(n) == j); - } - - SECTION("long") - { - long n = j.get(); - CHECK(json(n) == j); - } - - SECTION("unsigned long") - { - unsigned long n = j.get(); - CHECK(json(n) == j); - } - - SECTION("long long") - { - long long n = j.get(); - CHECK(json(n) == j); - } - - SECTION("unsigned long long") - { - unsigned long long n = j.get(); - CHECK(json(n) == j); - } - - SECTION("int8_t") - { - int8_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("int16_t") - { - int16_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("int32_t") - { - int32_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("int64_t") - { - int64_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("int8_fast_t") - { - int_fast8_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("int16_fast_t") - { - int_fast16_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("int32_fast_t") - { - int_fast32_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("int64_fast_t") - { - int_fast64_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("int8_least_t") - { - int_least8_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("int16_least_t") - { - int_least16_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("int32_least_t") - { - int_least32_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("int64_least_t") - { - int_least64_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("uint8_t") - { - uint8_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("uint16_t") - { - uint16_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("uint32_t") - { - uint32_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("uint64_t") - { - uint64_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("uint8_fast_t") - { - uint_fast8_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("uint16_fast_t") - { - uint_fast16_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("uint32_fast_t") - { - uint_fast32_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("uint64_fast_t") - { - uint_fast64_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("uint8_least_t") - { - uint_least8_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("uint16_least_t") - { - uint_least16_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("uint32_least_t") - { - uint_least32_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("uint64_least_t") - { - uint_least64_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("exception in case of a non-number type") - { - CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::object).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::array).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::string).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::boolean).get(), std::logic_error); - - CHECK_THROWS_WITH(json(json::value_t::null).get(), - "type must be number, but is null"); - CHECK_THROWS_WITH(json(json::value_t::object).get(), - "type must be number, but is object"); - CHECK_THROWS_WITH(json(json::value_t::array).get(), - "type must be number, but is array"); - CHECK_THROWS_WITH(json(json::value_t::string).get(), - "type must be number, but is string"); - CHECK_THROWS_WITH(json(json::value_t::boolean).get(), - "type must be number, but is boolean"); - - CHECK_NOTHROW(json(json::value_t::number_float).get()); - CHECK_NOTHROW(json(json::value_t::number_float).get()); - } - } - - SECTION("get an integer number (implicit)") - { - json::number_integer_t n_reference {42}; - json j(n_reference); - json::number_unsigned_t n_unsigned_reference {42u}; - json j_unsigned(n_unsigned_reference); - - SECTION("number_integer_t") - { - json::number_integer_t n = j.get(); - CHECK(json(n) == j); - } - - SECTION("number_unsigned_t") - { - json::number_unsigned_t n = j_unsigned.get(); - CHECK(json(n) == j_unsigned); - } - - SECTION("short") - { - short n = j; - CHECK(json(n) == j); - } - - SECTION("unsigned short") - { - unsigned short n = j_unsigned; - CHECK(json(n) == j_unsigned); - } - - SECTION("int") - { - int n = j; - CHECK(json(n) == j); - } - - SECTION("unsigned int") - { - unsigned int n = j_unsigned; - CHECK(json(n) == j_unsigned); - } - - SECTION("long") - { - long n = j; - CHECK(json(n) == j); - } - - SECTION("unsigned long") - { - unsigned long n = j_unsigned; - CHECK(json(n) == j_unsigned); - } - - SECTION("long long") - { - long long n = j; - CHECK(json(n) == j); - } - - SECTION("unsigned long long") - { - unsigned long long n = j_unsigned; - CHECK(json(n) == j_unsigned); - } - - SECTION("int8_t") - { - int8_t n = j; - CHECK(json(n) == j); - } - - SECTION("int16_t") - { - int16_t n = j; - CHECK(json(n) == j); - } - - SECTION("int32_t") - { - int32_t n = j; - CHECK(json(n) == j); - } - - SECTION("int64_t") - { - int64_t n = j; - CHECK(json(n) == j); - } - - SECTION("int8_fast_t") - { - int_fast8_t n = j; - CHECK(json(n) == j); - } - - SECTION("int16_fast_t") - { - int_fast16_t n = j; - CHECK(json(n) == j); - } - - SECTION("int32_fast_t") - { - int_fast32_t n = j; - CHECK(json(n) == j); - } - - SECTION("int64_fast_t") - { - int_fast64_t n = j; - CHECK(json(n) == j); - } - - SECTION("int8_least_t") - { - int_least8_t n = j; - CHECK(json(n) == j); - } - - SECTION("int16_least_t") - { - int_least16_t n = j; - CHECK(json(n) == j); - } - - SECTION("int32_least_t") - { - int_least32_t n = j; - CHECK(json(n) == j); - } - - SECTION("int64_least_t") - { - int_least64_t n = j; - CHECK(json(n) == j); - } - - SECTION("uint8_t") - { - uint8_t n = j_unsigned; - CHECK(json(n) == j_unsigned); - } - - SECTION("uint16_t") - { - uint16_t n = j_unsigned; - CHECK(json(n) == j_unsigned); - } - - SECTION("uint32_t") - { - uint32_t n = j_unsigned; - CHECK(json(n) == j_unsigned); - } - - SECTION("uint64_t") - { - uint64_t n = j_unsigned; - CHECK(json(n) == j_unsigned); - } - - SECTION("uint8_fast_t") - { - uint_fast8_t n = j_unsigned; - CHECK(json(n) == j_unsigned); - } - - SECTION("uint16_fast_t") - { - uint_fast16_t n = j_unsigned; - CHECK(json(n) == j_unsigned); - } - - SECTION("uint32_fast_t") - { - uint_fast32_t n = j_unsigned; - CHECK(json(n) == j_unsigned); - } - - SECTION("uint64_fast_t") - { - uint_fast64_t n = j_unsigned; - CHECK(json(n) == j_unsigned); - } - - SECTION("uint8_least_t") - { - uint_least8_t n = j_unsigned; - CHECK(json(n) == j_unsigned); - } - - SECTION("uint16_least_t") - { - uint_least16_t n = j_unsigned; - CHECK(json(n) == j_unsigned); - } - - SECTION("uint32_least_t") - { - uint_least32_t n = j_unsigned; - CHECK(json(n) == j_unsigned); - } - - SECTION("uint64_least_t") - { - uint_least64_t n = j_unsigned; - CHECK(json(n) == j_unsigned); - } - } - - SECTION("get a floating-point number (explicit)") - { - json::number_float_t n_reference {42.23}; - json j(n_reference); - - SECTION("number_float_t") - { - json::number_float_t n = j.get(); - CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float)); - } - - SECTION("float") - { - float n = j.get(); - CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float)); - } - - SECTION("double") - { - double n = j.get(); - CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float)); - } - - SECTION("exception in case of a non-string type") - { - CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::object).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::array).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::string).get(), std::logic_error); - CHECK_THROWS_AS(json(json::value_t::boolean).get(), std::logic_error); - - CHECK_THROWS_WITH(json(json::value_t::null).get(), - "type must be number, but is null"); - CHECK_THROWS_WITH(json(json::value_t::object).get(), - "type must be number, but is object"); - CHECK_THROWS_WITH(json(json::value_t::array).get(), - "type must be number, but is array"); - CHECK_THROWS_WITH(json(json::value_t::string).get(), - "type must be number, but is string"); - CHECK_THROWS_WITH(json(json::value_t::boolean).get(), - "type must be number, but is boolean"); - - CHECK_NOTHROW(json(json::value_t::number_integer).get()); - CHECK_NOTHROW(json(json::value_t::number_unsigned).get()); - } - } - - SECTION("get a floating-point number (implicit)") - { - json::number_float_t n_reference {42.23}; - json j(n_reference); - - SECTION("number_float_t") - { - json::number_float_t n = j; - CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float)); - } - - SECTION("float") - { - float n = j; - CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float)); - } - - SECTION("double") - { - double n = j; - CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float)); - } - } - - SECTION("more involved conversions") - { - SECTION("object-like STL containers") - { - json j1 = {{"one", 1}, {"two", 2}, {"three", 3}}; - json j2 = {{"one", 1u}, {"two", 2u}, {"three", 3u}}; - json j3 = {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}}; - json j4 = {{"one", true}, {"two", false}, {"three", true}}; - json j5 = {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}}; - - SECTION("std::map") - { - auto m1 = j1.get>(); - auto m2 = j2.get>(); - auto m3 = j3.get>(); - auto m4 = j4.get>(); - //auto m5 = j5.get>(); - } - - SECTION("std::unordered_map") - { - auto m1 = j1.get>(); - auto m2 = j2.get>(); - auto m3 = j3.get>(); - auto m4 = j4.get>(); - //auto m5 = j5.get>(); - //CHECK(m5["one"] == "eins"); - } - - SECTION("std::multimap") - { - auto m1 = j1.get>(); - auto m2 = j2.get>(); - auto m3 = j3.get>(); - auto m4 = j4.get>(); - //auto m5 = j5.get>(); - //CHECK(m5["one"] == "eins"); - } - - SECTION("std::unordered_multimap") - { - auto m1 = j1.get>(); - auto m2 = j2.get>(); - auto m3 = j3.get>(); - auto m4 = j4.get>(); - //auto m5 = j5.get>(); - //CHECK(m5["one"] == "eins"); - } - - SECTION("exception in case of a non-object type") - { - CHECK_THROWS_AS((json().get>()), std::logic_error); - CHECK_THROWS_WITH((json().get>()), "type must be object, but is null"); - } - } - - SECTION("array-like STL containers") - { - json j1 = {1, 2, 3, 4}; - json j2 = {1u, 2u, 3u, 4u}; - json j3 = {1.2, 2.3, 3.4, 4.5}; - json j4 = {true, false, true}; - json j5 = {"one", "two", "three"}; - - SECTION("std::list") - { - auto m1 = j1.get>(); - auto m2 = j2.get>(); - auto m3 = j3.get>(); - auto m4 = j4.get>(); - auto m5 = j5.get>(); - } - - //SECTION("std::forward_list") - //{ - // auto m1 = j1.get>(); - // auto m2 = j2.get>(); - // auto m3 = j3.get>(); - // auto m4 = j4.get>(); - // auto m5 = j5.get>(); - //} - - SECTION("std::vector") - { - auto m1 = j1.get>(); - auto m2 = j2.get>(); - auto m3 = j3.get>(); - auto m4 = j4.get>(); - auto m5 = j5.get>(); - } - - SECTION("std::deque") - { - auto m1 = j1.get>(); - auto m2 = j2.get>(); - auto m3 = j2.get>(); - auto m4 = j4.get>(); - auto m5 = j5.get>(); - } - - SECTION("std::set") - { - auto m1 = j1.get>(); - auto m2 = j2.get>(); - auto m3 = j3.get>(); - auto m4 = j4.get>(); - auto m5 = j5.get>(); - } - - SECTION("std::unordered_set") - { - auto m1 = j1.get>(); - auto m2 = j2.get>(); - auto m3 = j3.get>(); - auto m4 = j4.get>(); - auto m5 = j5.get>(); - } - - SECTION("exception in case of a non-object type") - { - CHECK_THROWS_AS((json().get>()), std::logic_error); - CHECK_THROWS_AS((json().get>()), std::logic_error); - CHECK_THROWS_AS((json().get>()), std::logic_error); - CHECK_THROWS_AS((json().get>()), std::logic_error); - - CHECK_THROWS_WITH((json().get>()), "type must be array, but is null"); - CHECK_THROWS_WITH((json().get>()), "type must be array, but is null"); - CHECK_THROWS_WITH((json().get>()), "type must be array, but is null"); - CHECK_THROWS_WITH((json().get>()), "type must be array, but is null"); - } - } - } -} - -TEST_CASE("pointer access") -{ - // create a JSON value with different types - json json_types = - { - {"boolean", true}, - { - "number", { - {"integer", 42}, - {"unsigned", 42u}, - {"floating-point", 17.23} - } - }, - {"string", "Hello, world!"}, - {"array", {1, 2, 3, 4, 5}}, - {"null", nullptr} - }; - - SECTION("pointer access to object_t") - { - using test_type = json::object_t; - json value = {{"one", 1}, {"two", 2}}; - - // check if pointers are returned correctly - test_type* p1 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p1 == value.get()); - - const test_type* p2 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p2 == value.get()); - - const test_type* const p3 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p3 == value.get()); - - // check if null pointers are returned correctly - CHECK(value.get_ptr() != nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - } - - SECTION("pointer access to const object_t") - { - using test_type = json::object_t; - const json value = {{"one", 1}, {"two", 2}}; - - // this should not compile - // test_type* p1 = value.get_ptr(); - - // check if pointers are returned correctly - const test_type* p2 = value.get_ptr(); - CHECK(*p2 == value.get()); - - const test_type* const p3 = value.get_ptr(); - CHECK(p2 == p3); - } - - SECTION("pointer access to array_t") - { - using test_type = json::array_t; - json value = {1, 2, 3, 4}; - - // check if pointers are returned correctly - test_type* p1 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p1 == value.get()); - - const test_type* p2 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p2 == value.get()); - - const test_type* const p3 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p3 == value.get()); - - // check if null pointers are returned correctly - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() != nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - } - - SECTION("pointer access to string_t") - { - using test_type = json::string_t; - json value = "hello"; - - // check if pointers are returned correctly - test_type* p1 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p1 == value.get()); - - const test_type* p2 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p2 == value.get()); - - const test_type* const p3 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p3 == value.get()); - - // check if null pointers are returned correctly - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() != nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - } - - SECTION("pointer access to boolean_t") - { - using test_type = json::boolean_t; - json value = false; - - // check if pointers are returned correctly - test_type* p1 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p1 == value.get()); - - const test_type* p2 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p2 == value.get()); - - const test_type* const p3 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p3 == value.get()); - - // check if null pointers are returned correctly - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() != nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - } - - SECTION("pointer access to number_integer_t") - { - using test_type = json::number_integer_t; - json value = 23; - - // check if pointers are returned correctly - test_type* p1 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p1 == value.get()); - - const test_type* p2 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p2 == value.get()); - - const test_type* const p3 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p3 == value.get()); - - // check if null pointers are returned correctly - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() != nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - } - - SECTION("pointer access to number_unsigned_t") - { - using test_type = json::number_unsigned_t; - json value = 23u; - - // check if pointers are returned correctly - test_type* p1 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p1 == value.get()); - - const test_type* p2 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p2 == value.get()); - - const test_type* const p3 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p3 == value.get()); - - // check if null pointers are returned correctly - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() != nullptr); - CHECK(value.get_ptr() != nullptr); - CHECK(value.get_ptr() == nullptr); - } - - SECTION("pointer access to number_float_t") - { - using test_type = json::number_float_t; - json value = 42.23; - - // check if pointers are returned correctly - test_type* p1 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p1 == Approx(value.get())); - - const test_type* p2 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p2 == Approx(value.get())); - - const test_type* const p3 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); - CHECK(*p3 == Approx(value.get())); - - // check if null pointers are returned correctly - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() != nullptr); - } -} - -TEST_CASE("reference access") -{ - // create a JSON value with different types - json json_types = - { - {"boolean", true}, - { - "number", { - {"integer", 42}, - {"floating-point", 17.23} - } - }, - {"string", "Hello, world!"}, - {"array", {1, 2, 3, 4, 5}}, - {"null", nullptr} - }; - - SECTION("reference access to object_t") - { - using test_type = json::object_t; - json value = {{"one", 1}, {"two", 2}}; - - // check if references are returned correctly - test_type& p1 = value.get_ref(); - CHECK(&p1 == value.get_ptr()); - CHECK(p1 == value.get()); - - const test_type& p2 = value.get_ref(); - CHECK(&p2 == value.get_ptr()); - CHECK(p2 == value.get()); - - // check if mismatching references throw correctly - CHECK_NOTHROW(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - } - - SECTION("const reference access to const object_t") - { - using test_type = json::object_t; - const json value = {{"one", 1}, {"two", 2}}; - - // this should not compile - // test_type& p1 = value.get_ref(); - - // check if references are returned correctly - const test_type& p2 = value.get_ref(); - CHECK(&p2 == value.get_ptr()); - CHECK(p2 == value.get()); - } - - SECTION("reference access to array_t") - { - using test_type = json::array_t; - json value = {1, 2, 3, 4}; - - // check if references are returned correctly - test_type& p1 = value.get_ref(); - CHECK(&p1 == value.get_ptr()); - CHECK(p1 == value.get()); - - const test_type& p2 = value.get_ref(); - CHECK(&p2 == value.get_ptr()); - CHECK(p2 == value.get()); - - // check if mismatching references throw correctly - CHECK_THROWS(value.get_ref()); - CHECK_NOTHROW(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - } - - SECTION("reference access to string_t") - { - using test_type = json::string_t; - json value = "hello"; - - // check if references are returned correctly - test_type& p1 = value.get_ref(); - CHECK(&p1 == value.get_ptr()); - CHECK(p1 == value.get()); - - const test_type& p2 = value.get_ref(); - CHECK(&p2 == value.get_ptr()); - CHECK(p2 == value.get()); - - // check if mismatching references throw correctly - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_NOTHROW(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - } - - SECTION("reference access to boolean_t") - { - using test_type = json::boolean_t; - json value = false; - - // check if references are returned correctly - test_type& p1 = value.get_ref(); - CHECK(&p1 == value.get_ptr()); - CHECK(p1 == value.get()); - - const test_type& p2 = value.get_ref(); - CHECK(&p2 == value.get_ptr()); - CHECK(p2 == value.get()); - - // check if mismatching references throw correctly - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_NOTHROW(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - } - - SECTION("reference access to number_integer_t") - { - using test_type = json::number_integer_t; - json value = 23; - - // check if references are returned correctly - test_type& p1 = value.get_ref(); - CHECK(&p1 == value.get_ptr()); - CHECK(p1 == value.get()); - - const test_type& p2 = value.get_ref(); - CHECK(&p2 == value.get_ptr()); - CHECK(p2 == value.get()); - - // check if mismatching references throw correctly - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_NOTHROW(value.get_ref()); - CHECK_THROWS(value.get_ref()); - } - - SECTION("reference access to number_float_t") - { - using test_type = json::number_float_t; - json value = 42.23; - - // check if references are returned correctly - test_type& p1 = value.get_ref(); - CHECK(&p1 == value.get_ptr()); - CHECK(p1 == value.get()); - - const test_type& p2 = value.get_ref(); - CHECK(&p2 == value.get_ptr()); - CHECK(p2 == value.get()); - - // check if mismatching references throw correctly - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_THROWS(value.get_ref()); - CHECK_NOTHROW(value.get_ref()); - } -} - -TEST_CASE("element access") -{ - SECTION("array") - { - json j = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - const json j_const = j; - - SECTION("access specified element with bounds checking") - { - SECTION("access within bounds") - { - CHECK(j.at(0) == json(1)); - CHECK(j.at(1) == json(1u)); - CHECK(j.at(2) == json(true)); - CHECK(j.at(3) == json(nullptr)); - CHECK(j.at(4) == json("string")); - CHECK(j.at(5) == json(42.23)); - CHECK(j.at(6) == json(json::object())); - CHECK(j.at(7) == json({1, 2, 3})); - - CHECK(j_const.at(0) == json(1)); - CHECK(j_const.at(1) == json(1u)); - CHECK(j_const.at(2) == json(true)); - CHECK(j_const.at(3) == json(nullptr)); - CHECK(j_const.at(4) == json("string")); - CHECK(j_const.at(5) == json(42.23)); - CHECK(j_const.at(6) == json(json::object())); - CHECK(j_const.at(7) == json({1, 2, 3})); - } - - SECTION("access outside bounds") - { - CHECK_THROWS_AS(j.at(8), std::out_of_range); - CHECK_THROWS_AS(j_const.at(8), std::out_of_range); - - CHECK_THROWS_WITH(j.at(8), "array index 8 is out of range"); - CHECK_THROWS_WITH(j_const.at(8), "array index 8 is out of range"); - } - - SECTION("access on non-array type") - { - SECTION("null") - { - json j_nonarray(json::value_t::null); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); - CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); - - CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with null"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with null"); - } - - SECTION("boolean") - { - json j_nonarray(json::value_t::boolean); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); - CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); - - CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with boolean"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with boolean"); - } - - SECTION("string") - { - json j_nonarray(json::value_t::string); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); - CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); - - CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with string"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with string"); - } - - SECTION("object") - { - json j_nonarray(json::value_t::object); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); - CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); - - CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with object"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with object"); - } - - SECTION("number (integer)") - { - json j_nonarray(json::value_t::number_integer); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); - CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); - - CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with number"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with number"); - } - - SECTION("number (unsigned)") - { - json j_nonarray(json::value_t::number_unsigned); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); - CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); - - CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with number"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with number"); - } - - SECTION("number (floating-point)") - { - json j_nonarray(json::value_t::number_float); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); - CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); - - CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with number"); - CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with number"); - } - } - } - - SECTION("front and back") - { - CHECK(j.front() == json(1)); - CHECK(j_const.front() == json(1)); - CHECK(j.back() == json({1, 2, 3})); - CHECK(j_const.back() == json({1, 2, 3})); - } - - SECTION("access specified element") - { - SECTION("access within bounds") - { - CHECK(j[0] == json(1)); - CHECK(j[1] == json(1u)); - CHECK(j[2] == json(true)); - CHECK(j[3] == json(nullptr)); - CHECK(j[4] == json("string")); - CHECK(j[5] == json(42.23)); - CHECK(j[6] == json(json::object())); - CHECK(j[7] == json({1, 2, 3})); - - CHECK(j_const[0] == json(1)); - CHECK(j_const[1] == json(1u)); - CHECK(j_const[2] == json(true)); - CHECK(j_const[3] == json(nullptr)); - CHECK(j_const[4] == json("string")); - CHECK(j_const[5] == json(42.23)); - CHECK(j_const[6] == json(json::object())); - CHECK(j_const[7] == json({1, 2, 3})); - } - - SECTION("access on non-array type") - { - SECTION("null") - { - SECTION("standard tests") - { - json j_nonarray(json::value_t::null); - const json j_nonarray_const(j_nonarray); - CHECK_NOTHROW(j_nonarray[0]); - CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); - CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with null"); - } - - SECTION("implicit transformation to properly filled array") - { - json j_nonarray; - j_nonarray[3] = 42; - CHECK(j_nonarray == json({nullptr, nullptr, nullptr, 42})); - } - } - - SECTION("boolean") - { - json j_nonarray(json::value_t::boolean); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray[0], std::domain_error); - CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); - CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with boolean"); - CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with boolean"); - } - - SECTION("string") - { - json j_nonarray(json::value_t::string); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray[0], std::domain_error); - CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); - CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with string"); - CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with string"); - } - - SECTION("object") - { - json j_nonarray(json::value_t::object); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray[0], std::domain_error); - CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); - CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with object"); - CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with object"); - } - - SECTION("number (integer)") - { - json j_nonarray(json::value_t::number_integer); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray[0], std::domain_error); - CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); - CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with number"); - CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with number"); - } - - SECTION("number (unsigned)") - { - json j_nonarray(json::value_t::number_unsigned); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray[0], std::domain_error); - CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); - CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with number"); - CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with number"); - } - - SECTION("number (floating-point)") - { - json j_nonarray(json::value_t::number_float); - const json j_nonarray_const(j_nonarray); - CHECK_THROWS_AS(j_nonarray[0], std::domain_error); - CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); - CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with number"); - CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with number"); - } - } - } - - SECTION("remove specified element") - { - SECTION("remove element by index") - { - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - jarray.erase(0); - CHECK(jarray == json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - jarray.erase(1); - CHECK(jarray == json({1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - jarray.erase(2); - CHECK(jarray == json({1, 1u, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - jarray.erase(3); - CHECK(jarray == json({1, 1u, true, "string", 42.23, json::object(), {1, 2, 3}})); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - jarray.erase(4); - CHECK(jarray == json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}})); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - jarray.erase(5); - CHECK(jarray == json({1, 1u, true, nullptr, "string", json::object(), {1, 2, 3}})); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - jarray.erase(6); - CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, {1, 2, 3}})); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - jarray.erase(7); - CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, json::object()})); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - CHECK_THROWS_AS(jarray.erase(8), std::out_of_range); - CHECK_THROWS_WITH(jarray.erase(8), "array index 8 is out of range"); - } - } - - SECTION("remove element by iterator") - { - SECTION("erase(begin())") - { - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::iterator it2 = jarray.erase(jarray.begin()); - CHECK(jarray == json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); - CHECK(*it2 == json(1u)); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::const_iterator it2 = jarray.erase(jarray.cbegin()); - CHECK(jarray == json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); - CHECK(*it2 == json(1u)); - } - } - - SECTION("erase(begin(), end())") - { - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::iterator it2 = jarray.erase(jarray.begin(), jarray.end()); - CHECK(jarray == json::array()); - CHECK(it2 == jarray.end()); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::const_iterator it2 = jarray.erase(jarray.cbegin(), jarray.cend()); - CHECK(jarray == json::array()); - CHECK(it2 == jarray.cend()); - } - } - - SECTION("erase(begin(), begin())") - { - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::iterator it2 = jarray.erase(jarray.begin(), jarray.begin()); - CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); - CHECK(*it2 == json(1)); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::const_iterator it2 = jarray.erase(jarray.cbegin(), jarray.cbegin()); - CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); - CHECK(*it2 == json(1)); - } - } - - SECTION("erase at offset") - { - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::iterator it = jarray.begin() + 4; - json::iterator it2 = jarray.erase(it); - CHECK(jarray == json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}})); - CHECK(*it2 == json(42.23)); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::const_iterator it = jarray.cbegin() + 4; - json::const_iterator it2 = jarray.erase(it); - CHECK(jarray == json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}})); - CHECK(*it2 == json(42.23)); - } - } - - SECTION("erase subrange") - { - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::iterator it2 = jarray.erase(jarray.begin() + 3, jarray.begin() + 6); - CHECK(jarray == json({1, 1u, true, json::object(), {1, 2, 3}})); - CHECK(*it2 == json::object()); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json::const_iterator it2 = jarray.erase(jarray.cbegin() + 3, jarray.cbegin() + 6); - CHECK(jarray == json({1, 1u, true, json::object(), {1, 2, 3}})); - CHECK(*it2 == json::object()); - } - } - - SECTION("different arrays") - { - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json jarray2 = {"foo", "bar"}; - CHECK_THROWS_AS(jarray.erase(jarray2.begin()), std::domain_error); - CHECK_THROWS_AS(jarray.erase(jarray.begin(), jarray2.end()), std::domain_error); - CHECK_THROWS_AS(jarray.erase(jarray2.begin(), jarray.end()), std::domain_error); - CHECK_THROWS_AS(jarray.erase(jarray2.begin(), jarray2.end()), std::domain_error); - - CHECK_THROWS_WITH(jarray.erase(jarray2.begin()), "iterator does not fit current value"); - CHECK_THROWS_WITH(jarray.erase(jarray.begin(), jarray2.end()), - "iterators do not fit current value"); - CHECK_THROWS_WITH(jarray.erase(jarray2.begin(), jarray.end()), - "iterators do not fit current value"); - CHECK_THROWS_WITH(jarray.erase(jarray2.begin(), jarray2.end()), - "iterators do not fit current value"); - } - { - json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; - json jarray2 = {"foo", "bar"}; - CHECK_THROWS_AS(jarray.erase(jarray2.cbegin()), std::domain_error); - CHECK_THROWS_AS(jarray.erase(jarray.cbegin(), jarray2.cend()), std::domain_error); - CHECK_THROWS_AS(jarray.erase(jarray2.cbegin(), jarray.cend()), std::domain_error); - CHECK_THROWS_AS(jarray.erase(jarray2.cbegin(), jarray2.cend()), std::domain_error); - - CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin()), "iterator does not fit current value"); - CHECK_THROWS_WITH(jarray.erase(jarray.cbegin(), jarray2.cend()), - "iterators do not fit current value"); - CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin(), jarray.cend()), - "iterators do not fit current value"); - CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin(), jarray2.cend()), - "iterators do not fit current value"); - } - } - } - - SECTION("remove element by index in non-array type") - { - SECTION("null") - { - json j_nonobject(json::value_t::null); - CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with null"); - } - - SECTION("boolean") - { - json j_nonobject(json::value_t::boolean); - CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with boolean"); - } - - SECTION("string") - { - json j_nonobject(json::value_t::string); - CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with string"); - } - - SECTION("object") - { - json j_nonobject(json::value_t::object); - CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with object"); - } - - SECTION("number (integer)") - { - json j_nonobject(json::value_t::number_integer); - CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with number"); - } - - SECTION("number (unsigned)") - { - json j_nonobject(json::value_t::number_unsigned); - CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with number"); - } - - SECTION("number (floating-point)") - { - json j_nonobject(json::value_t::number_float); - CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with number"); - } - } - } - } - - SECTION("object") - { - json j = {{"integer", 1}, {"unsigned", 1u}, {"floating", 42.23}, {"null", nullptr}, {"string", "hello world"}, {"boolean", true}, {"object", json::object()}, {"array", {1, 2, 3}}}; - const json j_const = j; - - SECTION("access specified element with bounds checking") - { - SECTION("access within bounds") - { - CHECK(j.at("integer") == json(1)); - CHECK(j.at("unsigned") == json(1u)); - CHECK(j.at("boolean") == json(true)); - CHECK(j.at("null") == json(nullptr)); - CHECK(j.at("string") == json("hello world")); - CHECK(j.at("floating") == json(42.23)); - CHECK(j.at("object") == json(json::object())); - CHECK(j.at("array") == json({1, 2, 3})); - - CHECK(j_const.at("integer") == json(1)); - CHECK(j_const.at("unsigned") == json(1u)); - CHECK(j_const.at("boolean") == json(true)); - CHECK(j_const.at("null") == json(nullptr)); - CHECK(j_const.at("string") == json("hello world")); - CHECK(j_const.at("floating") == json(42.23)); - CHECK(j_const.at("object") == json(json::object())); - CHECK(j_const.at("array") == json({1, 2, 3})); - } - - SECTION("access outside bounds") - { - CHECK_THROWS_AS(j.at("foo"), std::out_of_range); - CHECK_THROWS_AS(j_const.at("foo"), std::out_of_range); - CHECK_THROWS_WITH(j.at("foo"), "key 'foo' not found"); - CHECK_THROWS_WITH(j_const.at("foo"), "key 'foo' not found"); - } - - SECTION("access on non-object type") - { - SECTION("null") - { - json j_nonobject(json::value_t::null); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with null"); - CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with null"); - } - - SECTION("boolean") - { - json j_nonobject(json::value_t::boolean); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with boolean"); - CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with boolean"); - } - - SECTION("string") - { - json j_nonobject(json::value_t::string); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with string"); - CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with string"); - } - - SECTION("array") - { - json j_nonobject(json::value_t::array); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with array"); - CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with array"); - } - - SECTION("number (integer)") - { - json j_nonobject(json::value_t::number_integer); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with number"); - CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with number"); - } - - SECTION("number (unsigned)") - { - json j_nonobject(json::value_t::number_unsigned); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with number"); - CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with number"); - } - - SECTION("number (floating-point)") - { - json j_nonobject(json::value_t::number_float); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with number"); - CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with number"); - } - } - } - - SECTION("access specified element with default value") - { - SECTION("given a key") - { - SECTION("access existing value") - { - CHECK(j.value("integer", 2) == 1); - CHECK(j.value("integer", 1.0) == Approx(1)); - CHECK(j.value("unsigned", 2) == 1u); - CHECK(j.value("unsigned", 1.0) == Approx(1u)); - CHECK(j.value("null", json(1)) == json()); - CHECK(j.value("boolean", false) == true); - CHECK(j.value("string", "bar") == "hello world"); - CHECK(j.value("string", std::string("bar")) == "hello world"); - CHECK(j.value("floating", 12.34) == Approx(42.23)); - CHECK(j.value("floating", 12) == 42); - CHECK(j.value("object", json({{"foo", "bar"}})) == json(json::object())); - CHECK(j.value("array", json({10, 100})) == json({1, 2, 3})); - - CHECK(j_const.value("integer", 2) == 1); - CHECK(j_const.value("integer", 1.0) == Approx(1)); - CHECK(j_const.value("unsigned", 2) == 1u); - CHECK(j_const.value("unsigned", 1.0) == Approx(1u)); - CHECK(j_const.value("boolean", false) == true); - CHECK(j_const.value("string", "bar") == "hello world"); - CHECK(j_const.value("string", std::string("bar")) == "hello world"); - CHECK(j_const.value("floating", 12.34) == Approx(42.23)); - CHECK(j_const.value("floating", 12) == 42); - CHECK(j_const.value("object", json({{"foo", "bar"}})) == json(json::object())); - CHECK(j_const.value("array", json({10, 100})) == json({1, 2, 3})); - } - - SECTION("access non-existing value") - { - CHECK(j.value("_", 2) == 2); - CHECK(j.value("_", 2u) == 2u); - CHECK(j.value("_", false) == false); - CHECK(j.value("_", "bar") == "bar"); - CHECK(j.value("_", 12.34) == Approx(12.34)); - CHECK(j.value("_", json({{"foo", "bar"}})) == json({{"foo", "bar"}})); - CHECK(j.value("_", json({10, 100})) == json({10, 100})); - - CHECK(j_const.value("_", 2) == 2); - CHECK(j_const.value("_", 2u) == 2u); - CHECK(j_const.value("_", false) == false); - CHECK(j_const.value("_", "bar") == "bar"); - CHECK(j_const.value("_", 12.34) == Approx(12.34)); - CHECK(j_const.value("_", json({{"foo", "bar"}})) == json({{"foo", "bar"}})); - CHECK(j_const.value("_", json({10, 100})) == json({10, 100})); - } - - SECTION("access on non-object type") - { - SECTION("null") - { - json j_nonobject(json::value_t::null); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with null"); - CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with null"); - } - - SECTION("boolean") - { - json j_nonobject(json::value_t::boolean); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with boolean"); - CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with boolean"); - } - - SECTION("string") - { - json j_nonobject(json::value_t::string); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with string"); - CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with string"); - } - - SECTION("array") - { - json j_nonobject(json::value_t::array); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with array"); - CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with array"); - } - - SECTION("number (integer)") - { - json j_nonobject(json::value_t::number_integer); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number"); - CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number"); - } - - SECTION("number (unsigned)") - { - json j_nonobject(json::value_t::number_unsigned); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number"); - CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number"); - } - - SECTION("number (floating-point)") - { - json j_nonobject(json::value_t::number_float); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number"); - CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number"); - } - } - } - - SECTION("given a JSON pointer") - { - SECTION("access existing value") - { - CHECK(j.value("/integer"_json_pointer, 2) == 1); - CHECK(j.value("/integer"_json_pointer, 1.0) == Approx(1)); - CHECK(j.value("/unsigned"_json_pointer, 2) == 1u); - CHECK(j.value("/unsigned"_json_pointer, 1.0) == Approx(1u)); - CHECK(j.value("/null"_json_pointer, json(1)) == json()); - CHECK(j.value("/boolean"_json_pointer, false) == true); - CHECK(j.value("/string"_json_pointer, "bar") == "hello world"); - CHECK(j.value("/string"_json_pointer, std::string("bar")) == "hello world"); - CHECK(j.value("/floating"_json_pointer, 12.34) == Approx(42.23)); - CHECK(j.value("/floating"_json_pointer, 12) == 42); - CHECK(j.value("/object"_json_pointer, json({{"foo", "bar"}})) == json(json::object())); - CHECK(j.value("/array"_json_pointer, json({10, 100})) == json({1, 2, 3})); - - CHECK(j_const.value("/integer"_json_pointer, 2) == 1); - CHECK(j_const.value("/integer"_json_pointer, 1.0) == Approx(1)); - CHECK(j_const.value("/unsigned"_json_pointer, 2) == 1u); - CHECK(j_const.value("/unsigned"_json_pointer, 1.0) == Approx(1u)); - CHECK(j_const.value("/boolean"_json_pointer, false) == true); - CHECK(j_const.value("/string"_json_pointer, "bar") == "hello world"); - CHECK(j_const.value("/string"_json_pointer, std::string("bar")) == "hello world"); - CHECK(j_const.value("/floating"_json_pointer, 12.34) == Approx(42.23)); - CHECK(j_const.value("/floating"_json_pointer, 12) == 42); - CHECK(j_const.value("/object"_json_pointer, json({{"foo", "bar"}})) == json(json::object())); - CHECK(j_const.value("/array"_json_pointer, json({10, 100})) == json({1, 2, 3})); - } - - SECTION("access non-existing value") - { - CHECK(j.value("/not/existing"_json_pointer, 2) == 2); - CHECK(j.value("/not/existing"_json_pointer, 2u) == 2u); - CHECK(j.value("/not/existing"_json_pointer, false) == false); - CHECK(j.value("/not/existing"_json_pointer, "bar") == "bar"); - CHECK(j.value("/not/existing"_json_pointer, 12.34) == Approx(12.34)); - CHECK(j.value("/not/existing"_json_pointer, json({{"foo", "bar"}})) == json({{"foo", "bar"}})); - CHECK(j.value("/not/existing"_json_pointer, json({10, 100})) == json({10, 100})); - - CHECK(j_const.value("/not/existing"_json_pointer, 2) == 2); - CHECK(j_const.value("/not/existing"_json_pointer, 2u) == 2u); - CHECK(j_const.value("/not/existing"_json_pointer, false) == false); - CHECK(j_const.value("/not/existing"_json_pointer, "bar") == "bar"); - CHECK(j_const.value("/not/existing"_json_pointer, 12.34) == Approx(12.34)); - CHECK(j_const.value("/not/existing"_json_pointer, json({{"foo", "bar"}})) == json({{"foo", "bar"}})); - CHECK(j_const.value("/not/existing"_json_pointer, json({10, 100})) == json({10, 100})); - } - - SECTION("access on non-object type") - { - SECTION("null") - { - json j_nonobject(json::value_t::null); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with null"); - CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), "cannot use value() with null"); - } - - SECTION("boolean") - { - json j_nonobject(json::value_t::boolean); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with boolean"); - CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), - "cannot use value() with boolean"); - } - - SECTION("string") - { - json j_nonobject(json::value_t::string); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with string"); - CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), - "cannot use value() with string"); - } - - SECTION("array") - { - json j_nonobject(json::value_t::array); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with array"); - CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), "cannot use value() with array"); - } - - SECTION("number (integer)") - { - json j_nonobject(json::value_t::number_integer); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with number"); - CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), - "cannot use value() with number"); - } - - SECTION("number (unsigned)") - { - json j_nonobject(json::value_t::number_unsigned); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with number"); - CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), - "cannot use value() with number"); - } - - SECTION("number (floating-point)") - { - json j_nonobject(json::value_t::number_float); - const json j_nonobject_const(j_nonobject); - CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with number"); - CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), - "cannot use value() with number"); - } - } - } - } - - SECTION("front and back") - { - // "array" is the smallest key - CHECK(j.front() == json({1, 2, 3})); - CHECK(j_const.front() == json({1, 2, 3})); - // "unsigned" is the largest key - CHECK(j.back() == json(1u)); - CHECK(j_const.back() == json(1u)); - } - - SECTION("access specified element") - { - SECTION("access within bounds") - { - CHECK(j["integer"] == json(1)); - CHECK(j[json::object_t::key_type("integer")] == j["integer"]); - - CHECK(j["unsigned"] == json(1u)); - CHECK(j[json::object_t::key_type("unsigned")] == j["unsigned"]); - - CHECK(j["boolean"] == json(true)); - CHECK(j[json::object_t::key_type("boolean")] == j["boolean"]); - - CHECK(j["null"] == json(nullptr)); - CHECK(j[json::object_t::key_type("null")] == j["null"]); - - CHECK(j["string"] == json("hello world")); - CHECK(j[json::object_t::key_type("string")] == j["string"]); - - CHECK(j["floating"] == json(42.23)); - CHECK(j[json::object_t::key_type("floating")] == j["floating"]); - - CHECK(j["object"] == json(json::object())); - CHECK(j[json::object_t::key_type("object")] == j["object"]); - - CHECK(j["array"] == json({1, 2, 3})); - CHECK(j[json::object_t::key_type("array")] == j["array"]); - - CHECK(j_const["integer"] == json(1)); - CHECK(j_const[json::object_t::key_type("integer")] == j["integer"]); - - CHECK(j_const["boolean"] == json(true)); - CHECK(j_const[json::object_t::key_type("boolean")] == j["boolean"]); - - CHECK(j_const["null"] == json(nullptr)); - CHECK(j_const[json::object_t::key_type("null")] == j["null"]); - - CHECK(j_const["string"] == json("hello world")); - CHECK(j_const[json::object_t::key_type("string")] == j["string"]); - - CHECK(j_const["floating"] == json(42.23)); - CHECK(j_const[json::object_t::key_type("floating")] == j["floating"]); - - CHECK(j_const["object"] == json(json::object())); - CHECK(j_const[json::object_t::key_type("object")] == j["object"]); - - CHECK(j_const["array"] == json({1, 2, 3})); - CHECK(j_const[json::object_t::key_type("array")] == j["array"]); - } - - SECTION("access on non-object type") - { - SECTION("null") - { - json j_nonobject(json::value_t::null); - json j_nonobject2(json::value_t::null); - const json j_const_nonobject(j_nonobject); - CHECK_NOTHROW(j_nonobject["foo"]); - CHECK_NOTHROW(j_nonobject2[json::object_t::key_type("foo")]); - CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with null"); - CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with null"); - } - - SECTION("boolean") - { - json j_nonobject(json::value_t::boolean); - const json j_const_nonobject(j_nonobject); - CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with boolean"); - CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with boolean"); - CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with boolean"); - CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with boolean"); - } - - SECTION("string") - { - json j_nonobject(json::value_t::string); - const json j_const_nonobject(j_nonobject); - CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with string"); - CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with string"); - CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with string"); - CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with string"); - } - - SECTION("array") - { - json j_nonobject(json::value_t::array); - const json j_const_nonobject(j_nonobject); - CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with array"); - CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], "cannot use operator[] with array"); - CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with array"); - CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with array"); - } - - SECTION("number (integer)") - { - json j_nonobject(json::value_t::number_integer); - const json j_const_nonobject(j_nonobject); - CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with number"); - CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with number"); - CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with number"); - CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with number"); - } - - SECTION("number (unsigned)") - { - json j_nonobject(json::value_t::number_unsigned); - const json j_const_nonobject(j_nonobject); - CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with number"); - CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with number"); - CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with number"); - CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with number"); - } - - SECTION("number (floating-point)") - { - json j_nonobject(json::value_t::number_float); - const json j_const_nonobject(j_nonobject); - CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); - CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); - CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with number"); - CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with number"); - CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with number"); - CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], - "cannot use operator[] with number"); - } - } - } - - SECTION("remove specified element") - { - SECTION("remove element by key") - { - CHECK(j.find("integer") != j.end()); - CHECK(j.erase("integer") == 1); - CHECK(j.find("integer") == j.end()); - CHECK(j.erase("integer") == 0); - - CHECK(j.find("unsigned") != j.end()); - CHECK(j.erase("unsigned") == 1); - CHECK(j.find("unsigned") == j.end()); - CHECK(j.erase("unsigned") == 0); - - CHECK(j.find("boolean") != j.end()); - CHECK(j.erase("boolean") == 1); - CHECK(j.find("boolean") == j.end()); - CHECK(j.erase("boolean") == 0); - - CHECK(j.find("null") != j.end()); - CHECK(j.erase("null") == 1); - CHECK(j.find("null") == j.end()); - CHECK(j.erase("null") == 0); - - CHECK(j.find("string") != j.end()); - CHECK(j.erase("string") == 1); - CHECK(j.find("string") == j.end()); - CHECK(j.erase("string") == 0); - - CHECK(j.find("floating") != j.end()); - CHECK(j.erase("floating") == 1); - CHECK(j.find("floating") == j.end()); - CHECK(j.erase("floating") == 0); - - CHECK(j.find("object") != j.end()); - CHECK(j.erase("object") == 1); - CHECK(j.find("object") == j.end()); - CHECK(j.erase("object") == 0); - - CHECK(j.find("array") != j.end()); - CHECK(j.erase("array") == 1); - CHECK(j.find("array") == j.end()); - CHECK(j.erase("array") == 0); - } - - SECTION("remove element by iterator") - { - SECTION("erase(begin())") - { - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - json::iterator it2 = jobject.erase(jobject.begin()); - CHECK(jobject == json({{"b", 1}, {"c", 17u}})); - CHECK(*it2 == json(1)); - } - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - json::const_iterator it2 = jobject.erase(jobject.cbegin()); - CHECK(jobject == json({{"b", 1}, {"c", 17u}})); - CHECK(*it2 == json(1)); - } - } - - SECTION("erase(begin(), end())") - { - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - json::iterator it2 = jobject.erase(jobject.begin(), jobject.end()); - CHECK(jobject == json::object()); - CHECK(it2 == jobject.end()); - } - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - json::const_iterator it2 = jobject.erase(jobject.cbegin(), jobject.cend()); - CHECK(jobject == json::object()); - CHECK(it2 == jobject.cend()); - } - } - - SECTION("erase(begin(), begin())") - { - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - json::iterator it2 = jobject.erase(jobject.begin(), jobject.begin()); - CHECK(jobject == json({{"a", "a"}, {"b", 1}, {"c", 17u}})); - CHECK(*it2 == json("a")); - } - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - json::const_iterator it2 = jobject.erase(jobject.cbegin(), jobject.cbegin()); - CHECK(jobject == json({{"a", "a"}, {"b", 1}, {"c", 17u}})); - CHECK(*it2 == json("a")); - } - } - - SECTION("erase at offset") - { - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - json::iterator it = jobject.find("b"); - json::iterator it2 = jobject.erase(it); - CHECK(jobject == json({{"a", "a"}, {"c", 17u}})); - CHECK(*it2 == json(17)); - } - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - json::const_iterator it = jobject.find("b"); - json::const_iterator it2 = jobject.erase(it); - CHECK(jobject == json({{"a", "a"}, {"c", 17u}})); - CHECK(*it2 == json(17)); - } - } - - SECTION("erase subrange") - { - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; - json::iterator it2 = jobject.erase(jobject.find("b"), jobject.find("e")); - CHECK(jobject == json({{"a", "a"}, {"e", true}})); - CHECK(*it2 == json(true)); - } - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; - json::const_iterator it2 = jobject.erase(jobject.find("b"), jobject.find("e")); - CHECK(jobject == json({{"a", "a"}, {"e", true}})); - CHECK(*it2 == json(true)); - } - } - - SECTION("different objects") - { - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; - json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - CHECK_THROWS_AS(jobject.erase(jobject2.begin()), std::domain_error); - CHECK_THROWS_AS(jobject.erase(jobject.begin(), jobject2.end()), std::domain_error); - CHECK_THROWS_AS(jobject.erase(jobject2.begin(), jobject.end()), std::domain_error); - CHECK_THROWS_AS(jobject.erase(jobject2.begin(), jobject2.end()), std::domain_error); - CHECK_THROWS_WITH(jobject.erase(jobject2.begin()), "iterator does not fit current value"); - CHECK_THROWS_WITH(jobject.erase(jobject.begin(), jobject2.end()), - "iterators do not fit current value"); - CHECK_THROWS_WITH(jobject.erase(jobject2.begin(), jobject.end()), - "iterators do not fit current value"); - CHECK_THROWS_WITH(jobject.erase(jobject2.begin(), jobject2.end()), - "iterators do not fit current value"); - } - { - json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; - json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}}; - CHECK_THROWS_AS(jobject.erase(jobject2.cbegin()), std::domain_error); - CHECK_THROWS_AS(jobject.erase(jobject.cbegin(), jobject2.cend()), std::domain_error); - CHECK_THROWS_AS(jobject.erase(jobject2.cbegin(), jobject.cend()), std::domain_error); - CHECK_THROWS_AS(jobject.erase(jobject2.cbegin(), jobject2.cend()), std::domain_error); - CHECK_THROWS_WITH(jobject.erase(jobject2.cbegin()), "iterator does not fit current value"); - CHECK_THROWS_WITH(jobject.erase(jobject.cbegin(), jobject2.cend()), - "iterators do not fit current value"); - CHECK_THROWS_WITH(jobject.erase(jobject2.cbegin(), jobject.cend()), - "iterators do not fit current value"); - CHECK_THROWS_WITH(jobject.erase(jobject2.cbegin(), jobject2.cend()), - "iterators do not fit current value"); - } - } - } - - SECTION("remove element by key in non-object type") - { - SECTION("null") - { - json j_nonobject(json::value_t::null); - CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with null"); - } - - SECTION("boolean") - { - json j_nonobject(json::value_t::boolean); - CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with boolean"); - } - - SECTION("string") - { - json j_nonobject(json::value_t::string); - CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with string"); - } - - SECTION("array") - { - json j_nonobject(json::value_t::array); - CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with array"); - } - - SECTION("number (integer)") - { - json j_nonobject(json::value_t::number_integer); - CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with number"); - } - - SECTION("number (floating-point)") - { - json j_nonobject(json::value_t::number_float); - CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); - CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with number"); - } - } - } - - SECTION("find an element in an object") - { - SECTION("existing element") - { - for (auto key : - {"integer", "unsigned", "floating", "null", "string", "boolean", "object", "array" - }) - { - CHECK(j.find(key) != j.end()); - CHECK(*j.find(key) == j.at(key)); - CHECK(j_const.find(key) != j_const.end()); - CHECK(*j_const.find(key) == j_const.at(key)); - } - } - - SECTION("nonexisting element") - { - CHECK(j.find("foo") == j.end()); - CHECK(j_const.find("foo") == j_const.end()); - } - - SECTION("all types") - { - SECTION("null") - { - json j_nonarray(json::value_t::null); - const json j_nonarray_const(j_nonarray); - CHECK(j_nonarray.find("foo") == j_nonarray.end()); - CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); - } - - SECTION("string") - { - json j_nonarray(json::value_t::string); - const json j_nonarray_const(j_nonarray); - CHECK(j_nonarray.find("foo") == j_nonarray.end()); - CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); - } - - SECTION("object") - { - json j_nonarray(json::value_t::object); - const json j_nonarray_const(j_nonarray); - CHECK(j_nonarray.find("foo") == j_nonarray.end()); - CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); - } - - SECTION("array") - { - json j_nonarray(json::value_t::array); - const json j_nonarray_const(j_nonarray); - CHECK(j_nonarray.find("foo") == j_nonarray.end()); - CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); - } - - SECTION("boolean") - { - json j_nonarray(json::value_t::boolean); - const json j_nonarray_const(j_nonarray); - CHECK(j_nonarray.find("foo") == j_nonarray.end()); - CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); - } - - SECTION("number (integer)") - { - json j_nonarray(json::value_t::number_integer); - const json j_nonarray_const(j_nonarray); - CHECK(j_nonarray.find("foo") == j_nonarray.end()); - CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); - } - - SECTION("number (unsigned)") - { - json j_nonarray(json::value_t::number_unsigned); - const json j_nonarray_const(j_nonarray); - CHECK(j_nonarray.find("foo") == j_nonarray.end()); - CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); - } - - SECTION("number (floating-point)") - { - json j_nonarray(json::value_t::number_float); - const json j_nonarray_const(j_nonarray); - CHECK(j_nonarray.find("foo") == j_nonarray.end()); - CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); - } - } - } - - SECTION("count keys in an object") - { - SECTION("existing element") - { - for (auto key : - {"integer", "unsigned", "floating", "null", "string", "boolean", "object", "array" - }) - { - CHECK(j.count(key) == 1); - CHECK(j_const.count(key) == 1); - } - } - - SECTION("nonexisting element") - { - CHECK(j.count("foo") == 0); - CHECK(j_const.count("foo") == 0); - } - - SECTION("all types") - { - SECTION("null") - { - json j_nonobject(json::value_t::null); - const json j_nonobject_const(j_nonobject); - CHECK(j_nonobject.count("foo") == 0); - CHECK(j_nonobject_const.count("foo") == 0); - } - - SECTION("string") - { - json j_nonobject(json::value_t::string); - const json j_nonobject_const(j_nonobject); - CHECK(j_nonobject.count("foo") == 0); - CHECK(j_nonobject_const.count("foo") == 0); - } - - SECTION("object") - { - json j_nonobject(json::value_t::object); - const json j_nonobject_const(j_nonobject); - CHECK(j_nonobject.count("foo") == 0); - CHECK(j_nonobject_const.count("foo") == 0); - } - - SECTION("array") - { - json j_nonobject(json::value_t::array); - const json j_nonobject_const(j_nonobject); - CHECK(j_nonobject.count("foo") == 0); - CHECK(j_nonobject_const.count("foo") == 0); - } - - SECTION("boolean") - { - json j_nonobject(json::value_t::boolean); - const json j_nonobject_const(j_nonobject); - CHECK(j_nonobject.count("foo") == 0); - CHECK(j_nonobject_const.count("foo") == 0); - } - - SECTION("number (integer)") - { - json j_nonobject(json::value_t::number_integer); - const json j_nonobject_const(j_nonobject); - CHECK(j_nonobject.count("foo") == 0); - CHECK(j_nonobject_const.count("foo") == 0); - } - - SECTION("number (unsigned)") - { - json j_nonobject(json::value_t::number_unsigned); - const json j_nonobject_const(j_nonobject); - CHECK(j_nonobject.count("foo") == 0); - CHECK(j_nonobject_const.count("foo") == 0); - } - - SECTION("number (floating-point)") - { - json j_nonobject(json::value_t::number_float); - const json j_nonobject_const(j_nonobject); - CHECK(j_nonobject.count("foo") == 0); - CHECK(j_nonobject_const.count("foo") == 0); - } - } - } - } - - SECTION("other values") - { - SECTION("front and back") - { - SECTION("null") - { - { - json j; - CHECK_THROWS_AS(j.front(), std::out_of_range); - CHECK_THROWS_AS(j.back(), std::out_of_range); - CHECK_THROWS_WITH(j.front(), "cannot get value"); - CHECK_THROWS_WITH(j.back(), "cannot get value"); - } - { - const json j{}; - CHECK_THROWS_AS(j.front(), std::out_of_range); - CHECK_THROWS_AS(j.back(), std::out_of_range); - CHECK_THROWS_WITH(j.front(), "cannot get value"); - CHECK_THROWS_WITH(j.back(), "cannot get value"); - } - } - - SECTION("string") - { - { - json j = "foo"; - CHECK(j.front() == j); - CHECK(j.back() == j); - } - { - const json j = "bar"; - CHECK(j.front() == j); - CHECK(j.back() == j); - } - } - - SECTION("number (boolean)") - { - { - json j = false; - CHECK(j.front() == j); - CHECK(j.back() == j); - } - { - const json j = true; - CHECK(j.front() == j); - CHECK(j.back() == j); - } - } - - SECTION("number (integer)") - { - { - json j = 17; - CHECK(j.front() == j); - CHECK(j.back() == j); - } - { - const json j = 17; - CHECK(j.front() == j); - CHECK(j.back() == j); - } - } - - SECTION("number (unsigned)") - { - { - json j = 17u; - CHECK(j.front() == j); - CHECK(j.back() == j); - } - { - const json j = 17u; - CHECK(j.front() == j); - CHECK(j.back() == j); - } - } - - SECTION("number (floating point)") - { - { - json j = 23.42; - CHECK(j.front() == j); - CHECK(j.back() == j); - } - { - const json j = 23.42; - CHECK(j.front() == j); - CHECK(j.back() == j); - } - } - } - - SECTION("erase with one valid iterator") - { - SECTION("null") - { - { - json j; - CHECK_THROWS_AS(j.erase(j.begin()), std::domain_error); - CHECK_THROWS_WITH(j.erase(j.begin()), "cannot use erase() with null"); - } - { - json j; - CHECK_THROWS_AS(j.erase(j.cbegin()), std::domain_error); - CHECK_THROWS_WITH(j.erase(j.begin()), "cannot use erase() with null"); - } - } - - SECTION("string") - { - { - json j = "foo"; - json::iterator it = j.erase(j.begin()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - { - json j = "bar"; - json::const_iterator it = j.erase(j.cbegin()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - } - - SECTION("number (boolean)") - { - { - json j = false; - json::iterator it = j.erase(j.begin()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - { - json j = true; - json::const_iterator it = j.erase(j.cbegin()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - } - - SECTION("number (integer)") - { - { - json j = 17; - json::iterator it = j.erase(j.begin()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - { - json j = 17; - json::const_iterator it = j.erase(j.cbegin()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - } - - SECTION("number (unsigned)") - { - { - json j = 17u; - json::iterator it = j.erase(j.begin()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - { - json j = 17u; - json::const_iterator it = j.erase(j.cbegin()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - } - - SECTION("number (floating point)") - { - { - json j = 23.42; - json::iterator it = j.erase(j.begin()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - { - json j = 23.42; - json::const_iterator it = j.erase(j.cbegin()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - } - } - - SECTION("erase with one invalid iterator") - { - SECTION("string") - { - { - json j = "foo"; - CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); - } - { - json j = "bar"; - CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); - } - } - - SECTION("number (boolean)") - { - { - json j = false; - CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); - } - { - json j = true; - CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); - } - } - - SECTION("number (integer)") - { - { - json j = 17; - CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); - } - { - json j = 17; - CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); - } - } - - SECTION("number (unsigned)") - { - { - json j = 17u; - CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); - } - { - json j = 17u; - CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); - } - } - - SECTION("number (floating point)") - { - { - json j = 23.42; - CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); - } - { - json j = 23.42; - CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); - } - } - } - - SECTION("erase with two valid iterators") - { - SECTION("null") - { - { - json j; - CHECK_THROWS_AS(j.erase(j.begin(), j.end()), std::domain_error); - CHECK_THROWS_WITH(j.erase(j.begin(), j.end()), "cannot use erase() with null"); - } - { - json j; - CHECK_THROWS_AS(j.erase(j.cbegin(), j.cend()), std::domain_error); - CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cend()), "cannot use erase() with null"); - } - } - - SECTION("string") - { - { - json j = "foo"; - json::iterator it = j.erase(j.begin(), j.end()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - { - json j = "bar"; - json::const_iterator it = j.erase(j.cbegin(), j.cend()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - } - - SECTION("number (boolean)") - { - { - json j = false; - json::iterator it = j.erase(j.begin(), j.end()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - { - json j = true; - json::const_iterator it = j.erase(j.cbegin(), j.cend()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - } - - SECTION("number (integer)") - { - { - json j = 17; - json::iterator it = j.erase(j.begin(), j.end()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - { - json j = 17; - json::const_iterator it = j.erase(j.cbegin(), j.cend()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - } - - SECTION("number (unsigned)") - { - { - json j = 17u; - json::iterator it = j.erase(j.begin(), j.end()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - { - json j = 17u; - json::const_iterator it = j.erase(j.cbegin(), j.cend()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - } - - SECTION("number (floating point)") - { - { - json j = 23.42; - json::iterator it = j.erase(j.begin(), j.end()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - { - json j = 23.42; - json::const_iterator it = j.erase(j.cbegin(), j.cend()); - CHECK(j.type() == json::value_t::null); - CHECK(it == j.end()); - } - } - } - - SECTION("erase with two invalid iterators") - { - SECTION("string") - { - { - json j = "foo"; - CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); - } - { - json j = "bar"; - CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); - } - } - - SECTION("number (boolean)") - { - { - json j = false; - CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); - } - { - json j = true; - CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); - } - } - - SECTION("number (integer)") - { - { - json j = 17; - CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); - } - { - json j = 17; - CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); - } - } - - SECTION("number (unsigned)") - { - { - json j = 17u; - CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); - } - { - json j = 17u; - CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); - } - } - - SECTION("number (floating point)") - { - { - json j = 23.42; - CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); - } - { - json j = 23.42; - CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); - CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); - CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); - CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); - } - } - } - } -} - -TEST_CASE("iterators") -{ - SECTION("basic behavior") - { - SECTION("uninitialized") - { - json::iterator it; - CHECK(it.m_object == nullptr); - - json::const_iterator cit; - CHECK(cit.m_object == nullptr); - } - - SECTION("boolean") - { - json j = true; - json j_const(j); - - SECTION("json + begin/end") - { - json::iterator it = j.begin(); - CHECK(it != j.end()); - CHECK(*it == j); - - it++; - CHECK(it != j.begin()); - CHECK(it == j.end()); - - it--; - CHECK(it == j.begin()); - CHECK(it != j.end()); - CHECK(*it == j); - - ++it; - CHECK(it != j.begin()); - CHECK(it == j.end()); - - --it; - CHECK(it == j.begin()); - CHECK(it != j.end()); - CHECK(*it == j); - } - - SECTION("const json + begin/end") - { - json::const_iterator it = j_const.begin(); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.begin()); - CHECK(it == j_const.end()); - - it--; - CHECK(it == j_const.begin()); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.begin()); - CHECK(it == j_const.end()); - - --it; - CHECK(it == j_const.begin()); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - } - - SECTION("json + cbegin/cend") - { - json::const_iterator it = j.cbegin(); - CHECK(it != j.cend()); - CHECK(*it == j); - - it++; - CHECK(it != j.cbegin()); - CHECK(it == j.cend()); - - it--; - CHECK(it == j.cbegin()); - CHECK(it != j.cend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.cbegin()); - CHECK(it == j.cend()); - - --it; - CHECK(it == j.cbegin()); - CHECK(it != j.cend()); - CHECK(*it == j); - } - - SECTION("const json + cbegin/cend") - { - json::const_iterator it = j_const.cbegin(); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.cbegin()); - CHECK(it == j_const.cend()); - - it--; - CHECK(it == j_const.cbegin()); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.cbegin()); - CHECK(it == j_const.cend()); - - --it; - CHECK(it == j_const.cbegin()); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - } - - SECTION("json + rbegin/rend") - { - json::reverse_iterator it = j.rbegin(); - CHECK(it != j.rend()); - CHECK(*it == j); - - it++; - CHECK(it != j.rbegin()); - CHECK(it == j.rend()); - - it--; - CHECK(it == j.rbegin()); - CHECK(it != j.rend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.rbegin()); - CHECK(it == j.rend()); - - --it; - CHECK(it == j.rbegin()); - CHECK(it != j.rend()); - CHECK(*it == j); - } - - SECTION("json + crbegin/crend") - { - json::const_reverse_iterator it = j.crbegin(); - CHECK(it != j.crend()); - CHECK(*it == j); - - it++; - CHECK(it != j.crbegin()); - CHECK(it == j.crend()); - - it--; - CHECK(it == j.crbegin()); - CHECK(it != j.crend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.crbegin()); - CHECK(it == j.crend()); - - --it; - CHECK(it == j.crbegin()); - CHECK(it != j.crend()); - CHECK(*it == j); - } - - SECTION("const json + crbegin/crend") - { - json::const_reverse_iterator it = j_const.crbegin(); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.crbegin()); - CHECK(it == j_const.crend()); - - it--; - CHECK(it == j_const.crbegin()); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.crbegin()); - CHECK(it == j_const.crend()); - - --it; - CHECK(it == j_const.crbegin()); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - } - - SECTION("key/value") - { - auto it = j.begin(); - auto cit = j_const.cbegin(); - CHECK_THROWS_AS(it.key(), std::domain_error); - CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); - CHECK(it.value() == json(true)); - CHECK_THROWS_AS(cit.key(), std::domain_error); - CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); - CHECK(cit.value() == json(true)); - - auto rit = j.rend(); - auto crit = j.crend(); - CHECK_THROWS_AS(rit.key(), std::domain_error); - CHECK_THROWS_AS(rit.value(), std::out_of_range); - CHECK_THROWS_AS(crit.key(), std::domain_error); - CHECK_THROWS_AS(crit.value(), std::out_of_range); - CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(rit.value(), "cannot get value"); - CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(crit.value(), "cannot get value"); - } - } - - SECTION("string") - { - json j = "hello world"; - json j_const(j); - - SECTION("json + begin/end") - { - json::iterator it = j.begin(); - CHECK(it != j.end()); - CHECK(*it == j); - - it++; - CHECK(it != j.begin()); - CHECK(it == j.end()); - - it--; - CHECK(it == j.begin()); - CHECK(it != j.end()); - CHECK(*it == j); - - ++it; - CHECK(it != j.begin()); - CHECK(it == j.end()); - - --it; - CHECK(it == j.begin()); - CHECK(it != j.end()); - CHECK(*it == j); - } - - SECTION("const json + begin/end") - { - json::const_iterator it = j_const.begin(); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.begin()); - CHECK(it == j_const.end()); - - it--; - CHECK(it == j_const.begin()); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.begin()); - CHECK(it == j_const.end()); - - --it; - CHECK(it == j_const.begin()); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - } - - SECTION("json + cbegin/cend") - { - json::const_iterator it = j.cbegin(); - CHECK(it != j.cend()); - CHECK(*it == j); - - it++; - CHECK(it != j.cbegin()); - CHECK(it == j.cend()); - - it--; - CHECK(it == j.cbegin()); - CHECK(it != j.cend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.cbegin()); - CHECK(it == j.cend()); - - --it; - CHECK(it == j.cbegin()); - CHECK(it != j.cend()); - CHECK(*it == j); - } - - SECTION("const json + cbegin/cend") - { - json::const_iterator it = j_const.cbegin(); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.cbegin()); - CHECK(it == j_const.cend()); - - it--; - CHECK(it == j_const.cbegin()); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.cbegin()); - CHECK(it == j_const.cend()); - - --it; - CHECK(it == j_const.cbegin()); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - } - - SECTION("json + rbegin/rend") - { - json::reverse_iterator it = j.rbegin(); - CHECK(it != j.rend()); - CHECK(*it == j); - - it++; - CHECK(it != j.rbegin()); - CHECK(it == j.rend()); - - it--; - CHECK(it == j.rbegin()); - CHECK(it != j.rend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.rbegin()); - CHECK(it == j.rend()); - - --it; - CHECK(it == j.rbegin()); - CHECK(it != j.rend()); - CHECK(*it == j); - } - - SECTION("json + crbegin/crend") - { - json::const_reverse_iterator it = j.crbegin(); - CHECK(it != j.crend()); - CHECK(*it == j); - - it++; - CHECK(it != j.crbegin()); - CHECK(it == j.crend()); - - it--; - CHECK(it == j.crbegin()); - CHECK(it != j.crend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.crbegin()); - CHECK(it == j.crend()); - - --it; - CHECK(it == j.crbegin()); - CHECK(it != j.crend()); - CHECK(*it == j); - } - - SECTION("const json + crbegin/crend") - { - json::const_reverse_iterator it = j_const.crbegin(); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.crbegin()); - CHECK(it == j_const.crend()); - - it--; - CHECK(it == j_const.crbegin()); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.crbegin()); - CHECK(it == j_const.crend()); - - --it; - CHECK(it == j_const.crbegin()); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - } - - SECTION("key/value") - { - auto it = j.begin(); - auto cit = j_const.cbegin(); - CHECK_THROWS_AS(it.key(), std::domain_error); - CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); - CHECK(it.value() == json("hello world")); - CHECK_THROWS_AS(cit.key(), std::domain_error); - CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); - CHECK(cit.value() == json("hello world")); - - auto rit = j.rend(); - auto crit = j.crend(); - CHECK_THROWS_AS(rit.key(), std::domain_error); - CHECK_THROWS_AS(rit.value(), std::out_of_range); - CHECK_THROWS_AS(crit.key(), std::domain_error); - CHECK_THROWS_AS(crit.value(), std::out_of_range); - CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(rit.value(), "cannot get value"); - CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(crit.value(), "cannot get value"); - } - } - - SECTION("array") - { - json j = {1, 2, 3}; - json j_const(j); - - SECTION("json + begin/end") - { - json::iterator it_begin = j.begin(); - json::iterator it_end = j.end(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j[0]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[1]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[2]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("const json + begin/end") - { - json::const_iterator it_begin = j_const.begin(); - json::const_iterator it_end = j_const.end(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j_const[0]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j_const[1]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j_const[2]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("json + cbegin/cend") - { - json::const_iterator it_begin = j.cbegin(); - json::const_iterator it_end = j.cend(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j[0]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[1]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[2]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("const json + cbegin/cend") - { - json::const_iterator it_begin = j_const.cbegin(); - json::const_iterator it_end = j_const.cend(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j[0]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[1]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[2]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("json + rbegin/rend") - { - json::reverse_iterator it_begin = j.rbegin(); - json::reverse_iterator it_end = j.rend(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j[2]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[1]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[0]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("json + crbegin/crend") - { - json::const_reverse_iterator it_begin = j.crbegin(); - json::const_reverse_iterator it_end = j.crend(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j[2]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[1]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[0]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("const json + crbegin/crend") - { - json::const_reverse_iterator it_begin = j_const.crbegin(); - json::const_reverse_iterator it_end = j_const.crend(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j[2]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[1]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j[0]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("key/value") - { - auto it = j.begin(); - auto cit = j_const.cbegin(); - CHECK_THROWS_AS(it.key(), std::domain_error); - CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); - CHECK(it.value() == json(1)); - CHECK_THROWS_AS(cit.key(), std::domain_error); - CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); - CHECK(cit.value() == json(1)); - } - } - - SECTION("object") - { - json j = {{"A", 1}, {"B", 2}, {"C", 3}}; - json j_const(j); - - SECTION("json + begin/end") - { - json::iterator it_begin = j.begin(); - json::iterator it_end = j.end(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j["A"]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j["B"]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j["C"]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("const json + begin/end") - { - json::const_iterator it_begin = j_const.begin(); - json::const_iterator it_end = j_const.end(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j_const["A"]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j_const["B"]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j_const["C"]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("json + cbegin/cend") - { - json::const_iterator it_begin = j.cbegin(); - json::const_iterator it_end = j.cend(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j["A"]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j["B"]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j["C"]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("const json + cbegin/cend") - { - json::const_iterator it_begin = j_const.cbegin(); - json::const_iterator it_end = j_const.cend(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j_const["A"]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j_const["B"]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j_const["C"]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("json + rbegin/rend") - { - json::reverse_iterator it_begin = j.rbegin(); - json::reverse_iterator it_end = j.rend(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j["C"]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j["B"]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j["A"]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("json + crbegin/crend") - { - json::const_reverse_iterator it_begin = j.crbegin(); - json::const_reverse_iterator it_end = j.crend(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j["C"]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j["B"]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j["A"]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("const json + crbegin/crend") - { - json::const_reverse_iterator it_begin = j_const.crbegin(); - json::const_reverse_iterator it_end = j_const.crend(); - - auto it = it_begin; - CHECK(it != it_end); - CHECK(*it == j["C"]); - - it++; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j["B"]); - - ++it; - CHECK(it != it_begin); - CHECK(it != it_end); - CHECK(*it == j["A"]); - - ++it; - CHECK(it != it_begin); - CHECK(it == it_end); - } - - SECTION("key/value") - { - auto it = j.begin(); - auto cit = j_const.cbegin(); - CHECK(it.key() == "A"); - CHECK(it.value() == json(1)); - CHECK(cit.key() == "A"); - CHECK(cit.value() == json(1)); - } - } - - SECTION("number (integer)") - { - json j = 23; - json j_const(j); - - SECTION("json + begin/end") - { - json::iterator it = j.begin(); - CHECK(it != j.end()); - CHECK(*it == j); - - it++; - CHECK(it != j.begin()); - CHECK(it == j.end()); - - it--; - CHECK(it == j.begin()); - CHECK(it != j.end()); - CHECK(*it == j); - - ++it; - CHECK(it != j.begin()); - CHECK(it == j.end()); - - --it; - CHECK(it == j.begin()); - CHECK(it != j.end()); - CHECK(*it == j); - } - - SECTION("const json + begin/end") - { - json::const_iterator it = j_const.begin(); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.begin()); - CHECK(it == j_const.end()); - - it--; - CHECK(it == j_const.begin()); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.begin()); - CHECK(it == j_const.end()); - - --it; - CHECK(it == j_const.begin()); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - } - - SECTION("json + cbegin/cend") - { - json::const_iterator it = j.cbegin(); - CHECK(it != j.cend()); - CHECK(*it == j); - - it++; - CHECK(it != j.cbegin()); - CHECK(it == j.cend()); - - it--; - CHECK(it == j.cbegin()); - CHECK(it != j.cend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.cbegin()); - CHECK(it == j.cend()); - - --it; - CHECK(it == j.cbegin()); - CHECK(it != j.cend()); - CHECK(*it == j); - } - - SECTION("const json + cbegin/cend") - { - json::const_iterator it = j_const.cbegin(); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.cbegin()); - CHECK(it == j_const.cend()); - - it--; - CHECK(it == j_const.cbegin()); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.cbegin()); - CHECK(it == j_const.cend()); - - --it; - CHECK(it == j_const.cbegin()); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - } - - SECTION("json + rbegin/rend") - { - json::reverse_iterator it = j.rbegin(); - CHECK(it != j.rend()); - CHECK(*it == j); - - it++; - CHECK(it != j.rbegin()); - CHECK(it == j.rend()); - - it--; - CHECK(it == j.rbegin()); - CHECK(it != j.rend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.rbegin()); - CHECK(it == j.rend()); - - --it; - CHECK(it == j.rbegin()); - CHECK(it != j.rend()); - CHECK(*it == j); - } - - SECTION("json + crbegin/crend") - { - json::const_reverse_iterator it = j.crbegin(); - CHECK(it != j.crend()); - CHECK(*it == j); - - it++; - CHECK(it != j.crbegin()); - CHECK(it == j.crend()); - - it--; - CHECK(it == j.crbegin()); - CHECK(it != j.crend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.crbegin()); - CHECK(it == j.crend()); - - --it; - CHECK(it == j.crbegin()); - CHECK(it != j.crend()); - CHECK(*it == j); - } - - SECTION("const json + crbegin/crend") - { - json::const_reverse_iterator it = j_const.crbegin(); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.crbegin()); - CHECK(it == j_const.crend()); - - it--; - CHECK(it == j_const.crbegin()); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.crbegin()); - CHECK(it == j_const.crend()); - - --it; - CHECK(it == j_const.crbegin()); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - } - - SECTION("key/value") - { - auto it = j.begin(); - auto cit = j_const.cbegin(); - CHECK_THROWS_AS(it.key(), std::domain_error); - CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); - CHECK(it.value() == json(23)); - CHECK_THROWS_AS(cit.key(), std::domain_error); - CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); - CHECK(cit.value() == json(23)); - - auto rit = j.rend(); - auto crit = j.crend(); - CHECK_THROWS_AS(rit.key(), std::domain_error); - CHECK_THROWS_AS(rit.value(), std::out_of_range); - CHECK_THROWS_AS(crit.key(), std::domain_error); - CHECK_THROWS_AS(crit.value(), std::out_of_range); - CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(rit.value(), "cannot get value"); - CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(crit.value(), "cannot get value"); - } - } - - SECTION("number (unsigned)") - { - json j = 23u; - json j_const(j); - - SECTION("json + begin/end") - { - json::iterator it = j.begin(); - CHECK(it != j.end()); - CHECK(*it == j); - - it++; - CHECK(it != j.begin()); - CHECK(it == j.end()); - - it--; - CHECK(it == j.begin()); - CHECK(it != j.end()); - CHECK(*it == j); - - ++it; - CHECK(it != j.begin()); - CHECK(it == j.end()); - - --it; - CHECK(it == j.begin()); - CHECK(it != j.end()); - CHECK(*it == j); - } - - SECTION("const json + begin/end") - { - json::const_iterator it = j_const.begin(); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.begin()); - CHECK(it == j_const.end()); - - it--; - CHECK(it == j_const.begin()); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.begin()); - CHECK(it == j_const.end()); - - --it; - CHECK(it == j_const.begin()); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - } - - SECTION("json + cbegin/cend") - { - json::const_iterator it = j.cbegin(); - CHECK(it != j.cend()); - CHECK(*it == j); - - it++; - CHECK(it != j.cbegin()); - CHECK(it == j.cend()); - - it--; - CHECK(it == j.cbegin()); - CHECK(it != j.cend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.cbegin()); - CHECK(it == j.cend()); - - --it; - CHECK(it == j.cbegin()); - CHECK(it != j.cend()); - CHECK(*it == j); - } - - SECTION("const json + cbegin/cend") - { - json::const_iterator it = j_const.cbegin(); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.cbegin()); - CHECK(it == j_const.cend()); - - it--; - CHECK(it == j_const.cbegin()); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.cbegin()); - CHECK(it == j_const.cend()); - - --it; - CHECK(it == j_const.cbegin()); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - } - - SECTION("json + rbegin/rend") - { - json::reverse_iterator it = j.rbegin(); - CHECK(it != j.rend()); - CHECK(*it == j); - - it++; - CHECK(it != j.rbegin()); - CHECK(it == j.rend()); - - it--; - CHECK(it == j.rbegin()); - CHECK(it != j.rend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.rbegin()); - CHECK(it == j.rend()); - - --it; - CHECK(it == j.rbegin()); - CHECK(it != j.rend()); - CHECK(*it == j); - } - - SECTION("json + crbegin/crend") - { - json::const_reverse_iterator it = j.crbegin(); - CHECK(it != j.crend()); - CHECK(*it == j); - - it++; - CHECK(it != j.crbegin()); - CHECK(it == j.crend()); - - it--; - CHECK(it == j.crbegin()); - CHECK(it != j.crend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.crbegin()); - CHECK(it == j.crend()); - - --it; - CHECK(it == j.crbegin()); - CHECK(it != j.crend()); - CHECK(*it == j); - } - - SECTION("const json + crbegin/crend") - { - json::const_reverse_iterator it = j_const.crbegin(); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.crbegin()); - CHECK(it == j_const.crend()); - - it--; - CHECK(it == j_const.crbegin()); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.crbegin()); - CHECK(it == j_const.crend()); - - --it; - CHECK(it == j_const.crbegin()); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - } - - SECTION("key/value") - { - auto it = j.begin(); - auto cit = j_const.cbegin(); - CHECK_THROWS_AS(it.key(), std::domain_error); - CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); - CHECK(it.value() == json(23)); - CHECK_THROWS_AS(cit.key(), std::domain_error); - CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); - CHECK(cit.value() == json(23)); - - auto rit = j.rend(); - auto crit = j.crend(); - CHECK_THROWS_AS(rit.key(), std::domain_error); - CHECK_THROWS_AS(rit.value(), std::out_of_range); - CHECK_THROWS_AS(crit.key(), std::domain_error); - CHECK_THROWS_AS(crit.value(), std::out_of_range); - CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(rit.value(), "cannot get value"); - CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(crit.value(), "cannot get value"); - } - } - - SECTION("number (float)") - { - json j = 23.42; - json j_const(j); - - SECTION("json + begin/end") - { - json::iterator it = j.begin(); - CHECK(it != j.end()); - CHECK(*it == j); - - it++; - CHECK(it != j.begin()); - CHECK(it == j.end()); - - it--; - CHECK(it == j.begin()); - CHECK(it != j.end()); - CHECK(*it == j); - - ++it; - CHECK(it != j.begin()); - CHECK(it == j.end()); - - --it; - CHECK(it == j.begin()); - CHECK(it != j.end()); - CHECK(*it == j); - } - - SECTION("const json + begin/end") - { - json::const_iterator it = j_const.begin(); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.begin()); - CHECK(it == j_const.end()); - - it--; - CHECK(it == j_const.begin()); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.begin()); - CHECK(it == j_const.end()); - - --it; - CHECK(it == j_const.begin()); - CHECK(it != j_const.end()); - CHECK(*it == j_const); - } - - SECTION("json + cbegin/cend") - { - json::const_iterator it = j.cbegin(); - CHECK(it != j.cend()); - CHECK(*it == j); - - it++; - CHECK(it != j.cbegin()); - CHECK(it == j.cend()); - - it--; - CHECK(it == j.cbegin()); - CHECK(it != j.cend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.cbegin()); - CHECK(it == j.cend()); - - --it; - CHECK(it == j.cbegin()); - CHECK(it != j.cend()); - CHECK(*it == j); - } - - SECTION("const json + cbegin/cend") - { - json::const_iterator it = j_const.cbegin(); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.cbegin()); - CHECK(it == j_const.cend()); - - it--; - CHECK(it == j_const.cbegin()); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.cbegin()); - CHECK(it == j_const.cend()); - - --it; - CHECK(it == j_const.cbegin()); - CHECK(it != j_const.cend()); - CHECK(*it == j_const); - } - - SECTION("json + rbegin/rend") - { - json::reverse_iterator it = j.rbegin(); - CHECK(it != j.rend()); - CHECK(*it == j); - - it++; - CHECK(it != j.rbegin()); - CHECK(it == j.rend()); - - it--; - CHECK(it == j.rbegin()); - CHECK(it != j.rend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.rbegin()); - CHECK(it == j.rend()); - - --it; - CHECK(it == j.rbegin()); - CHECK(it != j.rend()); - CHECK(*it == j); - } - - SECTION("json + crbegin/crend") - { - json::const_reverse_iterator it = j.crbegin(); - CHECK(it != j.crend()); - CHECK(*it == j); - - it++; - CHECK(it != j.crbegin()); - CHECK(it == j.crend()); - - it--; - CHECK(it == j.crbegin()); - CHECK(it != j.crend()); - CHECK(*it == j); - - ++it; - CHECK(it != j.crbegin()); - CHECK(it == j.crend()); - - --it; - CHECK(it == j.crbegin()); - CHECK(it != j.crend()); - CHECK(*it == j); - } - - SECTION("const json + crbegin/crend") - { - json::const_reverse_iterator it = j_const.crbegin(); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - - it++; - CHECK(it != j_const.crbegin()); - CHECK(it == j_const.crend()); - - it--; - CHECK(it == j_const.crbegin()); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - - ++it; - CHECK(it != j_const.crbegin()); - CHECK(it == j_const.crend()); - - --it; - CHECK(it == j_const.crbegin()); - CHECK(it != j_const.crend()); - CHECK(*it == j_const); - } - - SECTION("key/value") - { - auto it = j.begin(); - auto cit = j_const.cbegin(); - CHECK_THROWS_AS(it.key(), std::domain_error); - CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); - CHECK(it.value() == json(23.42)); - CHECK_THROWS_AS(cit.key(), std::domain_error); - CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); - CHECK(cit.value() == json(23.42)); - - auto rit = j.rend(); - auto crit = j.crend(); - CHECK_THROWS_AS(rit.key(), std::domain_error); - CHECK_THROWS_AS(rit.value(), std::out_of_range); - CHECK_THROWS_AS(crit.key(), std::domain_error); - CHECK_THROWS_AS(crit.value(), std::out_of_range); - CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(rit.value(), "cannot get value"); - CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(crit.value(), "cannot get value"); - } - } - - SECTION("null") - { - json j = nullptr; - json j_const(j); - - SECTION("json + begin/end") - { - json::iterator it = j.begin(); - CHECK(it == j.end()); - } - - SECTION("const json + begin/end") - { - json::const_iterator it_begin = j_const.begin(); - json::const_iterator it_end = j_const.end(); - CHECK(it_begin == it_end); - } - - SECTION("json + cbegin/cend") - { - json::const_iterator it_begin = j.cbegin(); - json::const_iterator it_end = j.cend(); - CHECK(it_begin == it_end); - } - - SECTION("const json + cbegin/cend") - { - json::const_iterator it_begin = j_const.cbegin(); - json::const_iterator it_end = j_const.cend(); - CHECK(it_begin == it_end); - } - - SECTION("json + rbegin/rend") - { - json::reverse_iterator it = j.rbegin(); - CHECK(it == j.rend()); - } - - SECTION("json + crbegin/crend") - { - json::const_reverse_iterator it = j.crbegin(); - CHECK(it == j.crend()); - } - - SECTION("const json + crbegin/crend") - { - json::const_reverse_iterator it = j_const.crbegin(); - CHECK(it == j_const.crend()); - } - - SECTION("key/value") - { - auto it = j.begin(); - auto cit = j_const.cbegin(); - CHECK_THROWS_AS(it.key(), std::domain_error); - CHECK_THROWS_AS(it.value(), std::out_of_range); - CHECK_THROWS_AS(cit.key(), std::domain_error); - CHECK_THROWS_AS(cit.value(), std::out_of_range); - CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(it.value(), "cannot get value"); - CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(cit.value(), "cannot get value"); - - auto rit = j.rend(); - auto crit = j.crend(); - CHECK_THROWS_AS(rit.key(), std::domain_error); - CHECK_THROWS_AS(rit.value(), std::out_of_range); - CHECK_THROWS_AS(crit.key(), std::domain_error); - CHECK_THROWS_AS(crit.value(), std::out_of_range); - CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(rit.value(), "cannot get value"); - CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); - CHECK_THROWS_WITH(crit.value(), "cannot get value"); - } - } - } - - SECTION("iterator comparisons") - { - json j_values = {nullptr, true, 42, 42u, 23.23, {{"one", 1}, {"two", 2}}, {1, 2, 3, 4, 5}, "Hello, world"}; - - for (json& j : j_values) - { - auto it1 = j.begin(); - auto it2 = j.begin(); - auto it3 = j.begin(); - ++it2; - ++it3; - ++it3; - auto it1_c = j.cbegin(); - auto it2_c = j.cbegin(); - auto it3_c = j.cbegin(); - ++it2_c; - ++it3_c; - ++it3_c; - - // comparison: equal - { - CHECK(it1 == it1); - CHECK(not (it1 == it2)); - CHECK(not (it1 == it3)); - CHECK(not (it2 == it3)); - CHECK(it1_c == it1_c); - CHECK(not (it1_c == it2_c)); - CHECK(not (it1_c == it3_c)); - CHECK(not (it2_c == it3_c)); - } - - // comparison: not equal - { - // check definition - CHECK( (it1 != it1) == not(it1 == it1) ); - CHECK( (it1 != it2) == not(it1 == it2) ); - CHECK( (it1 != it3) == not(it1 == it3) ); - CHECK( (it2 != it3) == not(it2 == it3) ); - CHECK( (it1_c != it1_c) == not(it1_c == it1_c) ); - CHECK( (it1_c != it2_c) == not(it1_c == it2_c) ); - CHECK( (it1_c != it3_c) == not(it1_c == it3_c) ); - CHECK( (it2_c != it3_c) == not(it2_c == it3_c) ); - } - - // comparison: smaller - { - if (j.type() == json::value_t::object) - { - CHECK_THROWS_AS(it1 < it1, std::domain_error); - CHECK_THROWS_AS(it1 < it2, std::domain_error); - CHECK_THROWS_AS(it2 < it3, std::domain_error); - CHECK_THROWS_AS(it1 < it3, std::domain_error); - CHECK_THROWS_AS(it1_c < it1_c, std::domain_error); - CHECK_THROWS_AS(it1_c < it2_c, std::domain_error); - CHECK_THROWS_AS(it2_c < it3_c, std::domain_error); - CHECK_THROWS_AS(it1_c < it3_c, std::domain_error); - CHECK_THROWS_WITH(it1 < it1, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 < it2, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 < it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 < it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it1_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it2_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c < it3_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it3_c, "cannot compare order of object iterators"); - } - else - { - CHECK(not (it1 < it1)); - CHECK(it1 < it2); - CHECK(it1 < it3); - CHECK(it2 < it3); - CHECK(not (it1_c < it1_c)); - CHECK(it1_c < it2_c); - CHECK(it1_c < it3_c); - CHECK(it2_c < it3_c); - } - } - - // comparison: less than or equal - { - if (j.type() == json::value_t::object) - { - CHECK_THROWS_AS(it1 <= it1, std::domain_error); - CHECK_THROWS_AS(it1 <= it2, std::domain_error); - CHECK_THROWS_AS(it2 <= it3, std::domain_error); - CHECK_THROWS_AS(it1 <= it3, std::domain_error); - CHECK_THROWS_AS(it1_c <= it1_c, std::domain_error); - CHECK_THROWS_AS(it1_c <= it2_c, std::domain_error); - CHECK_THROWS_AS(it2_c <= it3_c, std::domain_error); - CHECK_THROWS_AS(it1_c <= it3_c, std::domain_error); - CHECK_THROWS_WITH(it1 <= it1, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 <= it2, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 <= it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 <= it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it1_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it2_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c <= it3_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it3_c, "cannot compare order of object iterators"); - } - else - { - // check definition - CHECK( (it1 <= it1) == not(it1 < it1) ); - CHECK( (it1 <= it2) == not(it2 < it1) ); - CHECK( (it1 <= it3) == not(it3 < it1) ); - CHECK( (it2 <= it3) == not(it3 < it2) ); - CHECK( (it1_c <= it1_c) == not(it1_c < it1_c) ); - CHECK( (it1_c <= it2_c) == not(it2_c < it1_c) ); - CHECK( (it1_c <= it3_c) == not(it3_c < it1_c) ); - CHECK( (it2_c <= it3_c) == not(it3_c < it2_c) ); - } - } - - // comparison: greater than - { - if (j.type() == json::value_t::object) - { - CHECK_THROWS_AS(it1 > it1, std::domain_error); - CHECK_THROWS_AS(it1 > it2, std::domain_error); - CHECK_THROWS_AS(it2 > it3, std::domain_error); - CHECK_THROWS_AS(it1 > it3, std::domain_error); - CHECK_THROWS_AS(it1_c > it1_c, std::domain_error); - CHECK_THROWS_AS(it1_c > it2_c, std::domain_error); - CHECK_THROWS_AS(it2_c > it3_c, std::domain_error); - CHECK_THROWS_AS(it1_c > it3_c, std::domain_error); - CHECK_THROWS_WITH(it1 > it1, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 > it2, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 > it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 > it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it1_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it2_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c > it3_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it3_c, "cannot compare order of object iterators"); - } - else - { - // check definition - CHECK( (it1 > it1) == (it1 < it1) ); - CHECK( (it1 > it2) == (it2 < it1) ); - CHECK( (it1 > it3) == (it3 < it1) ); - CHECK( (it2 > it3) == (it3 < it2) ); - CHECK( (it1_c > it1_c) == (it1_c < it1_c) ); - CHECK( (it1_c > it2_c) == (it2_c < it1_c) ); - CHECK( (it1_c > it3_c) == (it3_c < it1_c) ); - CHECK( (it2_c > it3_c) == (it3_c < it2_c) ); - } - } - - // comparison: greater than or equal - { - if (j.type() == json::value_t::object) - { - CHECK_THROWS_AS(it1 >= it1, std::domain_error); - CHECK_THROWS_AS(it1 >= it2, std::domain_error); - CHECK_THROWS_AS(it2 >= it3, std::domain_error); - CHECK_THROWS_AS(it1 >= it3, std::domain_error); - CHECK_THROWS_AS(it1_c >= it1_c, std::domain_error); - CHECK_THROWS_AS(it1_c >= it2_c, std::domain_error); - CHECK_THROWS_AS(it2_c >= it3_c, std::domain_error); - CHECK_THROWS_AS(it1_c >= it3_c, std::domain_error); - CHECK_THROWS_WITH(it1 >= it1, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 >= it2, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 >= it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 >= it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it1_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it2_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c >= it3_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it3_c, "cannot compare order of object iterators"); - } - else - { - // check definition - CHECK( (it1 >= it1) == not(it1 < it1) ); - CHECK( (it1 >= it2) == not(it1 < it2) ); - CHECK( (it1 >= it3) == not(it1 < it3) ); - CHECK( (it2 >= it3) == not(it2 < it3) ); - CHECK( (it1_c >= it1_c) == not(it1_c < it1_c) ); - CHECK( (it1_c >= it2_c) == not(it1_c < it2_c) ); - CHECK( (it1_c >= it3_c) == not(it1_c < it3_c) ); - CHECK( (it2_c >= it3_c) == not(it2_c < it3_c) ); - } - } - } - - // check exceptions if different objects are compared - for (auto j : j_values) - { - for (auto k : j_values) - { - if (j != k) - { - CHECK_THROWS_AS(j.begin() == k.begin(), std::domain_error); - CHECK_THROWS_AS(j.cbegin() == k.cbegin(), std::domain_error); - CHECK_THROWS_WITH(j.begin() == k.begin(), "cannot compare iterators of different containers"); - CHECK_THROWS_WITH(j.cbegin() == k.cbegin(), "cannot compare iterators of different containers"); - - CHECK_THROWS_AS(j.begin() < k.begin(), std::domain_error); - CHECK_THROWS_AS(j.cbegin() < k.cbegin(), std::domain_error); - CHECK_THROWS_WITH(j.begin() < k.begin(), "cannot compare iterators of different containers"); - CHECK_THROWS_WITH(j.cbegin() < k.cbegin(), "cannot compare iterators of different containers"); - } - } - } - } - - SECTION("iterator arithmetic") - { - json j_object = {{"one", 1}, {"two", 2}, {"three", 3}}; - json j_array = {1, 2, 3, 4, 5, 6}; - json j_null = nullptr; - json j_value = 42; - - SECTION("addition and subtraction") - { - SECTION("object") - { - { - auto it = j_object.begin(); - CHECK_THROWS_AS(it += 1, std::domain_error); - CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.cbegin(); - CHECK_THROWS_AS(it += 1, std::domain_error); - CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.begin(); - CHECK_THROWS_AS(it + 1, std::domain_error); - CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.cbegin(); - CHECK_THROWS_AS(it + 1, std::domain_error); - CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.begin(); - CHECK_THROWS_AS(it -= 1, std::domain_error); - CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.cbegin(); - CHECK_THROWS_AS(it -= 1, std::domain_error); - CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.begin(); - CHECK_THROWS_AS(it - 1, std::domain_error); - CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.cbegin(); - CHECK_THROWS_AS(it - 1, std::domain_error); - CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.begin(); - CHECK_THROWS_AS(it - it, std::domain_error); - CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators"); - } - { - auto it = j_object.cbegin(); - CHECK_THROWS_AS(it - it, std::domain_error); - CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators"); - } - } - - SECTION("array") - { - { - auto it = j_array.begin(); - it += 3; - CHECK((j_array.begin() + 3) == it); - CHECK((it - 3) == j_array.begin()); - CHECK((it - j_array.begin()) == 3); - CHECK(*it == json(4)); - it -= 2; - CHECK(*it == json(2)); - } - { - auto it = j_array.cbegin(); - it += 3; - CHECK((j_array.cbegin() + 3) == it); - CHECK((it - 3) == j_array.cbegin()); - CHECK((it - j_array.cbegin()) == 3); - CHECK(*it == json(4)); - it -= 2; - CHECK(*it == json(2)); - } - } - - SECTION("null") - { - { - auto it = j_null.begin(); - it += 3; - CHECK((j_null.begin() + 3) == it); - CHECK((it - 3) == j_null.begin()); - CHECK((it - j_null.begin()) == 3); - CHECK(it != j_null.end()); - it -= 3; - CHECK(it == j_null.end()); - } - { - auto it = j_null.cbegin(); - it += 3; - CHECK((j_null.cbegin() + 3) == it); - CHECK((it - 3) == j_null.cbegin()); - CHECK((it - j_null.cbegin()) == 3); - CHECK(it != j_null.cend()); - it -= 3; - CHECK(it == j_null.cend()); - } - } - - SECTION("value") - { - { - auto it = j_value.begin(); - it += 3; - CHECK((j_value.begin() + 3) == it); - CHECK((it - 3) == j_value.begin()); - CHECK((it - j_value.begin()) == 3); - CHECK(it != j_value.end()); - it -= 3; - CHECK(*it == json(42)); - } - { - auto it = j_value.cbegin(); - it += 3; - CHECK((j_value.cbegin() + 3) == it); - CHECK((it - 3) == j_value.cbegin()); - CHECK((it - j_value.cbegin()) == 3); - CHECK(it != j_value.cend()); - it -= 3; - CHECK(*it == json(42)); - } - } - } - - SECTION("subscript operator") - { - SECTION("object") - { - { - auto it = j_object.begin(); - CHECK_THROWS_AS(it[0], std::domain_error); - CHECK_THROWS_AS(it[1], std::domain_error); - CHECK_THROWS_WITH(it[0], "cannot use operator[] for object iterators"); - CHECK_THROWS_WITH(it[1], "cannot use operator[] for object iterators"); - } - { - auto it = j_object.cbegin(); - CHECK_THROWS_AS(it[0], std::domain_error); - CHECK_THROWS_AS(it[1], std::domain_error); - CHECK_THROWS_WITH(it[0], "cannot use operator[] for object iterators"); - CHECK_THROWS_WITH(it[1], "cannot use operator[] for object iterators"); - } - } - - SECTION("array") - { - { - auto it = j_array.begin(); - CHECK(it[0] == json(1)); - CHECK(it[1] == json(2)); - CHECK(it[2] == json(3)); - CHECK(it[3] == json(4)); - CHECK(it[4] == json(5)); - CHECK(it[5] == json(6)); - } - { - auto it = j_array.cbegin(); - CHECK(it[0] == json(1)); - CHECK(it[1] == json(2)); - CHECK(it[2] == json(3)); - CHECK(it[3] == json(4)); - CHECK(it[4] == json(5)); - CHECK(it[5] == json(6)); - } - } - - SECTION("null") - { - { - auto it = j_null.begin(); - CHECK_THROWS_AS(it[0], std::out_of_range); - CHECK_THROWS_AS(it[1], std::out_of_range); - CHECK_THROWS_WITH(it[0], "cannot get value"); - CHECK_THROWS_WITH(it[1], "cannot get value"); - } - { - auto it = j_null.cbegin(); - CHECK_THROWS_AS(it[0], std::out_of_range); - CHECK_THROWS_AS(it[1], std::out_of_range); - CHECK_THROWS_WITH(it[0], "cannot get value"); - CHECK_THROWS_WITH(it[1], "cannot get value"); - } - } - - SECTION("value") - { - { - auto it = j_value.begin(); - CHECK(it[0] == json(42)); - CHECK_THROWS_AS(it[1], std::out_of_range); - CHECK_THROWS_WITH(it[1], "cannot get value"); - } - { - auto it = j_value.cbegin(); - CHECK(it[0] == json(42)); - CHECK_THROWS_AS(it[1], std::out_of_range); - CHECK_THROWS_WITH(it[1], "cannot get value"); - } - } - } - } - - SECTION("reverse iterator comparisons") - { - json j_values = {nullptr, true, 42, 42u, 23.23, {{"one", 1}, {"two", 2}}, {1, 2, 3, 4, 5}, "Hello, world"}; - - for (json& j : j_values) - { - auto it1 = j.rbegin(); - auto it2 = j.rbegin(); - auto it3 = j.rbegin(); - ++it2; - ++it3; - ++it3; - auto it1_c = j.crbegin(); - auto it2_c = j.crbegin(); - auto it3_c = j.crbegin(); - ++it2_c; - ++it3_c; - ++it3_c; - - // comparison: equal - { - CHECK(it1 == it1); - CHECK(not (it1 == it2)); - CHECK(not (it1 == it3)); - CHECK(not (it2 == it3)); - CHECK(it1_c == it1_c); - CHECK(not (it1_c == it2_c)); - CHECK(not (it1_c == it3_c)); - CHECK(not (it2_c == it3_c)); - } - - // comparison: not equal - { - // check definition - CHECK( (it1 != it1) == not(it1 == it1) ); - CHECK( (it1 != it2) == not(it1 == it2) ); - CHECK( (it1 != it3) == not(it1 == it3) ); - CHECK( (it2 != it3) == not(it2 == it3) ); - CHECK( (it1_c != it1_c) == not(it1_c == it1_c) ); - CHECK( (it1_c != it2_c) == not(it1_c == it2_c) ); - CHECK( (it1_c != it3_c) == not(it1_c == it3_c) ); - CHECK( (it2_c != it3_c) == not(it2_c == it3_c) ); - } - - // comparison: smaller - { - if (j.type() == json::value_t::object) - { - CHECK_THROWS_AS(it1 < it1, std::domain_error); - CHECK_THROWS_AS(it1 < it2, std::domain_error); - CHECK_THROWS_AS(it2 < it3, std::domain_error); - CHECK_THROWS_AS(it1 < it3, std::domain_error); - CHECK_THROWS_AS(it1_c < it1_c, std::domain_error); - CHECK_THROWS_AS(it1_c < it2_c, std::domain_error); - CHECK_THROWS_AS(it2_c < it3_c, std::domain_error); - CHECK_THROWS_AS(it1_c < it3_c, std::domain_error); - CHECK_THROWS_WITH(it1 < it1, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 < it2, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 < it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 < it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it1_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it2_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c < it3_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c < it3_c, "cannot compare order of object iterators"); - } - else - { - CHECK(not (it1 < it1)); - CHECK(it1 < it2); - CHECK(it1 < it3); - CHECK(it2 < it3); - CHECK(not (it1_c < it1_c)); - CHECK(it1_c < it2_c); - CHECK(it1_c < it3_c); - CHECK(it2_c < it3_c); - } - } - - // comparison: less than or equal - { - if (j.type() == json::value_t::object) - { - CHECK_THROWS_AS(it1 <= it1, std::domain_error); - CHECK_THROWS_AS(it1 <= it2, std::domain_error); - CHECK_THROWS_AS(it2 <= it3, std::domain_error); - CHECK_THROWS_AS(it1 <= it3, std::domain_error); - CHECK_THROWS_AS(it1_c <= it1_c, std::domain_error); - CHECK_THROWS_AS(it1_c <= it2_c, std::domain_error); - CHECK_THROWS_AS(it2_c <= it3_c, std::domain_error); - CHECK_THROWS_AS(it1_c <= it3_c, std::domain_error); - CHECK_THROWS_WITH(it1 <= it1, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 <= it2, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 <= it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 <= it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it1_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it2_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c <= it3_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c <= it3_c, "cannot compare order of object iterators"); - } - else - { - // check definition - CHECK( (it1 <= it1) == not(it1 < it1) ); - CHECK( (it1 <= it2) == not(it2 < it1) ); - CHECK( (it1 <= it3) == not(it3 < it1) ); - CHECK( (it2 <= it3) == not(it3 < it2) ); - CHECK( (it1_c <= it1_c) == not(it1_c < it1_c) ); - CHECK( (it1_c <= it2_c) == not(it2_c < it1_c) ); - CHECK( (it1_c <= it3_c) == not(it3_c < it1_c) ); - CHECK( (it2_c <= it3_c) == not(it3_c < it2_c) ); - } - } - - // comparison: greater than - { - if (j.type() == json::value_t::object) - { - CHECK_THROWS_AS(it1 > it1, std::domain_error); - CHECK_THROWS_AS(it1 > it2, std::domain_error); - CHECK_THROWS_AS(it2 > it3, std::domain_error); - CHECK_THROWS_AS(it1 > it3, std::domain_error); - CHECK_THROWS_AS(it1_c > it1_c, std::domain_error); - CHECK_THROWS_AS(it1_c > it2_c, std::domain_error); - CHECK_THROWS_AS(it2_c > it3_c, std::domain_error); - CHECK_THROWS_AS(it1_c > it3_c, std::domain_error); - CHECK_THROWS_WITH(it1 > it1, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 > it2, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 > it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 > it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it1_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it2_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c > it3_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c > it3_c, "cannot compare order of object iterators"); - } - else - { - // check definition - CHECK( (it1 > it1) == (it1 < it1) ); - CHECK( (it1 > it2) == (it2 < it1) ); - CHECK( (it1 > it3) == (it3 < it1) ); - CHECK( (it2 > it3) == (it3 < it2) ); - CHECK( (it1_c > it1_c) == (it1_c < it1_c) ); - CHECK( (it1_c > it2_c) == (it2_c < it1_c) ); - CHECK( (it1_c > it3_c) == (it3_c < it1_c) ); - CHECK( (it2_c > it3_c) == (it3_c < it2_c) ); - } - } - - // comparison: greater than or equal - { - if (j.type() == json::value_t::object) - { - CHECK_THROWS_AS(it1 >= it1, std::domain_error); - CHECK_THROWS_AS(it1 >= it2, std::domain_error); - CHECK_THROWS_AS(it2 >= it3, std::domain_error); - CHECK_THROWS_AS(it1 >= it3, std::domain_error); - CHECK_THROWS_AS(it1_c >= it1_c, std::domain_error); - CHECK_THROWS_AS(it1_c >= it2_c, std::domain_error); - CHECK_THROWS_AS(it2_c >= it3_c, std::domain_error); - CHECK_THROWS_AS(it1_c >= it3_c, std::domain_error); - CHECK_THROWS_WITH(it1 >= it1, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 >= it2, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2 >= it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1 >= it3, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it1_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it2_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it2_c >= it3_c, "cannot compare order of object iterators"); - CHECK_THROWS_WITH(it1_c >= it3_c, "cannot compare order of object iterators"); - } - else - { - // check definition - CHECK( (it1 >= it1) == not(it1 < it1) ); - CHECK( (it1 >= it2) == not(it1 < it2) ); - CHECK( (it1 >= it3) == not(it1 < it3) ); - CHECK( (it2 >= it3) == not(it2 < it3) ); - CHECK( (it1_c >= it1_c) == not(it1_c < it1_c) ); - CHECK( (it1_c >= it2_c) == not(it1_c < it2_c) ); - CHECK( (it1_c >= it3_c) == not(it1_c < it3_c) ); - CHECK( (it2_c >= it3_c) == not(it2_c < it3_c) ); - } - } - } - - // check exceptions if different objects are compared - for (auto j : j_values) - { - for (auto k : j_values) - { - if (j != k) - { - CHECK_THROWS_AS(j.rbegin() == k.rbegin(), std::domain_error); - CHECK_THROWS_AS(j.crbegin() == k.crbegin(), std::domain_error); - CHECK_THROWS_WITH(j.rbegin() == k.rbegin(), "cannot compare iterators of different containers"); - CHECK_THROWS_WITH(j.crbegin() == k.crbegin(), "cannot compare iterators of different containers"); - - CHECK_THROWS_AS(j.rbegin() < k.rbegin(), std::domain_error); - CHECK_THROWS_AS(j.crbegin() < k.crbegin(), std::domain_error); - CHECK_THROWS_WITH(j.rbegin() < k.rbegin(), "cannot compare iterators of different containers"); - CHECK_THROWS_WITH(j.crbegin() < k.crbegin(), "cannot compare iterators of different containers"); - } - } - } - } - - SECTION("reverse iterator arithmetic") - { - json j_object = {{"one", 1}, {"two", 2}, {"three", 3}}; - json j_array = {1, 2, 3, 4, 5, 6}; - json j_null = nullptr; - json j_value = 42; - - SECTION("addition and subtraction") - { - SECTION("object") - { - { - auto it = j_object.rbegin(); - CHECK_THROWS_AS(it += 1, std::domain_error); - CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.crbegin(); - CHECK_THROWS_AS(it += 1, std::domain_error); - CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.rbegin(); - CHECK_THROWS_AS(it + 1, std::domain_error); - CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.crbegin(); - CHECK_THROWS_AS(it + 1, std::domain_error); - CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.rbegin(); - CHECK_THROWS_AS(it -= 1, std::domain_error); - CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.crbegin(); - CHECK_THROWS_AS(it -= 1, std::domain_error); - CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.rbegin(); - CHECK_THROWS_AS(it - 1, std::domain_error); - CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.crbegin(); - CHECK_THROWS_AS(it - 1, std::domain_error); - CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators"); - } - { - auto it = j_object.rbegin(); - CHECK_THROWS_AS(it - it, std::domain_error); - CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators"); - } - { - auto it = j_object.crbegin(); - CHECK_THROWS_AS(it - it, std::domain_error); - CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators"); - } - } - - SECTION("array") - { - { - auto it = j_array.rbegin(); - it += 3; - CHECK((j_array.rbegin() + 3) == it); - CHECK((it - 3) == j_array.rbegin()); - CHECK((j_array.rbegin() - it) == 3); - CHECK(*it == json(3)); - it -= 2; - CHECK(*it == json(5)); - } - { - auto it = j_array.crbegin(); - it += 3; - CHECK((j_array.crbegin() + 3) == it); - CHECK((it - 3) == j_array.crbegin()); - CHECK((j_array.crbegin() - it) == 3); - CHECK(*it == json(3)); - it -= 2; - CHECK(*it == json(5)); - } - } - - SECTION("null") - { - { - auto it = j_null.rbegin(); - it += 3; - CHECK((j_null.rbegin() + 3) == it); - CHECK((it - 3) == j_null.rbegin()); - CHECK((j_null.rbegin() - it) == 3); - CHECK(it != j_null.rend()); - it -= 3; - CHECK(it == j_null.rend()); - } - { - auto it = j_null.crbegin(); - it += 3; - CHECK((j_null.crbegin() + 3) == it); - CHECK((it - 3) == j_null.crbegin()); - CHECK((j_null.crbegin() - it) == 3); - CHECK(it != j_null.crend()); - it -= 3; - CHECK(it == j_null.crend()); - } - } - - SECTION("value") - { - { - auto it = j_value.rbegin(); - it += 3; - CHECK((j_value.rbegin() + 3) == it); - CHECK((it - 3) == j_value.rbegin()); - CHECK((j_value.rbegin() - it) == 3); - CHECK(it != j_value.rend()); - it -= 3; - CHECK(*it == json(42)); - } - { - auto it = j_value.crbegin(); - it += 3; - CHECK((j_value.crbegin() + 3) == it); - CHECK((it - 3) == j_value.crbegin()); - CHECK((j_value.crbegin() - it) == 3); - CHECK(it != j_value.crend()); - it -= 3; - CHECK(*it == json(42)); - } - } - } - - SECTION("subscript operator") - { - SECTION("object") - { - { - auto it = j_object.rbegin(); - CHECK_THROWS_AS(it[0], std::domain_error); - CHECK_THROWS_AS(it[1], std::domain_error); - CHECK_THROWS_WITH(it[0], "cannot use offsets with object iterators"); - CHECK_THROWS_WITH(it[1], "cannot use offsets with object iterators"); - } - { - auto it = j_object.crbegin(); - CHECK_THROWS_AS(it[0], std::domain_error); - CHECK_THROWS_AS(it[1], std::domain_error); - CHECK_THROWS_WITH(it[0], "cannot use offsets with object iterators"); - CHECK_THROWS_WITH(it[1], "cannot use offsets with object iterators"); - } - } - - SECTION("array") - { - { - auto it = j_array.rbegin(); - CHECK(it[0] == json(6)); - CHECK(it[1] == json(5)); - CHECK(it[2] == json(4)); - CHECK(it[3] == json(3)); - CHECK(it[4] == json(2)); - CHECK(it[5] == json(1)); - } - { - auto it = j_array.crbegin(); - CHECK(it[0] == json(6)); - CHECK(it[1] == json(5)); - CHECK(it[2] == json(4)); - CHECK(it[3] == json(3)); - CHECK(it[4] == json(2)); - CHECK(it[5] == json(1)); - } - } - - SECTION("null") - { - { - auto it = j_null.rbegin(); - CHECK_THROWS_AS(it[0], std::out_of_range); - CHECK_THROWS_AS(it[1], std::out_of_range); - CHECK_THROWS_WITH(it[0], "cannot get value"); - CHECK_THROWS_WITH(it[1], "cannot get value"); - } - { - auto it = j_null.crbegin(); - CHECK_THROWS_AS(it[0], std::out_of_range); - CHECK_THROWS_AS(it[1], std::out_of_range); - CHECK_THROWS_WITH(it[0], "cannot get value"); - CHECK_THROWS_WITH(it[1], "cannot get value"); - } - } - - SECTION("value") - { - { - auto it = j_value.rbegin(); - CHECK(it[0] == json(42)); - CHECK_THROWS_AS(it[1], std::out_of_range); - CHECK_THROWS_WITH(it[1], "cannot get value"); - } - { - auto it = j_value.crbegin(); - CHECK(it[0] == json(42)); - CHECK_THROWS_AS(it[1], std::out_of_range); - CHECK_THROWS_WITH(it[1], "cannot get value"); - } - } - } - } -} - -TEST_CASE("capacity") -{ - SECTION("empty()") - { - SECTION("boolean") - { - json j = true; - json j_const(j); - - SECTION("result of empty") - { - CHECK(j.empty() == false); - CHECK(j_const.empty() == false); - } - - SECTION("definition of empty") - { - CHECK(j.begin() != j.end()); - CHECK(j_const.begin() != j_const.end()); - } - } - - SECTION("string") - { - json j = "hello world"; - json j_const(j); - - SECTION("result of empty") - { - CHECK(j.empty() == false); - CHECK(j_const.empty() == false); - } - - SECTION("definition of empty") - { - CHECK(j.begin() != j.end()); - CHECK(j_const.begin() != j_const.end()); - } - } - - SECTION("array") - { - SECTION("empty array") - { - json j = json::array(); - json j_const(j); - - SECTION("result of empty") - { - CHECK(j.empty() == true); - CHECK(j_const.empty() == true); - } - - SECTION("definition of empty") - { - CHECK(j.begin() == j.end()); - CHECK(j_const.begin() == j_const.end()); - } - } - - SECTION("filled array") - { - json j = {1, 2, 3}; - json j_const(j); - - SECTION("result of empty") - { - CHECK(j.empty() == false); - CHECK(j_const.empty() == false); - } - - SECTION("definition of empty") - { - CHECK(j.begin() != j.end()); - CHECK(j_const.begin() != j_const.end()); - } - } - } - - SECTION("object") - { - SECTION("empty object") - { - json j = json::object(); - json j_const(j); - - SECTION("result of empty") - { - CHECK(j.empty() == true); - CHECK(j_const.empty() == true); - } - - SECTION("definition of empty") - { - CHECK(j.begin() == j.end()); - CHECK(j_const.begin() == j_const.end()); - } - } - - SECTION("filled object") - { - json j = {{"one", 1}, {"two", 2}, {"three", 3}}; - json j_const(j); - - SECTION("result of empty") - { - CHECK(j.empty() == false); - CHECK(j_const.empty() == false); - } - - SECTION("definition of empty") - { - CHECK(j.begin() != j.end()); - CHECK(j_const.begin() != j_const.end()); - } - } - } - - SECTION("number (integer)") - { - json j = 23; - json j_const(j); - - SECTION("result of empty") - { - CHECK(j.empty() == false); - CHECK(j_const.empty() == false); - } - - SECTION("definition of empty") - { - CHECK(j.begin() != j.end()); - CHECK(j_const.begin() != j_const.end()); - } - } - - SECTION("number (unsigned)") - { - json j = 23u; - json j_const(j); - - SECTION("result of empty") - { - CHECK(j.empty() == false); - CHECK(j_const.empty() == false); - } - - SECTION("definition of empty") - { - CHECK(j.begin() != j.end()); - CHECK(j_const.begin() != j_const.end()); - } - } - - SECTION("number (float)") - { - json j = 23.42; - json j_const(j); - - SECTION("result of empty") - { - CHECK(j.empty() == false); - CHECK(j_const.empty() == false); - } - - SECTION("definition of empty") - { - CHECK(j.begin() != j.end()); - CHECK(j_const.begin() != j_const.end()); - } - } - - SECTION("null") - { - json j = nullptr; - json j_const(j); - - SECTION("result of empty") - { - CHECK(j.empty() == true); - CHECK(j_const.empty() == true); - } - - SECTION("definition of empty") - { - CHECK(j.begin() == j.end()); - CHECK(j_const.begin() == j_const.end()); - } - } - } - - SECTION("size()") - { - SECTION("boolean") - { - json j = true; - json j_const(j); - - SECTION("result of size") - { - CHECK(j.size() == 1); - CHECK(j_const.size() == 1); - } - - SECTION("definition of size") - { - CHECK(std::distance(j.begin(), j.end()) == j.size()); - CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); - CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); - CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); - } - } - - SECTION("string") - { - json j = "hello world"; - json j_const(j); - - SECTION("result of size") - { - CHECK(j.size() == 1); - CHECK(j_const.size() == 1); - } - - SECTION("definition of size") - { - CHECK(std::distance(j.begin(), j.end()) == j.size()); - CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); - CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); - CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); - } - } - - SECTION("array") - { - SECTION("empty array") - { - json j = json::array(); - json j_const(j); - - SECTION("result of size") - { - CHECK(j.size() == 0); - CHECK(j_const.size() == 0); - } - - SECTION("definition of size") - { - CHECK(std::distance(j.begin(), j.end()) == j.size()); - CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); - CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); - CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); - } - } - - SECTION("filled array") - { - json j = {1, 2, 3}; - json j_const(j); - - SECTION("result of size") - { - CHECK(j.size() == 3); - CHECK(j_const.size() == 3); - } - - SECTION("definition of size") - { - CHECK(std::distance(j.begin(), j.end()) == j.size()); - CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); - CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); - CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); - } - } - } - - SECTION("object") - { - SECTION("empty object") - { - json j = json::object(); - json j_const(j); - - SECTION("result of size") - { - CHECK(j.size() == 0); - CHECK(j_const.size() == 0); - } - - SECTION("definition of size") - { - CHECK(std::distance(j.begin(), j.end()) == j.size()); - CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); - CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); - CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); - } - } - - SECTION("filled object") - { - json j = {{"one", 1}, {"two", 2}, {"three", 3}}; - json j_const(j); - - SECTION("result of size") - { - CHECK(j.size() == 3); - CHECK(j_const.size() == 3); - } - - SECTION("definition of size") - { - CHECK(std::distance(j.begin(), j.end()) == j.size()); - CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); - CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); - CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); - } - } - } - - SECTION("number (integer)") - { - json j = 23; - json j_const(j); - - SECTION("result of size") - { - CHECK(j.size() == 1); - CHECK(j_const.size() == 1); - } - - SECTION("definition of size") - { - CHECK(std::distance(j.begin(), j.end()) == j.size()); - CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); - CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); - CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); - } - } - - SECTION("number (unsigned)") - { - json j = 23u; - json j_const(j); - - SECTION("result of size") - { - CHECK(j.size() == 1); - CHECK(j_const.size() == 1); - } - - SECTION("definition of size") - { - CHECK(std::distance(j.begin(), j.end()) == j.size()); - CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); - CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); - CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); - } - } - - SECTION("number (float)") - { - json j = 23.42; - json j_const(j); - - SECTION("result of size") - { - CHECK(j.size() == 1); - CHECK(j_const.size() == 1); - } - - SECTION("definition of size") - { - CHECK(std::distance(j.begin(), j.end()) == j.size()); - CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); - CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); - CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); - } - } - - SECTION("null") - { - json j = nullptr; - json j_const(j); - - SECTION("result of size") - { - CHECK(j.size() == 0); - CHECK(j_const.size() == 0); - } - - SECTION("definition of size") - { - CHECK(std::distance(j.begin(), j.end()) == j.size()); - CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); - CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); - CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); - } - } - } - - SECTION("max_size()") - { - SECTION("boolean") - { - json j = true; - json j_const(j); - - SECTION("result of max_size") - { - CHECK(j.max_size() == 1); - CHECK(j_const.max_size() == 1); - } - } - - SECTION("string") - { - json j = "hello world"; - json j_const(j); - - SECTION("result of max_size") - { - CHECK(j.max_size() == 1); - CHECK(j_const.max_size() == 1); - } - } - - SECTION("array") - { - SECTION("empty array") - { - json j = json::array(); - json j_const(j); - - SECTION("result of max_size") - { - CHECK(j.max_size() >= j.size()); - CHECK(j_const.max_size() >= j_const.size()); - } - } - - SECTION("filled array") - { - json j = {1, 2, 3}; - json j_const(j); - - SECTION("result of max_size") - { - CHECK(j.max_size() >= j.size()); - CHECK(j_const.max_size() >= j_const.size()); - } - } - } - - SECTION("object") - { - SECTION("empty object") - { - json j = json::object(); - json j_const(j); - - SECTION("result of max_size") - { - CHECK(j.max_size() >= j.size()); - CHECK(j_const.max_size() >= j_const.size()); - } - } - - SECTION("filled object") - { - json j = {{"one", 1}, {"two", 2}, {"three", 3}}; - json j_const(j); - - SECTION("result of max_size") - { - CHECK(j.max_size() >= j.size()); - CHECK(j_const.max_size() >= j_const.size()); - } - } - } - - SECTION("number (integer)") - { - json j = 23; - json j_const(j); - - SECTION("result of max_size") - { - CHECK(j.max_size() == 1); - CHECK(j_const.max_size() == 1); - } - } - - SECTION("number (unsigned)") - { - json j = 23u; - json j_const(j); - - SECTION("result of max_size") - { - CHECK(j.max_size() == 1); - CHECK(j_const.max_size() == 1); - } - } - - SECTION("number (float)") - { - json j = 23.42; - json j_const(j); - - SECTION("result of max_size") - { - CHECK(j.max_size() == 1); - CHECK(j_const.max_size() == 1); - } - } - - SECTION("null") - { - json j = nullptr; - json j_const(j); - - SECTION("result of max_size") - { - CHECK(j.max_size() == 0); - CHECK(j_const.max_size() == 0); - } - } - } -} - -TEST_CASE("modifiers") -{ - SECTION("clear()") - { - SECTION("boolean") - { - json j = true; - - j.clear(); - CHECK(j == json(json::value_t::boolean)); - } - - SECTION("string") - { - json j = "hello world"; - - j.clear(); - CHECK(j == json(json::value_t::string)); - } - - SECTION("array") - { - SECTION("empty array") - { - json j = json::array(); - - j.clear(); - CHECK(j.empty()); - CHECK(j == json(json::value_t::array)); - } - - SECTION("filled array") - { - json j = {1, 2, 3}; - - j.clear(); - CHECK(j.empty()); - CHECK(j == json(json::value_t::array)); - } - } - - SECTION("object") - { - SECTION("empty object") - { - json j = json::object(); - - j.clear(); - CHECK(j.empty()); - CHECK(j == json(json::value_t::object)); - } - - SECTION("filled object") - { - json j = {{"one", 1}, {"two", 2}, {"three", 3}}; - - j.clear(); - CHECK(j.empty()); - CHECK(j == json(json::value_t::object)); - } - } - - SECTION("number (integer)") - { - json j = 23; - - j.clear(); - CHECK(j == json(json::value_t::number_integer)); - } - - SECTION("number (unsigned)") - { - json j = 23u; - - j.clear(); - CHECK(j == json(json::value_t::number_integer)); - } - - SECTION("number (float)") - { - json j = 23.42; - - j.clear(); - CHECK(j == json(json::value_t::number_float)); - } - - SECTION("null") - { - json j = nullptr; - - j.clear(); - CHECK(j == json(json::value_t::null)); - } - } - - SECTION("push_back()") - { - SECTION("to array") - { - SECTION("json&&") - { - SECTION("null") - { - json j; - j.push_back(1); - j.push_back(2); - CHECK(j.type() == json::value_t::array); - CHECK(j == json({1, 2})); - } - - SECTION("array") - { - json j = {1, 2, 3}; - j.push_back("Hello"); - CHECK(j.type() == json::value_t::array); - CHECK(j == json({1, 2, 3, "Hello"})); - } - - SECTION("other type") - { - json j = 1; - CHECK_THROWS_AS(j.push_back("Hello"), std::domain_error); - CHECK_THROWS_WITH(j.push_back("Hello"), "cannot use push_back() with number"); - } - } - - SECTION("const json&") - { - SECTION("null") - { - json j; - json k(1); - j.push_back(k); - j.push_back(k); - CHECK(j.type() == json::value_t::array); - CHECK(j == json({1, 1})); - } - - SECTION("array") - { - json j = {1, 2, 3}; - json k("Hello"); - j.push_back(k); - CHECK(j.type() == json::value_t::array); - CHECK(j == json({1, 2, 3, "Hello"})); - } - - SECTION("other type") - { - json j = 1; - json k("Hello"); - CHECK_THROWS_AS(j.push_back(k), std::domain_error); - CHECK_THROWS_WITH(j.push_back(k), "cannot use push_back() with number"); - } - } - } - - SECTION("to object") - { - SECTION("null") - { - json j; - j.push_back(json::object_t::value_type({"one", 1})); - j.push_back(json::object_t::value_type({"two", 2})); - CHECK(j.type() == json::value_t::object); - CHECK(j.size() == 2); - CHECK(j["one"] == json(1)); - CHECK(j["two"] == json(2)); - } - - SECTION("object") - { - json j(json::value_t::object); - j.push_back(json::object_t::value_type({"one", 1})); - j.push_back(json::object_t::value_type({"two", 2})); - CHECK(j.size() == 2); - CHECK(j["one"] == json(1)); - CHECK(j["two"] == json(2)); - } - - SECTION("other type") - { - json j = 1; - json k("Hello"); - CHECK_THROWS_AS(j.push_back(json::object_t::value_type({"one", 1})), std::domain_error); - CHECK_THROWS_WITH(j.push_back(json::object_t::value_type({"one", 1})), - "cannot use push_back() with number"); - } - } - - SECTION("with initializer_list") - { - SECTION("null") - { - json j; - j.push_back({"foo", "bar"}); - CHECK(j == json::array({{"foo", "bar"}})); - - json k; - k.push_back({1, 2, 3}); - CHECK(k == json::array({{1, 2, 3}})); - } - - SECTION("array") - { - json j = {1, 2, 3}; - j.push_back({"foo", "bar"}); - CHECK(j == json({1, 2, 3, {"foo", "bar"}})); - - json k = {1, 2, 3}; - k.push_back({1, 2, 3}); - CHECK(k == json({1, 2, 3, {1, 2, 3}})); - } - - SECTION("object") - { - json j = {{"key1", 1}}; - j.push_back({"key2", "bar"}); - CHECK(j == json({{"key1", 1}, {"key2", "bar"}})); - - json k = {{"key1", 1}}; - CHECK_THROWS_AS(k.push_back({1, 2, 3, 4}), std::domain_error); - CHECK_THROWS_WITH(k.push_back({1, 2, 3, 4}), "cannot use push_back() with object"); - } - } - } - - SECTION("operator+=") - { - SECTION("to array") - { - SECTION("json&&") - { - SECTION("null") - { - json j; - j += 1; - j += 2; - CHECK(j.type() == json::value_t::array); - CHECK(j == json({1, 2})); - } - - SECTION("array") - { - json j = {1, 2, 3}; - j += "Hello"; - CHECK(j.type() == json::value_t::array); - CHECK(j == json({1, 2, 3, "Hello"})); - } - - SECTION("other type") - { - json j = 1; - CHECK_THROWS_AS(j += "Hello", std::domain_error); - CHECK_THROWS_WITH(j += "Hello", "cannot use push_back() with number"); - } - } - - SECTION("const json&") - { - SECTION("null") - { - json j; - json k(1); - j += k; - j += k; - CHECK(j.type() == json::value_t::array); - CHECK(j == json({1, 1})); - } - - SECTION("array") - { - json j = {1, 2, 3}; - json k("Hello"); - j += k; - CHECK(j.type() == json::value_t::array); - CHECK(j == json({1, 2, 3, "Hello"})); - } - - SECTION("other type") - { - json j = 1; - json k("Hello"); - CHECK_THROWS_AS(j += k, std::domain_error); - CHECK_THROWS_WITH(j += k, "cannot use push_back() with number"); - } - } - } - - SECTION("to object") - { - SECTION("null") - { - json j; - j += json::object_t::value_type({"one", 1}); - j += json::object_t::value_type({"two", 2}); - CHECK(j.type() == json::value_t::object); - CHECK(j.size() == 2); - CHECK(j["one"] == json(1)); - CHECK(j["two"] == json(2)); - } - - SECTION("object") - { - json j(json::value_t::object); - j += json::object_t::value_type({"one", 1}); - j += json::object_t::value_type({"two", 2}); - CHECK(j.size() == 2); - CHECK(j["one"] == json(1)); - CHECK(j["two"] == json(2)); - } - - SECTION("other type") - { - json j = 1; - json k("Hello"); - CHECK_THROWS_AS(j += json::object_t::value_type({"one", 1}), std::domain_error); - CHECK_THROWS_WITH(j += json::object_t::value_type({"one", 1}), - "cannot use push_back() with number"); - } - } - - SECTION("with initializer_list") - { - SECTION("null") - { - json j; - j += {"foo", "bar"}; - CHECK(j == json::array({{"foo", "bar"}})); - - json k; - k += {1, 2, 3}; - CHECK(k == json::array({{1, 2, 3}})); - } - - SECTION("array") - { - json j = {1, 2, 3}; - j += {"foo", "bar"}; - CHECK(j == json({1, 2, 3, {"foo", "bar"}})); - - json k = {1, 2, 3}; - k += {1, 2, 3}; - CHECK(k == json({1, 2, 3, {1, 2, 3}})); - } - - SECTION("object") - { - json j = {{"key1", 1}}; - j += {"key2", "bar"}; - CHECK(j == json({{"key1", 1}, {"key2", "bar"}})); - - json k = {{"key1", 1}}; - CHECK_THROWS_AS((k += {1, 2, 3, 4}), std::domain_error); - CHECK_THROWS_WITH((k += {1, 2, 3, 4}), "cannot use push_back() with object"); - } - } - } - - SECTION("insert") - { - json j_array = {1, 2, 3, 4}; - json j_value = 5; - - SECTION("value at position") - { - SECTION("insert before begin()") - { - auto it = j_array.insert(j_array.begin(), j_value); - CHECK(j_array.size() == 5); - CHECK(*it == j_value); - CHECK(j_array.begin() == it); - CHECK(j_array == json({5, 1, 2, 3, 4})); - } - - SECTION("insert in the middle") - { - auto it = j_array.insert(j_array.begin() + 2, j_value); - CHECK(j_array.size() == 5); - CHECK(*it == j_value); - CHECK((it - j_array.begin()) == 2); - CHECK(j_array == json({1, 2, 5, 3, 4})); - } - - SECTION("insert before end()") - { - auto it = j_array.insert(j_array.end(), j_value); - CHECK(j_array.size() == 5); - CHECK(*it == j_value); - CHECK((j_array.end() - it) == 1); - CHECK(j_array == json({1, 2, 3, 4, 5})); - } - } - - SECTION("rvalue at position") - { - SECTION("insert before begin()") - { - auto it = j_array.insert(j_array.begin(), 5); - CHECK(j_array.size() == 5); - CHECK(*it == j_value); - CHECK(j_array.begin() == it); - CHECK(j_array == json({5, 1, 2, 3, 4})); - } - - SECTION("insert in the middle") - { - auto it = j_array.insert(j_array.begin() + 2, 5); - CHECK(j_array.size() == 5); - CHECK(*it == j_value); - CHECK((it - j_array.begin()) == 2); - CHECK(j_array == json({1, 2, 5, 3, 4})); - } - - SECTION("insert before end()") - { - auto it = j_array.insert(j_array.end(), 5); - CHECK(j_array.size() == 5); - CHECK(*it == j_value); - CHECK((j_array.end() - it) == 1); - CHECK(j_array == json({1, 2, 3, 4, 5})); - } - } - - SECTION("copies at position") - { - SECTION("insert before begin()") - { - auto it = j_array.insert(j_array.begin(), 3, 5); - CHECK(j_array.size() == 7); - CHECK(*it == j_value); - CHECK(j_array.begin() == it); - CHECK(j_array == json({5, 5, 5, 1, 2, 3, 4})); - } - - SECTION("insert in the middle") - { - auto it = j_array.insert(j_array.begin() + 2, 3, 5); - CHECK(j_array.size() == 7); - CHECK(*it == j_value); - CHECK((it - j_array.begin()) == 2); - CHECK(j_array == json({1, 2, 5, 5, 5, 3, 4})); - } - - SECTION("insert before end()") - { - auto it = j_array.insert(j_array.end(), 3, 5); - CHECK(j_array.size() == 7); - CHECK(*it == j_value); - CHECK((j_array.end() - it) == 3); - CHECK(j_array == json({1, 2, 3, 4, 5, 5, 5})); - } - - SECTION("insert nothing (count = 0)") - { - auto pos = j_array.end(); - auto it = j_array.insert(j_array.end(), 0, 5); - CHECK(j_array.size() == 4); - CHECK(it == pos); - CHECK(j_array == json({1, 2, 3, 4})); - } - } - - SECTION("range") - { - json j_other_array = {"first", "second"}; - - SECTION("proper usage") - { - auto it = j_array.insert(j_array.end(), j_other_array.begin(), j_other_array.end()); - CHECK(j_array.size() == 6); - CHECK(*it == *j_other_array.begin()); - CHECK((j_array.end() - it) == 2); - CHECK(j_array == json({1, 2, 3, 4, "first", "second"})); - } - - SECTION("empty range") - { - auto it = j_array.insert(j_array.end(), j_other_array.begin(), j_other_array.begin()); - CHECK(j_array.size() == 4); - CHECK(it == j_array.end()); - CHECK(j_array == json({1, 2, 3, 4})); - } - - SECTION("invalid iterators") - { - json j_other_array2 = {"first", "second"}; - - CHECK_THROWS_AS(j_array.insert(j_array.end(), j_array.begin(), j_array.end()), std::domain_error); - CHECK_THROWS_AS(j_array.insert(j_array.end(), j_other_array.begin(), j_other_array2.end()), - std::domain_error); - - CHECK_THROWS_WITH(j_array.insert(j_array.end(), j_array.begin(), j_array.end()), - "passed iterators may not belong to container"); - CHECK_THROWS_WITH(j_array.insert(j_array.end(), j_other_array.begin(), j_other_array2.end()), - "iterators do not fit"); - } - } - - SECTION("initializer list at position") - { - SECTION("insert before begin()") - { - auto it = j_array.insert(j_array.begin(), {7, 8, 9}); - CHECK(j_array.size() == 7); - CHECK(*it == json(7)); - CHECK(j_array.begin() == it); - CHECK(j_array == json({7, 8, 9, 1, 2, 3, 4})); - } - - SECTION("insert in the middle") - { - auto it = j_array.insert(j_array.begin() + 2, {7, 8, 9}); - CHECK(j_array.size() == 7); - CHECK(*it == json(7)); - CHECK((it - j_array.begin()) == 2); - CHECK(j_array == json({1, 2, 7, 8, 9, 3, 4})); - } - - SECTION("insert before end()") - { - auto it = j_array.insert(j_array.end(), {7, 8, 9}); - CHECK(j_array.size() == 7); - CHECK(*it == json(7)); - CHECK((j_array.end() - it) == 3); - CHECK(j_array == json({1, 2, 3, 4, 7, 8, 9})); - } - } - - SECTION("invalid iterator") - { - // pass iterator to a different array - json j_another_array = {1, 2}; - json j_yet_another_array = {"first", "second"}; - CHECK_THROWS_AS(j_array.insert(j_another_array.end(), 10), std::domain_error); - CHECK_THROWS_AS(j_array.insert(j_another_array.end(), j_value), std::domain_error); - CHECK_THROWS_AS(j_array.insert(j_another_array.end(), 10, 11), std::domain_error); - CHECK_THROWS_AS(j_array.insert(j_another_array.end(), j_yet_another_array.begin(), - j_yet_another_array.end()), std::domain_error); - CHECK_THROWS_AS(j_array.insert(j_another_array.end(), {1, 2, 3, 4}), std::domain_error); - - CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), 10), "iterator does not fit current value"); - CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), j_value), - "iterator does not fit current value"); - CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), 10, 11), - "iterator does not fit current value"); - CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), j_yet_another_array.begin(), - j_yet_another_array.end()), "iterator does not fit current value"); - CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), {1, 2, 3, 4}), - "iterator does not fit current value"); - } - - SECTION("non-array type") - { - // call insert on a non-array type - json j_nonarray = 3; - json j_yet_another_array = {"first", "second"}; - CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), 10), std::domain_error); - CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), j_value), std::domain_error); - CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), 10, 11), std::domain_error); - CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), j_yet_another_array.begin(), - j_yet_another_array.end()), std::domain_error); - CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), {1, 2, 3, 4}), std::domain_error); - - CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), 10), "cannot use insert() with number"); - CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), j_value), "cannot use insert() with number"); - CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), 10, 11), "cannot use insert() with number"); - CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), j_yet_another_array.begin(), - j_yet_another_array.end()), "cannot use insert() with number"); - CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), {1, 2, 3, 4}), - "cannot use insert() with number"); - } - } - - SECTION("swap()") - { - SECTION("json") - { - SECTION("member swap") - { - json j("hello world"); - json k(42.23); - - j.swap(k); - - CHECK(j == json(42.23)); - CHECK(k == json("hello world")); - } - - SECTION("nonmember swap") - { - json j("hello world"); - json k(42.23); - - std::swap(j, k); - - CHECK(j == json(42.23)); - CHECK(k == json("hello world")); - } - } - - SECTION("array_t") - { - SECTION("array_t type") - { - json j = {1, 2, 3, 4}; - json::array_t a = {"foo", "bar", "baz"}; - - j.swap(a); - - CHECK(j == json({"foo", "bar", "baz"})); - - j.swap(a); - - CHECK(j == json({1, 2, 3, 4})); - } - - SECTION("non-array_t type") - { - json j = 17; - json::array_t a = {"foo", "bar", "baz"}; - - CHECK_THROWS_AS(j.swap(a), std::domain_error); - CHECK_THROWS_WITH(j.swap(a), "cannot use swap() with number"); - } - } - - SECTION("object_t") - { - SECTION("object_t type") - { - json j = {{"one", 1}, {"two", 2}}; - json::object_t o = {{"cow", "Kuh"}, {"chicken", "Huhn"}}; - - j.swap(o); - - CHECK(j == json({{"cow", "Kuh"}, {"chicken", "Huhn"}})); - - j.swap(o); - - CHECK(j == json({{"one", 1}, {"two", 2}})); - } - - SECTION("non-object_t type") - { - json j = 17; - json::object_t o = {{"cow", "Kuh"}, {"chicken", "Huhn"}}; - - CHECK_THROWS_AS(j.swap(o), std::domain_error); - CHECK_THROWS_WITH(j.swap(o), "cannot use swap() with number"); - } - } - - SECTION("string_t") - { - SECTION("string_t type") - { - json j = "Hello world"; - json::string_t s = "Hallo Welt"; - - j.swap(s); - - CHECK(j == json("Hallo Welt")); - - j.swap(s); - - CHECK(j == json("Hello world")); - } - - SECTION("non-string_t type") - { - json j = 17; - json::string_t s = "Hallo Welt"; - - CHECK_THROWS_AS(j.swap(s), std::domain_error); - CHECK_THROWS_WITH(j.swap(s), "cannot use swap() with number"); - } - } - } -} - -TEST_CASE("lexicographical comparison operators") -{ - SECTION("types") - { - std::vector j_types = - { - json::value_t::null, - json::value_t::boolean, - json::value_t::number_integer, - json::value_t::number_unsigned, - json::value_t::number_float, - json::value_t::object, - json::value_t::array, - json::value_t::string - }; - - SECTION("comparison: less") - { - std::vector> expected = - { - {false, true, true, true, true, true, true, true}, - {false, false, true, true, true, true, true, true}, - {false, false, false, false, false, true, true, true}, - {false, false, false, false, false, true, true, true}, - {false, false, false, false, false, true, true, true}, - {false, false, false, false, false, false, true, true}, - {false, false, false, false, false, false, false, true}, - {false, false, false, false, false, false, false, false} - }; - - for (size_t i = 0; i < j_types.size(); ++i) - { - for (size_t j = 0; j < j_types.size(); ++j) - { - CAPTURE(i); - CAPTURE(j); - // check precomputed values - CHECK(operator<(j_types[i], j_types[j]) == expected[i][j]); - } - } - } - } - - SECTION("values") - { - json j_values = - { - nullptr, nullptr, - 17, 42, - 8u, 13u, - 3.14159, 23.42, - "foo", "bar", - true, false, - {1, 2, 3}, {"one", "two", "three"}, - {{"first", 1}, {"second", 2}}, {{"a", "A"}, {"b", {"B"}}} - }; - - SECTION("comparison: equal") - { - std::vector> expected = - { - {true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, - {true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, - {false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false}, - {false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false}, - {false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false}, - {false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false}, - {false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false}, - {false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false}, - {false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false}, - {false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false}, - {false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false}, - {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true} - }; - - for (size_t i = 0; i < j_values.size(); ++i) - { - for (size_t j = 0; j < j_values.size(); ++j) - { - CAPTURE(i); - CAPTURE(j); - // check precomputed values - CHECK( (j_values[i] == j_values[j]) == expected[i][j] ); - } - } - - // comparison with discarded elements - json j_discarded(json::value_t::discarded); - for (size_t i = 0; i < j_values.size(); ++i) - { - CHECK( (j_values[i] == j_discarded) == false); - CHECK( (j_discarded == j_values[i]) == false); - CHECK( (j_discarded == j_discarded) == false); - } - - // compare with null pointer - json j_null; - CHECK(j_null == nullptr); - CHECK(nullptr == j_null); - } - - SECTION("comparison: not equal") - { - for (size_t i = 0; i < j_values.size(); ++i) - { - for (size_t j = 0; j < j_values.size(); ++j) - { - CAPTURE(i); - CAPTURE(j); - // check definition - CHECK( (j_values[i] != j_values[j]) == not(j_values[i] == j_values[j]) ); - } - } - - // compare with null pointer - json j_null; - CHECK( (j_null != nullptr) == false); - CHECK( (nullptr != j_null) == false); - CHECK( (j_null != nullptr) == not(j_null == nullptr)); - CHECK( (nullptr != j_null) == not(nullptr == j_null)); - } - - SECTION("comparison: less") - { - std::vector> expected = - { - {false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true}, - {false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true}, - {false, false, false, true, false, false, false, true, true, true, false, false, true, true, true, true}, - {false, false, false, false, false, false, false, false, true, true, false, false, true, true, true, true}, - {false, false, true, true, false, true, false, true, true, true, false, false, true, true, true, true}, - {false, false, true, true, false, false, false, true, true, true, false, false, true, true, true, true}, - {false, false, true, true, true, true, false, true, true, true, false, false, true, true, true, true}, - {false, false, false, true, false, false, false, false, true, true, false, false, true, true, true, true}, - {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false}, - {false, false, true, true, true, true, true, true, true, true, false, false, true, true, true, true}, - {false, false, true, true, true, true, true, true, true, true, true, false, true, true, true, true}, - {false, false, false, false, false, false, false, false, true, true, false, false, false, true, false, false}, - {false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, true, true, false, false, true, true, false, false}, - {false, false, false, false, false, false, false, false, true, true, false, false, true, true, true, false} - }; - - for (size_t i = 0; i < j_values.size(); ++i) - { - for (size_t j = 0; j < j_values.size(); ++j) - { - CAPTURE(i); - CAPTURE(j); - // check precomputed values - CHECK( (j_values[i] < j_values[j]) == expected[i][j] ); - } - } - - // comparison with discarded elements - json j_discarded(json::value_t::discarded); - for (size_t i = 0; i < j_values.size(); ++i) - { - CAPTURE(i); - CHECK( (j_values[i] < j_discarded) == false); - CHECK( (j_discarded < j_values[i]) == false); - CHECK( (j_discarded < j_discarded) == false); - } - } - - SECTION("comparison: less than or equal equal") - { - for (size_t i = 0; i < j_values.size(); ++i) - { - for (size_t j = 0; j < j_values.size(); ++j) - { - CAPTURE(i); - CAPTURE(j); - // check definition - CHECK( (j_values[i] <= j_values[j]) == not(j_values[j] < j_values[i]) ); - } - } - } - - SECTION("comparison: greater than") - { - for (size_t i = 0; i < j_values.size(); ++i) - { - for (size_t j = 0; j < j_values.size(); ++j) - { - CAPTURE(i); - CAPTURE(j); - // check definition - CHECK( (j_values[i] > j_values[j]) == (j_values[j] < j_values[i]) ); - } - } - } - - SECTION("comparison: greater than or equal") - { - for (size_t i = 0; i < j_values.size(); ++i) - { - for (size_t j = 0; j < j_values.size(); ++j) - { - CAPTURE(i); - CAPTURE(j); - // check definition - CHECK( (j_values[i] >= j_values[j]) == not(j_values[i] < j_values[j]) ); - } - } - } - } -} - -TEST_CASE("serialization") -{ - SECTION("operator<<") - { - SECTION("no given width") - { - std::stringstream ss; - json j = {"foo", 1, 2, 3, false, {{"one", 1}}}; - ss << j; - CHECK(ss.str() == "[\"foo\",1,2,3,false,{\"one\":1}]"); - } - - SECTION("given width") - { - std::stringstream ss; - json j = {"foo", 1, 2, 3, false, {{"one", 1}}}; - ss << std::setw(4) << j; - CHECK(ss.str() == - "[\n \"foo\",\n 1,\n 2,\n 3,\n false,\n {\n \"one\": 1\n }\n]"); - } - } - - SECTION("operator>>") - { - SECTION("no given width") - { - std::stringstream ss; - json j = {"foo", 1, 2, 3, false, {{"one", 1}}}; - j >> ss; - CHECK(ss.str() == "[\"foo\",1,2,3,false,{\"one\":1}]"); - } - - SECTION("given width") - { - std::stringstream ss; - json j = {"foo", 1, 2, 3, false, {{"one", 1}}}; - ss.width(4); - j >> ss; - CHECK(ss.str() == - "[\n \"foo\",\n 1,\n 2,\n 3,\n false,\n {\n \"one\": 1\n }\n]"); - } - } -} - -TEST_CASE("deserialization") -{ - SECTION("stream") - { - std::stringstream ss; - ss << "[\"foo\",1,2,3,false,{\"one\":1}]"; - json j = json::parse(ss); - CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); - } - - SECTION("string") - { - auto s = "[\"foo\",1,2,3,false,{\"one\":1}]"; - json j = json::parse(s); - CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); - } - - SECTION("operator<<") - { - std::stringstream ss; - ss << "[\"foo\",1,2,3,false,{\"one\":1}]"; - json j; - j << ss; - CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); - } - - SECTION("operator>>") - { - std::stringstream ss; - ss << "[\"foo\",1,2,3,false,{\"one\":1}]"; - json j; - ss >> j; - CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); - } - - SECTION("user-defined string literal") - { - CHECK("[\"foo\",1,2,3,false,{\"one\":1}]"_json == json({"foo", 1, 2, 3, false, {{"one", 1}}})); - } -} - -TEST_CASE("iterator class") -{ - SECTION("construction") - { - SECTION("constructor") - { - SECTION("null") - { - json j(json::value_t::null); - json::iterator it(&j); - } - - SECTION("object") - { - json j(json::value_t::object); - json::iterator it(&j); - } - - SECTION("array") - { - json j(json::value_t::array); - json::iterator it(&j); - } - } - - SECTION("copy assignment") - { - json j(json::value_t::null); - json::iterator it(&j); - json::iterator it2(&j); - it2 = it; - } - } - - SECTION("initialization") - { - SECTION("set_begin") - { - SECTION("null") - { - json j(json::value_t::null); - json::iterator it(&j); - it.set_begin(); - CHECK(it == j.begin()); - } - - SECTION("object") - { - json j(json::value_t::object); - json::iterator it(&j); - it.set_begin(); - CHECK(it == j.begin()); - } - - SECTION("array") - { - json j(json::value_t::array); - json::iterator it(&j); - it.set_begin(); - CHECK(it == j.begin()); - } - } - - SECTION("set_end") - { - SECTION("null") - { - json j(json::value_t::null); - json::iterator it(&j); - it.set_end(); - CHECK(it == j.end()); - } - - SECTION("object") - { - json j(json::value_t::object); - json::iterator it(&j); - it.set_end(); - CHECK(it == j.end()); - } - - SECTION("array") - { - json j(json::value_t::array); - json::iterator it(&j); - it.set_end(); - CHECK(it == j.end()); - } - } - } - - SECTION("element access") - { - SECTION("operator*") - { - SECTION("null") - { - json j(json::value_t::null); - json::iterator it = j.begin(); - CHECK_THROWS_AS(*it, std::out_of_range); - CHECK_THROWS_WITH(*it, "cannot get value"); - } - - SECTION("number") - { - json j(17); - json::iterator it = j.begin(); - CHECK(*it == json(17)); - it = j.end(); - CHECK_THROWS_AS(*it, std::out_of_range); - CHECK_THROWS_WITH(*it, "cannot get value"); - } - - SECTION("object") - { - json j({{"foo", "bar"}}); - json::iterator it = j.begin(); - CHECK(*it == json("bar")); - } - - SECTION("array") - { - json j({1, 2, 3, 4}); - json::iterator it = j.begin(); - CHECK(*it == json(1)); - } - } - - SECTION("operator->") - { - SECTION("null") - { - json j(json::value_t::null); - json::iterator it = j.begin(); - CHECK_THROWS_AS(it->type_name(), std::out_of_range); - CHECK_THROWS_WITH(it->type_name(), "cannot get value"); - } - - SECTION("number") - { - json j(17); - json::iterator it = j.begin(); - CHECK(it->type_name() == "number"); - it = j.end(); - CHECK_THROWS_AS(it->type_name(), std::out_of_range); - CHECK_THROWS_WITH(it->type_name(), "cannot get value"); - } - - SECTION("object") - { - json j({{"foo", "bar"}}); - json::iterator it = j.begin(); - CHECK(it->type_name() == "string"); - } - - SECTION("array") - { - json j({1, 2, 3, 4}); - json::iterator it = j.begin(); - CHECK(it->type_name() == "number"); - } - } - } - - SECTION("increment/decrement") - { - SECTION("post-increment") - { - SECTION("null") - { - json j(json::value_t::null); - json::iterator it = j.begin(); - CHECK(it.m_it.primitive_iterator == 1); - it++; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); - } - - SECTION("number") - { - json j(17); - json::iterator it = j.begin(); - CHECK(it.m_it.primitive_iterator == 0); - it++; - CHECK(it.m_it.primitive_iterator == 1); - it++; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); - } - - SECTION("object") - { - json j({{"foo", "bar"}}); - json::iterator it = j.begin(); - CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); - it++; - CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); - } - - SECTION("array") - { - json j({1, 2, 3, 4}); - json::iterator it = j.begin(); - CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); - it++; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - it++; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - it++; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - it++; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); - } - } - - SECTION("pre-increment") - { - SECTION("null") - { - json j(json::value_t::null); - json::iterator it = j.begin(); - CHECK(it.m_it.primitive_iterator == 1); - ++it; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); - } - - SECTION("number") - { - json j(17); - json::iterator it = j.begin(); - CHECK(it.m_it.primitive_iterator == 0); - ++it; - CHECK(it.m_it.primitive_iterator == 1); - ++it; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); - } - - SECTION("object") - { - json j({{"foo", "bar"}}); - json::iterator it = j.begin(); - CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); - ++it; - CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); - } - - SECTION("array") - { - json j({1, 2, 3, 4}); - json::iterator it = j.begin(); - CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); - ++it; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - ++it; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - ++it; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - ++it; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); - } - } - - SECTION("post-decrement") - { - SECTION("null") - { - json j(json::value_t::null); - json::iterator it = j.end(); - CHECK(it.m_it.primitive_iterator == 1); - } - - SECTION("number") - { - json j(17); - json::iterator it = j.end(); - CHECK(it.m_it.primitive_iterator == 1); - it--; - CHECK(it.m_it.primitive_iterator == 0); - it--; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); - } - - SECTION("object") - { - json j({{"foo", "bar"}}); - json::iterator it = j.end(); - CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); - it--; - CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); - } - - SECTION("array") - { - json j({1, 2, 3, 4}); - json::iterator it = j.end(); - CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); - it--; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - it--; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - it--; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - it--; - CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - } - } - - SECTION("pre-decrement") - { - SECTION("null") - { - json j(json::value_t::null); - json::iterator it = j.end(); - CHECK(it.m_it.primitive_iterator == 1); - } - - SECTION("number") - { - json j(17); - json::iterator it = j.end(); - CHECK(it.m_it.primitive_iterator == 1); - --it; - CHECK(it.m_it.primitive_iterator == 0); - --it; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); - } - - SECTION("object") - { - json j({{"foo", "bar"}}); - json::iterator it = j.end(); - CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); - --it; - CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); - } - - SECTION("array") - { - json j({1, 2, 3, 4}); - json::iterator it = j.end(); - CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); - --it; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - --it; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - --it; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - --it; - CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - } - } - } -} - -TEST_CASE("const_iterator class") -{ - SECTION("construction") - { - SECTION("constructor") - { - SECTION("null") - { - json j(json::value_t::null); - json::const_iterator it(&j); - } - - SECTION("object") - { - json j(json::value_t::object); - json::const_iterator it(&j); - } - - SECTION("array") - { - json j(json::value_t::array); - json::const_iterator it(&j); - } - } - - SECTION("copy assignment") - { - json j(json::value_t::null); - json::const_iterator it(&j); - json::const_iterator it2(&j); - it2 = it; - } - } - - SECTION("initialization") - { - SECTION("set_begin") - { - SECTION("null") - { - json j(json::value_t::null); - json::const_iterator it(&j); - it.set_begin(); - CHECK(it == j.cbegin()); - } - - SECTION("object") - { - json j(json::value_t::object); - json::const_iterator it(&j); - it.set_begin(); - CHECK(it == j.cbegin()); - } - - SECTION("array") - { - json j(json::value_t::array); - json::const_iterator it(&j); - it.set_begin(); - CHECK(it == j.cbegin()); - } - } - - SECTION("set_end") - { - SECTION("null") - { - json j(json::value_t::null); - json::const_iterator it(&j); - it.set_end(); - CHECK(it == j.cend()); - } - - SECTION("object") - { - json j(json::value_t::object); - json::const_iterator it(&j); - it.set_end(); - CHECK(it == j.cend()); - } - - SECTION("array") - { - json j(json::value_t::array); - json::const_iterator it(&j); - it.set_end(); - CHECK(it == j.cend()); - } - } - } - - SECTION("element access") - { - SECTION("operator*") - { - SECTION("null") - { - json j(json::value_t::null); - json::const_iterator it = j.cbegin(); - CHECK_THROWS_AS(*it, std::out_of_range); - CHECK_THROWS_WITH(*it, "cannot get value"); - } - - SECTION("number") - { - json j(17); - json::const_iterator it = j.cbegin(); - CHECK(*it == json(17)); - it = j.cend(); - CHECK_THROWS_AS(*it, std::out_of_range); - CHECK_THROWS_WITH(*it, "cannot get value"); - } - - SECTION("object") - { - json j({{"foo", "bar"}}); - json::const_iterator it = j.cbegin(); - CHECK(*it == json("bar")); - } - - SECTION("array") - { - json j({1, 2, 3, 4}); - json::const_iterator it = j.cbegin(); - CHECK(*it == json(1)); - } - } - - SECTION("operator->") - { - SECTION("null") - { - json j(json::value_t::null); - json::const_iterator it = j.cbegin(); - CHECK_THROWS_AS(it->type_name(), std::out_of_range); - CHECK_THROWS_WITH(it->type_name(), "cannot get value"); - } - - SECTION("number") - { - json j(17); - json::const_iterator it = j.cbegin(); - CHECK(it->type_name() == "number"); - it = j.cend(); - CHECK_THROWS_AS(it->type_name(), std::out_of_range); - CHECK_THROWS_WITH(it->type_name(), "cannot get value"); - } - - SECTION("object") - { - json j({{"foo", "bar"}}); - json::const_iterator it = j.cbegin(); - CHECK(it->type_name() == "string"); - } - - SECTION("array") - { - json j({1, 2, 3, 4}); - json::const_iterator it = j.cbegin(); - CHECK(it->type_name() == "number"); - } - } - } - - SECTION("increment/decrement") - { - SECTION("post-increment") - { - SECTION("null") - { - json j(json::value_t::null); - json::const_iterator it = j.cbegin(); - CHECK(it.m_it.primitive_iterator == 1); - it++; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); - } - - SECTION("number") - { - json j(17); - json::const_iterator it = j.cbegin(); - CHECK(it.m_it.primitive_iterator == 0); - it++; - CHECK(it.m_it.primitive_iterator == 1); - it++; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); - } - - SECTION("object") - { - json j({{"foo", "bar"}}); - json::const_iterator it = j.cbegin(); - CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); - it++; - CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); - } - - SECTION("array") - { - json j({1, 2, 3, 4}); - json::const_iterator it = j.cbegin(); - CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); - it++; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - it++; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - it++; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - it++; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); - } - } - - SECTION("pre-increment") - { - SECTION("null") - { - json j(json::value_t::null); - json::const_iterator it = j.cbegin(); - CHECK(it.m_it.primitive_iterator == 1); - ++it; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); - } - - SECTION("number") - { - json j(17); - json::const_iterator it = j.cbegin(); - CHECK(it.m_it.primitive_iterator == 0); - ++it; - CHECK(it.m_it.primitive_iterator == 1); - ++it; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); - } - - SECTION("object") - { - json j({{"foo", "bar"}}); - json::const_iterator it = j.cbegin(); - CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); - ++it; - CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); - } - - SECTION("array") - { - json j({1, 2, 3, 4}); - json::const_iterator it = j.cbegin(); - CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); - ++it; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - ++it; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - ++it; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - ++it; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); - } - } - - SECTION("post-decrement") - { - SECTION("null") - { - json j(json::value_t::null); - json::const_iterator it = j.cend(); - CHECK(it.m_it.primitive_iterator == 1); - } - - SECTION("number") - { - json j(17); - json::const_iterator it = j.cend(); - CHECK(it.m_it.primitive_iterator == 1); - it--; - CHECK(it.m_it.primitive_iterator == 0); - it--; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); - } - - SECTION("object") - { - json j({{"foo", "bar"}}); - json::const_iterator it = j.cend(); - CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); - it--; - CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); - } - - SECTION("array") - { - json j({1, 2, 3, 4}); - json::const_iterator it = j.cend(); - CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); - it--; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - it--; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - it--; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - it--; - CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - } - } - - SECTION("pre-decrement") - { - SECTION("null") - { - json j(json::value_t::null); - json::const_iterator it = j.cend(); - CHECK(it.m_it.primitive_iterator == 1); - } - - SECTION("number") - { - json j(17); - json::const_iterator it = j.cend(); - CHECK(it.m_it.primitive_iterator == 1); - --it; - CHECK(it.m_it.primitive_iterator == 0); - --it; - CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); - } - - SECTION("object") - { - json j({{"foo", "bar"}}); - json::const_iterator it = j.cend(); - CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); - --it; - CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); - } - - SECTION("array") - { - json j({1, 2, 3, 4}); - json::const_iterator it = j.cend(); - CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); - --it; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - --it; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - --it; - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - --it; - CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); - CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); - } - } - } -} - -TEST_CASE("convenience functions") -{ - SECTION("type name as string") - { - CHECK(json(json::value_t::null).type_name() == "null"); - CHECK(json(json::value_t::object).type_name() == "object"); - CHECK(json(json::value_t::array).type_name() == "array"); - CHECK(json(json::value_t::number_integer).type_name() == "number"); - CHECK(json(json::value_t::number_float).type_name() == "number"); - CHECK(json(json::value_t::boolean).type_name() == "boolean"); - CHECK(json(json::value_t::string).type_name() == "string"); - CHECK(json(json::value_t::discarded).type_name() == "discarded"); - } - - SECTION("string escape") - { - CHECK(json::escape_string("\"") == "\\\""); - CHECK(json::escape_string("\\") == "\\\\"); - CHECK(json::escape_string("\b") == "\\b"); - CHECK(json::escape_string("\f") == "\\f"); - CHECK(json::escape_string("\n") == "\\n"); - CHECK(json::escape_string("\r") == "\\r"); - CHECK(json::escape_string("\t") == "\\t"); - - CHECK(json::escape_string("\x01") == "\\u0001"); - CHECK(json::escape_string("\x02") == "\\u0002"); - CHECK(json::escape_string("\x03") == "\\u0003"); - CHECK(json::escape_string("\x04") == "\\u0004"); - CHECK(json::escape_string("\x05") == "\\u0005"); - CHECK(json::escape_string("\x06") == "\\u0006"); - CHECK(json::escape_string("\x07") == "\\u0007"); - CHECK(json::escape_string("\x08") == "\\b"); - CHECK(json::escape_string("\x09") == "\\t"); - CHECK(json::escape_string("\x0a") == "\\n"); - CHECK(json::escape_string("\x0b") == "\\u000b"); - CHECK(json::escape_string("\x0c") == "\\f"); - CHECK(json::escape_string("\x0d") == "\\r"); - CHECK(json::escape_string("\x0e") == "\\u000e"); - CHECK(json::escape_string("\x0f") == "\\u000f"); - CHECK(json::escape_string("\x10") == "\\u0010"); - CHECK(json::escape_string("\x11") == "\\u0011"); - CHECK(json::escape_string("\x12") == "\\u0012"); - CHECK(json::escape_string("\x13") == "\\u0013"); - CHECK(json::escape_string("\x14") == "\\u0014"); - CHECK(json::escape_string("\x15") == "\\u0015"); - CHECK(json::escape_string("\x16") == "\\u0016"); - CHECK(json::escape_string("\x17") == "\\u0017"); - CHECK(json::escape_string("\x18") == "\\u0018"); - CHECK(json::escape_string("\x19") == "\\u0019"); - CHECK(json::escape_string("\x1a") == "\\u001a"); - CHECK(json::escape_string("\x1b") == "\\u001b"); - CHECK(json::escape_string("\x1c") == "\\u001c"); - CHECK(json::escape_string("\x1d") == "\\u001d"); - CHECK(json::escape_string("\x1e") == "\\u001e"); - CHECK(json::escape_string("\x1f") == "\\u001f"); - } -} - -TEST_CASE("lexer class") -{ - SECTION("scan") - { - SECTION("structural characters") - { - CHECK(json::lexer("[").scan() == json::lexer::token_type::begin_array); - CHECK(json::lexer("]").scan() == json::lexer::token_type::end_array); - CHECK(json::lexer("{").scan() == json::lexer::token_type::begin_object); - CHECK(json::lexer("}").scan() == json::lexer::token_type::end_object); - CHECK(json::lexer(",").scan() == json::lexer::token_type::value_separator); - CHECK(json::lexer(":").scan() == json::lexer::token_type::name_separator); - } - - SECTION("literal names") - { - CHECK(json::lexer("null").scan() == json::lexer::token_type::literal_null); - CHECK(json::lexer("true").scan() == json::lexer::token_type::literal_true); - CHECK(json::lexer("false").scan() == json::lexer::token_type::literal_false); - } - - SECTION("numbers") - { - CHECK(json::lexer("0").scan() == json::lexer::token_type::value_number); - CHECK(json::lexer("1").scan() == json::lexer::token_type::value_number); - CHECK(json::lexer("2").scan() == json::lexer::token_type::value_number); - CHECK(json::lexer("3").scan() == json::lexer::token_type::value_number); - CHECK(json::lexer("4").scan() == json::lexer::token_type::value_number); - CHECK(json::lexer("5").scan() == json::lexer::token_type::value_number); - CHECK(json::lexer("6").scan() == json::lexer::token_type::value_number); - CHECK(json::lexer("7").scan() == json::lexer::token_type::value_number); - CHECK(json::lexer("8").scan() == json::lexer::token_type::value_number); - CHECK(json::lexer("9").scan() == json::lexer::token_type::value_number); - } - - SECTION("whitespace") - { - // result is end_of_input, because not token is following - CHECK(json::lexer(" ").scan() == json::lexer::token_type::end_of_input); - CHECK(json::lexer("\t").scan() == json::lexer::token_type::end_of_input); - CHECK(json::lexer("\n").scan() == json::lexer::token_type::end_of_input); - CHECK(json::lexer("\r").scan() == json::lexer::token_type::end_of_input); - CHECK(json::lexer(" \t\n\r\n\t ").scan() == json::lexer::token_type::end_of_input); - } - } - - SECTION("token_type_name") - { - CHECK(json::lexer::token_type_name(json::lexer::token_type::uninitialized) == ""); - CHECK(json::lexer::token_type_name(json::lexer::token_type::literal_true) == "true literal"); - CHECK(json::lexer::token_type_name(json::lexer::token_type::literal_false) == "false literal"); - CHECK(json::lexer::token_type_name(json::lexer::token_type::literal_null) == "null literal"); - CHECK(json::lexer::token_type_name(json::lexer::token_type::value_string) == "string literal"); - CHECK(json::lexer::token_type_name(json::lexer::token_type::value_number) == "number literal"); - CHECK(json::lexer::token_type_name(json::lexer::token_type::begin_array) == "'['"); - CHECK(json::lexer::token_type_name(json::lexer::token_type::begin_object) == "'{'"); - CHECK(json::lexer::token_type_name(json::lexer::token_type::end_array) == "']'"); - CHECK(json::lexer::token_type_name(json::lexer::token_type::end_object) == "'}'"); - CHECK(json::lexer::token_type_name(json::lexer::token_type::name_separator) == "':'"); - CHECK(json::lexer::token_type_name(json::lexer::token_type::value_separator) == "','"); - CHECK(json::lexer::token_type_name(json::lexer::token_type::parse_error) == ""); - CHECK(json::lexer::token_type_name(json::lexer::token_type::end_of_input) == "end of input"); - } - - SECTION("parse errors on first character") - { - for (int c = 1; c < 128; ++c) - { - auto s = std::string(1, c); - - switch (c) - { - // single characters that are valid tokens - case ('['): - case (']'): - case ('{'): - case ('}'): - case (','): - case (':'): - case ('0'): - case ('1'): - case ('2'): - case ('3'): - case ('4'): - case ('5'): - case ('6'): - case ('7'): - case ('8'): - case ('9'): - { - CHECK(json::lexer(s.c_str()).scan() != json::lexer::token_type::parse_error); - break; - } - - // whitespace - case (' '): - case ('\t'): - case ('\n'): - case ('\r'): - { - CHECK(json::lexer(s.c_str()).scan() == json::lexer::token_type::end_of_input); - break; - } - - // anything else is not expected - default: - { - CHECK(json::lexer(s.c_str()).scan() == json::lexer::token_type::parse_error); - break; - } - } - } - } - - SECTION("to_unicode") - { - CHECK(json::lexer::to_unicode(0x1F4A9) == "💩"); - CHECK_THROWS_AS(json::lexer::to_unicode(0x200000), std::out_of_range); - CHECK_THROWS_WITH(json::lexer::to_unicode(0x200000), "code points above 0x10FFFF are invalid"); - } -} - -TEST_CASE("parser class") -{ - SECTION("parse") - { - SECTION("null") - { - CHECK(json::parser("null").parse() == json(nullptr)); - } - - SECTION("true") - { - CHECK(json::parser("true").parse() == json(true)); - } - - SECTION("false") - { - CHECK(json::parser("false").parse() == json(false)); - } - - SECTION("array") - { - SECTION("empty array") - { - CHECK(json::parser("[]").parse() == json(json::value_t::array)); - CHECK(json::parser("[ ]").parse() == json(json::value_t::array)); - } - - SECTION("nonempty array") - { - CHECK(json::parser("[true, false, null]").parse() == json({true, false, nullptr})); - } - } - - SECTION("object") - { - SECTION("empty object") - { - CHECK(json::parser("{}").parse() == json(json::value_t::object)); - CHECK(json::parser("{ }").parse() == json(json::value_t::object)); - } - - SECTION("nonempty object") - { - CHECK(json::parser("{\"\": true, \"one\": 1, \"two\": null}").parse() == json({{"", true}, {"one", 1}, {"two", nullptr}})); - } - } - - SECTION("string") - { - // empty string - CHECK(json::parser("\"\"").parse() == json(json::value_t::string)); - - SECTION("errors") - { - // error: tab in string - CHECK_THROWS_AS(json::parser("\"\t\"").parse(), std::invalid_argument); - CHECK_THROWS_WITH(json::parser("\"\t\"").parse(), "parse error - unexpected '\"'"); - // error: newline in string - CHECK_THROWS_AS(json::parser("\"\n\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\r\"").parse(), std::invalid_argument); - CHECK_THROWS_WITH(json::parser("\"\n\"").parse(), "parse error - unexpected '\"'"); - CHECK_THROWS_WITH(json::parser("\"\r\"").parse(), "parse error - unexpected '\"'"); - // error: backspace in string - CHECK_THROWS_AS(json::parser("\"\b\"").parse(), std::invalid_argument); - CHECK_THROWS_WITH(json::parser("\"\b\"").parse(), "parse error - unexpected '\"'"); - // improve code coverage - CHECK_THROWS_AS(json::parser("\uFF01").parse(), std::invalid_argument); - // unescaped control characters - CHECK_THROWS_AS(json::parser("\"\x00\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x01\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x02\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x03\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x04\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x05\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x06\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x07\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x08\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x09\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x0a\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x0b\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x0c\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x0d\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x0e\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x0f\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x10\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x11\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x12\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x13\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x14\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x15\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x16\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x17\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x18\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x19\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x1a\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x1b\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x1c\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x1d\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x1e\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\x1f\"").parse(), std::invalid_argument); - } - - SECTION("escaped") - { - // quotation mark "\"" - auto r1 = R"("\"")"_json; - CHECK(json::parser("\"\\\"\"").parse() == r1); - // reverse solidus "\\" - auto r2 = R"("\\")"_json; - CHECK(json::parser("\"\\\\\"").parse() == r2); - // solidus - CHECK(json::parser("\"\\/\"").parse() == R"("/")"_json); - // backspace - CHECK(json::parser("\"\\b\"").parse() == json("\b")); - // formfeed - CHECK(json::parser("\"\\f\"").parse() == json("\f")); - // newline - CHECK(json::parser("\"\\n\"").parse() == json("\n")); - // carriage return - CHECK(json::parser("\"\\r\"").parse() == json("\r")); - // horizontal tab - CHECK(json::parser("\"\\t\"").parse() == json("\t")); - - CHECK(json::parser("\"\\u0001\"").parse().get() == "\x01"); - CHECK(json::parser("\"\\u000a\"").parse().get() == "\n"); - CHECK(json::parser("\"\\u00b0\"").parse().get() == "°"); - CHECK(json::parser("\"\\u0c00\"").parse().get() == "ఀ"); - CHECK(json::parser("\"\\ud000\"").parse().get() == "퀀"); - CHECK(json::parser("\"\\u000E\"").parse().get() == "\x0E"); - CHECK(json::parser("\"\\u00F0\"").parse().get() == "ð"); - CHECK(json::parser("\"\\u0100\"").parse().get() == "Ā"); - CHECK(json::parser("\"\\u2000\"").parse().get() == " "); - CHECK(json::parser("\"\\uFFFF\"").parse().get() == "￿"); - CHECK(json::parser("\"\\u20AC\"").parse().get() == "€"); - CHECK(json::parser("\"€\"").parse().get() == "€"); - CHECK(json::parser("\"🎈\"").parse().get() == "🎈"); - - CHECK(json::parse("\"\\ud80c\\udc60\"").get() == u8"\U00013060"); - CHECK(json::parse("\"\\ud83c\\udf1e\"").get() == "🌞"); - } - } - - SECTION("number") - { - SECTION("integers") - { - SECTION("without exponent") - { - CHECK(json::parser("-128").parse() == json(-128)); - CHECK(json::parser("-0").parse() == json(-0)); - CHECK(json::parser("0").parse() == json(0)); - CHECK(json::parser("128").parse() == json(128)); - } - - SECTION("with exponent") - { - CHECK(json::parser("0e1").parse() == json(0e1)); - CHECK(json::parser("0E1").parse() == json(0e1)); - - CHECK(json::parser("10000E-4").parse() == json(10000e-4)); - CHECK(json::parser("10000E-3").parse() == json(10000e-3)); - CHECK(json::parser("10000E-2").parse() == json(10000e-2)); - CHECK(json::parser("10000E-1").parse() == json(10000e-1)); - CHECK(json::parser("10000E0").parse() == json(10000e0)); - CHECK(json::parser("10000E1").parse() == json(10000e1)); - CHECK(json::parser("10000E2").parse() == json(10000e2)); - CHECK(json::parser("10000E3").parse() == json(10000e3)); - CHECK(json::parser("10000E4").parse() == json(10000e4)); - - CHECK(json::parser("10000e-4").parse() == json(10000e-4)); - CHECK(json::parser("10000e-3").parse() == json(10000e-3)); - CHECK(json::parser("10000e-2").parse() == json(10000e-2)); - CHECK(json::parser("10000e-1").parse() == json(10000e-1)); - CHECK(json::parser("10000e0").parse() == json(10000e0)); - CHECK(json::parser("10000e1").parse() == json(10000e1)); - CHECK(json::parser("10000e2").parse() == json(10000e2)); - CHECK(json::parser("10000e3").parse() == json(10000e3)); - CHECK(json::parser("10000e4").parse() == json(10000e4)); - - CHECK(json::parser("-0e1").parse() == json(-0e1)); - CHECK(json::parser("-0E1").parse() == json(-0e1)); - CHECK(json::parser("-0E123").parse() == json(-0e123)); - } - - SECTION("edge cases") - { - // From RFC7159, Section 6: - // Note that when such software is used, numbers that are - // integers and are in the range [-(2**53)+1, (2**53)-1] - // are interoperable in the sense that implementations will - // agree exactly on their numeric values. - - // -(2**53)+1 - CHECK(json::parser("-9007199254740991").parse().get() == -9007199254740991); - // (2**53)-1 - CHECK(json::parser("9007199254740991").parse().get() == 9007199254740991); - } - - SECTION("over the edge cases") // issue #178 - Integer conversion to unsigned (incorrect handling of 64 bit integers) - { - // While RFC7159, Section 6 specifies a preference for support - // for ranges in range of IEEE 754-2008 binary64 (double precision) - // this does not accommodate 64 bit integers without loss of accuracy. - // As 64 bit integers are now widely used in software, it is desirable - // to expand support to to the full 64 bit (signed and unsigned) range - // i.e. -(2**63) -> (2**64)-1. - - // -(2**63) ** Note: compilers see negative literals as negated positive numbers (hence the -1)) - CHECK(json::parser("-9223372036854775808").parse().get() == -9223372036854775807 - 1); - // (2**63)-1 - CHECK(json::parser("9223372036854775807").parse().get() == 9223372036854775807); - // (2**64)-1 - CHECK(json::parser("18446744073709551615").parse().get() == 18446744073709551615u); - } - } - - SECTION("floating-point") - { - SECTION("without exponent") - { - CHECK(json::parser("-128.5").parse() == json(-128.5)); - CHECK(json::parser("0.999").parse() == json(0.999)); - CHECK(json::parser("128.5").parse() == json(128.5)); - CHECK(json::parser("-0.0").parse() == json(-0.0)); - } - - SECTION("with exponent") - { - CHECK(json::parser("-128.5E3").parse() == json(-128.5E3)); - CHECK(json::parser("-128.5E-3").parse() == json(-128.5E-3)); - CHECK(json::parser("-0.0e1").parse() == json(-0.0e1)); - CHECK(json::parser("-0.0E1").parse() == json(-0.0e1)); - } - } - - SECTION("invalid numbers") - { - CHECK_THROWS_AS(json::parser("01").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("--1").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("1.").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("1E").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("1E-").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("1.E1").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-1E").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-0E#").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-0E-#").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-0#").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-0.0:").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-0.0Z").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-0E123:").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-0e0-:").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-0e-:").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-0f").parse(), std::invalid_argument); - - // numbers must not begin with "+" - CHECK_THROWS_AS(json::parser("+1").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("+0").parse(), std::invalid_argument); - - CHECK_THROWS_WITH(json::parser("01").parse(), - "parse error - unexpected number literal; expected end of input"); - CHECK_THROWS_WITH(json::parser("--1").parse(), "parse error - unexpected '-'"); - CHECK_THROWS_WITH(json::parser("1.").parse(), - "parse error - unexpected '.'; expected end of input"); - CHECK_THROWS_WITH(json::parser("1E").parse(), - "parse error - unexpected 'E'; expected end of input"); - CHECK_THROWS_WITH(json::parser("1E-").parse(), - "parse error - unexpected 'E'; expected end of input"); - CHECK_THROWS_WITH(json::parser("1.E1").parse(), - "parse error - unexpected '.'; expected end of input"); - CHECK_THROWS_WITH(json::parser("-1E").parse(), - "parse error - unexpected 'E'; expected end of input"); - CHECK_THROWS_WITH(json::parser("-0E#").parse(), - "parse error - unexpected 'E'; expected end of input"); - CHECK_THROWS_WITH(json::parser("-0E-#").parse(), - "parse error - unexpected 'E'; expected end of input"); - CHECK_THROWS_WITH(json::parser("-0#").parse(), - "parse error - unexpected '#'; expected end of input"); - CHECK_THROWS_WITH(json::parser("-0.0:").parse(), - "parse error - unexpected ':'; expected end of input"); - CHECK_THROWS_WITH(json::parser("-0.0Z").parse(), - "parse error - unexpected 'Z'; expected end of input"); - CHECK_THROWS_WITH(json::parser("-0E123:").parse(), - "parse error - unexpected ':'; expected end of input"); - CHECK_THROWS_WITH(json::parser("-0e0-:").parse(), - "parse error - unexpected '-'; expected end of input"); - CHECK_THROWS_WITH(json::parser("-0e-:").parse(), - "parse error - unexpected 'e'; expected end of input"); - CHECK_THROWS_WITH(json::parser("-0f").parse(), - "parse error - unexpected 'f'; expected end of input"); - } - } - } - - SECTION("parse errors") - { - // unexpected end of number - CHECK_THROWS_AS(json::parser("0.").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("--").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-0.").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-.").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("-:").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("0.:").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("e.").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("1e.").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("1e/").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("1e:").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("1E.").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("1E/").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("1E:").parse(), std::invalid_argument); - CHECK_THROWS_WITH(json::parser("0.").parse(), - "parse error - unexpected '.'; expected end of input"); - CHECK_THROWS_WITH(json::parser("-").parse(), "parse error - unexpected '-'"); - CHECK_THROWS_WITH(json::parser("--").parse(), - "parse error - unexpected '-'"); - CHECK_THROWS_WITH(json::parser("-0.").parse(), - "parse error - unexpected '.'; expected end of input"); - CHECK_THROWS_WITH(json::parser("-.").parse(), - "parse error - unexpected '-'"); - CHECK_THROWS_WITH(json::parser("-:").parse(), - "parse error - unexpected '-'"); - CHECK_THROWS_WITH(json::parser("0.:").parse(), - "parse error - unexpected '.'; expected end of input"); - CHECK_THROWS_WITH(json::parser("e.").parse(), - "parse error - unexpected 'e'"); - CHECK_THROWS_WITH(json::parser("1e.").parse(), - "parse error - unexpected 'e'; expected end of input"); - CHECK_THROWS_WITH(json::parser("1e/").parse(), - "parse error - unexpected 'e'; expected end of input"); - CHECK_THROWS_WITH(json::parser("1e:").parse(), - "parse error - unexpected 'e'; expected end of input"); - CHECK_THROWS_WITH(json::parser("1E.").parse(), - "parse error - unexpected 'E'; expected end of input"); - CHECK_THROWS_WITH(json::parser("1E/").parse(), - "parse error - unexpected 'E'; expected end of input"); - CHECK_THROWS_WITH(json::parser("1E:").parse(), - "parse error - unexpected 'E'; expected end of input"); - - // unexpected end of null - CHECK_THROWS_AS(json::parser("n").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("nu").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("nul").parse(), std::invalid_argument); - CHECK_THROWS_WITH(json::parser("n").parse(), "parse error - unexpected 'n'"); - CHECK_THROWS_WITH(json::parser("nu").parse(), - "parse error - unexpected 'n'"); - CHECK_THROWS_WITH(json::parser("nul").parse(), - "parse error - unexpected 'n'"); - - // unexpected end of true - CHECK_THROWS_AS(json::parser("t").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("tr").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("tru").parse(), std::invalid_argument); - CHECK_THROWS_WITH(json::parser("t").parse(), "parse error - unexpected 't'"); - CHECK_THROWS_WITH(json::parser("tr").parse(), - "parse error - unexpected 't'"); - CHECK_THROWS_WITH(json::parser("tru").parse(), - "parse error - unexpected 't'"); - - // unexpected end of false - CHECK_THROWS_AS(json::parser("f").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("fa").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("fal").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("fals").parse(), std::invalid_argument); - CHECK_THROWS_WITH(json::parser("f").parse(), "parse error - unexpected 'f'"); - CHECK_THROWS_WITH(json::parser("fa").parse(), - "parse error - unexpected 'f'"); - CHECK_THROWS_WITH(json::parser("fal").parse(), - "parse error - unexpected 'f'"); - CHECK_THROWS_WITH(json::parser("fals").parse(), - "parse error - unexpected 'f'"); - - // missing/unexpected end of array - CHECK_THROWS_AS(json::parser("[").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("[1").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("[1,").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("[1,]").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("]").parse(), std::invalid_argument); - CHECK_THROWS_WITH(json::parser("[").parse(), - "parse error - unexpected end of input"); - CHECK_THROWS_WITH(json::parser("[1").parse(), - "parse error - unexpected end of input; expected ']'"); - CHECK_THROWS_WITH(json::parser("[1,").parse(), - "parse error - unexpected end of input"); - CHECK_THROWS_WITH(json::parser("[1,]").parse(), - "parse error - unexpected ']'"); - CHECK_THROWS_WITH(json::parser("]").parse(), "parse error - unexpected ']'"); - - // missing/unexpected end of object - CHECK_THROWS_AS(json::parser("{").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("{\"foo\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("{\"foo\":").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("{\"foo\":}").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("{\"foo\":1,}").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("}").parse(), std::invalid_argument); - CHECK_THROWS_WITH(json::parser("{").parse(), - "parse error - unexpected end of input; expected string literal"); - CHECK_THROWS_WITH(json::parser("{\"foo\"").parse(), - "parse error - unexpected end of input; expected ':'"); - CHECK_THROWS_WITH(json::parser("{\"foo\":").parse(), - "parse error - unexpected end of input"); - CHECK_THROWS_WITH(json::parser("{\"foo\":}").parse(), - "parse error - unexpected '}'"); - CHECK_THROWS_WITH(json::parser("{\"foo\":1,}").parse(), - "parse error - unexpected '}'; expected string literal"); - CHECK_THROWS_WITH(json::parser("}").parse(), "parse error - unexpected '}'"); - - // missing/unexpected end of string - CHECK_THROWS_AS(json::parser("\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\\\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\\u\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\\u0\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\\u01\"").parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser("\"\\u012\"").parse(), std::invalid_argument); - CHECK_THROWS_WITH(json::parser("\"").parse(), - "parse error - unexpected '\"'"); - CHECK_THROWS_WITH(json::parser("\"\\\"").parse(), - "parse error - unexpected '\"'"); - CHECK_THROWS_WITH(json::parser("\"\\u\"").parse(), - "parse error - unexpected '\"'"); - CHECK_THROWS_WITH(json::parser("\"\\u0\"").parse(), - "parse error - unexpected '\"'"); - CHECK_THROWS_WITH(json::parser("\"\\u01\"").parse(), - "parse error - unexpected '\"'"); - CHECK_THROWS_WITH(json::parser("\"\\u012\"").parse(), - "parse error - unexpected '\"'"); - - // invalid escapes - for (int c = 1; c < 128; ++c) - { - auto s = std::string("\"\\") + std::string(1, c) + "\""; - - switch (c) - { - // valid escapes - case ('"'): - case ('\\'): - case ('/'): - case ('b'): - case ('f'): - case ('n'): - case ('r'): - case ('t'): - { - CHECK_NOTHROW(json::parser(s).parse()); - break; - } - - // \u must be followed with four numbers, so we skip it here - case ('u'): - { - break; - } - - // any other combination of backslash and character is invalid - default: - { - CHECK_THROWS_AS(json::parser(s).parse(), std::invalid_argument); - CHECK_THROWS_WITH(json::parser(s).parse(), "parse error - unexpected '\"'"); - break; - } - } - } - - // invalid \uxxxx escapes - { - // check whether character is a valid hex character - const auto valid = [](int c) - { - switch (c) - { - case ('0'): - case ('1'): - case ('2'): - case ('3'): - case ('4'): - case ('5'): - case ('6'): - case ('7'): - case ('8'): - case ('9'): - case ('a'): - case ('b'): - case ('c'): - case ('d'): - case ('e'): - case ('f'): - case ('A'): - case ('B'): - case ('C'): - case ('D'): - case ('E'): - case ('F'): - { - return true; - } - - default: - { - return false; - } - } - }; - - for (int c = 1; c < 128; ++c) - { - std::string s = "\"\\u"; - - // create a string with the iterated character at each position - auto s1 = s + "000" + std::string(1, c) + "\""; - auto s2 = s + "00" + std::string(1, c) + "0\""; - auto s3 = s + "0" + std::string(1, c) + "00\""; - auto s4 = s + std::string(1, c) + "000\""; - - if (valid(c)) - { - CHECK_NOTHROW(json::parser(s1).parse()); - CHECK_NOTHROW(json::parser(s2).parse()); - CHECK_NOTHROW(json::parser(s3).parse()); - CHECK_NOTHROW(json::parser(s4).parse()); - } - else - { - CHECK_THROWS_AS(json::parser(s1).parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser(s2).parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser(s3).parse(), std::invalid_argument); - CHECK_THROWS_AS(json::parser(s4).parse(), std::invalid_argument); - - CHECK_THROWS_WITH(json::parser(s1).parse(), "parse error - unexpected '\"'"); - CHECK_THROWS_WITH(json::parser(s2).parse(), "parse error - unexpected '\"'"); - CHECK_THROWS_WITH(json::parser(s3).parse(), "parse error - unexpected '\"'"); - CHECK_THROWS_WITH(json::parser(s4).parse(), "parse error - unexpected '\"'"); - } - } - } - - // missing part of a surrogate pair - CHECK_THROWS_AS(json::parse("\"\\uD80C\""), std::invalid_argument); - CHECK_THROWS_WITH(json::parse("\"\\uD80C\""), "missing low surrogate"); - // invalid surrogate pair - CHECK_THROWS_AS(json::parse("\"\\uD80C\\uD80C\""), std::invalid_argument); - CHECK_THROWS_AS(json::parse("\"\\uD80C\\u0000\""), std::invalid_argument); - CHECK_THROWS_AS(json::parse("\"\\uD80C\\uFFFF\""), std::invalid_argument); - CHECK_THROWS_WITH(json::parse("\"\\uD80C\\uD80C\""), - "missing or wrong low surrogate"); - CHECK_THROWS_WITH(json::parse("\"\\uD80C\\u0000\""), - "missing or wrong low surrogate"); - CHECK_THROWS_WITH(json::parse("\"\\uD80C\\uFFFF\""), - "missing or wrong low surrogate"); - } - - SECTION("callback function") - { - auto s_object = R"( - { - "foo": 2, - "bar": { - "baz": 1 - } - } - )"; - - auto s_array = R"( - [1,2,[3,4,5],4,5] - )"; - - SECTION("filter nothing") - { - json j_object = json::parse(s_object, [](int, json::parse_event_t, const json&) - { - return true; - }); - - CHECK (j_object == json({{"foo", 2}, {"bar", {{"baz", 1}}}})); - - json j_array = json::parse(s_array, [](int, json::parse_event_t, const json&) - { - return true; - }); - - CHECK (j_array == json({1, 2, {3, 4, 5}, 4, 5})); - } - - SECTION("filter everything") - { - json j_object = json::parse(s_object, [](int, json::parse_event_t, const json&) - { - return false; - }); - - // the top-level object will be discarded, leaving a null - CHECK (j_object.is_null()); - - json j_array = json::parse(s_array, [](int, json::parse_event_t, const json&) - { - return false; - }); - - // the top-level array will be discarded, leaving a null - CHECK (j_array.is_null()); - } - - SECTION("filter specific element") - { - json j_object = json::parse(s_object, [](int, json::parse_event_t, const json & j) - { - // filter all number(2) elements - if (j == json(2)) - { - return false; - } - else - { - return true; - } - }); - - CHECK (j_object == json({{"bar", {{"baz", 1}}}})); - - json j_array = json::parse(s_array, [](int, json::parse_event_t, const json & j) - { - if (j == json(2)) - { - return false; - } - else - { - return true; - } - }); - - CHECK (j_array == json({1, {3, 4, 5}, 4, 5})); - } - - SECTION("filter specific events") - { - SECTION("first closing event") - { - { - json j_object = json::parse(s_object, [](int, json::parse_event_t e, const json&) - { - static bool first = true; - if (e == json::parse_event_t::object_end and first) - { - first = false; - return false; - } - else - { - return true; - } - }); - - // the first completed object will be discarded - CHECK (j_object == json({{"foo", 2}})); - } - - { - json j_array = json::parse(s_array, [](int, json::parse_event_t e, const json&) - { - static bool first = true; - if (e == json::parse_event_t::array_end and first) - { - first = false; - return false; - } - else - { - return true; - } - }); - - // the first completed array will be discarded - CHECK (j_array == json({1, 2, 4, 5})); - } - } - } - - SECTION("special cases") - { - // the following test cases cover the situation in which an empty - // object and array is discarded only after the closing character - // has been read - - json j_empty_object = json::parse("{}", [](int, json::parse_event_t e, const json&) - { - if (e == json::parse_event_t::object_end) - { - return false; - } - else - { - return true; - } - }); - CHECK(j_empty_object == json()); - - json j_empty_array = json::parse("[]", [](int, json::parse_event_t e, const json&) - { - if (e == json::parse_event_t::array_end) - { - return false; - } - else - { - return true; - } - }); - CHECK(j_empty_array == json()); - } - } - - SECTION("copy constructor") - { - json::string_t* s = new json::string_t("[1,2,3,4]"); - json::parser p(*s); - delete s; - CHECK(p.parse() == json({1, 2, 3, 4})); - } -} - -TEST_CASE("README", "[hide]") -{ - { - // redirect std::cout for the README file - auto old_cout_buffer = std::cout.rdbuf(); - std::ostringstream new_stream; - std::cout.rdbuf(new_stream.rdbuf()); - { - // create an empty structure (null) - json j; - - // add a number that is stored as double (note the implicit conversion of j to an object) - j["pi"] = 3.141; - - // add a Boolean that is stored as bool - j["happy"] = true; - - // add a string that is stored as std::string - j["name"] = "Niels"; - - // add another null object by passing nullptr - j["nothing"] = nullptr; - - // add an object inside the object - j["answer"]["everything"] = 42; - - // add an array that is stored as std::vector (using an initializer list) - j["list"] = { 1, 0, 2 }; - - // add another object (using an initializer list of pairs) - j["object"] = { {"currency", "USD"}, {"value", 42.99} }; - - // instead, you could also write (which looks very similar to the JSON above) - json j2 = - { - {"pi", 3.141}, - {"happy", true}, - {"name", "Niels"}, - {"nothing", nullptr}, - { - "answer", { - {"everything", 42} - } - }, - {"list", {1, 0, 2}}, - { - "object", { - {"currency", "USD"}, - {"value", 42.99} - } - } - }; - } - - { - // ways to express the empty array [] - json empty_array_implicit = {{}}; - json empty_array_explicit = json::array(); - - // a way to express the empty object {} - json empty_object_explicit = json::object(); - - // a way to express an _array_ of key/value pairs [["currency", "USD"], ["value", 42.99]] - json array_not_object = { json::array({"currency", "USD"}), json::array({"value", 42.99}) }; - } - - { - // create object from string literal - json j = "{ \"happy\": true, \"pi\": 3.141 }"_json; - - // or even nicer with a raw string literal - auto j2 = R"( - { - "happy": true, - "pi": 3.141 - } - )"_json; - - // or explicitly - auto j3 = json::parse("{ \"happy\": true, \"pi\": 3.141 }"); - - // explicit conversion to string - std::string s = j.dump(); // {\"happy\":true,\"pi\":3.141} - - // serialization with pretty printing - // pass in the amount of spaces to indent - std::cout << j.dump(4) << std::endl; - // { - // "happy": true, - // "pi": 3.141 - // } - - std::cout << std::setw(2) << j << std::endl; - } - - { - // create an array using push_back - json j; - j.push_back("foo"); - j.push_back(1); - j.push_back(true); - - // iterate the array - for (json::iterator it = j.begin(); it != j.end(); ++it) - { - std::cout << *it << '\n'; - } - - // range-based for - for (auto element : j) - { - std::cout << element << '\n'; - } - - // getter/setter - const std::string tmp = j[0]; - j[1] = 42; - bool foo = j.at(2); - - // other stuff - j.size(); // 3 entries - j.empty(); // false - j.type(); // json::value_t::array - j.clear(); // the array is empty again - - // comparison - j == "[\"foo\", 1, true]"_json; // true - - // create an object - json o; - o["foo"] = 23; - o["bar"] = false; - o["baz"] = 3.141; - - // find an entry - if (o.find("foo") != o.end()) - { - // there is an entry with key "foo" - } - } - - { - std::vector c_vector {1, 2, 3, 4}; - json j_vec(c_vector); - // [1, 2, 3, 4] - - std::deque c_deque {1.2f, 2.3f, 3.4f, 5.6f}; - json j_deque(c_deque); - // [1.2, 2.3, 3.4, 5.6] - - std::list c_list {true, true, false, true}; - json j_list(c_list); - // [true, true, false, true] - - std::forward_list c_flist {12345678909876, 23456789098765, 34567890987654, 45678909876543}; - json j_flist(c_flist); - // [12345678909876, 23456789098765, 34567890987654, 45678909876543] - - std::array c_array {{1, 2, 3, 4}}; - json j_array(c_array); - // [1, 2, 3, 4] - - std::set c_set {"one", "two", "three", "four", "one"}; - json j_set(c_set); // only one entry for "one" is used - // ["four", "one", "three", "two"] - - std::unordered_set c_uset {"one", "two", "three", "four", "one"}; - json j_uset(c_uset); // only one entry for "one" is used - // maybe ["two", "three", "four", "one"] - - std::multiset c_mset {"one", "two", "one", "four"}; - json j_mset(c_mset); // only one entry for "one" is used - // maybe ["one", "two", "four"] - - std::unordered_multiset c_umset {"one", "two", "one", "four"}; - json j_umset(c_umset); // both entries for "one" are used - // maybe ["one", "two", "one", "four"] - } - - { - std::map c_map { {"one", 1}, {"two", 2}, {"three", 3} }; - json j_map(c_map); - // {"one": 1, "two": 2, "three": 3} - - std::unordered_map c_umap { {"one", 1.2f}, {"two", 2.3f}, {"three", 3.4f} }; - json j_umap(c_umap); - // {"one": 1.2, "two": 2.3, "three": 3.4} - - std::multimap c_mmap { {"one", true}, {"two", true}, {"three", false}, {"three", true} }; - json j_mmap(c_mmap); // only one entry for key "three" is used - // maybe {"one": true, "two": true, "three": true} - - std::unordered_multimap c_ummap { {"one", true}, {"two", true}, {"three", false}, {"three", true} }; - json j_ummap(c_ummap); // only one entry for key "three" is used - // maybe {"one": true, "two": true, "three": true} - } - - { - // strings - std::string s1 = "Hello, world!"; - json js = s1; - std::string s2 = js; - - // Booleans - bool b1 = true; - json jb = b1; - bool b2 = jb; - - // numbers - int i = 42; - json jn = i; - double f = jn; - - // etc. - - std::string vs = js.get(); - bool vb = jb.get(); - int vi = jn.get(); - - // etc. - } - - { - // a JSON value - json j_original = R"({ - "baz": ["one", "two", "three"], - "foo": "bar" - })"_json; - - // access members with a JSON pointer (RFC 6901) - j_original["/baz/1"_json_pointer]; - // "two" - - // a JSON patch (RFC 6902) - json j_patch = R"([ - { "op": "replace", "path": "/baz", "value": "boo" }, - { "op": "add", "path": "/hello", "value": ["world"] }, - { "op": "remove", "path": "/foo"} - ])"_json; - - // apply the patch - json j_result = j_original.patch(j_patch); - // { - // "baz": "boo", - // "hello": ["world"] - // } - - // calculate a JSON patch from two JSON values - json::diff(j_result, j_original); - // [ - // { "op":" replace", "path": "/baz", "value": ["one", "two", "three"] }, - // { "op":"remove","path":"/hello" }, - // { "op":"add","path":"/foo","value":"bar" } - // ] - } - - // restore old std::cout - std::cout.rdbuf(old_cout_buffer); - } -} - -TEST_CASE("algorithms") -{ - json j_array = {13, 29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz"}; - json j_object = {{"one", 1}, {"two", 2}}; - - SECTION("non-modifying sequence operations") - { - SECTION("std::all_of") - { - CHECK(std::all_of(j_array.begin(), j_array.end(), [](const json & value) - { - return value.size() > 0; - })); - CHECK(std::all_of(j_object.begin(), j_object.end(), [](const json & value) - { - return value.type() == json::value_t::number_integer; - })); - } - - SECTION("std::any_of") - { - CHECK(std::any_of(j_array.begin(), j_array.end(), [](const json & value) - { - return value.is_string() and value.get() == "foo"; - })); - CHECK(std::any_of(j_object.begin(), j_object.end(), [](const json & value) - { - return value.get() > 1; - })); - } - - SECTION("std::none_of") - { - CHECK(std::none_of(j_array.begin(), j_array.end(), [](const json & value) - { - return value.size() == 0; - })); - CHECK(std::none_of(j_object.begin(), j_object.end(), [](const json & value) - { - return value.get() <= 0; - })); - } - - SECTION("std::for_each") - { - SECTION("reading") - { - int sum = 0; - - std::for_each(j_array.cbegin(), j_array.cend(), [&sum](const json & value) - { - if (value.is_number()) - { - sum += static_cast(value); - } - }); - - CHECK(sum == 45); - } - - SECTION("writing") - { - auto add17 = [](json & value) - { - if (value.is_array()) - { - value.push_back(17); - } - }; - - std::for_each(j_array.begin(), j_array.end(), add17); - - CHECK(j_array[6] == json({1, 2, 3, 17})); - } - } - - SECTION("std::count") - { - CHECK(std::count(j_array.begin(), j_array.end(), json(true)) == 1); - } - - SECTION("std::count_if") - { - CHECK(std::count_if(j_array.begin(), j_array.end(), [](const json & value) - { - return (value.is_number()); - }) == 3); - CHECK(std::count_if(j_array.begin(), j_array.end(), [](const json&) - { - return true; - }) == 9); - } - - SECTION("std::mismatch") - { - json j_array2 = {13, 29, 3, {{"one", 1}, {"two", 2}, {"three", 3}}, true, false, {1, 2, 3}, "foo", "baz"}; - auto res = std::mismatch(j_array.begin(), j_array.end(), j_array2.begin()); - CHECK(*res.first == json({{"one", 1}, {"two", 2}})); - CHECK(*res.second == json({{"one", 1}, {"two", 2}, {"three", 3}})); - } - - SECTION("std::equal") - { - SECTION("using operator==") - { - CHECK(std::equal(j_array.begin(), j_array.end(), j_array.begin())); - CHECK(std::equal(j_object.begin(), j_object.end(), j_object.begin())); - CHECK(not std::equal(j_array.begin(), j_array.end(), j_object.begin())); - } - - SECTION("using user-defined comparison") - { - // compare objects only by size of its elements - json j_array2 = {13, 29, 3, {"Hello", "World"}, true, false, {{"one", 1}, {"two", 2}, {"three", 3}}, "foo", "baz"}; - CHECK(not std::equal(j_array.begin(), j_array.end(), j_array2.begin())); - CHECK(std::equal(j_array.begin(), j_array.end(), j_array2.begin(), - [](const json & a, const json & b) - { - return (a.size() == b.size()); - })); - } - } - - SECTION("std::find") - { - auto it = std::find(j_array.begin(), j_array.end(), json(false)); - CHECK(std::distance(j_array.begin(), it) == 5); - } - - SECTION("std::find_if") - { - auto it = std::find_if(j_array.begin(), j_array.end(), - [](const json & value) - { - return value.is_boolean(); - }); - CHECK(std::distance(j_array.begin(), it) == 4); - } - - SECTION("std::find_if_not") - { - auto it = std::find_if_not(j_array.begin(), j_array.end(), - [](const json & value) - { - return value.is_number(); - }); - CHECK(std::distance(j_array.begin(), it) == 3); - } - - SECTION("std::adjacent_find") - { - CHECK(std::adjacent_find(j_array.begin(), j_array.end()) == j_array.end()); - CHECK(std::adjacent_find(j_array.begin(), j_array.end(), - [](const json & v1, const json & v2) - { - return v1.type() == v2.type(); - }) == j_array.begin()); - } - } - - SECTION("modifying sequence operations") - { - SECTION("std::reverse") - { - std::reverse(j_array.begin(), j_array.end()); - CHECK(j_array == json({"baz", "foo", {1, 2, 3}, false, true, {{"one", 1}, {"two", 2}}, 3, 29, 13})); - } - - SECTION("std::rotate") - { - std::rotate(j_array.begin(), j_array.begin() + 1, j_array.end()); - CHECK(j_array == json({29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz", 13})); - } - - SECTION("std::partition") - { - auto it = std::partition(j_array.begin(), j_array.end(), [](const json & v) - { - return v.is_string(); - }); - CHECK(std::distance(j_array.begin(), it) == 2); - CHECK(not it[2].is_string()); - } - } - - SECTION("sorting operations") - { - SECTION("std::sort") - { - SECTION("with standard comparison") - { - json j = {13, 29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz", nullptr}; - std::sort(j.begin(), j.end()); - CHECK(j == json({nullptr, false, true, 3, 13, 29, {{"one", 1}, {"two", 2}}, {1, 2, 3}, "baz", "foo"})); - } - - SECTION("with user-defined comparison") - { - json j = {3, {{"one", 1}, {"two", 2}}, {1, 2, 3}, nullptr}; - std::sort(j.begin(), j.end(), [](const json & a, const json & b) - { - return a.size() < b.size(); - }); - CHECK(j == json({nullptr, 3, {{"one", 1}, {"two", 2}}, {1, 2, 3}})); - } - - SECTION("sorting an object") - { - json j({{"one", 1}, {"two", 2}}); - CHECK_THROWS_AS(std::sort(j.begin(), j.end()), std::domain_error); - CHECK_THROWS_WITH(std::sort(j.begin(), j.end()), "cannot use offsets with object iterators"); - } - } - - SECTION("std::partial_sort") - { - json j = {13, 29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz", nullptr}; - std::partial_sort(j.begin(), j.begin() + 4, j.end()); - CHECK(j == json({nullptr, false, true, 3, {{"one", 1}, {"two", 2}}, 29, {1, 2, 3}, "foo", "baz", 13})); - } - } - - SECTION("set operations") - { - SECTION("std::merge") - { - { - json j1 = {2, 4, 6, 8}; - json j2 = {1, 2, 3, 5, 7}; - json j3; - - std::merge(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3)); - CHECK(j3 == json({1, 2, 2, 3, 4, 5, 6, 7, 8})); - } - } - - SECTION("std::set_difference") - { - json j1 = {1, 2, 3, 4, 5, 6, 7, 8}; - json j2 = {1, 2, 3, 5, 7}; - json j3; - - std::set_difference(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3)); - CHECK(j3 == json({4, 6, 8})); - } - - SECTION("std::set_intersection") - { - json j1 = {1, 2, 3, 4, 5, 6, 7, 8}; - json j2 = {1, 2, 3, 5, 7}; - json j3; - - std::set_intersection(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3)); - CHECK(j3 == json({1, 2, 3, 5, 7})); - } - - SECTION("std::set_union") - { - json j1 = {2, 4, 6, 8}; - json j2 = {1, 2, 3, 5, 7}; - json j3; - - std::set_union(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3)); - CHECK(j3 == json({1, 2, 3, 4, 5, 6, 7, 8})); - } - - SECTION("std::set_symmetric_difference") - { - json j1 = {2, 4, 6, 8}; - json j2 = {1, 2, 3, 5, 7}; - json j3; - - std::set_symmetric_difference(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3)); - CHECK(j3 == json({1, 3, 4, 5, 6, 7, 8})); - } - } - - SECTION("heap operations") - { - std::make_heap(j_array.begin(), j_array.end()); - CHECK(std::is_heap(j_array.begin(), j_array.end())); - std::sort_heap(j_array.begin(), j_array.end()); - CHECK(j_array == json({false, true, 3, 13, 29, {{"one", 1}, {"two", 2}}, {1, 2, 3}, "baz", "foo"})); - } -} - -TEST_CASE("concepts") -{ - SECTION("container requirements for json") - { - // X: container class: json - // T: type of objects: json - // a, b: values of type X: json - - // TABLE 96 - Container Requirements - - // X::value_type must return T - CHECK((std::is_same::value)); - - // X::reference must return lvalue of T - CHECK((std::is_same::value)); - - // X::const_reference must return const lvalue of T - CHECK((std::is_same::value)); - - // X::iterator must return iterator whose value_type is T - CHECK((std::is_same::value)); - // X::iterator must meet the forward iterator requirements - CHECK((std::is_base_of::iterator_category>::value)); - // X::iterator must be convertible to X::const_iterator - CHECK((std::is_convertible::value)); - - // X::const_iterator must return iterator whose value_type is T - CHECK((std::is_same::value)); - // X::const_iterator must meet the forward iterator requirements - CHECK((std::is_base_of::iterator_category>::value)); - - // X::difference_type must return a signed integer - CHECK((std::is_signed::value)); - // X::difference_type must be identical to X::iterator::difference_type - CHECK((std::is_same::value)); - // X::difference_type must be identical to X::const_iterator::difference_type - CHECK((std::is_same::value)); - - // X::size_type must return an unsigned integer - CHECK((std::is_unsigned::value)); - // X::size_type can represent any non-negative value of X::difference_type - CHECK(std::numeric_limits::max() <= - std::numeric_limits::max()); - - // the expression "X u" has the post-condition "u.empty()" - { - json u; - CHECK(u.empty()); - } - - // the expression "X()" has the post-condition "X().empty()" - CHECK(json().empty()); - } - - SECTION("class json") - { - SECTION("DefaultConstructible") - { - CHECK(std::is_nothrow_default_constructible::value); - } - - SECTION("MoveConstructible") - { - CHECK(std::is_nothrow_move_constructible::value); - } - - SECTION("CopyConstructible") - { - CHECK(std::is_copy_constructible::value); - } - - SECTION("MoveAssignable") - { - CHECK(std::is_nothrow_move_assignable::value); - } - - SECTION("CopyAssignable") - { - CHECK(std::is_copy_assignable::value); - } - - SECTION("Destructible") - { - CHECK(std::is_nothrow_destructible::value); - } - - SECTION("StandardLayoutType") - { - CHECK(std::is_standard_layout::value); - } - } - - SECTION("class iterator") - { - SECTION("CopyConstructible") - { - CHECK(std::is_nothrow_copy_constructible::value); - CHECK(std::is_nothrow_copy_constructible::value); - } - - SECTION("CopyAssignable") - { - // STL iterators used by json::iterator don't pass this test in Debug mode -#if !defined(_MSC_VER) || (_ITERATOR_DEBUG_LEVEL == 0) - CHECK(std::is_nothrow_copy_assignable::value); - CHECK(std::is_nothrow_copy_assignable::value); -#endif - } - - SECTION("Destructible") - { - CHECK(std::is_nothrow_destructible::value); - CHECK(std::is_nothrow_destructible::value); - } - - SECTION("Swappable") - { - { - json j {1, 2, 3}; - json::iterator it1 = j.begin(); - json::iterator it2 = j.end(); - std::swap(it1, it2); - CHECK(it1 == j.end()); - CHECK(it2 == j.begin()); - } - { - json j {1, 2, 3}; - json::const_iterator it1 = j.cbegin(); - json::const_iterator it2 = j.cend(); - std::swap(it1, it2); - CHECK(it1 == j.end()); - CHECK(it2 == j.begin()); - } - } - } -} - -TEST_CASE("iterator_wrapper") -{ - SECTION("object") - { - SECTION("value") - { - json j = {{"A", 1}, {"B", 2}}; - int counter = 1; - - for (auto i : json::iterator_wrapper(j)) - { - switch (counter++) - { - case 1: - { - CHECK(i.key() == "A"); - CHECK(i.value() == json(1)); - break; - } - - case 2: - { - CHECK(i.key() == "B"); - CHECK(i.value() == json(2)); - break; - } - - default: - { - break; - } - } - } - - CHECK(counter == 3); - } - - SECTION("reference") - { - json j = {{"A", 1}, {"B", 2}}; - int counter = 1; - - for (auto& i : json::iterator_wrapper(j)) - { - switch (counter++) - { - case 1: - { - CHECK(i.key() == "A"); - CHECK(i.value() == json(1)); - - // change the value - i.value() = json(11); - CHECK(i.value() == json(11)); - break; - } - - case 2: - { - CHECK(i.key() == "B"); - CHECK(i.value() == json(2)); - - // change the value - i.value() = json(22); - CHECK(i.value() == json(22)); - break; - } - - default: - { - break; - } - } - } - - CHECK(counter == 3); - - // check if values where changed - CHECK(j == json({{"A", 11}, {"B", 22}})); - } - - SECTION("const value") - { - json j = {{"A", 1}, {"B", 2}}; - int counter = 1; - - for (const auto i : json::iterator_wrapper(j)) - { - switch (counter++) - { - case 1: - { - CHECK(i.key() == "A"); - CHECK(i.value() == json(1)); - break; - } - - case 2: - { - CHECK(i.key() == "B"); - CHECK(i.value() == json(2)); - break; - } - - default: - { - break; - } - } - } - - CHECK(counter == 3); - } - - SECTION("const reference") - { - json j = {{"A", 1}, {"B", 2}}; - int counter = 1; - - for (const auto& i : json::iterator_wrapper(j)) - { - switch (counter++) - { - case 1: - { - CHECK(i.key() == "A"); - CHECK(i.value() == json(1)); - break; - } - - case 2: - { - CHECK(i.key() == "B"); - CHECK(i.value() == json(2)); - break; - } - - default: - { - break; - } - } - } - - CHECK(counter == 3); - } - } - - SECTION("const object") - { - SECTION("value") - { - const json j = {{"A", 1}, {"B", 2}}; - int counter = 1; - - for (auto i : json::iterator_wrapper(j)) - { - switch (counter++) - { - case 1: - { - CHECK(i.key() == "A"); - CHECK(i.value() == json(1)); - break; - } - - case 2: - { - CHECK(i.key() == "B"); - CHECK(i.value() == json(2)); - break; - } - - default: - { - break; - } - } - } - - CHECK(counter == 3); - } - - SECTION("reference") - { - const json j = {{"A", 1}, {"B", 2}}; - int counter = 1; - - for (auto& i : json::iterator_wrapper(j)) - { - switch (counter++) - { - case 1: - { - CHECK(i.key() == "A"); - CHECK(i.value() == json(1)); - break; - } - - case 2: - { - CHECK(i.key() == "B"); - CHECK(i.value() == json(2)); - break; - } - - default: - { - break; - } - } - } - - CHECK(counter == 3); - } - - SECTION("const value") - { - const json j = {{"A", 1}, {"B", 2}}; - int counter = 1; - - for (const auto i : json::iterator_wrapper(j)) - { - switch (counter++) - { - case 1: - { - CHECK(i.key() == "A"); - CHECK(i.value() == json(1)); - break; - } - - case 2: - { - CHECK(i.key() == "B"); - CHECK(i.value() == json(2)); - break; - } - - default: - { - break; - } - } - } - - CHECK(counter == 3); - } - - SECTION("const reference") - { - const json j = {{"A", 1}, {"B", 2}}; - int counter = 1; - - for (const auto& i : json::iterator_wrapper(j)) - { - switch (counter++) - { - case 1: - { - CHECK(i.key() == "A"); - CHECK(i.value() == json(1)); - break; - } - - case 2: - { - CHECK(i.key() == "B"); - CHECK(i.value() == json(2)); - break; - } - - default: - { - break; - } - } - } - - CHECK(counter == 3); - } - } - - SECTION("array") - { - SECTION("value") - { - json j = {"A", "B"}; - int counter = 1; - - for (auto i : json::iterator_wrapper(j)) - { - switch (counter++) - { - case 1: - { - CHECK(i.key() == "0"); - CHECK(i.value() == "A"); - break; - } - - case 2: - { - CHECK(i.key() == "1"); - CHECK(i.value() == "B"); - break; - } - - default: - { - break; - } - } - } - - CHECK(counter == 3); - } - - SECTION("reference") - { - json j = {"A", "B"}; - int counter = 1; - - for (auto& i : json::iterator_wrapper(j)) - { - switch (counter++) - { - case 1: - { - CHECK(i.key() == "0"); - CHECK(i.value() == "A"); - - // change the value - i.value() = "AA"; - CHECK(i.value() == "AA"); - break; - } - - case 2: - { - CHECK(i.key() == "1"); - CHECK(i.value() == "B"); - - // change the value - i.value() = "BB"; - CHECK(i.value() == "BB"); - break; - } - - default: - { - break; - } - } - } - - CHECK(counter == 3); - - // check if values where changed - CHECK(j == json({"AA", "BB"})); - } - - SECTION("const value") - { - json j = {"A", "B"}; - int counter = 1; - - for (const auto i : json::iterator_wrapper(j)) - { - switch (counter++) - { - case 1: - { - CHECK(i.key() == "0"); - CHECK(i.value() == "A"); - break; - } - - case 2: - { - CHECK(i.key() == "1"); - CHECK(i.value() == "B"); - break; - } - - default: - { - break; - } - } - } - - CHECK(counter == 3); - } - - SECTION("const reference") - { - json j = {"A", "B"}; - int counter = 1; - - for (const auto& i : json::iterator_wrapper(j)) - { - switch (counter++) - { - case 1: - { - CHECK(i.key() == "0"); - CHECK(i.value() == "A"); - break; - } - - case 2: - { - CHECK(i.key() == "1"); - CHECK(i.value() == "B"); - break; - } - - default: - { - break; - } - } - } - - CHECK(counter == 3); - } - } - - SECTION("const array") - { - SECTION("value") - { - const json j = {"A", "B"}; - int counter = 1; - - for (auto i : json::iterator_wrapper(j)) - { - switch (counter++) - { - case 1: - { - CHECK(i.key() == "0"); - CHECK(i.value() == "A"); - break; - } - - case 2: - { - CHECK(i.key() == "1"); - CHECK(i.value() == "B"); - break; - } - - default: - { - break; - } - } - } - - CHECK(counter == 3); - } - - SECTION("reference") - { - const json j = {"A", "B"}; - int counter = 1; - - for (auto& i : json::iterator_wrapper(j)) - { - switch (counter++) - { - case 1: - { - CHECK(i.key() == "0"); - CHECK(i.value() == "A"); - break; - } - - case 2: - { - CHECK(i.key() == "1"); - CHECK(i.value() == "B"); - break; - } - - default: - { - break; - } - } - } - - CHECK(counter == 3); - } - - SECTION("const value") - { - const json j = {"A", "B"}; - int counter = 1; - - for (const auto i : json::iterator_wrapper(j)) - { - switch (counter++) - { - case 1: - { - CHECK(i.key() == "0"); - CHECK(i.value() == "A"); - break; - } - - case 2: - { - CHECK(i.key() == "1"); - CHECK(i.value() == "B"); - break; - } - - default: - { - break; - } - } - } - - CHECK(counter == 3); - } - - SECTION("const reference") - { - const json j = {"A", "B"}; - int counter = 1; - - for (const auto& i : json::iterator_wrapper(j)) - { - switch (counter++) - { - case 1: - { - CHECK(i.key() == "0"); - CHECK(i.value() == "A"); - break; - } - - case 2: - { - CHECK(i.key() == "1"); - CHECK(i.value() == "B"); - break; - } - - default: - { - break; - } - } - } - - CHECK(counter == 3); - } - } - - SECTION("primitive") - { - SECTION("value") - { - json j = 1; - int counter = 1; - - for (auto i : json::iterator_wrapper(j)) - { - ++counter; - CHECK(i.key() == ""); - CHECK(i.value() == json(1)); - } - - CHECK(counter == 2); - } - - SECTION("reference") - { - json j = 1; - int counter = 1; - - for (auto& i : json::iterator_wrapper(j)) - { - ++counter; - CHECK(i.key() == ""); - CHECK(i.value() == json(1)); - - // change value - i.value() = json(2); - } - - CHECK(counter == 2); - - // check if value has changed - CHECK(j == json(2)); - } - - SECTION("const value") - { - json j = 1; - int counter = 1; - - for (const auto i : json::iterator_wrapper(j)) - { - ++counter; - CHECK(i.key() == ""); - CHECK(i.value() == json(1)); - } - - CHECK(counter == 2); - } - - SECTION("const reference") - { - json j = 1; - int counter = 1; - - for (const auto& i : json::iterator_wrapper(j)) - { - ++counter; - CHECK(i.key() == ""); - CHECK(i.value() == json(1)); - } - - CHECK(counter == 2); - } - } - - SECTION("const primitive") - { - SECTION("value") - { - const json j = 1; - int counter = 1; - - for (auto i : json::iterator_wrapper(j)) - { - ++counter; - CHECK(i.key() == ""); - CHECK(i.value() == json(1)); - } - - CHECK(counter == 2); - } - - SECTION("reference") - { - const json j = 1; - int counter = 1; - - for (auto& i : json::iterator_wrapper(j)) - { - ++counter; - CHECK(i.key() == ""); - CHECK(i.value() == json(1)); - } - - CHECK(counter == 2); - } - - SECTION("const value") - { - const json j = 1; - int counter = 1; - - for (const auto i : json::iterator_wrapper(j)) - { - ++counter; - CHECK(i.key() == ""); - CHECK(i.value() == json(1)); - } - - CHECK(counter == 2); - } - - SECTION("const reference") - { - const json j = 1; - int counter = 1; - - for (const auto& i : json::iterator_wrapper(j)) - { - ++counter; - CHECK(i.key() == ""); - CHECK(i.value() == json(1)); - } - - CHECK(counter == 2); - } - } -} - -TEST_CASE("compliance tests from json.org") -{ - // test cases are from http://json.org/JSON_checker/ - - SECTION("expected failures") - { - for (auto filename : - { - //"test/data/json_tests/fail1.json", - "test/data/json_tests/fail2.json", - "test/data/json_tests/fail3.json", - "test/data/json_tests/fail4.json", - "test/data/json_tests/fail5.json", - "test/data/json_tests/fail6.json", - "test/data/json_tests/fail7.json", - "test/data/json_tests/fail8.json", - "test/data/json_tests/fail9.json", - "test/data/json_tests/fail10.json", - "test/data/json_tests/fail11.json", - "test/data/json_tests/fail12.json", - "test/data/json_tests/fail13.json", - "test/data/json_tests/fail14.json", - "test/data/json_tests/fail15.json", - "test/data/json_tests/fail16.json", - "test/data/json_tests/fail17.json", - //"test/data/json_tests/fail18.json", - "test/data/json_tests/fail19.json", - "test/data/json_tests/fail20.json", - "test/data/json_tests/fail21.json", - "test/data/json_tests/fail22.json", - "test/data/json_tests/fail23.json", - "test/data/json_tests/fail24.json", - "test/data/json_tests/fail25.json", - "test/data/json_tests/fail26.json", - "test/data/json_tests/fail27.json", - "test/data/json_tests/fail28.json", - "test/data/json_tests/fail29.json", - "test/data/json_tests/fail30.json", - "test/data/json_tests/fail31.json", - "test/data/json_tests/fail32.json", - "test/data/json_tests/fail33.json" - }) - { - CAPTURE(filename); - json j; - std::ifstream f(filename); - CHECK_THROWS_AS(j << f, std::invalid_argument); - } - } - - SECTION("expected passes") - { - for (auto filename : - { - "test/data/json_tests/pass1.json", - "test/data/json_tests/pass2.json", - "test/data/json_tests/pass3.json" - }) - { - CAPTURE(filename); - json j; - std::ifstream f(filename); - CHECK_NOTHROW(j << f); - } - } -} - -TEST_CASE("compliance tests from nativejson-benchmark") -{ - // test cases from https://github.com/miloyip/nativejson-benchmark/blob/master/src/main.cpp - - SECTION("doubles") - { - auto TEST_DOUBLE = [](const std::string & json_string, const double expected) - { - CAPTURE(json_string); - CAPTURE(expected); - CHECK(json::parse(json_string)[0].get() == Approx(expected)); - }; - - TEST_DOUBLE("[0.0]", 0.0); - TEST_DOUBLE("[-0.0]", -0.0); - TEST_DOUBLE("[1.0]", 1.0); - TEST_DOUBLE("[-1.0]", -1.0); - TEST_DOUBLE("[1.5]", 1.5); - TEST_DOUBLE("[-1.5]", -1.5); - TEST_DOUBLE("[3.1416]", 3.1416); - TEST_DOUBLE("[1E10]", 1E10); - TEST_DOUBLE("[1e10]", 1e10); - TEST_DOUBLE("[1E+10]", 1E+10); - TEST_DOUBLE("[1E-10]", 1E-10); - TEST_DOUBLE("[-1E10]", -1E10); - TEST_DOUBLE("[-1e10]", -1e10); - TEST_DOUBLE("[-1E+10]", -1E+10); - TEST_DOUBLE("[-1E-10]", -1E-10); - TEST_DOUBLE("[1.234E+10]", 1.234E+10); - TEST_DOUBLE("[1.234E-10]", 1.234E-10); - TEST_DOUBLE("[1.79769e+308]", 1.79769e+308); - TEST_DOUBLE("[2.22507e-308]", 2.22507e-308); - TEST_DOUBLE("[-1.79769e+308]", -1.79769e+308); - TEST_DOUBLE("[-2.22507e-308]", -2.22507e-308); - TEST_DOUBLE("[4.9406564584124654e-324]", 4.9406564584124654e-324); // minimum denormal - TEST_DOUBLE("[2.2250738585072009e-308]", 2.2250738585072009e-308); // Max subnormal double - TEST_DOUBLE("[2.2250738585072014e-308]", 2.2250738585072014e-308); // Min normal positive double - TEST_DOUBLE("[1.7976931348623157e+308]", 1.7976931348623157e+308); // Max double - TEST_DOUBLE("[1e-10000]", 0.0); // must underflow - TEST_DOUBLE("[18446744073709551616]", - 18446744073709551616.0); // 2^64 (max of uint64_t + 1, force to use double) - TEST_DOUBLE("[-9223372036854775809]", - -9223372036854775809.0); // -2^63 - 1(min of int64_t + 1, force to use double) - TEST_DOUBLE("[0.9868011474609375]", - 0.9868011474609375); // https://github.com/miloyip/rapidjson/issues/120 - TEST_DOUBLE("[123e34]", 123e34); // Fast Path Cases In Disguise - TEST_DOUBLE("[45913141877270640000.0]", 45913141877270640000.0); - TEST_DOUBLE("[2.2250738585072011e-308]", - 2.2250738585072011e-308); - //TEST_DOUBLE("[1e-00011111111111]", 0.0); - //TEST_DOUBLE("[-1e-00011111111111]", -0.0); - TEST_DOUBLE("[1e-214748363]", 0.0); - TEST_DOUBLE("[1e-214748364]", 0.0); - //TEST_DOUBLE("[1e-21474836311]", 0.0); - TEST_DOUBLE("[0.017976931348623157e+310]", 1.7976931348623157e+308); // Max double in another form - - // Since - // abs((2^-1022 - 2^-1074) - 2.2250738585072012e-308) = 3.109754131239141401123495768877590405345064751974375599... ¡Á 10^-324 - // abs((2^-1022) - 2.2250738585072012e-308) = 1.830902327173324040642192159804623318305533274168872044... ¡Á 10 ^ -324 - // So 2.2250738585072012e-308 should round to 2^-1022 = 2.2250738585072014e-308 - TEST_DOUBLE("[2.2250738585072012e-308]", - 2.2250738585072014e-308); - - // More closer to normal/subnormal boundary - // boundary = 2^-1022 - 2^-1075 = 2.225073858507201136057409796709131975934819546351645648... ¡Á 10^-308 - TEST_DOUBLE("[2.22507385850720113605740979670913197593481954635164564e-308]", - 2.2250738585072009e-308); - TEST_DOUBLE("[2.22507385850720113605740979670913197593481954635164565e-308]", - 2.2250738585072014e-308); - - // 1.0 is in (1.0 - 2^-54, 1.0 + 2^-53) - // 1.0 - 2^-54 = 0.999999999999999944488848768742172978818416595458984375 - TEST_DOUBLE("[0.999999999999999944488848768742172978818416595458984375]", 1.0); // round to even - TEST_DOUBLE("[0.999999999999999944488848768742172978818416595458984374]", - 0.99999999999999989); // previous double - TEST_DOUBLE("[0.999999999999999944488848768742172978818416595458984376]", 1.0); // next double - // 1.0 + 2^-53 = 1.00000000000000011102230246251565404236316680908203125 - TEST_DOUBLE("[1.00000000000000011102230246251565404236316680908203125]", 1.0); // round to even - TEST_DOUBLE("[1.00000000000000011102230246251565404236316680908203124]", 1.0); // previous double - TEST_DOUBLE("[1.00000000000000011102230246251565404236316680908203126]", - 1.00000000000000022); // next double - - // Numbers from https://github.com/floitsch/double-conversion/blob/master/test/cctest/test-strtod.cc - - TEST_DOUBLE("[72057594037927928.0]", 72057594037927928.0); - TEST_DOUBLE("[72057594037927936.0]", 72057594037927936.0); - TEST_DOUBLE("[72057594037927932.0]", 72057594037927936.0); - TEST_DOUBLE("[7205759403792793199999e-5]", 72057594037927928.0); - TEST_DOUBLE("[7205759403792793200001e-5]", 72057594037927936.0); - - TEST_DOUBLE("[9223372036854774784.0]", 9223372036854774784.0); - TEST_DOUBLE("[9223372036854775808.0]", 9223372036854775808.0); - TEST_DOUBLE("[9223372036854775296.0]", 9223372036854775808.0); - TEST_DOUBLE("[922337203685477529599999e-5]", 9223372036854774784.0); - TEST_DOUBLE("[922337203685477529600001e-5]", 9223372036854775808.0); - - TEST_DOUBLE("[10141204801825834086073718800384]", 10141204801825834086073718800384.0); - TEST_DOUBLE("[10141204801825835211973625643008]", 10141204801825835211973625643008.0); - TEST_DOUBLE("[10141204801825834649023672221696]", 10141204801825835211973625643008.0); - TEST_DOUBLE("[1014120480182583464902367222169599999e-5]", 10141204801825834086073718800384.0); - TEST_DOUBLE("[1014120480182583464902367222169600001e-5]", 10141204801825835211973625643008.0); - - TEST_DOUBLE("[5708990770823838890407843763683279797179383808]", - 5708990770823838890407843763683279797179383808.0); - TEST_DOUBLE("[5708990770823839524233143877797980545530986496]", - 5708990770823839524233143877797980545530986496.0); - TEST_DOUBLE("[5708990770823839207320493820740630171355185152]", - 5708990770823839524233143877797980545530986496.0); - TEST_DOUBLE("[5708990770823839207320493820740630171355185151999e-3]", - 5708990770823838890407843763683279797179383808.0); - TEST_DOUBLE("[5708990770823839207320493820740630171355185152001e-3]", - 5708990770823839524233143877797980545530986496.0); - - { - char n1e308[312]; // '1' followed by 308 '0' - n1e308[0] = '['; - n1e308[1] = '1'; - for (int j = 2; j < 310; j++) - { - n1e308[j] = '0'; - } - n1e308[310] = ']'; - n1e308[311] = '\0'; - TEST_DOUBLE(n1e308, 1E308); - } - - // Cover trimming - TEST_DOUBLE( - "[2.22507385850720113605740979670913197593481954635164564802342610972482222202107694551652952390813508" - "7914149158913039621106870086438694594645527657207407820621743379988141063267329253552286881372149012" - "9811224514518898490572223072852551331557550159143974763979834118019993239625482890171070818506906306" - "6665599493827577257201576306269066333264756530000924588831643303777979186961204949739037782970490505" - "1080609940730262937128958950003583799967207254304360284078895771796150945516748243471030702609144621" - "5722898802581825451803257070188608721131280795122334262883686223215037756666225039825343359745688844" - "2390026549819838548794829220689472168983109969836584681402285424333066033985088644580400103493397042" - "7567186443383770486037861622771738545623065874679014086723327636718751234567890123456789012345678901" - "e-308]", - 2.2250738585072014e-308); - } - - SECTION("strings") - { - auto TEST_STRING = [](const std::string & json_string, const std::string & expected) - { - CAPTURE(json_string); - CAPTURE(expected); - CHECK(json::parse(json_string)[0].get() == expected); - }; - - TEST_STRING("[\"\"]", ""); - TEST_STRING("[\"Hello\"]", "Hello"); - TEST_STRING("[\"Hello\\nWorld\"]", "Hello\nWorld"); - //TEST_STRING("[\"Hello\\u0000World\"]", "Hello\0World"); - TEST_STRING("[\"\\\"\\\\/\\b\\f\\n\\r\\t\"]", "\"\\/\b\f\n\r\t"); - TEST_STRING("[\"\\u0024\"]", "\x24"); // Dollar sign U+0024 - TEST_STRING("[\"\\u00A2\"]", "\xC2\xA2"); // Cents sign U+00A2 - TEST_STRING("[\"\\u20AC\"]", "\xE2\x82\xAC"); // Euro sign U+20AC - TEST_STRING("[\"\\uD834\\uDD1E\"]", "\xF0\x9D\x84\x9E"); // G clef sign U+1D11E - } - - SECTION("roundtrip") - { - // test cases are from https://github.com/miloyip/nativejson-benchmark/tree/master/test/data/roundtrip - - for (auto filename : - { - "test/data/json_roundtrip/roundtrip01.json", - "test/data/json_roundtrip/roundtrip02.json", - "test/data/json_roundtrip/roundtrip03.json", - "test/data/json_roundtrip/roundtrip04.json", - "test/data/json_roundtrip/roundtrip05.json", - "test/data/json_roundtrip/roundtrip06.json", - "test/data/json_roundtrip/roundtrip07.json", - "test/data/json_roundtrip/roundtrip08.json", - "test/data/json_roundtrip/roundtrip09.json", - "test/data/json_roundtrip/roundtrip10.json", - "test/data/json_roundtrip/roundtrip11.json", - "test/data/json_roundtrip/roundtrip12.json", - "test/data/json_roundtrip/roundtrip13.json", - "test/data/json_roundtrip/roundtrip14.json", - "test/data/json_roundtrip/roundtrip15.json", - "test/data/json_roundtrip/roundtrip16.json", - "test/data/json_roundtrip/roundtrip17.json", - "test/data/json_roundtrip/roundtrip18.json", - "test/data/json_roundtrip/roundtrip19.json", - "test/data/json_roundtrip/roundtrip20.json", - "test/data/json_roundtrip/roundtrip21.json", - "test/data/json_roundtrip/roundtrip22.json", - "test/data/json_roundtrip/roundtrip23.json", - //"test/data/json_roundtrip/roundtrip24.json", // roundtrip error - //"test/data/json_roundtrip/roundtrip25.json", // roundtrip error - //"test/data/json_roundtrip/roundtrip26.json", // roundtrip error - //"test/data/json_roundtrip/roundtrip27.json", // roundtrip error - //"test/data/json_roundtrip/roundtrip28.json", // roundtrip error - "test/data/json_roundtrip/roundtrip29.json", - //"test/data/json_roundtrip/roundtrip30.json", // roundtrip error - //"test/data/json_roundtrip/roundtrip31.json", // roundtrip error - "test/data/json_roundtrip/roundtrip32.json" - }) - { - CAPTURE(filename); - std::ifstream f(filename); - std::string json_string( (std::istreambuf_iterator(f) ), - (std::istreambuf_iterator()) ); - - json j = json::parse(json_string); - CHECK(j.dump() == json_string); - } - } -} - -TEST_CASE("test suite from json-test-suite") -{ - SECTION("read all sample.json") - { - // read a file with all unicode characters stored as single-character - // strings in a JSON array - std::ifstream f("test/data/json_testsuite/sample.json"); - json j; - CHECK_NOTHROW(j << f); - - // the array has 3 elements - CHECK(j.size() == 3); - } -} - -TEST_CASE("json.org examples") -{ - // here, we list all JSON values from http://json.org/example - - SECTION("1.json") - { - std::ifstream f("test/data/json.org/1.json"); - json j; - CHECK_NOTHROW(j << f); - } - - SECTION("2.json") - { - std::ifstream f("test/data/json.org/2.json"); - json j; - CHECK_NOTHROW(j << f); - } - - SECTION("3.json") - { - std::ifstream f("test/data/json.org/3.json"); - json j; - CHECK_NOTHROW(j << f); - } - - SECTION("4.json") - { - std::ifstream f("test/data/json.org/4.json"); - json j; - CHECK_NOTHROW(j << f); - } - - SECTION("5.json") - { - std::ifstream f("test/data/json.org/5.json"); - json j; - CHECK_NOTHROW(j << f); - } -} - -TEST_CASE("RFC 7159 examples") -{ - // here, we list all JSON values from the RFC 7159 document - - SECTION("7. Strings") - { - CHECK(json::parse("\"\\u005C\"") == json("\\")); - CHECK(json::parse("\"\\uD834\\uDD1E\"") == json("𝄞")); - CHECK(json::parse("\"𝄞\"") == json("𝄞")); - } - - SECTION("8.3 String Comparison") - { - CHECK(json::parse("\"a\\b\"") == json::parse("\"a\u005Cb\"")); - } - - SECTION("13 Examples") - { - { - CHECK_NOTHROW(json(R"( - { - "Image": { - "Width": 800, - "Height": 600, - "Title": "View from 15th Floor", - "Thumbnail": { - "Url": "http://www.example.com/image/481989943", - "Height": 125, - "Width": 100 - }, - "Animated" : false, - "IDs": [116, 943, 234, 38793] - } - } - )")); - } - - { - CHECK_NOTHROW(json(R"( - [ - { - "precision": "zip", - "Latitude": 37.7668, - "Longitude": -122.3959, - "Address": "", - "City": "SAN FRANCISCO", - "State": "CA", - "Zip": "94107", - "Country": "US" - }, - { - "precision": "zip", - "Latitude": 37.371991, - "Longitude": -122.026020, - "Address": "", - "City": "SUNNYVALE", - "State": "CA", - "Zip": "94085", - "Country": "US" - } - ])")); - } - - CHECK(json::parse("\"Hello world!\"") == json("Hello world!")); - CHECK(json::parse("42") == json(42)); - CHECK(json::parse("true") == json(true)); - } -} - -TEST_CASE("Unicode", "[hide]") -{ - SECTION("full enumeration of Unicode code points") - { - // create an escaped string from a code point - const auto codepoint_to_unicode = [](std::size_t cp) - { - // copd points are represented as a six-character sequence: a - // reverse solidus, followed by the lowercase letter u, followed - // by four hexadecimal digits that encode the character's code - // point - std::stringstream ss; - ss << "\\u" << std::setw(4) << std::setfill('0') << std::hex << cp; - return ss.str(); - }; - - // generate all UTF-8 code points; in total, 1112064 code points are - // generated: 0x1FFFFF code points - 2048 invalid values between - // 0xD800 and 0xDFFF. - for (std::size_t cp = 0; cp <= 0x10FFFFu; ++cp) - { - // The Unicode standard permanently reserves these code point - // values for UTF-16 encoding of the high and low surrogates, and - // they will never be assigned a character, so there should be no - // reason to encode them. The official Unicode standard says that - // no UTF forms, including UTF-16, can encode these code points. - if (cp >= 0xD800u and cp <= 0xDFFFu) - { - // if we would not skip these code points, we would get a - // "missing low surrogate" exception - continue; - } - - // string to store the code point as in \uxxxx format - std::string escaped_string; - // string to store the code point as unescaped character sequence - std::string unescaped_string; - - if (cp < 0x10000u) - { - // code points in the Basic Multilingual Plane can be - // represented with one \\uxxxx sequence - escaped_string = codepoint_to_unicode(cp); - - // All Unicode characters may be placed within the quotation - // marks, except for the characters that must be escaped: - // quotation mark, reverse solidus, and the control characters - // (U+0000 through U+001F); we ignore these code points as - // they are checked with codepoint_to_unicode. - if (cp > 0x1f and cp != 0x22 and cp != 0x5c) - { - unescaped_string = json::lexer::to_unicode(cp); - } - } - else - { - // To escape an extended character that is not in the Basic - // Multilingual Plane, the character is represented as a - // 12-character sequence, encoding the UTF-16 surrogate pair - const auto codepoint1 = 0xd800u + (((cp - 0x10000u) >> 10) & 0x3ffu); - const auto codepoint2 = 0xdc00u + ((cp - 0x10000u) & 0x3ffu); - escaped_string = codepoint_to_unicode(codepoint1); - escaped_string += codepoint_to_unicode(codepoint2); - unescaped_string += json::lexer::to_unicode(codepoint1, codepoint2); - } - - // all other code points are valid and must not yield parse errors - CAPTURE(cp); - CAPTURE(escaped_string); - CAPTURE(unescaped_string); - - json j1, j2, j3, j4; - CHECK_NOTHROW(j1 = json::parse("\"" + escaped_string + "\"")); - CHECK_NOTHROW(j2 = json::parse(j1.dump())); - CHECK(j1 == j2); - - CHECK_NOTHROW(j3 = json::parse("\"" + unescaped_string + "\"")); - CHECK_NOTHROW(j4 = json::parse(j3.dump())); - CHECK(j3 == j4); - } - } - - SECTION("read all unicode characters") - { - // read a file with all unicode characters stored as single-character - // strings in a JSON array - std::ifstream f("test/data/json_nlohmann_tests/all_unicode.json"); - json j; - CHECK_NOTHROW(j << f); - - // the array has 1112064 + 1 elemnts (a terminating "null" value) - // Note: 1112064 = 0x1FFFFF code points - 2048 invalid values between - // 0xD800 and 0xDFFF. - CHECK(j.size() == 1112065); - - SECTION("check JSON Pointers") - { - for (auto s : j) - { - // skip non-string JSON values - if (not s.is_string()) - { - continue; - } - - std::string ptr = s; - - // tilde must be followed by 0 or 1 - if (ptr == "~") - { - ptr += "0"; - } - - // JSON Pointers must begin with "/" - ptr = "/" + ptr; - - CHECK_NOTHROW(json::json_pointer("/" + ptr)); - - // check escape/unescape roundtrip - auto escaped = json::json_pointer::escape(ptr); - json::json_pointer::unescape(escaped); - CHECK(escaped == ptr); - } - } - } - - SECTION("ignore byte-order-mark") - { - // read a file with a UTF-8 BOM - std::ifstream f("test/data/json_nlohmann_tests/bom.json"); - json j; - CHECK_NOTHROW(j << f); - } - - SECTION("error for incomplete/wrong BOM") - { - CHECK_THROWS_AS(json::parse("\xef\xbb"), std::invalid_argument); - CHECK_THROWS_AS(json::parse("\xef\xbb\xbb"), std::invalid_argument); - } -} - -TEST_CASE("JSON pointers") -{ - SECTION("errors") - { - CHECK_THROWS_AS(json::json_pointer("foo"), std::domain_error); - CHECK_THROWS_WITH(json::json_pointer("foo"), "JSON pointer must be empty or begin with '/'"); - - CHECK_THROWS_AS(json::json_pointer("/~~"), std::domain_error); - CHECK_THROWS_WITH(json::json_pointer("/~~"), "escape error: '~' must be followed with '0' or '1'"); - - CHECK_THROWS_AS(json::json_pointer("/~"), std::domain_error); - CHECK_THROWS_WITH(json::json_pointer("/~"), "escape error: '~' must be followed with '0' or '1'"); - - json::json_pointer p; - CHECK_THROWS_AS(p.top(), std::domain_error); - CHECK_THROWS_WITH(p.top(), "JSON pointer has no parent"); - CHECK_THROWS_AS(p.pop_back(), std::domain_error); - CHECK_THROWS_WITH(p.pop_back(), "JSON pointer has no parent"); - } - - SECTION("examples from RFC 6901") - { - SECTION("nonconst access") - { - json j = R"( - { - "foo": ["bar", "baz"], - "": 0, - "a/b": 1, - "c%d": 2, - "e^f": 3, - "g|h": 4, - "i\\j": 5, - "k\"l": 6, - " ": 7, - "m~n": 8 - } - )"_json; - - // the whole document - CHECK(j[json::json_pointer()] == j); - CHECK(j[json::json_pointer("")] == j); - - // array access - CHECK(j[json::json_pointer("/foo")] == j["foo"]); - CHECK(j[json::json_pointer("/foo/0")] == j["foo"][0]); - CHECK(j[json::json_pointer("/foo/1")] == j["foo"][1]); - CHECK(j["/foo/1"_json_pointer] == j["foo"][1]); - - // checked array access - CHECK(j.at(json::json_pointer("/foo/0")) == j["foo"][0]); - CHECK(j.at(json::json_pointer("/foo/1")) == j["foo"][1]); - - // empty string access - CHECK(j[json::json_pointer("/")] == j[""]); - - // other cases - CHECK(j[json::json_pointer("/ ")] == j[" "]); - CHECK(j[json::json_pointer("/c%d")] == j["c%d"]); - CHECK(j[json::json_pointer("/e^f")] == j["e^f"]); - CHECK(j[json::json_pointer("/g|h")] == j["g|h"]); - CHECK(j[json::json_pointer("/i\\j")] == j["i\\j"]); - CHECK(j[json::json_pointer("/k\"l")] == j["k\"l"]); - - // checked access - CHECK(j.at(json::json_pointer("/ ")) == j[" "]); - CHECK(j.at(json::json_pointer("/c%d")) == j["c%d"]); - CHECK(j.at(json::json_pointer("/e^f")) == j["e^f"]); - CHECK(j.at(json::json_pointer("/g|h")) == j["g|h"]); - CHECK(j.at(json::json_pointer("/i\\j")) == j["i\\j"]); - CHECK(j.at(json::json_pointer("/k\"l")) == j["k\"l"]); - - // escaped access - CHECK(j[json::json_pointer("/a~1b")] == j["a/b"]); - CHECK(j[json::json_pointer("/m~0n")] == j["m~n"]); - - // unescaped access - CHECK_THROWS_AS(j[json::json_pointer("/a/b")], std::out_of_range); - CHECK_THROWS_WITH(j[json::json_pointer("/a/b")], "unresolved reference token 'b'"); - // "/a/b" works for JSON {"a": {"b": 42}} - CHECK(json({{"a", {{"b", 42}}}})[json::json_pointer("/a/b")] == json(42)); - - // unresolved access - json j_primitive = 1; - CHECK_THROWS_AS(j_primitive["/foo"_json_pointer], std::out_of_range); - CHECK_THROWS_WITH(j_primitive["/foo"_json_pointer], "unresolved reference token 'foo'"); - CHECK_THROWS_AS(j_primitive.at("/foo"_json_pointer), std::out_of_range); - CHECK_THROWS_WITH(j_primitive.at("/foo"_json_pointer), "unresolved reference token 'foo'"); - } - - SECTION("const access") - { - const json j = R"( - { - "foo": ["bar", "baz"], - "": 0, - "a/b": 1, - "c%d": 2, - "e^f": 3, - "g|h": 4, - "i\\j": 5, - "k\"l": 6, - " ": 7, - "m~n": 8 - } - )"_json; - - // the whole document - CHECK(j[json::json_pointer()] == j); - CHECK(j[json::json_pointer("")] == j); - - // array access - CHECK(j[json::json_pointer("/foo")] == j["foo"]); - CHECK(j[json::json_pointer("/foo/0")] == j["foo"][0]); - CHECK(j[json::json_pointer("/foo/1")] == j["foo"][1]); - CHECK(j["/foo/1"_json_pointer] == j["foo"][1]); - - // checked array access - CHECK(j.at(json::json_pointer("/foo/0")) == j["foo"][0]); - CHECK(j.at(json::json_pointer("/foo/1")) == j["foo"][1]); - - // empty string access - CHECK(j[json::json_pointer("/")] == j[""]); - - // other cases - CHECK(j[json::json_pointer("/ ")] == j[" "]); - CHECK(j[json::json_pointer("/c%d")] == j["c%d"]); - CHECK(j[json::json_pointer("/e^f")] == j["e^f"]); - CHECK(j[json::json_pointer("/g|h")] == j["g|h"]); - CHECK(j[json::json_pointer("/i\\j")] == j["i\\j"]); - CHECK(j[json::json_pointer("/k\"l")] == j["k\"l"]); - - // checked access - CHECK(j.at(json::json_pointer("/ ")) == j[" "]); - CHECK(j.at(json::json_pointer("/c%d")) == j["c%d"]); - CHECK(j.at(json::json_pointer("/e^f")) == j["e^f"]); - CHECK(j.at(json::json_pointer("/g|h")) == j["g|h"]); - CHECK(j.at(json::json_pointer("/i\\j")) == j["i\\j"]); - CHECK(j.at(json::json_pointer("/k\"l")) == j["k\"l"]); - - // escaped access - CHECK(j[json::json_pointer("/a~1b")] == j["a/b"]); - CHECK(j[json::json_pointer("/m~0n")] == j["m~n"]); - - // unescaped access - CHECK_THROWS_AS(j.at(json::json_pointer("/a/b")), std::out_of_range); - CHECK_THROWS_WITH(j.at(json::json_pointer("/a/b")), "key 'a' not found"); - - // unresolved access - const json j_primitive = 1; - CHECK_THROWS_AS(j_primitive["/foo"_json_pointer], std::out_of_range); - CHECK_THROWS_WITH(j_primitive["/foo"_json_pointer], "unresolved reference token 'foo'"); - CHECK_THROWS_AS(j_primitive.at("/foo"_json_pointer), std::out_of_range); - CHECK_THROWS_WITH(j_primitive.at("/foo"_json_pointer), "unresolved reference token 'foo'"); - } - - SECTION("user-defined string literal") - { - json j = R"( - { - "foo": ["bar", "baz"], - "": 0, - "a/b": 1, - "c%d": 2, - "e^f": 3, - "g|h": 4, - "i\\j": 5, - "k\"l": 6, - " ": 7, - "m~n": 8 - } - )"_json; - - // the whole document - CHECK(j[""_json_pointer] == j); - - // array access - CHECK(j["/foo"_json_pointer] == j["foo"]); - CHECK(j["/foo/0"_json_pointer] == j["foo"][0]); - CHECK(j["/foo/1"_json_pointer] == j["foo"][1]); - } - } - - SECTION("array access") - { - SECTION("nonconst access") - { - json j = {1, 2, 3}; - const json j_const = j; - - // check reading access - CHECK(j["/0"_json_pointer] == j[0]); - CHECK(j["/1"_json_pointer] == j[1]); - CHECK(j["/2"_json_pointer] == j[2]); - - // assign to existing index - j["/1"_json_pointer] = 13; - CHECK(j[1] == json(13)); - - // assign to nonexisting index - j["/3"_json_pointer] = 33; - CHECK(j[3] == json(33)); - - // assign to nonexisting index (with gap) - j["/5"_json_pointer] = 55; - CHECK(j == json({1, 13, 3, 33, nullptr, 55})); - - // error with leading 0 - CHECK_THROWS_AS(j["/01"_json_pointer], std::domain_error); - CHECK_THROWS_WITH(j["/01"_json_pointer], "array index must not begin with '0'"); - CHECK_THROWS_AS(j_const["/01"_json_pointer], std::domain_error); - CHECK_THROWS_WITH(j_const["/01"_json_pointer], "array index must not begin with '0'"); - CHECK_THROWS_AS(j.at("/01"_json_pointer), std::domain_error); - CHECK_THROWS_WITH(j.at("/01"_json_pointer), "array index must not begin with '0'"); - CHECK_THROWS_AS(j_const.at("/01"_json_pointer), std::domain_error); - CHECK_THROWS_WITH(j_const.at("/01"_json_pointer), "array index must not begin with '0'"); - - // error with incorrect numbers - CHECK_THROWS_AS(j["/one"_json_pointer] = 1, std::invalid_argument); - - // assign to "-" - j["/-"_json_pointer] = 99; - CHECK(j == json({1, 13, 3, 33, nullptr, 55, 99})); - - // error when using "-" in const object - CHECK_THROWS_AS(j_const["/-"_json_pointer], std::out_of_range); - CHECK_THROWS_WITH(j_const["/-"_json_pointer], "array index '-' (3) is out of range"); - - // error when using "-" with at - CHECK_THROWS_AS(j.at("/-"_json_pointer), std::out_of_range); - CHECK_THROWS_WITH(j.at("/-"_json_pointer), "array index '-' (7) is out of range"); - CHECK_THROWS_AS(j_const.at("/-"_json_pointer), std::out_of_range); - CHECK_THROWS_WITH(j_const.at("/-"_json_pointer), "array index '-' (3) is out of range"); - } - - SECTION("const access") - { - const json j = {1, 2, 3}; - - // check reading access - CHECK(j["/0"_json_pointer] == j[0]); - CHECK(j["/1"_json_pointer] == j[1]); - CHECK(j["/2"_json_pointer] == j[2]); - - // assign to nonexisting index - CHECK_THROWS_AS(j.at("/3"_json_pointer), std::out_of_range); - CHECK_THROWS_WITH(j.at("/3"_json_pointer), "array index 3 is out of range"); - - // assign to nonexisting index (with gap) - CHECK_THROWS_AS(j.at("/5"_json_pointer), std::out_of_range); - CHECK_THROWS_WITH(j.at("/5"_json_pointer), "array index 5 is out of range"); - - // assign to "-" - CHECK_THROWS_AS(j["/-"_json_pointer], std::out_of_range); - CHECK_THROWS_WITH(j["/-"_json_pointer], "array index '-' (3) is out of range"); - CHECK_THROWS_AS(j.at("/-"_json_pointer), std::out_of_range); - CHECK_THROWS_WITH(j.at("/-"_json_pointer), "array index '-' (3) is out of range"); - } - - } - - SECTION("flatten") - { - json j = - { - {"pi", 3.141}, - {"happy", true}, - {"name", "Niels"}, - {"nothing", nullptr}, - { - "answer", { - {"everything", 42} - } - }, - {"list", {1, 0, 2}}, - { - "object", { - {"currency", "USD"}, - {"value", 42.99}, - {"", "empty string"}, - {"/", "slash"}, - {"~", "tilde"}, - {"~1", "tilde1"} - } - } - }; - - json j_flatten = - { - {"/pi", 3.141}, - {"/happy", true}, - {"/name", "Niels"}, - {"/nothing", nullptr}, - {"/answer/everything", 42}, - {"/list/0", 1}, - {"/list/1", 0}, - {"/list/2", 2}, - {"/object/currency", "USD"}, - {"/object/value", 42.99}, - {"/object/", "empty string"}, - {"/object/~1", "slash"}, - {"/object/~0", "tilde"}, - {"/object/~01", "tilde1"} - }; - - // check if flattened result is as expected - CHECK(j.flatten() == j_flatten); - - // check if unflattened result is as expected - CHECK(j_flatten.unflatten() == j); - - // error for nonobjects - CHECK_THROWS_AS(json(1).unflatten(), std::domain_error); - CHECK_THROWS_WITH(json(1).unflatten(), "only objects can be unflattened"); - - // error for nonprimitve values - CHECK_THROWS_AS(json({{"/1", {1, 2, 3}}}).unflatten(), std::domain_error); - CHECK_THROWS_WITH(json({{"/1", {1, 2, 3}}}).unflatten(), "values in object must be primitive"); - - // error for conflicting values - json j_error = {{"", 42}, {"/foo", 17}}; - CHECK_THROWS_AS(j_error.unflatten(), std::domain_error); - CHECK_THROWS_WITH(j_error.unflatten(), "invalid value to unflatten"); - - // explicit roundtrip check - CHECK(j.flatten().unflatten() == j); - - // roundtrip for primitive values - json j_null; - CHECK(j_null.flatten().unflatten() == j_null); - json j_number = 42; - CHECK(j_number.flatten().unflatten() == j_number); - json j_boolean = false; - CHECK(j_boolean.flatten().unflatten() == j_boolean); - json j_string = "foo"; - CHECK(j_string.flatten().unflatten() == j_string); - - // roundtrip for empty structured values (will be unflattened to null) - json j_array(json::value_t::array); - CHECK(j_array.flatten().unflatten() == json()); - json j_object(json::value_t::object); - CHECK(j_object.flatten().unflatten() == json()); - } - - SECTION("string representation") - { - for (auto ptr : - {"", "/foo", "/foo/0", "/", "/a~1b", "/c%d", "/e^f", "/g|h", "/i\\j", "/k\"l", "/ ", "/m~0n" - }) - { - CHECK(json::json_pointer(ptr).to_string() == ptr); - } - } -} - -TEST_CASE("JSON patch") -{ - SECTION("examples from RFC 6902") - { - SECTION("4. Operations") - { - // the ordering of members in JSON objects is not significant: - json op1 = R"({ "op": "add", "path": "/a/b/c", "value": "foo" })"_json; - json op2 = R"({ "path": "/a/b/c", "op": "add", "value": "foo" })"_json; - json op3 = R"({ "value": "foo", "path": "/a/b/c", "op": "add" })"_json; - - // check if the operation objects are equivalent - CHECK(op1 == op2); - CHECK(op1 == op3); - } - - SECTION("4.1 add") - { - json patch = R"([{ "op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ] }])"_json; - - // However, the object itself or an array containing it does need - // to exist, and it remains an error for that not to be the case. - // For example, an "add" with a target location of "/a/b" starting - // with this document - json doc1 = R"({ "a": { "foo": 1 } })"_json; - - // is not an error, because "a" exists, and "b" will be added to - // its value. - CHECK_NOTHROW(doc1.patch(patch)); - CHECK(doc1.patch(patch) == R"( - { - "a": { - "foo": 1, - "b": { - "c": [ "foo", "bar" ] - } - } - } - )"_json); - - // It is an error in this document: - json doc2 = R"({ "q": { "bar": 2 } })"_json; - - // because "a" does not exist. - CHECK_THROWS_AS(doc2.patch(patch), std::out_of_range); - CHECK_THROWS_WITH(doc2.patch(patch), "key 'a' not found"); - } - - SECTION("4.2 remove") - { - // If removing an element from an array, any elements above the - // specified index are shifted one position to the left. - json doc = {1, 2, 3, 4}; - json patch = {{{"op", "remove"}, {"path", "/1"}}}; - CHECK(doc.patch(patch) == json({1, 3, 4})); - } - - SECTION("A.1. Adding an Object Member") - { - // An example target JSON document: - json doc = R"( - { "foo": "bar"} - )"_json; - - // A JSON Patch document: - json patch = R"( - [ - { "op": "add", "path": "/baz", "value": "qux" } - ] - )"_json; - - // The resulting JSON document: - json expected = R"( - { - "baz": "qux", - "foo": "bar" - } - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == expected); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, expected)) == expected); - } - - SECTION("A.2. Adding an Array Element") - { - // An example target JSON document: - json doc = R"( - { "foo": [ "bar", "baz" ] } - )"_json; - - // A JSON Patch document: - json patch = R"( - [ - { "op": "add", "path": "/foo/1", "value": "qux" } - ] - )"_json; - - // The resulting JSON document: - json expected = R"( - { "foo": [ "bar", "qux", "baz" ] } - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == expected); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, expected)) == expected); - } - - SECTION("A.3. Removing an Object Member") - { - // An example target JSON document: - json doc = R"( - { - "baz": "qux", - "foo": "bar" - } - )"_json; - - // A JSON Patch document: - json patch = R"( - [ - { "op": "remove", "path": "/baz" } - ] - )"_json; - - // The resulting JSON document: - json expected = R"( - { "foo": "bar" } - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == expected); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, expected)) == expected); - } - - SECTION("A.4. Removing an Array Element") - { - // An example target JSON document: - json doc = R"( - { "foo": [ "bar", "qux", "baz" ] } - )"_json; - - // A JSON Patch document: - json patch = R"( - [ - { "op": "remove", "path": "/foo/1" } - ] - )"_json; - - // The resulting JSON document: - json expected = R"( - { "foo": [ "bar", "baz" ] } - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == expected); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, expected)) == expected); - } - - SECTION("A.5. Replacing a Value") - { - // An example target JSON document: - json doc = R"( - { - "baz": "qux", - "foo": "bar" - } - )"_json; - - // A JSON Patch document: - json patch = R"( - [ - { "op": "replace", "path": "/baz", "value": "boo" } - ] - )"_json; - - json expected = R"( - { - "baz": "boo", - "foo": "bar" - } - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == expected); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, expected)) == expected); - } - - SECTION("A.6. Moving a Value") - { - // An example target JSON document: - json doc = R"( - { - "foo": { - "bar": "baz", - "waldo": "fred" - }, - "qux": { - "corge": "grault" - } - } - )"_json; - - // A JSON Patch document: - json patch = R"( - [ - { "op": "move", "from": "/foo/waldo", "path": "/qux/thud" } - ] - )"_json; - - // The resulting JSON document: - json expected = R"( - { - "foo": { - "bar": "baz" - }, - "qux": { - "corge": "grault", - "thud": "fred" - } - } - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == expected); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, expected)) == expected); - } - - SECTION("A.7. Moving a Value") - { - // An example target JSON document: - json doc = R"( - { "foo": [ "all", "grass", "cows", "eat" ] } - )"_json; - - // A JSON Patch document: - json patch = R"( - [ - { "op": "move", "from": "/foo/1", "path": "/foo/3" } - ] - )"_json; - - // The resulting JSON document: - json expected = R"( - { "foo": [ "all", "cows", "eat", "grass" ] } - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == expected); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, expected)) == expected); - } - - SECTION("A.8. Testing a Value: Success") - { - // An example target JSON document: - json doc = R"( - { - "baz": "qux", - "foo": [ "a", 2, "c" ] - } - )"_json; - - // A JSON Patch document that will result in successful evaluation: - json patch = R"( - [ - { "op": "test", "path": "/baz", "value": "qux" }, - { "op": "test", "path": "/foo/1", "value": 2 } - ] - )"_json; - - // check if evaluation does not throw - CHECK_NOTHROW(doc.patch(patch)); - // check if patched document is unchanged - CHECK(doc.patch(patch) == doc); - } - - SECTION("A.9. Testing a Value: Error") - { - // An example target JSON document: - json doc = R"( - { "baz": "qux" } - )"_json; - - // A JSON Patch document that will result in an error condition: - json patch = R"( - [ - { "op": "test", "path": "/baz", "value": "bar" } - ] - )"_json; - - // check that evaluation throws - CHECK_THROWS_AS(doc.patch(patch), std::domain_error); - CHECK_THROWS_WITH(doc.patch(patch), "unsuccessful: " + patch[0].dump()); - } - - SECTION("A.10. Adding a Nested Member Object") - { - // An example target JSON document: - json doc = R"( - { "foo": "bar" } - )"_json; - - // A JSON Patch document: - json patch = R"( - [ - { "op": "add", "path": "/child", "value": { "grandchild": { } } } - ] - )"_json; - - // The resulting JSON document: - json expected = R"( - { - "foo": "bar", - "child": { - "grandchild": { - } - } - } - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == expected); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, expected)) == expected); - } - - SECTION("A.11. Ignoring Unrecognized Elements") - { - // An example target JSON document: - json doc = R"( - { "foo": "bar" } - )"_json; - - // A JSON Patch document: - json patch = R"( - [ - { "op": "add", "path": "/baz", "value": "qux", "xyz": 123 } - ] - )"_json; - - json expected = R"( - { - "foo": "bar", - "baz": "qux" - } - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == expected); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, expected)) == expected); - } - - SECTION("A.12. Adding to a Nonexistent Target") - { - // An example target JSON document: - json doc = R"( - { "foo": "bar" } - )"_json; - - // A JSON Patch document: - json patch = R"( - [ - { "op": "add", "path": "/baz/bat", "value": "qux" } - ] - )"_json; - - // This JSON Patch document, applied to the target JSON document - // above, would result in an error (therefore, it would not be - // applied), because the "add" operation's target location that - // references neither the root of the document, nor a member of - // an existing object, nor a member of an existing array. - - CHECK_THROWS_AS(doc.patch(patch), std::out_of_range); - CHECK_THROWS_WITH(doc.patch(patch), "key 'baz' not found"); - } - - // A.13. Invalid JSON Patch Document - // not applicable - - SECTION("A.14. Escape Ordering") - { - // An example target JSON document: - json doc = R"( - { - "/": 9, - "~1": 10 - } - )"_json; - - // A JSON Patch document: - json patch = R"( - [ - {"op": "test", "path": "/~01", "value": 10} - ] - )"_json; - - json expected = R"( - { - "/": 9, - "~1": 10 - } - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == expected); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, expected)) == expected); - } - - SECTION("A.15. Comparing Strings and Numbers") - { - // An example target JSON document: - json doc = R"( - { - "/": 9, - "~1": 10 - } - )"_json; - - // A JSON Patch document that will result in an error condition: - json patch = R"( - [ - {"op": "test", "path": "/~01", "value": "10"} - ] - )"_json; - - // check that evaluation throws - CHECK_THROWS_AS(doc.patch(patch), std::domain_error); - CHECK_THROWS_WITH(doc.patch(patch), "unsuccessful: " + patch[0].dump()); - } - - SECTION("A.16. Adding an Array Value") - { - // An example target JSON document: - json doc = R"( - { "foo": ["bar"] } - )"_json; - - // A JSON Patch document: - json patch = R"( - [ - { "op": "add", "path": "/foo/-", "value": ["abc", "def"] } - ] - )"_json; - - // The resulting JSON document: - json expected = R"( - { "foo": ["bar", ["abc", "def"]] } - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == expected); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, expected)) == expected); - } - } - - SECTION("own examples") - { - SECTION("add") - { - SECTION("add to the root element") - { - // If the path is the root of the target document - the - // specified value becomes the entire content of the target - // document. - - // An example target JSON document: - json doc = 17; - - // A JSON Patch document: - json patch = R"( - [ - { "op": "add", "path": "", "value": [1,2,3] } - ] - )"_json; - - // The resulting JSON document: - json expected = {1, 2, 3}; - - // check if patched value is as expected - CHECK(doc.patch(patch) == expected); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, expected)) == expected); - } - - SECTION("add to end of the array") - { - // The specified index MUST NOT be greater than the number of - // elements in the array. The example below uses and index of - // exactly the number of elements in the array which is legal. - - // An example target JSON document: - json doc = {0, 1, 2}; - - // A JSON Patch document: - json patch = R"( - [ - { "op": "add", "path": "/3", "value": 3 } - ] - )"_json; - - // The resulting JSON document: - json expected = {0, 1, 2, 3}; - - // check if patched value is as expected - CHECK(doc.patch(patch) == expected); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, expected)) == expected); - } - } - - SECTION("copy") - { - // An example target JSON document: - json doc = R"( - { - "foo": { - "bar": "baz", - "waldo": "fred" - }, - "qux": { - "corge": "grault" - } - } - )"_json; - - // A JSON Patch document: - json patch = R"( - [ - { "op": "copy", "from": "/foo/waldo", "path": "/qux/thud" } - ] - )"_json; - - // The resulting JSON document: - json expected = R"( - { - "foo": { - "bar": "baz", - "waldo": "fred" - }, - "qux": { - "corge": "grault", - "thud": "fred" - } - } - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == expected); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, expected)) == expected); - } - - SECTION("replace") - { - json j = "string"; - json patch = {{{"op", "replace"}, {"path", ""}, {"value", 1}}}; - CHECK(j.patch(patch) == json(1)); - } - - SECTION("documentation GIF") - { - { - // a JSON patch - json p1 = R"( - [{"op": "add", "path": "/GB", "value": "London"}] - )"_json; - - // a JSON value - json source = R"( - {"D": "Berlin", "F": "Paris"} - )"_json; - - // apply the patch - json target = source.patch(p1); - // target = { "D": "Berlin", "F": "Paris", "GB": "London" } - CHECK(target == R"({ "D": "Berlin", "F": "Paris", "GB": "London" })"_json); - - // create a diff from two JSONs - json p2 = json::diff(target, source); - // p2 = [{"op": "delete", "path": "/GB"}] - CHECK(p2 == R"([{"op":"remove","path":"/GB"}])"_json); - } - { - // a JSON value - json j = {"good", "bad", "ugly"}; - - // a JSON pointer - auto ptr = json::json_pointer("/2"); - - // use to access elements - j[ptr] = {{"it", "cattivo"}}; - CHECK(j == R"(["good","bad",{"it":"cattivo"}])"_json); - - // use user-defined string literal - j["/2/en"_json_pointer] = "ugly"; - CHECK(j == R"(["good","bad",{"en":"ugly","it":"cattivo"}])"_json); - - json flat = j.flatten(); - CHECK(flat == R"({"/0":"good","/1":"bad","/2/en":"ugly","/2/it":"cattivo"})"_json); - } - } - } - - SECTION("errors") - { - SECTION("unknown operation") - { - SECTION("not an array") - { - json j; - json patch = {{"op", "add"}, {"path", ""}, {"value", 1}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "JSON patch must be an array of objects"); - } - - SECTION("not an array of objects") - { - json j; - json patch = {"op", "add", "path", "", "value", 1}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "JSON patch must be an array of objects"); - } - - SECTION("missing 'op'") - { - json j; - json patch = {{{"foo", "bar"}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation must have member 'op'"); - } - - SECTION("non-string 'op'") - { - json j; - json patch = {{{"op", 1}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation must have string member 'op'"); - } - - SECTION("invalid operation") - { - json j; - json patch = {{{"op", "foo"}, {"path", ""}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation value 'foo' is invalid"); - } - } - - SECTION("add") - { - SECTION("missing 'path'") - { - json j; - json patch = {{{"op", "add"}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'add' must have member 'path'"); - } - - SECTION("non-string 'path'") - { - json j; - json patch = {{{"op", "add"}, {"path", 1}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'add' must have string member 'path'"); - } - - SECTION("missing 'value'") - { - json j; - json patch = {{{"op", "add"}, {"path", ""}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'add' must have member 'value'"); - } - - SECTION("invalid array index") - { - json j = {1, 2}; - json patch = {{{"op", "add"}, {"path", "/4"}, {"value", 4}}}; - CHECK_THROWS_AS(j.patch(patch), std::out_of_range); - CHECK_THROWS_WITH(j.patch(patch), "array index 4 is out of range"); - } - } - - SECTION("remove") - { - SECTION("missing 'path'") - { - json j; - json patch = {{{"op", "remove"}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'remove' must have member 'path'"); - } - - SECTION("non-string 'path'") - { - json j; - json patch = {{{"op", "remove"}, {"path", 1}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'remove' must have string member 'path'"); - } - - SECTION("nonexisting target location (array)") - { - json j = {1, 2, 3}; - json patch = {{{"op", "remove"}, {"path", "/17"}}}; - CHECK_THROWS_AS(j.patch(patch), std::out_of_range); - CHECK_THROWS_WITH(j.patch(patch), "array index 17 is out of range"); - } - - SECTION("nonexisting target location (object)") - { - json j = {{"foo", 1}, {"bar", 2}}; - json patch = {{{"op", "remove"}, {"path", "/baz"}}}; - CHECK_THROWS_AS(j.patch(patch), std::out_of_range); - CHECK_THROWS_WITH(j.patch(patch), "key 'baz' not found"); - } - - SECTION("root element as target location") - { - json j = "string"; - json patch = {{{"op", "remove"}, {"path", ""}}}; - CHECK_THROWS_AS(j.patch(patch), std::domain_error); - CHECK_THROWS_WITH(j.patch(patch), "JSON pointer has no parent"); - } - } - - SECTION("replace") - { - SECTION("missing 'path'") - { - json j; - json patch = {{{"op", "replace"}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'replace' must have member 'path'"); - } - - SECTION("non-string 'path'") - { - json j; - json patch = {{{"op", "replace"}, {"path", 1}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'replace' must have string member 'path'"); - } - - SECTION("missing 'value'") - { - json j; - json patch = {{{"op", "replace"}, {"path", ""}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'replace' must have member 'value'"); - } - - SECTION("nonexisting target location (array)") - { - json j = {1, 2, 3}; - json patch = {{{"op", "replace"}, {"path", "/17"}, {"value", 19}}}; - CHECK_THROWS_AS(j.patch(patch), std::out_of_range); - CHECK_THROWS_WITH(j.patch(patch), "array index 17 is out of range"); - } - - SECTION("nonexisting target location (object)") - { - json j = {{"foo", 1}, {"bar", 2}}; - json patch = {{{"op", "replace"}, {"path", "/baz"}, {"value", 3}}}; - CHECK_THROWS_AS(j.patch(patch), std::out_of_range); - CHECK_THROWS_WITH(j.patch(patch), "key 'baz' not found"); - } - } - - SECTION("move") - { - SECTION("missing 'path'") - { - json j; - json patch = {{{"op", "move"}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have member 'path'"); - } - - SECTION("non-string 'path'") - { - json j; - json patch = {{{"op", "move"}, {"path", 1}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have string member 'path'"); - } - - SECTION("missing 'from'") - { - json j; - json patch = {{{"op", "move"}, {"path", ""}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have member 'from'"); - } - - SECTION("non-string 'from'") - { - json j; - json patch = {{{"op", "move"}, {"path", ""}, {"from", 1}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have string member 'from'"); - } - - SECTION("nonexisting from location (array)") - { - json j = {1, 2, 3}; - json patch = {{{"op", "move"}, {"path", "/0"}, {"from", "/5"}}}; - CHECK_THROWS_AS(j.patch(patch), std::out_of_range); - CHECK_THROWS_WITH(j.patch(patch), "array index 5 is out of range"); - } - - SECTION("nonexisting from location (object)") - { - json j = {{"foo", 1}, {"bar", 2}}; - json patch = {{{"op", "move"}, {"path", "/baz"}, {"from", "/baz"}}}; - CHECK_THROWS_AS(j.patch(patch), std::out_of_range); - CHECK_THROWS_WITH(j.patch(patch), "key 'baz' not found"); - } - } - - SECTION("copy") - { - SECTION("missing 'path'") - { - json j; - json patch = {{{"op", "copy"}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have member 'path'"); - } - - SECTION("non-string 'path'") - { - json j; - json patch = {{{"op", "copy"}, {"path", 1}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have string member 'path'"); - } - - SECTION("missing 'from'") - { - json j; - json patch = {{{"op", "copy"}, {"path", ""}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have member 'from'"); - } - - SECTION("non-string 'from'") - { - json j; - json patch = {{{"op", "copy"}, {"path", ""}, {"from", 1}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have string member 'from'"); - } - - SECTION("nonexisting from location (array)") - { - json j = {1, 2, 3}; - json patch = {{{"op", "copy"}, {"path", "/0"}, {"from", "/5"}}}; - CHECK_THROWS_AS(j.patch(patch), std::out_of_range); - CHECK_THROWS_WITH(j.patch(patch), "array index 5 is out of range"); - } - - SECTION("nonexisting from location (object)") - { - json j = {{"foo", 1}, {"bar", 2}}; - json patch = {{{"op", "copy"}, {"path", "/fob"}, {"from", "/baz"}}}; - CHECK_THROWS_AS(j.patch(patch), std::out_of_range); - CHECK_THROWS_WITH(j.patch(patch), "key 'baz' not found"); - } - } - - SECTION("test") - { - SECTION("missing 'path'") - { - json j; - json patch = {{{"op", "test"}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'test' must have member 'path'"); - } - - SECTION("non-string 'path'") - { - json j; - json patch = {{{"op", "test"}, {"path", 1}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'test' must have string member 'path'"); - } - - SECTION("missing 'value'") - { - json j; - json patch = {{{"op", "test"}, {"path", ""}}}; - CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); - CHECK_THROWS_WITH(j.patch(patch), "operation 'test' must have member 'value'"); - } - } - } - - SECTION("Examples from jsonpatch.com") - { - SECTION("Simple Example") - { - // The original document - json doc = R"( - { - "baz": "qux", - "foo": "bar" - } - )"_json; - - // The patch - json patch = R"( - [ - { "op": "replace", "path": "/baz", "value": "boo" }, - { "op": "add", "path": "/hello", "value": ["world"] }, - { "op": "remove", "path": "/foo"} - ] - )"_json; - - // The result - json result = R"( - { - "baz": "boo", - "hello": ["world"] - } - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == result); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, result)) == result); - } - - SECTION("Operations") - { - // The original document - json doc = R"( - { - "biscuits": [ - {"name":"Digestive"}, - {"name": "Choco Liebniz"} - ] - } - )"_json; - - SECTION("add") - { - // The patch - json patch = R"( - [ - {"op": "add", "path": "/biscuits/1", "value": {"name": "Ginger Nut"}} - ] - )"_json; - - // The result - json result = R"( - { - "biscuits": [ - {"name": "Digestive"}, - {"name": "Ginger Nut"}, - {"name": "Choco Liebniz"} - ] - } - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == result); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, result)) == result); - } - - SECTION("remove") - { - // The patch - json patch = R"( - [ - {"op": "remove", "path": "/biscuits"} - ] - )"_json; - - // The result - json result = R"( - {} - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == result); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, result)) == result); - } - - SECTION("replace") - { - // The patch - json patch = R"( - [ - {"op": "replace", "path": "/biscuits/0/name", "value": "Chocolate Digestive"} - ] - )"_json; - - // The result - json result = R"( - { - "biscuits": [ - {"name": "Chocolate Digestive"}, - {"name": "Choco Liebniz"} - ] - } - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == result); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, result)) == result); - } - - SECTION("copy") - { - // The patch - json patch = R"( - [ - {"op": "copy", "from": "/biscuits/0", "path": "/best_biscuit"} - ] - )"_json; - - // The result - json result = R"( - { - "biscuits": [ - {"name": "Digestive"}, - {"name": "Choco Liebniz"} - ], - "best_biscuit": { - "name": "Digestive" - } - } - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == result); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, result)) == result); - } - - SECTION("move") - { - // The patch - json patch = R"( - [ - {"op": "move", "from": "/biscuits", "path": "/cookies"} - ] - )"_json; - - // The result - json result = R"( - { - "cookies": [ - {"name": "Digestive"}, - {"name": "Choco Liebniz"} - ] - } - )"_json; - - // check if patched value is as expected - CHECK(doc.patch(patch) == result); - - // check roundtrip - CHECK(doc.patch(json::diff(doc, result)) == result); - } - - SECTION("test") - { - // The patch - json patch = R"( - [ - {"op": "test", "path": "/best_biscuit/name", "value": "Choco Liebniz"} - ] - )"_json; - - // the test will fail - CHECK_THROWS_AS(doc.patch(patch), std::domain_error); - CHECK_THROWS_WITH(doc.patch(patch), "unsuccessful: " + patch[0].dump()); - } - } - } - - SECTION("Examples from bruth.github.io/jsonpatch-js") - { - SECTION("add") - { - CHECK(R"( {} )"_json.patch( - R"( [{"op": "add", "path": "/foo", "value": "bar"}] )"_json - ) == R"( {"foo": "bar"} )"_json); - - CHECK(R"( {"foo": [1, 3]} )"_json.patch( - R"( [{"op": "add", "path": "/foo", "value": "bar"}] )"_json - ) == R"( {"foo": "bar"} )"_json); - - CHECK(R"( {"foo": [{}]} )"_json.patch( - R"( [{"op": "add", "path": "/foo/0/bar", "value": "baz"}] )"_json - ) == R"( {"foo": [{"bar": "baz"}]} )"_json); - } - - SECTION("remove") - { - CHECK(R"( {"foo": "bar"} )"_json.patch( - R"( [{"op": "remove", "path": "/foo"}] )"_json - ) == R"( {} )"_json); - - CHECK(R"( {"foo": [1, 2, 3]} )"_json.patch( - R"( [{"op": "remove", "path": "/foo/1"}] )"_json - ) == R"( {"foo": [1, 3]} )"_json); - - CHECK(R"( {"foo": [{"bar": "baz"}]} )"_json.patch( - R"( [{"op": "remove", "path": "/foo/0/bar"}] )"_json - ) == R"( {"foo": [{}]} )"_json); - } - - SECTION("replace") - { - CHECK(R"( {"foo": "bar"} )"_json.patch( - R"( [{"op": "replace", "path": "/foo", "value": 1}] )"_json - ) == R"( {"foo": 1} )"_json); - - CHECK(R"( {"foo": [1, 2, 3]} )"_json.patch( - R"( [{"op": "replace", "path": "/foo/1", "value": 4}] )"_json - ) == R"( {"foo": [1, 4, 3]} )"_json); - - CHECK(R"( {"foo": [{"bar": "baz"}]} )"_json.patch( - R"( [{"op": "replace", "path": "/foo/0/bar", "value": 1}] )"_json - ) == R"( {"foo": [{"bar": 1}]} )"_json); - } - - SECTION("move") - { - CHECK(R"( {"foo": [1, 2, 3]} )"_json.patch( - R"( [{"op": "move", "from": "/foo", "path": "/bar"}] )"_json - ) == R"( {"bar": [1, 2, 3]} )"_json); - } - - SECTION("copy") - { - CHECK(R"( {"foo": [1, 2, 3]} )"_json.patch( - R"( [{"op": "copy", "from": "/foo/1", "path": "/bar"}] )"_json - ) == R"( {"foo": [1, 2, 3], "bar": 2} )"_json); - } - - SECTION("copy") - { - CHECK_NOTHROW(R"( {"foo": "bar"} )"_json.patch( - R"( [{"op": "test", "path": "/foo", "value": "bar"}] )"_json)); - } - } -} - -TEST_CASE("regression tests") -{ - SECTION("issue #60 - Double quotation mark is not parsed correctly") - { - SECTION("escape_dobulequote") - { - auto s = "[\"\\\"foo\\\"\"]"; - json j = json::parse(s); - auto expected = R"(["\"foo\""])"_json; - CHECK(j == expected); - } - } - - SECTION("issue #70 - Handle infinity and NaN cases") - { - SECTION("NAN value") - { - CHECK(json(NAN) == json()); - CHECK(json(json::number_float_t(NAN)) == json()); - } - - SECTION("infinity") - { - CHECK(json(INFINITY) == json()); - CHECK(json(json::number_float_t(INFINITY)) == json()); - } - } - - SECTION("pull request #71 - handle enum type") - { - enum { t = 0 }; - json j = json::array(); - j.push_back(t); - - j.push_back(json::object( - { - {"game_type", t} - })); - } - - SECTION("issue #76 - dump() / parse() not idempotent") - { - // create JSON object - json fields; - fields["one"] = std::string("one"); - fields["two"] = std::string("two three"); - fields["three"] = std::string("three \"four\""); - - // create another JSON object by deserializing the serialization - std::string payload = fields.dump(); - json parsed_fields = json::parse(payload); - - // check individual fields to match both objects - CHECK(parsed_fields["one"] == fields["one"]); - CHECK(parsed_fields["two"] == fields["two"]); - CHECK(parsed_fields["three"] == fields["three"]); - - // check individual fields to match original input - CHECK(parsed_fields["one"] == std::string("one")); - CHECK(parsed_fields["two"] == std::string("two three")); - CHECK(parsed_fields["three"] == std::string("three \"four\"")); - - // check equality of the objects - CHECK(parsed_fields == fields); - - // check equality of the serialized objects - CHECK(fields.dump() == parsed_fields.dump()); - - // check everything in one line - CHECK(fields == json::parse(fields.dump())); - } - - SECTION("issue #82 - lexer::get_number return NAN") - { - const auto content = R"( - { - "Test":"Test1", - "Number":100, - "Foo":42.42 - })"; - - std::stringstream ss; - ss << content; - json j; - ss >> j; - - std::string test = j["Test"]; - CHECK(test == "Test1"); - int number = j["Number"]; - CHECK(number == 100); - float foo = j["Foo"]; - CHECK(foo == Approx(42.42)); - } - - SECTION("issue #89 - nonstandard integer type") - { - // create JSON class with nonstandard integer number type - using custom_json = - nlohmann::basic_json; - custom_json j; - j["int_1"] = 1; - // we need to cast to int to compile with Catch - the value is int32_t - CHECK(static_cast(j["int_1"]) == 1); - - // tests for correct handling of non-standard integers that overflow the type selected by the user - - // unsigned integer object creation - expected to wrap and still be stored as an integer - j = 4294967296U; // 2^32 - CHECK(static_cast(j.type()) == static_cast(custom_json::value_t::number_unsigned)); - CHECK(j.get() == 0); // Wrap - - // unsigned integer parsing - expected to overflow and be stored as a float - j = custom_json::parse("4294967296"); // 2^32 - CHECK(static_cast(j.type()) == static_cast(custom_json::value_t::number_float)); - CHECK(j.get() == 4294967296.0f); - - // integer object creation - expected to wrap and still be stored as an integer - j = -2147483649LL; // -2^31-1 - CHECK(static_cast(j.type()) == static_cast(custom_json::value_t::number_integer)); - CHECK(j.get() == 2147483647); // Wrap - - // integer parsing - expected to overflow and be stored as a float with rounding - j = custom_json::parse("-2147483649"); // -2^31 - CHECK(static_cast(j.type()) == static_cast(custom_json::value_t::number_float)); - CHECK(j.get() == -2147483650.0f); - } - - SECTION("issue #93 reverse_iterator operator inheritance problem") - { - { - json a = {1, 2, 3}; - json::reverse_iterator rit = a.rbegin(); - ++rit; - CHECK(*rit == json(2)); - CHECK(rit.value() == json(2)); - } - { - json a = {1, 2, 3}; - json::reverse_iterator rit = ++a.rbegin(); - } - { - json a = {1, 2, 3}; - json::reverse_iterator rit = a.rbegin(); - ++rit; - json b = {0, 0, 0}; - std::transform(rit, a.rend(), b.rbegin(), [](json el) - { - return el; - }); - CHECK(b == json({0, 1, 2})); - } - { - json a = {1, 2, 3}; - json b = {0, 0, 0}; - std::transform(++a.rbegin(), a.rend(), b.rbegin(), [](json el) - { - return el; - }); - CHECK(b == json({0, 1, 2})); - } - } - - SECTION("issue #100 - failed to iterator json object with reverse_iterator") - { - json config = - { - { "111", 111 }, - { "112", 112 }, - { "113", 113 } - }; - - std::stringstream ss; - - for (auto it = config.begin(); it != config.end(); ++it) - { - ss << it.key() << ": " << it.value() << '\n'; - } - - for (auto it = config.rbegin(); it != config.rend(); ++it) - { - ss << it.key() << ": " << it.value() << '\n'; - } - - CHECK(ss.str() == "111: 111\n112: 112\n113: 113\n113: 113\n112: 112\n111: 111\n"); - } - - SECTION("issue #101 - binary string causes numbers to be dumped as hex") - { - int64_t number = 10; - std::string bytes{"\x00" "asdf\n", 6}; - json j; - j["int64"] = number; - j["binary string"] = bytes; - // make sure the number is really printed as decimal "10" and not as - // hexadecimal "a" - CHECK(j.dump() == "{\"binary string\":\"\\u0000asdf\\n\",\"int64\":10}"); - } - - SECTION("issue #111 - subsequent unicode chars") - { - std::string bytes{0x7, 0x7}; - json j; - j["string"] = bytes; - CHECK(j["string"] == "\u0007\u0007"); - } - - SECTION("issue #144 - implicit assignment to std::string fails") - { - json o = {{"name", "value"}}; - - std::string s1 = o["name"]; - CHECK(s1 == "value"); - - std::string s2; - s2 = o["name"]; - - CHECK(s2 == "value"); - } - - SECTION("issue #146 - character following a surrogate pair is skipped") - { - CHECK(json::parse("\"\\ud80c\\udc60abc\"").get() == u8"\U00013060abc"); - } - - SECTION("issue #171 - Cannot index by key of type static constexpr const char*") - { - json j; - - // Non-const access with key as "char []" - char array_key[] = "Key1"; - CHECK_NOTHROW(j[array_key] = 1); - CHECK(j[array_key] == json(1)); - - // Non-const access with key as "const char[]" - const char const_array_key[] = "Key2"; - CHECK_NOTHROW(j[const_array_key] = 2); - CHECK(j[const_array_key] == json(2)); - - // Non-const access with key as "char *" - char _ptr_key[] = "Key3"; - char* ptr_key = &_ptr_key[0]; - CHECK_NOTHROW(j[ptr_key] = 3); - CHECK(j[ptr_key] == json(3)); - - // Non-const access with key as "const char *" - const char* const_ptr_key = "Key4"; - CHECK_NOTHROW(j[const_ptr_key] = 4); - CHECK(j[const_ptr_key] == json(4)); - - // Non-const access with key as "static constexpr const char *" - static constexpr const char* constexpr_ptr_key = "Key5"; - CHECK_NOTHROW(j[constexpr_ptr_key] = 5); - CHECK(j[constexpr_ptr_key] == json(5)); - - const json j_const = j; - - // Const access with key as "char []" - CHECK(j_const[array_key] == json(1)); - - // Const access with key as "const char[]" - CHECK(j_const[const_array_key] == json(2)); - - // Const access with key as "char *" - CHECK(j_const[ptr_key] == json(3)); - - // Const access with key as "const char *" - CHECK(j_const[const_ptr_key] == json(4)); - - // Const access with key as "static constexpr const char *" - CHECK(j_const[constexpr_ptr_key] == json(5)); - } - - SECTION("issue #186 miloyip/nativejson-benchmark: floating-point parsing") - { - json j; - - j = json::parse("-0.0"); - CHECK(j.get() == -0.0); - - j = json::parse("2.22507385850720113605740979670913197593481954635164564e-308"); - CHECK(j.get() == 2.2250738585072009e-308); - - j = json::parse("0.999999999999999944488848768742172978818416595458984374"); - CHECK(j.get() == 0.99999999999999989); - - j = json::parse("1.00000000000000011102230246251565404236316680908203126"); - CHECK(j.get() == 1.00000000000000022); - - j = json::parse("7205759403792793199999e-5"); - CHECK(j.get() == 72057594037927928.0); - - j = json::parse("922337203685477529599999e-5"); - CHECK(j.get() == 9223372036854774784.0); - - j = json::parse("1014120480182583464902367222169599999e-5"); - CHECK(j.get() == 10141204801825834086073718800384.0); - - j = json::parse("5708990770823839207320493820740630171355185151999e-3"); - CHECK(j.get() == 5708990770823838890407843763683279797179383808.0); - - // create JSON class with nonstandard float number type - - // float - nlohmann::basic_json j_float = - 1.23e25f; - CHECK(j_float.get() == 1.23e25f); - - // double - nlohmann::basic_json j_double = - 1.23e35f; - CHECK(j_double.get() == 1.23e35f); - - // long double - nlohmann::basic_json - j_long_double = 1.23e45L; - CHECK(j_long_double.get() == 1.23e45L); - } - - SECTION("issue #228 - double values are serialized with commas as decimal points") - { - json j1a = 23.42; - json j1b = json::parse("23.42"); - - json j2a = 2342e-2; - //issue #230 - //json j2b = json::parse("2342e-2"); - - json j3a = 10E3; - json j3b = json::parse("10E3"); - json j3c = json::parse("10e3"); - - // class to create a locale that would use a comma for decimals - class CommaDecimalSeparator : public std::numpunct - { - protected: - char do_decimal_point() const - { - return ','; - } - }; - - // change locale to mess with decimal points - std::locale::global(std::locale(std::locale(), new CommaDecimalSeparator)); - - CHECK(j1a.dump() == "23.42"); - CHECK(j1b.dump() == "23.42"); - - // check if locale is properly reset - std::stringstream ss; - ss.imbue(std::locale(std::locale(), new CommaDecimalSeparator)); - ss << 47.11; - CHECK(ss.str() == "47,11"); - ss << j1a; - CHECK(ss.str() == "47,1123.42"); - ss << 47.11; - CHECK(ss.str() == "47,1123.4247,11"); - - CHECK(j2a.dump() == "23.42"); - //issue #230 - //CHECK(j2b.dump() == "23.42"); - - CHECK(j3a.dump() == "10000"); - CHECK(j3b.dump() == "10000"); - CHECK(j3c.dump() == "10000"); - //CHECK(j3b.dump() == "1E04"); // roundtrip error - //CHECK(j3c.dump() == "1e04"); // roundtrip error - } - - SECTION("issue #233 - Can't use basic_json::iterator as a base iterator for std::move_iterator") - { - json source = {"a", "b", "c"}; - json expected = {"a", "b"}; - json dest; - - std::copy_n(std::make_move_iterator(source.begin()), 2, std::back_inserter(dest)); - - CHECK(dest == expected); - } - - SECTION("issue #235 - ambiguous overload for 'push_back' and 'operator+='") - { - json data = {{"key", "value"}}; - data.push_back({"key2", "value2"}); - data += {"key3", "value3"}; - - CHECK(data == json({{"key", "value"}, {"key2", "value2"}, {"key3", "value3"}})); - } - - SECTION("issue #269 - diff generates incorrect patch when removing multiple array elements") - { - json doc = R"( { "arr1": [1, 2, 3, 4] } )"_json; - json expected = R"( { "arr1": [1, 2] } )"_json; - - // check roundtrip - CHECK(doc.patch(json::diff(doc, expected)) == expected); - } - - SECTION("issue #283 - value() does not work with _json_pointer types") - { - json j = - { - {"object", {{"key1", 1}, {"key2", 2}}}, - }; - - int at_integer = j.at("/object/key2"_json_pointer); - int val_integer = j.value("/object/key2"_json_pointer, 0); - - CHECK(at_integer == val_integer); - } -} - -// special test case to check if memory is leaked if constructor throws - -template -struct my_allocator : std::allocator -{ - template - void construct(T*, Args&& ...) - { - throw std::bad_alloc(); - } -}; - -TEST_CASE("bad_alloc") -{ - SECTION("bad_alloc") - { - // create JSON type using the throwing allocator - using my_json = nlohmann::basic_json; - - // creating an object should throw - CHECK_THROWS_AS(my_json j(my_json::value_t::object), std::bad_alloc); - } -}