From 4444ef93965e462119857dc8e7527456fd37a914 Mon Sep 17 00:00:00 2001 From: Niels Date: Sun, 31 Jul 2016 14:39:15 +0200 Subject: [PATCH 01/77] version bump --- doc/json.gif | Bin 450319 -> 450236 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/json.gif b/doc/json.gif index 46f005dd0da32ad9a2723e34edf4a8219699d118..62a1a2eb93de1c62c23214006a154c28ee043348 100644 GIT binary patch delta 12053 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 V)RDdQ{|A{r`}_a^ delta 12098 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 Date: Sun, 31 Jul 2016 23:03:14 +0200 Subject: [PATCH 02/77] switch off assertions for benchmarks --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 15d00f6e5..70e8859d2 100644 --- a/Makefile +++ b/Makefile @@ -87,7 +87,7 @@ pretty: # benchmarks json_benchmarks: benchmarks/benchmarks.cpp benchmarks/benchpress.hpp benchmarks/cxxopts.hpp src/json.hpp cd benchmarks/files/numbers ; python generate.py - $(CXX) -std=c++11 $(CXXFLAGS) -O3 -flto -I src -I benchmarks $< $(LDFLAGS) -o $@ + $(CXX) -std=c++11 $(CXXFLAGS) -DNDEBUG -O3 -flto -I src -I benchmarks $< $(LDFLAGS) -o $@ ./json_benchmarks From 5541e6f6f90162ae42c6935335aa320d87b42b76 Mon Sep 17 00:00:00 2001 From: Niels Date: Thu, 4 Aug 2016 07:24:46 +0200 Subject: [PATCH 03/77] split unit tests --- .gitignore | 2 ++ .travis.yml | 6 +++--- Makefile | 12 +++++++----- README.md | 3 +-- test/CMakeLists.txt | 1 + test/Makefile | 21 +++++++++++++++++++++ test/src/unit-runner.cpp | 30 ++++++++++++++++++++++++++++++ test/src/unit.cpp | 2 +- 8 files changed, 66 insertions(+), 11 deletions(-) create mode 100644 test/Makefile create mode 100644 test/src/unit-runner.cpp diff --git a/.gitignore b/.gitignore index fd41a2e3c..b82214e9a 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,5 @@ android doc/xml benchmarks/files/numbers/*.json + +*.o diff --git a/.travis.yml b/.travis.yml index ffe05ec6c..4af6bf748 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,7 +24,7 @@ matrix: - make clean - touch src/json.hpp - make json_unit CXXFLAGS="-fprofile-arcs -ftest-coverage -std=c++11 -lstdc++" CXX=$COMPILER - - ./json_unit "*" + - test/json_unit "*" - coveralls --exclude test/src/catch.hpp --exclude test/src/unit.cpp --include src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' - bash <(curl -s https://codecov.io/bash) env: COMPILER=g++-4.9 @@ -164,9 +164,9 @@ script: - uname -a - $COMPILER --version - make CXX=$COMPILER - - ./json_unit "*" + - test/json_unit "*" - if [ `which valgrind` ]; then - valgrind --error-exitcode=1 --leak-check=full ./json_unit ; + valgrind --error-exitcode=1 --leak-check=full test/json_unit ; fi - if [ `which brew` ]; then brew update ; diff --git a/Makefile b/Makefile index 70e8859d2..199d65da9 100644 --- a/Makefile +++ b/Makefile @@ -12,18 +12,20 @@ clean: rm -fr json_unit json_benchmarks fuzz fuzz-testing *.dSYM rm -fr benchmarks/files/numbers/*.json $(MAKE) clean -Cdoc + $(MAKE) clean -Ctest ########################################################################## # unit tests ########################################################################## -# additional flags -FLAGS = -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 +# build unit tests +json_unit: + @$(MAKE) -C test -# build unit tests (TODO: Does this want its own makefile?) -json_unit: test/src/unit.cpp src/json.hpp test/src/catch.hpp - $(CXX) -std=c++11 $(CXXFLAGS) $(FLAGS) $(CPPFLAGS) -I src -I test $< $(LDFLAGS) -o $@ +# run unit tests +check: json_unit + test/json_unit "*" ########################################################################## diff --git a/README.md b/README.md index c0bb61b17..722bf880b 100644 --- a/README.md +++ b/README.md @@ -501,8 +501,7 @@ Thanks a lot for helping out! To compile and run the tests, you need to execute ```sh -$ make -$ ./json_unit "*" +$ make check =============================================================================== All tests passed (8905012 assertions in 32 test cases) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c66b19c83..4d12de37d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -3,6 +3,7 @@ set(JSON_UNITTEST_TARGET_NAME "json_unit") add_executable(${JSON_UNITTEST_TARGET_NAME} "src/catch.hpp" "src/unit.cpp" + "src/unit-runner.cpp" ) set_target_properties(${JSON_UNITTEST_TARGET_NAME} PROPERTIES diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 000000000..0d4edec78 --- /dev/null +++ b/test/Makefile @@ -0,0 +1,21 @@ +########################################################################## +# unit tests +########################################################################## + +# 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 +INCDIRS = -I ../src -I . + +SOURCES = src/unit-runner.cpp src/unit.cpp +OBJECTS = $(SOURCES:.cpp=.o) + +all: json_unit + +json_unit: $(OBJECTS) ../src/json.hpp src/catch.hpp + $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJECTS) -o $@ + +%.o: %.cpp + $(CXX) $(CXXFLAGS) $(INCDIRS) -c $< -o $@ + +clean: + rm -fr json_unit $(OBJECTS) diff --git a/test/src/unit-runner.cpp b/test/src/unit-runner.cpp new file mode 100644 index 000000000..ec957b7a8 --- /dev/null +++ b/test/src/unit-runner.cpp @@ -0,0 +1,30 @@ +/* + __ _____ _____ _____ + __| | __| | | | 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. +*/ + +#define CATCH_CONFIG_MAIN +#include "catch.hpp" diff --git a/test/src/unit.cpp b/test/src/unit.cpp index 79a4bb090..a5c348bb5 100644 --- a/test/src/unit.cpp +++ b/test/src/unit.cpp @@ -26,12 +26,12 @@ 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 From be5cf0e3bacbcc1282132df077fef1870711f799 Mon Sep 17 00:00:00 2001 From: Niels Date: Thu, 4 Aug 2016 07:33:44 +0200 Subject: [PATCH 04/77] forgot to pass CPPFLAGS --- test/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Makefile b/test/Makefile index 0d4edec78..d11e1e341 100644 --- a/test/Makefile +++ b/test/Makefile @@ -4,7 +4,7 @@ # 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 -INCDIRS = -I ../src -I . +CPPFLAGS += -I ../src -I . SOURCES = src/unit-runner.cpp src/unit.cpp OBJECTS = $(SOURCES:.cpp=.o) @@ -15,7 +15,7 @@ json_unit: $(OBJECTS) ../src/json.hpp src/catch.hpp $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJECTS) -o $@ %.o: %.cpp - $(CXX) $(CXXFLAGS) $(INCDIRS) -c $< -o $@ + $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@ clean: rm -fr json_unit $(OBJECTS) From 3944ecd470f50d99bb7020b330a3caf6fbc34d2a Mon Sep 17 00:00:00 2001 From: Niels Date: Thu, 4 Aug 2016 07:40:04 +0200 Subject: [PATCH 05/77] chose correct flags --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4af6bf748..06f25879a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -61,7 +61,7 @@ matrix: - LLVM_ARCHIVE_PATH=$HOME/clang+llvm.tar.xz - COMPILER=clang++ - CPPFLAGS="-I $HOME/clang-$LLVM_VERSION/include/c++/v1" - - CXXFLAGS=-lc++ + - LDFLAGS=-lc++ - PATH=$HOME/clang-$LLVM_VERSION/bin:$PATH - LD_LIBRARY_PATH=$HOME/clang-$LLVM_VERSION/lib:$LD_LIBRARY_PATH before_install: From d80329034e568b8f52ced7aa537581757b75b426 Mon Sep 17 00:00:00 2001 From: Niels Date: Thu, 4 Aug 2016 21:55:47 +0200 Subject: [PATCH 06/77] split test suite in one file per test case --- Makefile | 3 +- test/CMakeLists.txt | 28 +- test/Makefile | 40 +- test/src/unit-algorithms.cpp | 318 + .../{unit-runner.cpp => unit-allocator.cpp} | 35 +- test/src/unit-capacity.cpp | 562 + test/src/unit-class_const_iterator.cpp | 401 + test/src/unit-class_iterator.cpp | 401 + test/src/unit-class_lexer.cpp | 155 + test/src/unit-class_parser.cpp | 753 + test/src/unit-comparison.cpp | 246 + test/src/unit-concepts.cpp | 169 + test/src/unit-constructor.cpp | 1470 ++ test/src/unit-convenience.cpp | 92 + test/src/unit-conversions.cpp | 1014 ++ test/src/unit-deserialization.cpp | 73 + test/src/unit-element_access.cpp | 1871 ++ test/src/unit-inspection.cpp | 373 + test/src/unit-iterator_wrapper.cpp | 729 + test/src/unit-iterators.cpp | 2352 +++ test/src/unit-json_patch.cpp | 1217 ++ test/src/unit-json_pointer.cpp | 388 + test/src/unit-modifiers.cpp | 713 + test/src/unit-pointer_access.cpp | 263 + test/src/unit-readme.cpp | 299 + test/src/unit-reference_access.cpp | 202 + test/src/unit-regression.cpp | 443 + test/src/unit-serialization.cpp | 76 + test/src/unit-testsuites.cpp | 436 + test/src/unit-unicode.cpp | 176 + test/src/unit.cpp | 14379 +--------------- 31 files changed, 15291 insertions(+), 14386 deletions(-) create mode 100644 test/src/unit-algorithms.cpp rename test/src/{unit-runner.cpp => unit-allocator.cpp} (64%) create mode 100644 test/src/unit-capacity.cpp create mode 100644 test/src/unit-class_const_iterator.cpp create mode 100644 test/src/unit-class_iterator.cpp create mode 100644 test/src/unit-class_lexer.cpp create mode 100644 test/src/unit-class_parser.cpp create mode 100644 test/src/unit-comparison.cpp create mode 100644 test/src/unit-concepts.cpp create mode 100644 test/src/unit-constructor.cpp create mode 100644 test/src/unit-convenience.cpp create mode 100644 test/src/unit-conversions.cpp create mode 100644 test/src/unit-deserialization.cpp create mode 100644 test/src/unit-element_access.cpp create mode 100644 test/src/unit-inspection.cpp create mode 100644 test/src/unit-iterator_wrapper.cpp create mode 100644 test/src/unit-iterators.cpp create mode 100644 test/src/unit-json_patch.cpp create mode 100644 test/src/unit-json_pointer.cpp create mode 100644 test/src/unit-modifiers.cpp create mode 100644 test/src/unit-pointer_access.cpp create mode 100644 test/src/unit-readme.cpp create mode 100644 test/src/unit-reference_access.cpp create mode 100644 test/src/unit-regression.cpp create mode 100644 test/src/unit-serialization.cpp create mode 100644 test/src/unit-testsuites.cpp create mode 100644 test/src/unit-unicode.cpp diff --git a/Makefile b/Makefile index 199d65da9..b6116cc4a 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 4d12de37d..696a7f3f6 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 d11e1e341..c56a2ae9b 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 000000000..75c69da57 --- /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 ec957b7a8..dcf8aa35a 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 000000000..95100da9e --- /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 000000000..4908b5ce9 --- /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 000000000..1d4b29015 --- /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 000000000..708a8cbfa --- /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 000000000..fe0055034 --- /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 000000000..a8ce4c8ad --- /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 000000000..9cbdd8e90 --- /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 000000000..e4842a580 --- /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 000000000..4d3c9b7cf --- /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 000000000..982efe420 --- /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 000000000..781885746 --- /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 000000000..bd33e1e04 --- /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 000000000..25fcb5353 --- /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 000000000..5d6764144 --- /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 000000000..06dcb6aaa --- /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 000000000..af4d45476 --- /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 000000000..b14a45931 --- /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 000000000..9cb336846 --- /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 000000000..bae9ee4c5 --- /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 000000000..7e7ef6b2c --- /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 000000000..922c4825d --- /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 000000000..da4c3d23d --- /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 000000000..13333ff2a --- /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 000000000..54f094792 --- /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 000000000..8efb6154a --- /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 a5c348bb5..ec957b7a8 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); - } -} From 91b6e223d91ece4c305448c4cef33d29a0f35827 Mon Sep 17 00:00:00 2001 From: Niels Date: Thu, 4 Aug 2016 22:04:55 +0200 Subject: [PATCH 07/77] adjusted capacity test cases --- test/src/unit-capacity.cpp | 40 +++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/test/src/unit-capacity.cpp b/test/src/unit-capacity.cpp index 95100da9e..f671ae99c 100644 --- a/test/src/unit-capacity.cpp +++ b/test/src/unit-capacity.cpp @@ -48,8 +48,8 @@ TEST_CASE("capacity") SECTION("definition of empty") { - CHECK(j.begin() != j.end()); - CHECK(j_const.begin() != j_const.end()); + CHECK(j.empty() == (j.begin() == j.end())); + CHECK(j_const.empty() == (j_const.begin() == j_const.end())); } } @@ -66,8 +66,8 @@ TEST_CASE("capacity") SECTION("definition of empty") { - CHECK(j.begin() != j.end()); - CHECK(j_const.begin() != j_const.end()); + CHECK(j.empty() == (j.begin() == j.end())); + CHECK(j_const.empty() == (j_const.begin() == j_const.end())); } } @@ -86,8 +86,8 @@ TEST_CASE("capacity") SECTION("definition of empty") { - CHECK(j.begin() == j.end()); - CHECK(j_const.begin() == j_const.end()); + CHECK(j.empty() == (j.begin() == j.end())); + CHECK(j_const.empty() == (j_const.begin() == j_const.end())); } } @@ -104,8 +104,8 @@ TEST_CASE("capacity") SECTION("definition of empty") { - CHECK(j.begin() != j.end()); - CHECK(j_const.begin() != j_const.end()); + CHECK(j.empty() == (j.begin() == j.end())); + CHECK(j_const.empty() == (j_const.begin() == j_const.end())); } } } @@ -125,8 +125,8 @@ TEST_CASE("capacity") SECTION("definition of empty") { - CHECK(j.begin() == j.end()); - CHECK(j_const.begin() == j_const.end()); + CHECK(j.empty() == (j.begin() == j.end())); + CHECK(j_const.empty() == (j_const.begin() == j_const.end())); } } @@ -143,8 +143,8 @@ TEST_CASE("capacity") SECTION("definition of empty") { - CHECK(j.begin() != j.end()); - CHECK(j_const.begin() != j_const.end()); + CHECK(j.empty() == (j.begin() == j.end())); + CHECK(j_const.empty() == (j_const.begin() == j_const.end())); } } } @@ -162,8 +162,8 @@ TEST_CASE("capacity") SECTION("definition of empty") { - CHECK(j.begin() != j.end()); - CHECK(j_const.begin() != j_const.end()); + CHECK(j.empty() == (j.begin() == j.end())); + CHECK(j_const.empty() == (j_const.begin() == j_const.end())); } } @@ -180,8 +180,8 @@ TEST_CASE("capacity") SECTION("definition of empty") { - CHECK(j.begin() != j.end()); - CHECK(j_const.begin() != j_const.end()); + CHECK(j.empty() == (j.begin() == j.end())); + CHECK(j_const.empty() == (j_const.begin() == j_const.end())); } } @@ -198,8 +198,8 @@ TEST_CASE("capacity") SECTION("definition of empty") { - CHECK(j.begin() != j.end()); - CHECK(j_const.begin() != j_const.end()); + CHECK(j.empty() == (j.begin() == j.end())); + CHECK(j_const.empty() == (j_const.begin() == j_const.end())); } } @@ -216,8 +216,8 @@ TEST_CASE("capacity") SECTION("definition of empty") { - CHECK(j.begin() == j.end()); - CHECK(j_const.begin() == j_const.end()); + CHECK(j.empty() == (j.begin() == j.end())); + CHECK(j_const.empty() == (j_const.begin() == j_const.end())); } } } From ff592c6d50f1b49e3c4c38dac014bf2b5b65fb1d Mon Sep 17 00:00:00 2001 From: Niels Date: Thu, 4 Aug 2016 22:05:05 +0200 Subject: [PATCH 08/77] adjusted warning flags --- test/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Makefile b/test/Makefile index c56a2ae9b..9edda2d25 100644 --- a/test/Makefile +++ b/test/Makefile @@ -3,7 +3,7 @@ ########################################################################## # 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 -Wno-keyword-macro -Wno-float-equal +CXXFLAGS += -std=c++11 -Wall -Wextra -pedantic -Weffc++ -Wcast-align -Wcast-qual -Wno-ctor-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.cpp \ From bd75ef9f271da4093f8e409329464811673b3de4 Mon Sep 17 00:00:00 2001 From: Niels Date: Thu, 4 Aug 2016 22:05:16 +0200 Subject: [PATCH 09/77] added check-fast flag --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index b6116cc4a..f08d5cf37 100644 --- a/Makefile +++ b/Makefile @@ -27,6 +27,9 @@ json_unit: check: json_unit test/json_unit "*" +check-fast: json_unit + test/json_unit + ########################################################################## # documentation tests From 2d3374c8b2661c83e457f3b6511c3b6f8d386cee Mon Sep 17 00:00:00 2001 From: Niels Date: Thu, 4 Aug 2016 22:10:30 +0200 Subject: [PATCH 10/77] removed codecov --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 06f25879a..b0cde48df 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,8 +25,7 @@ matrix: - touch src/json.hpp - make json_unit CXXFLAGS="-fprofile-arcs -ftest-coverage -std=c++11 -lstdc++" CXX=$COMPILER - test/json_unit "*" - - coveralls --exclude test/src/catch.hpp --exclude test/src/unit.cpp --include src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' - - bash <(curl -s https://codecov.io/bash) + - coveralls --exclude test/src/catch.hpp --exclude test/src/unit*.cpp --include src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' env: COMPILER=g++-4.9 - os: linux From 38f562af2a707c197732b73005bbcb47c993fbbe Mon Sep 17 00:00:00 2001 From: Niels Date: Thu, 4 Aug 2016 22:51:08 +0200 Subject: [PATCH 11/77] reorganization into smaller test units --- .travis.yml | 2 +- test/CMakeLists.txt | 9 +- test/Makefile | 9 +- ...-constructor.cpp => unit-constructor1.cpp} | 159 --- test/src/unit-constructor2.cpp | 191 ++++ test/src/unit-element_access1.cpp | 947 ++++++++++++++++++ ...nt_access.cpp => unit-element_access2.cpp} | 914 +---------------- ...unit-iterators.cpp => unit-iterators1.cpp} | 840 +--------------- test/src/unit-iterators2.cpp | 873 ++++++++++++++++ 9 files changed, 2026 insertions(+), 1918 deletions(-) rename test/src/{unit-constructor.cpp => unit-constructor1.cpp} (93%) create mode 100644 test/src/unit-constructor2.cpp create mode 100644 test/src/unit-element_access1.cpp rename test/src/{unit-element_access.cpp => unit-element_access2.cpp} (56%) rename test/src/{unit-iterators.cpp => unit-iterators1.cpp} (55%) create mode 100644 test/src/unit-iterators2.cpp diff --git a/.travis.yml b/.travis.yml index b0cde48df..1ab50bfad 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,7 @@ matrix: - touch src/json.hpp - make json_unit CXXFLAGS="-fprofile-arcs -ftest-coverage -std=c++11 -lstdc++" CXX=$COMPILER - test/json_unit "*" - - coveralls --exclude test/src/catch.hpp --exclude test/src/unit*.cpp --include src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' + - coveralls --exclude test/src/catch.hpp --exclude test/src/unit-algorithms.cpp --exclude test/src/unit-allocator.cpp --exclude test/src/unit-capacity.cpp --exclude test/src/unit-class_const_iterator.cpp --exclude test/src/unit-class_iterator.cpp --exclude test/src/unit-class_lexer.cpp --exclude test/src/unit-class_parser.cpp --exclude test/src/unit-comparison.cpp --exclude test/src/unit-concepts.cpp --exclude test/src/unit-constructor1.cpp --exclude test/src/unit-constructor2.cpp --exclude test/src/unit-convenience.cpp --exclude test/src/unit-conversions.cpp --exclude test/src/unit-deserialization.cpp --exclude test/src/unit-element_access1.cpp --exclude test/src/unit-element_access2.cpp --exclude test/src/unit-inspection.cpp --exclude test/src/unit-iterator_wrapper.cpp --exclude test/src/unit-iterators1.cpp --exclude test/src/unit-iterators2.cpp --exclude test/src/unit-json_patch.cpp --exclude test/src/unit-json_pointer.cpp --exclude test/src/unit-modifiers.cpp --exclude test/src/unit-pointer_access.cpp --exclude test/src/unit-readme.cpp --exclude test/src/unit-reference_access.cpp --exclude test/src/unit-regression.cpp --exclude test/src/unit-serialization.cpp --exclude test/src/unit-testsuites.cpp --exclude test/src/unit-unicode.cpp --include src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' env: COMPILER=g++-4.9 - os: linux diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 696a7f3f6..782d5b53f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -12,14 +12,17 @@ add_executable(${JSON_UNITTEST_TARGET_NAME} "src/unit-class_parser.cpp" "src/unit-comparison.cpp" "src/unit-concepts.cpp" - "src/unit-constructor.cpp" + "src/unit-constructor1.cpp" + "src/unit-constructor2.cpp" "src/unit-convenience.cpp" "src/unit-conversions.cpp" "src/unit-deserialization.cpp" - "src/unit-element_access.cpp" + "src/unit-element_access1.cpp" + "src/unit-element_access2.cpp" "src/unit-inspection.cpp" "src/unit-iterator_wrapper.cpp" - "src/unit-iterators.cpp" + "src/unit-iterators1.cpp" + "src/unit-iterators2.cpp" "src/unit-json_patch.cpp" "src/unit-json_pointer.cpp" "src/unit-modifiers.cpp" diff --git a/test/Makefile b/test/Makefile index 9edda2d25..7bc8274f1 100644 --- a/test/Makefile +++ b/test/Makefile @@ -16,14 +16,17 @@ SOURCES = src/unit.cpp \ src/unit-class_parser.cpp \ src/unit-comparison.cpp \ src/unit-concepts.cpp \ - src/unit-constructor.cpp \ + src/unit-constructor1.cpp \ + src/unit-constructor2.cpp \ src/unit-convenience.cpp \ src/unit-conversions.cpp \ src/unit-deserialization.cpp \ - src/unit-element_access.cpp \ + src/unit-element_access1.cpp \ + src/unit-element_access2.cpp \ src/unit-inspection.cpp \ src/unit-iterator_wrapper.cpp \ - src/unit-iterators.cpp \ + src/unit-iterators1.cpp \ + src/unit-iterators2.cpp \ src/unit-json_patch.cpp \ src/unit-json_pointer.cpp \ src/unit-modifiers.cpp \ diff --git a/test/src/unit-constructor.cpp b/test/src/unit-constructor1.cpp similarity index 93% rename from test/src/unit-constructor.cpp rename to test/src/unit-constructor1.cpp index e4842a580..87728648a 100644 --- a/test/src/unit-constructor.cpp +++ b/test/src/unit-constructor1.cpp @@ -1309,162 +1309,3 @@ TEST_CASE("constructors") } } } - -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-constructor2.cpp b/test/src/unit-constructor2.cpp new file mode 100644 index 000000000..b5f1a5e39 --- /dev/null +++ b/test/src/unit-constructor2.cpp @@ -0,0 +1,191 @@ +/* + __ _____ _____ _____ + __| | __| | | | 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("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-element_access1.cpp b/test/src/unit-element_access1.cpp new file mode 100644 index 000000000..0e515a8e4 --- /dev/null +++ b/test/src/unit-element_access1.cpp @@ -0,0 +1,947 @@ +/* + __ _____ _____ _____ + __| | __| | | | 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 1") +{ + 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("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-element_access.cpp b/test/src/unit-element_access2.cpp similarity index 56% rename from test/src/unit-element_access.cpp rename to test/src/unit-element_access2.cpp index bd33e1e04..adadb726c 100644 --- a/test/src/unit-element_access.cpp +++ b/test/src/unit-element_access2.cpp @@ -31,466 +31,8 @@ SOFTWARE. #include "json.hpp" using nlohmann::json; -TEST_CASE("element access") +TEST_CASE("element access 2") { - 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}}}; @@ -1414,458 +956,4 @@ TEST_CASE("element access") } } } - - 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-iterators.cpp b/test/src/unit-iterators1.cpp similarity index 55% rename from test/src/unit-iterators.cpp rename to test/src/unit-iterators1.cpp index 06dcb6aaa..f8b4e6bd8 100644 --- a/test/src/unit-iterators.cpp +++ b/test/src/unit-iterators1.cpp @@ -32,7 +32,7 @@ SOFTWARE. #include "json.hpp" using nlohmann::json; -TEST_CASE("iterators") +TEST_CASE("iterators 1") { SECTION("basic behavior") { @@ -1511,842 +1511,4 @@ TEST_CASE("iterators") } } } - - 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-iterators2.cpp b/test/src/unit-iterators2.cpp new file mode 100644 index 000000000..cc5d98185 --- /dev/null +++ b/test/src/unit-iterators2.cpp @@ -0,0 +1,873 @@ +/* + __ _____ _____ _____ + __| | __| | | | 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("iterators 2") +{ + 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"); + } + } + } + } +} From 00046f6ff10ec0067cc2376a5b09434ceb04c026 Mon Sep 17 00:00:00 2001 From: Niels Date: Fri, 5 Aug 2016 08:18:19 +0200 Subject: [PATCH 12/77] fix for coveralls --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1ab50bfad..1d973fd3c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,7 @@ matrix: - touch src/json.hpp - make json_unit CXXFLAGS="-fprofile-arcs -ftest-coverage -std=c++11 -lstdc++" CXX=$COMPILER - test/json_unit "*" - - coveralls --exclude test/src/catch.hpp --exclude test/src/unit-algorithms.cpp --exclude test/src/unit-allocator.cpp --exclude test/src/unit-capacity.cpp --exclude test/src/unit-class_const_iterator.cpp --exclude test/src/unit-class_iterator.cpp --exclude test/src/unit-class_lexer.cpp --exclude test/src/unit-class_parser.cpp --exclude test/src/unit-comparison.cpp --exclude test/src/unit-concepts.cpp --exclude test/src/unit-constructor1.cpp --exclude test/src/unit-constructor2.cpp --exclude test/src/unit-convenience.cpp --exclude test/src/unit-conversions.cpp --exclude test/src/unit-deserialization.cpp --exclude test/src/unit-element_access1.cpp --exclude test/src/unit-element_access2.cpp --exclude test/src/unit-inspection.cpp --exclude test/src/unit-iterator_wrapper.cpp --exclude test/src/unit-iterators1.cpp --exclude test/src/unit-iterators2.cpp --exclude test/src/unit-json_patch.cpp --exclude test/src/unit-json_pointer.cpp --exclude test/src/unit-modifiers.cpp --exclude test/src/unit-pointer_access.cpp --exclude test/src/unit-readme.cpp --exclude test/src/unit-reference_access.cpp --exclude test/src/unit-regression.cpp --exclude test/src/unit-serialization.cpp --exclude test/src/unit-testsuites.cpp --exclude test/src/unit-unicode.cpp --include src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' + - cd test ; coveralls --exclude src/catch.hpp --exclude src/unit-algorithms.cpp --exclude src/unit-allocator.cpp --exclude src/unit-capacity.cpp --exclude src/unit-class_const_iterator.cpp --exclude src/unit-class_iterator.cpp --exclude src/unit-class_lexer.cpp --exclude src/unit-class_parser.cpp --exclude src/unit-comparison.cpp --exclude src/unit-concepts.cpp --exclude src/unit-constructor1.cpp --exclude src/unit-constructor2.cpp --exclude src/unit-convenience.cpp --exclude src/unit-conversions.cpp --exclude src/unit-deserialization.cpp --exclude src/unit-element_access1.cpp --exclude src/unit-element_access2.cpp --exclude src/unit-inspection.cpp --exclude src/unit-iterator_wrapper.cpp --exclude src/unit-iterators1.cpp --exclude src/unit-iterators2.cpp --exclude src/unit-json_patch.cpp --exclude src/unit-json_pointer.cpp --exclude src/unit-modifiers.cpp --exclude src/unit-pointer_access.cpp --exclude src/unit-readme.cpp --exclude src/unit-reference_access.cpp --exclude src/unit-regression.cpp --exclude src/unit-serialization.cpp --exclude src/unit-testsuites.cpp --exclude src/unit-unicode.cpp --include ../src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' env: COMPILER=g++-4.9 - os: linux From d3c6ed08d6423da4393c0d5651ea6a8b31085782 Mon Sep 17 00:00:00 2001 From: Niels Date: Fri, 5 Aug 2016 08:40:42 +0200 Subject: [PATCH 13/77] set build-root --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1d973fd3c..424286610 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,8 +25,7 @@ matrix: - touch src/json.hpp - make json_unit CXXFLAGS="-fprofile-arcs -ftest-coverage -std=c++11 -lstdc++" CXX=$COMPILER - test/json_unit "*" - - cd test ; coveralls --exclude src/catch.hpp --exclude src/unit-algorithms.cpp --exclude src/unit-allocator.cpp --exclude src/unit-capacity.cpp --exclude src/unit-class_const_iterator.cpp --exclude src/unit-class_iterator.cpp --exclude src/unit-class_lexer.cpp --exclude src/unit-class_parser.cpp --exclude src/unit-comparison.cpp --exclude src/unit-concepts.cpp --exclude src/unit-constructor1.cpp --exclude src/unit-constructor2.cpp --exclude src/unit-convenience.cpp --exclude src/unit-conversions.cpp --exclude src/unit-deserialization.cpp --exclude src/unit-element_access1.cpp --exclude src/unit-element_access2.cpp --exclude src/unit-inspection.cpp --exclude src/unit-iterator_wrapper.cpp --exclude src/unit-iterators1.cpp --exclude src/unit-iterators2.cpp --exclude src/unit-json_patch.cpp --exclude src/unit-json_pointer.cpp --exclude src/unit-modifiers.cpp --exclude src/unit-pointer_access.cpp --exclude src/unit-readme.cpp --exclude src/unit-reference_access.cpp --exclude src/unit-regression.cpp --exclude src/unit-serialization.cpp --exclude src/unit-testsuites.cpp --exclude src/unit-unicode.cpp --include ../src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' - env: COMPILER=g++-4.9 + - coveralls --build-root test --exclude src/catch.hpp --exclude src/unit-algorithms.cpp --exclude src/unit-allocator.cpp --exclude src/unit-capacity.cpp --exclude src/unit-class_const_iterator.cpp --exclude src/unit-class_iterator.cpp --exclude src/unit-class_lexer.cpp --exclude src/unit-class_parser.cpp --exclude src/unit-comparison.cpp --exclude src/unit-concepts.cpp --exclude src/unit-constructor1.cpp --exclude src/unit-constructor2.cpp --exclude src/unit-convenience.cpp --exclude src/unit-conversions.cpp --exclude src/unit-deserialization.cpp --exclude src/unit-element_access1.cpp --exclude src/unit-element_access2.cpp --exclude src/unit-inspection.cpp --exclude src/unit-iterator_wrapper.cpp --exclude src/unit-iterators1.cpp --exclude src/unit-iterators2.cpp --exclude src/unit-json_patch.cpp --exclude src/unit-json_pointer.cpp --exclude src/unit-modifiers.cpp --exclude src/unit-pointer_access.cpp --exclude src/unit-readme.cpp --exclude src/unit-reference_access.cpp --exclude src/unit-regression.cpp --exclude src/unit-serialization.cpp --exclude src/unit-testsuites.cpp --exclude src/unit-unicode.cpp --include ../src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' - os: linux compiler: gcc From fa4fd334b287d5e9d24589e9d12693ae479aa0f9 Mon Sep 17 00:00:00 2001 From: Niels Date: Fri, 5 Aug 2016 08:56:58 +0200 Subject: [PATCH 14/77] accidentially deleted env --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 424286610..929095bee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,6 +26,7 @@ matrix: - make json_unit CXXFLAGS="-fprofile-arcs -ftest-coverage -std=c++11 -lstdc++" CXX=$COMPILER - test/json_unit "*" - coveralls --build-root test --exclude src/catch.hpp --exclude src/unit-algorithms.cpp --exclude src/unit-allocator.cpp --exclude src/unit-capacity.cpp --exclude src/unit-class_const_iterator.cpp --exclude src/unit-class_iterator.cpp --exclude src/unit-class_lexer.cpp --exclude src/unit-class_parser.cpp --exclude src/unit-comparison.cpp --exclude src/unit-concepts.cpp --exclude src/unit-constructor1.cpp --exclude src/unit-constructor2.cpp --exclude src/unit-convenience.cpp --exclude src/unit-conversions.cpp --exclude src/unit-deserialization.cpp --exclude src/unit-element_access1.cpp --exclude src/unit-element_access2.cpp --exclude src/unit-inspection.cpp --exclude src/unit-iterator_wrapper.cpp --exclude src/unit-iterators1.cpp --exclude src/unit-iterators2.cpp --exclude src/unit-json_patch.cpp --exclude src/unit-json_pointer.cpp --exclude src/unit-modifiers.cpp --exclude src/unit-pointer_access.cpp --exclude src/unit-readme.cpp --exclude src/unit-reference_access.cpp --exclude src/unit-regression.cpp --exclude src/unit-serialization.cpp --exclude src/unit-testsuites.cpp --exclude src/unit-unicode.cpp --include ../src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' + env: COMPILER=g++-4.9 - os: linux compiler: gcc From 0b34ddd47a5b06ec3a16246d03f60ffd39f8c5a8 Mon Sep 17 00:00:00 2001 From: Niels Date: Tue, 9 Aug 2016 18:19:54 +0200 Subject: [PATCH 15/77] another try for coveralls --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 929095bee..63111591e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ matrix: after_success: - make clean - touch src/json.hpp - - make json_unit CXXFLAGS="-fprofile-arcs -ftest-coverage -std=c++11 -lstdc++" CXX=$COMPILER + - make -C test CXXFLAGS="-fprofile-arcs -ftest-coverage -std=c++11 -lstdc++" CXX=$COMPILER - test/json_unit "*" - coveralls --build-root test --exclude src/catch.hpp --exclude src/unit-algorithms.cpp --exclude src/unit-allocator.cpp --exclude src/unit-capacity.cpp --exclude src/unit-class_const_iterator.cpp --exclude src/unit-class_iterator.cpp --exclude src/unit-class_lexer.cpp --exclude src/unit-class_parser.cpp --exclude src/unit-comparison.cpp --exclude src/unit-concepts.cpp --exclude src/unit-constructor1.cpp --exclude src/unit-constructor2.cpp --exclude src/unit-convenience.cpp --exclude src/unit-conversions.cpp --exclude src/unit-deserialization.cpp --exclude src/unit-element_access1.cpp --exclude src/unit-element_access2.cpp --exclude src/unit-inspection.cpp --exclude src/unit-iterator_wrapper.cpp --exclude src/unit-iterators1.cpp --exclude src/unit-iterators2.cpp --exclude src/unit-json_patch.cpp --exclude src/unit-json_pointer.cpp --exclude src/unit-modifiers.cpp --exclude src/unit-pointer_access.cpp --exclude src/unit-readme.cpp --exclude src/unit-reference_access.cpp --exclude src/unit-regression.cpp --exclude src/unit-serialization.cpp --exclude src/unit-testsuites.cpp --exclude src/unit-unicode.cpp --include ../src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' env: COMPILER=g++-4.9 From ff612e0e39b76a75e062f4355e6faa0c7a285a25 Mon Sep 17 00:00:00 2001 From: Niels Date: Tue, 9 Aug 2016 18:33:06 +0200 Subject: [PATCH 16/77] reverted last commit --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 63111591e..929095bee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ matrix: after_success: - make clean - touch src/json.hpp - - make -C test CXXFLAGS="-fprofile-arcs -ftest-coverage -std=c++11 -lstdc++" CXX=$COMPILER + - make json_unit CXXFLAGS="-fprofile-arcs -ftest-coverage -std=c++11 -lstdc++" CXX=$COMPILER - test/json_unit "*" - coveralls --build-root test --exclude src/catch.hpp --exclude src/unit-algorithms.cpp --exclude src/unit-allocator.cpp --exclude src/unit-capacity.cpp --exclude src/unit-class_const_iterator.cpp --exclude src/unit-class_iterator.cpp --exclude src/unit-class_lexer.cpp --exclude src/unit-class_parser.cpp --exclude src/unit-comparison.cpp --exclude src/unit-concepts.cpp --exclude src/unit-constructor1.cpp --exclude src/unit-constructor2.cpp --exclude src/unit-convenience.cpp --exclude src/unit-conversions.cpp --exclude src/unit-deserialization.cpp --exclude src/unit-element_access1.cpp --exclude src/unit-element_access2.cpp --exclude src/unit-inspection.cpp --exclude src/unit-iterator_wrapper.cpp --exclude src/unit-iterators1.cpp --exclude src/unit-iterators2.cpp --exclude src/unit-json_patch.cpp --exclude src/unit-json_pointer.cpp --exclude src/unit-modifiers.cpp --exclude src/unit-pointer_access.cpp --exclude src/unit-readme.cpp --exclude src/unit-reference_access.cpp --exclude src/unit-regression.cpp --exclude src/unit-serialization.cpp --exclude src/unit-testsuites.cpp --exclude src/unit-unicode.cpp --include ../src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' env: COMPILER=g++-4.9 From 117fd59abd1854bd4d90f5276cc4f82268a58ae2 Mon Sep 17 00:00:00 2001 From: Niels Date: Tue, 9 Aug 2016 22:18:13 +0200 Subject: [PATCH 17/77] first try --- .travis.yml | 371 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 212 insertions(+), 159 deletions(-) diff --git a/.travis.yml b/.travis.yml index 929095bee..fc631eeeb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,175 +1,228 @@ +######################### +# project configuration # +######################### + +# C++ project language: cpp -dist: trusty -sudo: required +# container-based build +sudo: false -env: - global: - # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created - # via the "travis encrypt" command using the project repo's public key - - secure: "m89SSgE+ASLO38rSKx7MTXK3n5NkP9bIx95jwY71YEiuFzib30PDJ/DifKnXxBjvy/AkCGztErQRk/8ZCvq+4HXozU2knEGnL/RUitvlwbhzfh2D4lmS3BvWBGS3N3NewoPBrRmdcvnT0xjOGXxtZaJ3P74TkB9GBnlz/HmKORA=" +addons: + apt: + packages: + - g++-6 + sources: &sources + - ubuntu-toolchain-r-test + + +################ +# build matrix # +################ -# from http://stackoverflow.com/a/32127147/266378 matrix: include: - - os: linux - compiler: gcc - addons: - apt: - sources: ['ubuntu-toolchain-r-test'] - packages: ['g++-4.9', 'valgrind', 'python-pip', 'python-yaml'] - before_script: - - pip install --user git+git://github.com/eddyxu/cpp-coveralls.git - after_success: - - make clean - - touch src/json.hpp - - make json_unit CXXFLAGS="-fprofile-arcs -ftest-coverage -std=c++11 -lstdc++" CXX=$COMPILER - - test/json_unit "*" - - coveralls --build-root test --exclude src/catch.hpp --exclude src/unit-algorithms.cpp --exclude src/unit-allocator.cpp --exclude src/unit-capacity.cpp --exclude src/unit-class_const_iterator.cpp --exclude src/unit-class_iterator.cpp --exclude src/unit-class_lexer.cpp --exclude src/unit-class_parser.cpp --exclude src/unit-comparison.cpp --exclude src/unit-concepts.cpp --exclude src/unit-constructor1.cpp --exclude src/unit-constructor2.cpp --exclude src/unit-convenience.cpp --exclude src/unit-conversions.cpp --exclude src/unit-deserialization.cpp --exclude src/unit-element_access1.cpp --exclude src/unit-element_access2.cpp --exclude src/unit-inspection.cpp --exclude src/unit-iterator_wrapper.cpp --exclude src/unit-iterators1.cpp --exclude src/unit-iterators2.cpp --exclude src/unit-json_patch.cpp --exclude src/unit-json_pointer.cpp --exclude src/unit-modifiers.cpp --exclude src/unit-pointer_access.cpp --exclude src/unit-readme.cpp --exclude src/unit-reference_access.cpp --exclude src/unit-regression.cpp --exclude src/unit-serialization.cpp --exclude src/unit-testsuites.cpp --exclude src/unit-unicode.cpp --include ../src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' - env: COMPILER=g++-4.9 - - os: linux - compiler: gcc - before_install: echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-certificates.crt - addons: - apt: - sources: ['ubuntu-toolchain-r-test'] - packages: ['g++-5', 'valgrind'] - coverity_scan: - project: - name: "nlohmann/json" - description: "Build submitted via Travis CI" - notification_email: niels.lohmann@gmail.com - build_command_prepend: "make clean ; sudo cp $(which g++-5) $(which g++)" - build_command: "make" - branch_pattern: coverity_scan - env: COMPILER=g++-5 + - os: osx + osx_image: xcode7.3 - - os: linux - compiler: gcc - addons: - apt: - sources: ['ubuntu-toolchain-r-test'] - packages: ['g++-6', 'valgrind'] - env: COMPILER=g++-6 + - os: osx + osx_image: xcode8 - # from https://github.com/travis-ci/travis-ci/issues/6120 - - os: linux - env: - - LLVM_VERSION=3.8.0 - - LLVM_ARCHIVE_PATH=$HOME/clang+llvm.tar.xz - - COMPILER=clang++ - - CPPFLAGS="-I $HOME/clang-$LLVM_VERSION/include/c++/v1" - - LDFLAGS=-lc++ - - PATH=$HOME/clang-$LLVM_VERSION/bin:$PATH - - LD_LIBRARY_PATH=$HOME/clang-$LLVM_VERSION/lib:$LD_LIBRARY_PATH - before_install: - - wget http://llvm.org/releases/$LLVM_VERSION/clang+llvm-$LLVM_VERSION-x86_64-linux-gnu-ubuntu-14.04.tar.xz -O $LLVM_ARCHIVE_PATH - - mkdir $HOME/clang-$LLVM_VERSION - - tar xf $LLVM_ARCHIVE_PATH -C $HOME/clang-$LLVM_VERSION --strip-components 1 + - os: linux + env: COMPILER=g++-6 + compiler: gcc - # Clang 3.5 is not able to compile the code, - # see https://travis-ci.org/nlohmann/json/jobs/126720186 -# - os: linux -# compiler: clang -# addons: -# apt: -# sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6'] -# packages: ['clang-3.6', 'valgrind'] -# env: COMPILER=clang++-3.6 -# -# - os: linux -# compiler: clang -# addons: -# apt: -# sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.7'] -# packages: ['clang-3.7', 'valgrind'] -# env: COMPILER=clang++-3.7 -# -# - os: linux -# compiler: clang -# addons: -# apt: -# sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.8'] -# packages: ['clang-3.8', 'valgrind'] -# env: COMPILER=clang++-3.8 +##################### +# installation step # +##################### - # - os: linux - # compiler: clang - # addons: - # apt: - # sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise'] - # packages: ['clang-3.9', 'valgrind'] - # env: COMPILER=clang++-3.9 +install: + - if [[ "${COMPILER}" != "" ]]; then export CXX=${COMPILER}; fi - - os: osx - osx_image: beta-xcode6.1 - compiler: clang - env: - - COMPILER=clang - - CXXFLAGS=-lstdc++ - - os: osx - osx_image: beta-xcode6.2 - compiler: clang - env: - - COMPILER=clang - - CXXFLAGS=-lstdc++ - - - os: osx - osx_image: beta-xcode6.3 - compiler: clang - env: - - COMPILER=clang - - CXXFLAGS=-lstdc++ - - - os: osx - osx_image: xcode6.4 - compiler: clang - env: - - COMPILER=clang - - CXXFLAGS=-lstdc++ - - - os: osx - osx_image: xcode7.1 - compiler: clang - env: - - COMPILER=clang - - CXXFLAGS=-lstdc++ - - - os: osx - osx_image: xcode7.2 - compiler: clang - env: - - COMPILER=clang - - CXXFLAGS=-lstdc++ - - - os: osx - osx_image: xcode7.3 - compiler: clang - env: - - COMPILER=clang - - CXXFLAGS=-lstdc++ - - - os: osx - osx_image: xcode8 - compiler: clang - env: - - COMPILER=clang - - CXXFLAGS=-lstdc++ +################ +# build script # +################ script: - - uname -a - - $COMPILER --version - - make CXX=$COMPILER - - test/json_unit "*" - - if [ `which valgrind` ]; then - valgrind --error-exitcode=1 --leak-check=full test/json_unit ; - fi - - if [ `which brew` ]; then - brew update ; - brew tap nlohmann/json ; - brew install nlohmann_json --HEAD ; - brew test nlohmann_json ; - fi + - make + - test/json_unit + +#language: cpp +# +#dist: trusty +#sudo: required +# +#env: +# global: +# # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created +# # via the "travis encrypt" command using the project repo's public key +# - secure: "m89SSgE+ASLO38rSKx7MTXK3n5NkP9bIx95jwY71YEiuFzib30PDJ/DifKnXxBjvy/AkCGztErQRk/8ZCvq+4HXozU2knEGnL/RUitvlwbhzfh2D4lmS3BvWBGS3N3NewoPBrRmdcvnT0xjOGXxtZaJ3P74TkB9GBnlz/HmKORA=" +# +## from http://stackoverflow.com/a/32127147/266378 +#matrix: +# include: +# - os: linux +# compiler: gcc +# addons: +# apt: +# sources: ['ubuntu-toolchain-r-test'] +# packages: ['g++-4.9', 'valgrind', 'python-pip', 'python-yaml'] +# before_script: +# - pip install --user git+git://github.com/eddyxu/cpp-coveralls.git +# after_success: +# - make clean +# - touch src/json.hpp +# - make json_unit CXXFLAGS="-fprofile-arcs -ftest-coverage -std=c++11 -lstdc++" CXX=$COMPILER +# - test/json_unit "*" +# - coveralls --build-root test --exclude src/catch.hpp --exclude src/unit-algorithms.cpp --exclude src/unit-allocator.cpp --exclude src/unit-capacity.cpp --exclude src/unit-class_const_iterator.cpp --exclude src/unit-class_iterator.cpp --exclude src/unit-class_lexer.cpp --exclude src/unit-class_parser.cpp --exclude src/unit-comparison.cpp --exclude src/unit-concepts.cpp --exclude src/unit-constructor1.cpp --exclude src/unit-constructor2.cpp --exclude src/unit-convenience.cpp --exclude src/unit-conversions.cpp --exclude src/unit-deserialization.cpp --exclude src/unit-element_access1.cpp --exclude src/unit-element_access2.cpp --exclude src/unit-inspection.cpp --exclude src/unit-iterator_wrapper.cpp --exclude src/unit-iterators1.cpp --exclude src/unit-iterators2.cpp --exclude src/unit-json_patch.cpp --exclude src/unit-json_pointer.cpp --exclude src/unit-modifiers.cpp --exclude src/unit-pointer_access.cpp --exclude src/unit-readme.cpp --exclude src/unit-reference_access.cpp --exclude src/unit-regression.cpp --exclude src/unit-serialization.cpp --exclude src/unit-testsuites.cpp --exclude src/unit-unicode.cpp --include ../src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' +# env: COMPILER=g++-4.9 +# +# - os: linux +# compiler: gcc +# before_install: echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-certificates.crt +# addons: +# apt: +# sources: ['ubuntu-toolchain-r-test'] +# packages: ['g++-5', 'valgrind'] +# coverity_scan: +# project: +# name: "nlohmann/json" +# description: "Build submitted via Travis CI" +# notification_email: niels.lohmann@gmail.com +# build_command_prepend: "make clean ; sudo cp $(which g++-5) $(which g++)" +# build_command: "make" +# branch_pattern: coverity_scan +# env: COMPILER=g++-5 +# +# - os: linux +# compiler: gcc +# addons: +# apt: +# sources: ['ubuntu-toolchain-r-test'] +# packages: ['g++-6', 'valgrind'] +# env: COMPILER=g++-6 +# +# # from https://github.com/travis-ci/travis-ci/issues/6120 +# - os: linux +# env: +# - LLVM_VERSION=3.8.0 +# - LLVM_ARCHIVE_PATH=$HOME/clang+llvm.tar.xz +# - COMPILER=clang++ +# - CPPFLAGS="-I $HOME/clang-$LLVM_VERSION/include/c++/v1" +# - LDFLAGS=-lc++ +# - PATH=$HOME/clang-$LLVM_VERSION/bin:$PATH +# - LD_LIBRARY_PATH=$HOME/clang-$LLVM_VERSION/lib:$LD_LIBRARY_PATH +# before_install: +# - wget http://llvm.org/releases/$LLVM_VERSION/clang+llvm-$LLVM_VERSION-x86_64-linux-gnu-ubuntu-14.04.tar.xz -O $LLVM_ARCHIVE_PATH +# - mkdir $HOME/clang-$LLVM_VERSION +# - tar xf $LLVM_ARCHIVE_PATH -C $HOME/clang-$LLVM_VERSION --strip-components 1 +# +# # Clang 3.5 is not able to compile the code, +# # see https://travis-ci.org/nlohmann/json/jobs/126720186 +# +## - os: linux +## compiler: clang +## addons: +## apt: +## sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6'] +## packages: ['clang-3.6', 'valgrind'] +## env: COMPILER=clang++-3.6 +## +## - os: linux +## compiler: clang +## addons: +## apt: +## sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.7'] +## packages: ['clang-3.7', 'valgrind'] +## env: COMPILER=clang++-3.7 +## +## - os: linux +## compiler: clang +## addons: +## apt: +## sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.8'] +## packages: ['clang-3.8', 'valgrind'] +## env: COMPILER=clang++-3.8 +# +# # - os: linux +# # compiler: clang +# # addons: +# # apt: +# # sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise'] +# # packages: ['clang-3.9', 'valgrind'] +# # env: COMPILER=clang++-3.9 +# +# - os: osx +# osx_image: beta-xcode6.1 +# compiler: clang +# env: +# - COMPILER=clang +# - CXXFLAGS=-lstdc++ +# +# - os: osx +# osx_image: beta-xcode6.2 +# compiler: clang +# env: +# - COMPILER=clang +# - CXXFLAGS=-lstdc++ +# +# - os: osx +# osx_image: beta-xcode6.3 +# compiler: clang +# env: +# - COMPILER=clang +# - CXXFLAGS=-lstdc++ +# +# - os: osx +# osx_image: xcode6.4 +# compiler: clang +# env: +# - COMPILER=clang +# - CXXFLAGS=-lstdc++ +# +# - os: osx +# osx_image: xcode7.1 +# compiler: clang +# env: +# - COMPILER=clang +# - CXXFLAGS=-lstdc++ +# +# - os: osx +# osx_image: xcode7.2 +# compiler: clang +# env: +# - COMPILER=clang +# - CXXFLAGS=-lstdc++ +# +# - os: osx +# osx_image: xcode7.3 +# compiler: clang +# env: +# - COMPILER=clang +# - CXXFLAGS=-lstdc++ +# +# - os: osx +# osx_image: xcode8 +# compiler: clang +# env: +# - COMPILER=clang +# - CXXFLAGS=-lstdc++ +# +#script: +# - uname -a +# - $COMPILER --version +# - make CXX=$COMPILER +# - test/json_unit "*" +# - if [ `which valgrind` ]; then +# valgrind --error-exitcode=1 --leak-check=full test/json_unit ; +# fi +# - if [ `which brew` ]; then +# brew update ; +# brew tap nlohmann/json ; +# brew install nlohmann_json --HEAD ; +# brew test nlohmann_json ; +# fi +# \ No newline at end of file From 4b37082e365103695fbfff01d412d78951f7bcad Mon Sep 17 00:00:00 2001 From: Niels Date: Tue, 9 Aug 2016 22:29:01 +0200 Subject: [PATCH 18/77] more GCC versions --- .travis.yml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index fc631eeeb..167167967 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,8 +10,6 @@ sudo: false addons: apt: - packages: - - g++-6 sources: &sources - ubuntu-toolchain-r-test @@ -30,8 +28,18 @@ matrix: osx_image: xcode8 - os: linux - env: COMPILER=g++-6 compiler: gcc + env: COMPILER=g++-4.9 + addons: + apt: + packages: g++-4.9 + + - os: linux + compiler: gcc + env: COMPILER=g++-5 + addons: + apt: + packages: g++-5 ##################### @@ -47,6 +55,8 @@ install: ################ script: + - uname -a + - $CXX --version - make - test/json_unit From be05dbe6188013f82267ebf19d43fb68ca5ba77a Mon Sep 17 00:00:00 2001 From: Niels Date: Tue, 9 Aug 2016 22:33:31 +0200 Subject: [PATCH 19/77] moved sources --- .travis.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 167167967..2f541e42c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,11 +8,6 @@ language: cpp # container-based build sudo: false -addons: - apt: - sources: &sources - - ubuntu-toolchain-r-test - ################ # build matrix # @@ -32,6 +27,7 @@ matrix: env: COMPILER=g++-4.9 addons: apt: + sources: ['ubuntu-toolchain-r-test'] packages: g++-4.9 - os: linux @@ -39,6 +35,7 @@ matrix: env: COMPILER=g++-5 addons: apt: + sources: ['ubuntu-toolchain-r-test'] packages: g++-5 From 6150ffb9dce6fd2588d57a53c1de30ab9ee59d88 Mon Sep 17 00:00:00 2001 From: Niels Date: Tue, 9 Aug 2016 22:49:08 +0200 Subject: [PATCH 20/77] more compilers --- .travis.yml | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/.travis.yml b/.travis.yml index 2f541e42c..e0b2db509 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,12 +16,34 @@ sudo: false matrix: include: + # OSX / Clang + + - os: osx + osx_image: beta-xcode6.1 + + - os: osx + osx_image: beta-xcode6.2 + + - os: osx + osx_image: beta-xcode6.3 + + - os: osx + osx_image: xcode6.4 + + - os: osx + osx_image: xcode7.1 + + - os: osx + osx_image: xcode7.2 + - os: osx osx_image: xcode7.3 - os: osx osx_image: xcode8 + # Linux / GCC + - os: linux compiler: gcc env: COMPILER=g++-4.9 @@ -38,6 +60,14 @@ matrix: sources: ['ubuntu-toolchain-r-test'] packages: g++-5 + - os: linux + compiler: gcc + env: COMPILER=g++-6 + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: g++-6 + ##################### # installation step # @@ -56,6 +86,12 @@ script: - $CXX --version - make - test/json_unit + - if [ `which brew` ]; then + brew update ; + brew tap nlohmann/json ; + brew install nlohmann_json --HEAD ; + brew test nlohmann_json ; + fi #language: cpp # From b76861dde53ec85cd09d3467c3a8e94f33cae7f8 Mon Sep 17 00:00:00 2001 From: Niels Date: Tue, 9 Aug 2016 23:05:42 +0200 Subject: [PATCH 21/77] commit to re-fix issue #195 --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index e0b2db509..9b8feb6f3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,8 +5,8 @@ # C++ project language: cpp -# container-based build -sudo: false +dist: trusty +sudo: required ################ From b1c1fe9d39abbeb2c11fdac8238a265c916e3056 Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 10 Aug 2016 16:22:53 +0200 Subject: [PATCH 22/77] added Clang 3.8 --- .travis.yml | 35 +++++++++++++++++++++++++++++++++++ test/Makefile | 2 +- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9b8feb6f3..c102ab005 100644 --- a/.travis.yml +++ b/.travis.yml @@ -68,14 +68,49 @@ matrix: sources: ['ubuntu-toolchain-r-test'] packages: g++-6 + # Linux / Clang + + - os: linux + env: LLVM_VERSION=3.8.0 + compiler: clang ##################### # installation step # ##################### +cache: + directories: + - ${TRAVIS_BUILD_DIR}/deps/llvm-3.8.0 + + install: + - DEPS_DIR="${TRAVIS_BUILD_DIR}/deps" + - mkdir -p ${DEPS_DIR} && cd ${DEPS_DIR} + - if [[ "${COMPILER}" != "" ]]; then export CXX=${COMPILER}; fi + - | + if [[ "${LLVM_VERSION}" != "" ]]; then + LLVM_DIR=${DEPS_DIR}/llvm-${LLVM_VERSION} + if [[ -z "$(ls -A ${LLVM_DIR})" ]]; then + LLVM_URL="http://llvm.org/releases/${LLVM_VERSION}/llvm-${LLVM_VERSION}.src.tar.xz" + LIBCXX_URL="http://llvm.org/releases/${LLVM_VERSION}/libcxx-${LLVM_VERSION}.src.tar.xz" + LIBCXXABI_URL="http://llvm.org/releases/${LLVM_VERSION}/libcxxabi-${LLVM_VERSION}.src.tar.xz" + CLANG_URL="http://llvm.org/releases/${LLVM_VERSION}/clang+llvm-${LLVM_VERSION}-x86_64-linux-gnu-ubuntu-14.04.tar.xz" + mkdir -p ${LLVM_DIR} ${LLVM_DIR}/build ${LLVM_DIR}/projects/libcxx ${LLVM_DIR}/projects/libcxxabi ${LLVM_DIR}/clang + travis_retry wget --quiet -O - ${LLVM_URL} | tar --strip-components=1 -xJ -C ${LLVM_DIR} + travis_retry wget --quiet -O - ${LIBCXX_URL} | tar --strip-components=1 -xJ -C ${LLVM_DIR}/projects/libcxx + travis_retry wget --quiet -O - ${LIBCXXABI_URL} | tar --strip-components=1 -xJ -C ${LLVM_DIR}/projects/libcxxabi + travis_retry wget --quiet -O - ${CLANG_URL} | tar --strip-components=1 -xJ -C ${LLVM_DIR}/clang + (cd ${LLVM_DIR}/build && cmake .. -DCMAKE_INSTALL_PREFIX=${LLVM_DIR}/install -DCMAKE_CXX_COMPILER=clang++) + (cd ${LLVM_DIR}/build/projects/libcxx && make install -j2) + (cd ${LLVM_DIR}/build/projects/libcxxabi && make install -j2) + fi + export CXXFLAGS="-nostdinc++ -isystem ${LLVM_DIR}/install/include/c++/v1" + export LDFLAGS="-L ${LLVM_DIR}/install/lib -l c++ -l c++abi" + export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${LLVM_DIR}/install/lib" + export PATH="${LLVM_DIR}/clang/bin:${PATH}" + fi ################ # build script # diff --git a/test/Makefile b/test/Makefile index 7bc8274f1..8e1ad6a00 100644 --- a/test/Makefile +++ b/test/Makefile @@ -3,7 +3,7 @@ ########################################################################## # additional flags -CXXFLAGS += -std=c++11 -Wall -Wextra -pedantic -Weffc++ -Wcast-align -Wcast-qual -Wno-ctor-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 +CXXFLAGS += -std=c++11 -Wall -Wextra -pedantic -Weffc++ -Wcast-align -Wcast-qual -Wno-ctor-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-float-equal CPPFLAGS += -I ../src -I . SOURCES = src/unit.cpp \ From 407e8dbb8e318033e905a7545b73c267cb9bae8c Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 10 Aug 2016 16:25:35 +0200 Subject: [PATCH 23/77] fixed YAML error --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index c102ab005..3cdf45f2d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -70,9 +70,9 @@ matrix: # Linux / Clang - - os: linux - env: LLVM_VERSION=3.8.0 - compiler: clang + - os: linux + env: LLVM_VERSION=3.8.0 + compiler: clang ##################### # installation step # From 31963723d3692c22d7f4cb866e06a259ca23c6d9 Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 10 Aug 2016 16:35:57 +0200 Subject: [PATCH 24/77] no directory change --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3cdf45f2d..de6e25b1a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -85,7 +85,7 @@ cache: install: - DEPS_DIR="${TRAVIS_BUILD_DIR}/deps" - - mkdir -p ${DEPS_DIR} && cd ${DEPS_DIR} + - mkdir -p ${DEPS_DIR} - if [[ "${COMPILER}" != "" ]]; then export CXX=${COMPILER}; fi From 997bc5d1ab216dbdf2877a7237aecae7c477ee52 Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 10 Aug 2016 16:56:05 +0200 Subject: [PATCH 25/77] more clang versions --- .travis.yml | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/.travis.yml b/.travis.yml index de6e25b1a..b204d375f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -70,17 +70,62 @@ matrix: # Linux / Clang + - os: linux + env: LLVM_VERSION=3.5.0 + compiler: clang + + - os: linux + env: LLVM_VERSION=3.5.1 + compiler: clang + + - os: linux + env: LLVM_VERSION=3.5.2 + compiler: clang + + - os: linux + env: LLVM_VERSION=3.6.0 + compiler: clang + + - os: linux + env: LLVM_VERSION=3.6.1 + compiler: clang + + - os: linux + env: LLVM_VERSION=3.6.2 + compiler: clang + + - os: linux + env: LLVM_VERSION=3.7.0 + compiler: clang + + - os: linux + env: LLVM_VERSION=3.7.1 + compiler: clang + - os: linux env: LLVM_VERSION=3.8.0 compiler: clang + - os: linux + env: LLVM_VERSION=3.8.1 + compiler: clang + ##################### # installation step # ##################### cache: directories: + - ${TRAVIS_BUILD_DIR}/deps/llvm-3.5.2 + - ${TRAVIS_BUILD_DIR}/deps/llvm-3.5.1 + - ${TRAVIS_BUILD_DIR}/deps/llvm-3.5.0 + - ${TRAVIS_BUILD_DIR}/deps/llvm-3.6.2 + - ${TRAVIS_BUILD_DIR}/deps/llvm-3.6.1 + - ${TRAVIS_BUILD_DIR}/deps/llvm-3.6.0 + - ${TRAVIS_BUILD_DIR}/deps/llvm-3.7.0 + - ${TRAVIS_BUILD_DIR}/deps/llvm-3.7.1 - ${TRAVIS_BUILD_DIR}/deps/llvm-3.8.0 + - ${TRAVIS_BUILD_DIR}/deps/llvm-3.8.1 install: From 4d90331718f015b7ee90d914690b9412d740fc0a Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 10 Aug 2016 17:42:33 +0200 Subject: [PATCH 26/77] a test --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b204d375f..344aa2190 100644 --- a/.travis.yml +++ b/.travis.yml @@ -151,7 +151,7 @@ install: (cd ${LLVM_DIR}/build/projects/libcxx && make install -j2) (cd ${LLVM_DIR}/build/projects/libcxxabi && make install -j2) fi - export CXXFLAGS="-nostdinc++ -isystem ${LLVM_DIR}/install/include/c++/v1" + export CXXFLAGS="-stdlib=libc++ -nostdinc++ -isystem ${LLVM_DIR}/install/include/c++/v1" export LDFLAGS="-L ${LLVM_DIR}/install/lib -l c++ -l c++abi" export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${LLVM_DIR}/install/lib" export PATH="${LLVM_DIR}/clang/bin:${PATH}" From a2e923de32d04012f201c2cc8fe4b79e425b5608 Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 10 Aug 2016 18:38:31 +0200 Subject: [PATCH 27/77] removed clang 3.5.x --- .travis.yml | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/.travis.yml b/.travis.yml index 344aa2190..f1db0a253 100644 --- a/.travis.yml +++ b/.travis.yml @@ -70,18 +70,6 @@ matrix: # Linux / Clang - - os: linux - env: LLVM_VERSION=3.5.0 - compiler: clang - - - os: linux - env: LLVM_VERSION=3.5.1 - compiler: clang - - - os: linux - env: LLVM_VERSION=3.5.2 - compiler: clang - - os: linux env: LLVM_VERSION=3.6.0 compiler: clang @@ -116,9 +104,6 @@ matrix: cache: directories: - - ${TRAVIS_BUILD_DIR}/deps/llvm-3.5.2 - - ${TRAVIS_BUILD_DIR}/deps/llvm-3.5.1 - - ${TRAVIS_BUILD_DIR}/deps/llvm-3.5.0 - ${TRAVIS_BUILD_DIR}/deps/llvm-3.6.2 - ${TRAVIS_BUILD_DIR}/deps/llvm-3.6.1 - ${TRAVIS_BUILD_DIR}/deps/llvm-3.6.0 @@ -151,7 +136,7 @@ install: (cd ${LLVM_DIR}/build/projects/libcxx && make install -j2) (cd ${LLVM_DIR}/build/projects/libcxxabi && make install -j2) fi - export CXXFLAGS="-stdlib=libc++ -nostdinc++ -isystem ${LLVM_DIR}/install/include/c++/v1" + export CXXFLAGS="-nostdinc++ -isystem ${LLVM_DIR}/install/include/c++/v1" export LDFLAGS="-L ${LLVM_DIR}/install/lib -l c++ -l c++abi" export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${LLVM_DIR}/install/lib" export PATH="${LLVM_DIR}/clang/bin:${PATH}" @@ -166,6 +151,9 @@ script: - $CXX --version - make - test/json_unit + - if [ `which valgrind` ]; then + valgrind --error-exitcode=1 --leak-check=full test/json_unit ; + fi - if [ `which brew` ]; then brew update ; brew tap nlohmann/json ; From efe1d52629b6d661b39cf1774285096c940f7ce5 Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 10 Aug 2016 18:54:19 +0200 Subject: [PATCH 28/77] added coverity, coveralls, and valgrind --- .travis.yml | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 78 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index f1db0a253..2e6d4a1ca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,17 @@ dist: trusty sudo: required +################### +# global settings # +################### + +env: + global: + # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created + # via the "travis encrypt" command using the project repo's public key + - secure: "m89SSgE+ASLO38rSKx7MTXK3n5NkP9bIx95jwY71YEiuFzib30PDJ/DifKnXxBjvy/AkCGztErQRk/8ZCvq+4HXozU2knEGnL/RUitvlwbhzfh2D4lmS3BvWBGS3N3NewoPBrRmdcvnT0xjOGXxtZaJ3P74TkB9GBnlz/HmKORA=" + + ################ # build matrix # ################ @@ -98,10 +109,66 @@ matrix: env: LLVM_VERSION=3.8.1 compiler: clang + # Valgrind + + - os: linux + compiler: gcc + env: + - COMPILER=g++-4.9 + - SPECIAL=valgrind + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: [g++-4.9, valgrind] + after_success: + - valgrind --error-exitcode=1 --leak-check=full test/json_unit * ; + + # Coveralls + + - os: linux + compiler: gcc + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-4.9', 'valgrind', 'python-pip', 'python-yaml'] + before_script: + - pip install --user git+git://github.com/eddyxu/cpp-coveralls.git + after_success: + - make clean + - touch src/json.hpp + - make json_unit CXXFLAGS="-fprofile-arcs -ftest-coverage -std=c++11 -lstdc++" CXX=$COMPILER + - test/json_unit "*" + - coveralls --build-root test --exclude src/catch.hpp --exclude src/unit-algorithms.cpp --exclude src/unit-allocator.cpp --exclude src/unit-capacity.cpp --exclude src/unit-class_const_iterator.cpp --exclude src/unit-class_iterator.cpp --exclude src/unit-class_lexer.cpp --exclude src/unit-class_parser.cpp --exclude src/unit-comparison.cpp --exclude src/unit-concepts.cpp --exclude src/unit-constructor1.cpp --exclude src/unit-constructor2.cpp --exclude src/unit-convenience.cpp --exclude src/unit-conversions.cpp --exclude src/unit-deserialization.cpp --exclude src/unit-element_access1.cpp --exclude src/unit-element_access2.cpp --exclude src/unit-inspection.cpp --exclude src/unit-iterator_wrapper.cpp --exclude src/unit-iterators1.cpp --exclude src/unit-iterators2.cpp --exclude src/unit-json_patch.cpp --exclude src/unit-json_pointer.cpp --exclude src/unit-modifiers.cpp --exclude src/unit-pointer_access.cpp --exclude src/unit-readme.cpp --exclude src/unit-reference_access.cpp --exclude src/unit-regression.cpp --exclude src/unit-serialization.cpp --exclude src/unit-testsuites.cpp --exclude src/unit-unicode.cpp --include ../src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' + env: + - COMPILER=g++-4.9 + - SPECIAL=coveralls + + # Coverity (only for branch coverity_scan) + + - os: linux + compiler: gcc + before_install: echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-certificates.crt + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-5', 'valgrind'] + coverity_scan: + project: + name: "nlohmann/json" + description: "Build submitted via Travis CI" + notification_email: niels.lohmann@gmail.com + build_command_prepend: "make clean ; sudo cp $(which g++-5) $(which g++)" + build_command: "make" + branch_pattern: coverity_scan + env: + - COMPILER=g++-5 + - SPECIAL=coverity + ##################### # installation step # ##################### +# set directories to cache cache: directories: - ${TRAVIS_BUILD_DIR}/deps/llvm-3.6.2 @@ -114,11 +181,14 @@ cache: install: + # create deps dir if not existing - DEPS_DIR="${TRAVIS_BUILD_DIR}/deps" - mkdir -p ${DEPS_DIR} + # make sure CXX is correctly set - if [[ "${COMPILER}" != "" ]]; then export CXX=${COMPILER}; fi + # install LLVM/clang when LLVM_VERSION is set - | if [[ "${LLVM_VERSION}" != "" ]]; then LLVM_DIR=${DEPS_DIR}/llvm-${LLVM_VERSION} @@ -147,13 +217,17 @@ install: ################ script: + # show OS/compiler version - uname -a - $CXX --version + + # compile - make - - test/json_unit - - if [ `which valgrind` ]; then - valgrind --error-exitcode=1 --leak-check=full test/json_unit ; - fi + + # execute unit tests + - test/json_unit "*" + + # check if homebrew works (only checks develop branch) - if [ `which brew` ]; then brew update ; brew tap nlohmann/json ; From ce30526ee8bdf057ac81d5fa0c0d00e7eb683c61 Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 10 Aug 2016 19:35:27 +0200 Subject: [PATCH 29/77] fixed Valgrind call --- .travis.yml | 131 +--------------------------------------------------- 1 file changed, 1 insertion(+), 130 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2e6d4a1ca..903a4c587 100644 --- a/.travis.yml +++ b/.travis.yml @@ -121,7 +121,7 @@ matrix: sources: ['ubuntu-toolchain-r-test'] packages: [g++-4.9, valgrind] after_success: - - valgrind --error-exitcode=1 --leak-check=full test/json_unit * ; + - valgrind --error-exitcode=1 --leak-check=full test/json_unit "*" ; # Coveralls @@ -282,132 +282,3 @@ script: # branch_pattern: coverity_scan # env: COMPILER=g++-5 # -# - os: linux -# compiler: gcc -# addons: -# apt: -# sources: ['ubuntu-toolchain-r-test'] -# packages: ['g++-6', 'valgrind'] -# env: COMPILER=g++-6 -# -# # from https://github.com/travis-ci/travis-ci/issues/6120 -# - os: linux -# env: -# - LLVM_VERSION=3.8.0 -# - LLVM_ARCHIVE_PATH=$HOME/clang+llvm.tar.xz -# - COMPILER=clang++ -# - CPPFLAGS="-I $HOME/clang-$LLVM_VERSION/include/c++/v1" -# - LDFLAGS=-lc++ -# - PATH=$HOME/clang-$LLVM_VERSION/bin:$PATH -# - LD_LIBRARY_PATH=$HOME/clang-$LLVM_VERSION/lib:$LD_LIBRARY_PATH -# before_install: -# - wget http://llvm.org/releases/$LLVM_VERSION/clang+llvm-$LLVM_VERSION-x86_64-linux-gnu-ubuntu-14.04.tar.xz -O $LLVM_ARCHIVE_PATH -# - mkdir $HOME/clang-$LLVM_VERSION -# - tar xf $LLVM_ARCHIVE_PATH -C $HOME/clang-$LLVM_VERSION --strip-components 1 -# -# # Clang 3.5 is not able to compile the code, -# # see https://travis-ci.org/nlohmann/json/jobs/126720186 -# -## - os: linux -## compiler: clang -## addons: -## apt: -## sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6'] -## packages: ['clang-3.6', 'valgrind'] -## env: COMPILER=clang++-3.6 -## -## - os: linux -## compiler: clang -## addons: -## apt: -## sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.7'] -## packages: ['clang-3.7', 'valgrind'] -## env: COMPILER=clang++-3.7 -## -## - os: linux -## compiler: clang -## addons: -## apt: -## sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.8'] -## packages: ['clang-3.8', 'valgrind'] -## env: COMPILER=clang++-3.8 -# -# # - os: linux -# # compiler: clang -# # addons: -# # apt: -# # sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise'] -# # packages: ['clang-3.9', 'valgrind'] -# # env: COMPILER=clang++-3.9 -# -# - os: osx -# osx_image: beta-xcode6.1 -# compiler: clang -# env: -# - COMPILER=clang -# - CXXFLAGS=-lstdc++ -# -# - os: osx -# osx_image: beta-xcode6.2 -# compiler: clang -# env: -# - COMPILER=clang -# - CXXFLAGS=-lstdc++ -# -# - os: osx -# osx_image: beta-xcode6.3 -# compiler: clang -# env: -# - COMPILER=clang -# - CXXFLAGS=-lstdc++ -# -# - os: osx -# osx_image: xcode6.4 -# compiler: clang -# env: -# - COMPILER=clang -# - CXXFLAGS=-lstdc++ -# -# - os: osx -# osx_image: xcode7.1 -# compiler: clang -# env: -# - COMPILER=clang -# - CXXFLAGS=-lstdc++ -# -# - os: osx -# osx_image: xcode7.2 -# compiler: clang -# env: -# - COMPILER=clang -# - CXXFLAGS=-lstdc++ -# -# - os: osx -# osx_image: xcode7.3 -# compiler: clang -# env: -# - COMPILER=clang -# - CXXFLAGS=-lstdc++ -# -# - os: osx -# osx_image: xcode8 -# compiler: clang -# env: -# - COMPILER=clang -# - CXXFLAGS=-lstdc++ -# -#script: -# - uname -a -# - $COMPILER --version -# - make CXX=$COMPILER -# - test/json_unit "*" -# - if [ `which valgrind` ]; then -# valgrind --error-exitcode=1 --leak-check=full test/json_unit ; -# fi -# - if [ `which brew` ]; then -# brew update ; -# brew tap nlohmann/json ; -# brew install nlohmann_json --HEAD ; -# brew test nlohmann_json ; -# fi -# \ No newline at end of file From d02e67d4a957d050ab4233209c452fdf433b238a Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 10 Aug 2016 20:56:26 +0200 Subject: [PATCH 30/77] coveralls with lcov --- .gitignore | 12 ++--- .travis.yml | 134 +++++++++++++++++++++++++++++--------------------- test/Makefile | 2 +- 3 files changed, 85 insertions(+), 63 deletions(-) diff --git a/.gitignore b/.gitignore index b82214e9a..c7e847c4c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,18 +1,16 @@ json_unit json_benchmarks - fuzz-testing *.dSYM +*.o +*.gcno +*.gcda working -html +doc/xml +doc/html me.nlohmann.json.docset -android -doc/xml - benchmarks/files/numbers/*.json - -*.o diff --git a/.travis.yml b/.travis.yml index 903a4c587..03da02d5e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,6 +27,85 @@ env: matrix: include: + # Valgrind + + - os: linux + compiler: gcc + env: + - COMPILER=g++-4.9 + - SPECIAL=valgrind + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: [g++-4.9, valgrind] + after_success: + - valgrind --error-exitcode=1 --leak-check=full test/json_unit -s "*" ; + + # Coveralls + + #- os: linux + # compiler: gcc + # addons: + # apt: + # sources: ['ubuntu-toolchain-r-test'] + # packages: ['g++-4.9', 'valgrind', 'python-pip', 'python-yaml'] + # before_script: + # - pip install --user git+git://github.com/eddyxu/cpp-coveralls.git + # after_success: + # - make clean + # - touch src/json.hpp + # - make json_unit CXXFLAGS="-fprofile-arcs -ftest-coverage -std=c++11 -lstdc++" CXX=$COMPILER + # - test/json_unit "*" + # - coveralls --build-root test --exclude src/catch.hpp --exclude src/unit-algorithms.cpp --exclude src/unit-allocator.cpp --exclude src/unit-capacity.cpp --exclude src/unit-class_const_iterator.cpp --exclude src/unit-class_iterator.cpp --exclude src/unit-class_lexer.cpp --exclude src/unit-class_parser.cpp --exclude src/unit-comparison.cpp --exclude src/unit-concepts.cpp --exclude src/unit-constructor1.cpp --exclude src/unit-constructor2.cpp --exclude src/unit-convenience.cpp --exclude src/unit-conversions.cpp --exclude src/unit-deserialization.cpp --exclude src/unit-element_access1.cpp --exclude src/unit-element_access2.cpp --exclude src/unit-inspection.cpp --exclude src/unit-iterator_wrapper.cpp --exclude src/unit-iterators1.cpp --exclude src/unit-iterators2.cpp --exclude src/unit-json_patch.cpp --exclude src/unit-json_pointer.cpp --exclude src/unit-modifiers.cpp --exclude src/unit-pointer_access.cpp --exclude src/unit-readme.cpp --exclude src/unit-reference_access.cpp --exclude src/unit-regression.cpp --exclude src/unit-serialization.cpp --exclude src/unit-testsuites.cpp --exclude src/unit-unicode.cpp --include ../src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' + # env: + # - COMPILER=g++-4.9 + # - SPECIAL=coveralls + + - os: linux + compiler: gcc + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-4.9', 'rubygems'] + before_script: + - wget http://ftp.de.debian.org/debian/pool/main/l/lcov/lcov_1.11.orig.tar.gz + - tar xf lcov_1.11.orig.tar.gz + - sudo make -C lcov-1.11/ install + - gem install coveralls-lcov + after_success: + - make clean + - CXXFLAGS="--coverage -g -O0" CPPFLAGS="-DNDEBUG" make + - test/json_unit "*" + - coveralls --build-root test --exclude src/catch.hpp --exclude src/unit-algorithms.cpp --exclude src/unit-allocator.cpp --exclude src/unit-capacity.cpp --exclude src/unit-class_const_iterator.cpp --exclude src/unit-class_iterator.cpp --exclude src/unit-class_lexer.cpp --exclude src/unit-class_parser.cpp --exclude src/unit-comparison.cpp --exclude src/unit-concepts.cpp --exclude src/unit-constructor1.cpp --exclude src/unit-constructor2.cpp --exclude src/unit-convenience.cpp --exclude src/unit-conversions.cpp --exclude src/unit-deserialization.cpp --exclude src/unit-element_access1.cpp --exclude src/unit-element_access2.cpp --exclude src/unit-inspection.cpp --exclude src/unit-iterator_wrapper.cpp --exclude src/unit-iterators1.cpp --exclude src/unit-iterators2.cpp --exclude src/unit-json_patch.cpp --exclude src/unit-json_pointer.cpp --exclude src/unit-modifiers.cpp --exclude src/unit-pointer_access.cpp --exclude src/unit-readme.cpp --exclude src/unit-reference_access.cpp --exclude src/unit-regression.cpp --exclude src/unit-serialization.cpp --exclude src/unit-testsuites.cpp --exclude src/unit-unicode.cpp --include ../src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' + - lcov --directory src --directory test/src --capture --output-file coverage.info --rc lcov_branch_coverage=1 --no-external + - lcov --remove coverage.info 'test/src/*' --output-file coverage.info --rc lcov_branch_coverage=1 + - lcov --list coverage.info --rc lcov_branch_coverage=1 + - coveralls-lcov --repo-token F9bs4Nop10JRgqPQXRcifyQKYhb3FczkS coverage.info + env: + - COMPILER=g++-4.9 + - SPECIAL=coveralls + + # Coverity (only for branch coverity_scan) + + - os: linux + compiler: gcc + before_install: echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-certificates.crt + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-5', 'valgrind'] + coverity_scan: + project: + name: "nlohmann/json" + description: "Build submitted via Travis CI" + notification_email: niels.lohmann@gmail.com + build_command_prepend: "make clean ; sudo cp $(which g++-5) $(which g++)" + build_command: "make" + branch_pattern: coverity_scan + env: + - COMPILER=g++-5 + - SPECIAL=coverity + # OSX / Clang - os: osx @@ -109,61 +188,6 @@ matrix: env: LLVM_VERSION=3.8.1 compiler: clang - # Valgrind - - - os: linux - compiler: gcc - env: - - COMPILER=g++-4.9 - - SPECIAL=valgrind - addons: - apt: - sources: ['ubuntu-toolchain-r-test'] - packages: [g++-4.9, valgrind] - after_success: - - valgrind --error-exitcode=1 --leak-check=full test/json_unit "*" ; - - # Coveralls - - - os: linux - compiler: gcc - addons: - apt: - sources: ['ubuntu-toolchain-r-test'] - packages: ['g++-4.9', 'valgrind', 'python-pip', 'python-yaml'] - before_script: - - pip install --user git+git://github.com/eddyxu/cpp-coveralls.git - after_success: - - make clean - - touch src/json.hpp - - make json_unit CXXFLAGS="-fprofile-arcs -ftest-coverage -std=c++11 -lstdc++" CXX=$COMPILER - - test/json_unit "*" - - coveralls --build-root test --exclude src/catch.hpp --exclude src/unit-algorithms.cpp --exclude src/unit-allocator.cpp --exclude src/unit-capacity.cpp --exclude src/unit-class_const_iterator.cpp --exclude src/unit-class_iterator.cpp --exclude src/unit-class_lexer.cpp --exclude src/unit-class_parser.cpp --exclude src/unit-comparison.cpp --exclude src/unit-concepts.cpp --exclude src/unit-constructor1.cpp --exclude src/unit-constructor2.cpp --exclude src/unit-convenience.cpp --exclude src/unit-conversions.cpp --exclude src/unit-deserialization.cpp --exclude src/unit-element_access1.cpp --exclude src/unit-element_access2.cpp --exclude src/unit-inspection.cpp --exclude src/unit-iterator_wrapper.cpp --exclude src/unit-iterators1.cpp --exclude src/unit-iterators2.cpp --exclude src/unit-json_patch.cpp --exclude src/unit-json_pointer.cpp --exclude src/unit-modifiers.cpp --exclude src/unit-pointer_access.cpp --exclude src/unit-readme.cpp --exclude src/unit-reference_access.cpp --exclude src/unit-regression.cpp --exclude src/unit-serialization.cpp --exclude src/unit-testsuites.cpp --exclude src/unit-unicode.cpp --include ../src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' - env: - - COMPILER=g++-4.9 - - SPECIAL=coveralls - - # Coverity (only for branch coverity_scan) - - - os: linux - compiler: gcc - before_install: echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-certificates.crt - addons: - apt: - sources: ['ubuntu-toolchain-r-test'] - packages: ['g++-5', 'valgrind'] - coverity_scan: - project: - name: "nlohmann/json" - description: "Build submitted via Travis CI" - notification_email: niels.lohmann@gmail.com - build_command_prepend: "make clean ; sudo cp $(which g++-5) $(which g++)" - build_command: "make" - branch_pattern: coverity_scan - env: - - COMPILER=g++-5 - - SPECIAL=coverity - ##################### # installation step # ##################### diff --git a/test/Makefile b/test/Makefile index 8e1ad6a00..ead1f0738 100644 --- a/test/Makefile +++ b/test/Makefile @@ -51,4 +51,4 @@ json_unit: $(OBJECTS) ../src/json.hpp src/catch.hpp @$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@ clean: - rm -fr json_unit $(OBJECTS) + rm -fr json_unit $(OBJECTS) $(SOURCES:.cpp=.gcno) $(SOURCES:.cpp=.gcda) From 1c4ca6d7b1ca3caec06ca81f57abb1b2e15c41e7 Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 10 Aug 2016 21:00:36 +0200 Subject: [PATCH 31/77] rubygems -> ruby --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 03da02d5e..ce3fc057b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -66,7 +66,7 @@ matrix: addons: apt: sources: ['ubuntu-toolchain-r-test'] - packages: ['g++-4.9', 'rubygems'] + packages: ['g++-4.9', 'ruby'] before_script: - wget http://ftp.de.debian.org/debian/pool/main/l/lcov/lcov_1.11.orig.tar.gz - tar xf lcov_1.11.orig.tar.gz From 5db41313ba4b8652c90af2294871a662387210e9 Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 10 Aug 2016 21:33:03 +0200 Subject: [PATCH 32/77] valgrind + full unit tests takes too long --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ce3fc057b..59f074814 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,7 +39,7 @@ matrix: sources: ['ubuntu-toolchain-r-test'] packages: [g++-4.9, valgrind] after_success: - - valgrind --error-exitcode=1 --leak-check=full test/json_unit -s "*" ; + - valgrind --error-exitcode=1 --leak-check=full test/json_unit # Coveralls From 46174879efbd6bad0c8d5105429562ab3cb1e9df Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 10 Aug 2016 22:41:09 +0200 Subject: [PATCH 33/77] clean up --- .travis.yml | 20 +--- README.md | 8 +- test/src/unit-pointer_access.cpp | 190 ++++++++++++++++++++++++++++++- 3 files changed, 193 insertions(+), 25 deletions(-) diff --git a/.travis.yml b/.travis.yml index 59f074814..ad1d12d97 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,25 +41,7 @@ matrix: after_success: - valgrind --error-exitcode=1 --leak-check=full test/json_unit - # Coveralls - - #- os: linux - # compiler: gcc - # addons: - # apt: - # sources: ['ubuntu-toolchain-r-test'] - # packages: ['g++-4.9', 'valgrind', 'python-pip', 'python-yaml'] - # before_script: - # - pip install --user git+git://github.com/eddyxu/cpp-coveralls.git - # after_success: - # - make clean - # - touch src/json.hpp - # - make json_unit CXXFLAGS="-fprofile-arcs -ftest-coverage -std=c++11 -lstdc++" CXX=$COMPILER - # - test/json_unit "*" - # - coveralls --build-root test --exclude src/catch.hpp --exclude src/unit-algorithms.cpp --exclude src/unit-allocator.cpp --exclude src/unit-capacity.cpp --exclude src/unit-class_const_iterator.cpp --exclude src/unit-class_iterator.cpp --exclude src/unit-class_lexer.cpp --exclude src/unit-class_parser.cpp --exclude src/unit-comparison.cpp --exclude src/unit-concepts.cpp --exclude src/unit-constructor1.cpp --exclude src/unit-constructor2.cpp --exclude src/unit-convenience.cpp --exclude src/unit-conversions.cpp --exclude src/unit-deserialization.cpp --exclude src/unit-element_access1.cpp --exclude src/unit-element_access2.cpp --exclude src/unit-inspection.cpp --exclude src/unit-iterator_wrapper.cpp --exclude src/unit-iterators1.cpp --exclude src/unit-iterators2.cpp --exclude src/unit-json_patch.cpp --exclude src/unit-json_pointer.cpp --exclude src/unit-modifiers.cpp --exclude src/unit-pointer_access.cpp --exclude src/unit-readme.cpp --exclude src/unit-reference_access.cpp --exclude src/unit-regression.cpp --exclude src/unit-serialization.cpp --exclude src/unit-testsuites.cpp --exclude src/unit-unicode.cpp --include ../src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9' - # env: - # - COMPILER=g++-4.9 - # - SPECIAL=coveralls + # Coveralls (http://gronlier.fr/blog/2015/01/adding-code-coverage-to-your-c-project/) - os: linux compiler: gcc diff --git a/README.md b/README.md index 722bf880b..c6fa73b4a 100644 --- a/README.md +++ b/README.md @@ -416,7 +416,13 @@ The following compilers are currently used in continuous integration at [Travis] | GCC 4.9.3 | Ubuntu 14.04.4 LTS | g++-4.9 (Ubuntu 4.9.3-8ubuntu2~14.04) 4.9.3 | | GCC 5.3.0 | Ubuntu 14.04.4 LTS | g++-5 (Ubuntu 5.3.0-3ubuntu1~14.04) 5.3.0 20151204 | | GCC 6.1.1 | Ubuntu 14.04.4 LTS | g++-6 (Ubuntu 6.1.1-3ubuntu11~14.04.1) 6.1.1 20160511 | +| Clang 3.6.0 | Ubuntu 14.04.4 LTS | clang version 3.6.0 (tags/RELEASE_360/final) | +| Clang 3.6.1 | Ubuntu 14.04.4 LTS | clang version 3.6.1 (tags/RELEASE_361/final) | +| Clang 3.6.2 | Ubuntu 14.04.4 LTS | clang version 3.6.2 (tags/RELEASE_362/final) | +| Clang 3.7.0 | Ubuntu 14.04.4 LTS | clang version 3.7.0 (tags/RELEASE_370/final) | +| Clang 3.7.1 | Ubuntu 14.04.4 LTS | clang version 3.7.1 (tags/RELEASE_371/final) | | Clang 3.8.0 | Ubuntu 14.04.4 LTS | clang version 3.8.0 (tags/RELEASE_380/final) | +| Clang 3.8.1 | Ubuntu 14.04.4 LTS | clang version 3.8.1 (tags/RELEASE_381/final) | | Clang Xcode 6.1 | Darwin Kernel Version 13.4.0 (OSX 10.9.5) | Apple LLVM version 6.0 (clang-600.0.54) (based on LLVM 3.5svn) | | Clang Xcode 6.2 | Darwin Kernel Version 13.4.0 (OSX 10.9.5) | Apple LLVM version 6.0 (clang-600.0.57) (based on LLVM 3.5svn) | | Clang Xcode 6.3 | Darwin Kernel Version 14.3.0 (OSX 10.10.3) | Apple LLVM version 6.1.0 (clang-602.0.49) (based on LLVM 3.6.0svn) | @@ -504,7 +510,7 @@ To compile and run the tests, you need to execute $ make check =============================================================================== -All tests passed (8905012 assertions in 32 test cases) +All tests passed (8905099 assertions in 32 test cases) ``` For more information, have a look at the file [.travis.yml](https://github.com/nlohmann/json/blob/master/.travis.yml). diff --git a/test/src/unit-pointer_access.cpp b/test/src/unit-pointer_access.cpp index bae9ee4c5..9353b5b0d 100644 --- a/test/src/unit-pointer_access.cpp +++ b/test/src/unit-pointer_access.cpp @@ -79,18 +79,30 @@ TEST_CASE("pointer access") SECTION("pointer access to const object_t") { - using test_type = json::object_t; + using test_type = const 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 + 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(p2 == p3); + 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 array_t") @@ -121,6 +133,34 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); } + SECTION("pointer access to const array_t") + { + using test_type = const json::array_t; + const 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; @@ -149,6 +189,34 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); } + SECTION("pointer access to const string_t") + { + using test_type = const json::string_t; + const 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; @@ -177,6 +245,34 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); } + SECTION("pointer access to const boolean_t") + { + using test_type = const json::boolean_t; + const 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; @@ -205,6 +301,34 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); } + SECTION("pointer access to const number_integer_t") + { + using test_type = const json::number_integer_t; + const 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; @@ -233,6 +357,34 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); } + SECTION("pointer access to const number_unsigned_t") + { + using test_type = const json::number_unsigned_t; + const 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; @@ -260,4 +412,32 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() != nullptr); } + + SECTION("pointer access to const number_float_t") + { + using test_type = const json::number_float_t; + const 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); + } } From 03de590372d73efae2a5bab1cd72bf3a276e1d5b Mon Sep 17 00:00:00 2001 From: Niels Date: Thu, 11 Aug 2016 20:52:48 +0200 Subject: [PATCH 34/77] improved documentation for #289 --- README.md | 2 +- src/json.hpp | 19 ++++++++++++++----- src/json.hpp.re2c | 19 ++++++++++++++----- 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index c6fa73b4a..de70912b4 100644 --- a/README.md +++ b/README.md @@ -498,7 +498,7 @@ Thanks a lot for helping out! ## Notes -- The code contains numerous debug **assertions** which can be switched off by defining the preprocessor macro `NDEBUG`, see the [documentation of `assert`](http://en.cppreference.com/w/cpp/error/assert). +- The code contains numerous debug **assertions** which can be switched off by defining the preprocessor macro `NDEBUG`, see the [documentation of `assert`](http://en.cppreference.com/w/cpp/error/assert). In particular, note [`operator[]`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a2e26bd0b0168abb61f67ad5bcd5b9fa1.html#a2e26bd0b0168abb61f67ad5bcd5b9fa1) implements **unchecked access** for const objects: If the given key is not present, the behavior is undefined (think of a dereferenced null pointer) and yields an [assertion failure](https://github.com/nlohmann/json/issues/289) if assertions are switched on. If you are not sure whether an element in an object exists, use checked access with the [`at()` function](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a674de1ee73e6bf4843fc5dc1351fb726.html#a674de1ee73e6bf4843fc5dc1351fb726). - As the exact type of a number is not defined in the [JSON specification](http://rfc7159.net/rfc7159), this library tries to choose the best fitting C++ number type automatically. As a result, the type `double` may be used to store numbers which may yield [**floating-point exceptions**](https://github.com/nlohmann/json/issues/181) in certain rare situations if floating-point exceptions have been unmasked in the calling code. These exceptions are not caused by the library and need to be fixed in the calling code, such as by re-masking the exceptions prior to calling library functions. diff --git a/src/json.hpp b/src/json.hpp index 878fb899f..5ab8ec264 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -1843,7 +1843,8 @@ class basic_json @param[in] first begin of the range to copy from (included) @param[in] last end of the range to copy from (excluded) - @pre Iterators @a first and @a last must be initialized. + @pre Iterators @a first and @a last must be initialized. **This + precondition is enforced with an assertion.** @throw std::domain_error if iterators are not compatible; that is, do not belong to the same JSON value; example: `"iterators are not compatible"` @@ -3509,6 +3510,9 @@ class basic_json @return const reference to the element at key @a key + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** + @throw std::domain_error if JSON is not an object; example: `"cannot use operator[] with null"` @@ -3667,6 +3671,9 @@ class basic_json @return const reference to the element at key @a key + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** + @throw std::domain_error if JSON is not an object; example: `"cannot use operator[] with null"` @@ -3867,7 +3874,8 @@ class basic_json @complexity Constant. @pre The JSON value must not be `null` (would throw `std::out_of_range`) - or an empty array or object (undefined behavior, guarded by assertions). + or an empty array or object (undefined behavior, **guarded by + assertions**). @post The JSON value remains unchanged. @throw std::out_of_range when called on `null` value @@ -3909,7 +3917,8 @@ class basic_json @complexity Constant. @pre The JSON value must not be `null` (would throw `std::out_of_range`) - or an empty array or object (undefined behavior, guarded by assertions). + or an empty array or object (undefined behavior, **guarded by + assertions**). @post The JSON value remains unchanged. @throw std::out_of_range when called on `null` value. @@ -6592,8 +6601,8 @@ class basic_json @note An iterator is called *initialized* when a pointer to a JSON value has been set (e.g., by a constructor or a copy assignment). If the iterator is default-constructed, it is *uninitialized* and most - methods are undefined. The library uses assertions to detect calls - on uninitialized iterators. + methods are undefined. **The library uses assertions to detect calls + on uninitialized iterators.** @requirement The class satisfies the following concept requirements: - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 32482ea85..0d8ffa155 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -1843,7 +1843,8 @@ class basic_json @param[in] first begin of the range to copy from (included) @param[in] last end of the range to copy from (excluded) - @pre Iterators @a first and @a last must be initialized. + @pre Iterators @a first and @a last must be initialized. **This + precondition is enforced with an assertion.** @throw std::domain_error if iterators are not compatible; that is, do not belong to the same JSON value; example: `"iterators are not compatible"` @@ -3509,6 +3510,9 @@ class basic_json @return const reference to the element at key @a key + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** + @throw std::domain_error if JSON is not an object; example: `"cannot use operator[] with null"` @@ -3667,6 +3671,9 @@ class basic_json @return const reference to the element at key @a key + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** + @throw std::domain_error if JSON is not an object; example: `"cannot use operator[] with null"` @@ -3867,7 +3874,8 @@ class basic_json @complexity Constant. @pre The JSON value must not be `null` (would throw `std::out_of_range`) - or an empty array or object (undefined behavior, guarded by assertions). + or an empty array or object (undefined behavior, **guarded by + assertions**). @post The JSON value remains unchanged. @throw std::out_of_range when called on `null` value @@ -3909,7 +3917,8 @@ class basic_json @complexity Constant. @pre The JSON value must not be `null` (would throw `std::out_of_range`) - or an empty array or object (undefined behavior, guarded by assertions). + or an empty array or object (undefined behavior, **guarded by + assertions**). @post The JSON value remains unchanged. @throw std::out_of_range when called on `null` value. @@ -6592,8 +6601,8 @@ class basic_json @note An iterator is called *initialized* when a pointer to a JSON value has been set (e.g., by a constructor or a copy assignment). If the iterator is default-constructed, it is *uninitialized* and most - methods are undefined. The library uses assertions to detect calls - on uninitialized iterators. + methods are undefined. **The library uses assertions to detect calls + on uninitialized iterators.** @requirement The class satisfies the following concept requirements: - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): From b76f5506d7da8efcb05cab9c4e84f5774c0436c8 Mon Sep 17 00:00:00 2001 From: Niels Date: Sun, 14 Aug 2016 14:18:25 +0200 Subject: [PATCH 35/77] added a parser for continguous containers --- src/json.hpp | 94 +++++++++++++++++++++++----------- src/json.hpp.re2c | 94 +++++++++++++++++++++++----------- test/src/unit-class_parser.cpp | 48 +++++++++++++++-- 3 files changed, 169 insertions(+), 67 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 5ab8ec264..752493d02 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -3960,7 +3960,7 @@ class basic_json @return Iterator following the last removed element. If the iterator @a pos refers to the last element, the `end()` iterator is returned. - @tparam InteratorType an @ref iterator or @ref const_iterator + @tparam IteratorType an @ref iterator or @ref const_iterator @post Invalidates iterators and references at or after the point of the erase, including the `end()` iterator. @@ -3982,7 +3982,7 @@ class basic_json @liveexample{The example shows the result of `erase()` for different JSON types.,erase__IteratorType} - @sa @ref erase(InteratorType, InteratorType) -- removes the elements in + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in the given range @sa @ref erase(const typename object_t::key_type&) -- removes the element from an object at the given key @@ -3991,13 +3991,13 @@ class basic_json @since version 1.0.0 */ - template ::value or - std::is_same::value + std::is_same::value or + std::is_same::value , int>::type = 0> - InteratorType erase(InteratorType pos) + IteratorType erase(IteratorType pos) { // make sure iterator fits the current value if (this != pos.m_object) @@ -4005,7 +4005,7 @@ class basic_json throw std::domain_error("iterator does not fit current value"); } - InteratorType result = end(); + IteratorType result = end(); switch (m_type) { @@ -4069,7 +4069,7 @@ class basic_json @return Iterator following the last removed element. If the iterator @a second refers to the last element, the `end()` iterator is returned. - @tparam InteratorType an @ref iterator or @ref const_iterator + @tparam IteratorType an @ref iterator or @ref const_iterator @post Invalidates iterators and references at or after the point of the erase, including the `end()` iterator. @@ -4092,7 +4092,7 @@ class basic_json @liveexample{The example shows the result of `erase()` for different JSON types.,erase__IteratorType_IteratorType} - @sa @ref erase(InteratorType) -- removes the element at a given position + @sa @ref erase(IteratorType) -- removes the element at a given position @sa @ref erase(const typename object_t::key_type&) -- removes the element from an object at the given key @sa @ref erase(const size_type) -- removes the element from an array at @@ -4100,13 +4100,13 @@ class basic_json @since version 1.0.0 */ - template ::value or - std::is_same::value + std::is_same::value or + std::is_same::value , int>::type = 0> - InteratorType erase(InteratorType first, InteratorType last) + IteratorType erase(IteratorType first, IteratorType last) { // make sure iterator fits the current value if (this != first.m_object or this != last.m_object) @@ -4114,7 +4114,7 @@ class basic_json throw std::domain_error("iterators do not fit current value"); } - InteratorType result = end(); + IteratorType result = end(); switch (m_type) { @@ -4186,8 +4186,8 @@ class basic_json @liveexample{The example shows the effect of `erase()`.,erase__key_type} - @sa @ref erase(InteratorType) -- removes the element at a given position - @sa @ref erase(InteratorType, InteratorType) -- removes the elements in + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in the given range @sa @ref erase(const size_type) -- removes the element from an array at the given index @@ -4223,8 +4223,8 @@ class basic_json @liveexample{The example shows the effect of `erase()`.,erase__size_type} - @sa @ref erase(InteratorType) -- removes the element at a given position - @sa @ref erase(InteratorType, InteratorType) -- removes the elements in + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in the given range @sa @ref erase(const typename object_t::key_type&) -- removes the element from an object at the given key @@ -7504,21 +7504,29 @@ class basic_json /// the char type to use in the lexer using lexer_char_t = unsigned char; - /// constructor with a given buffer - explicit lexer(const string_t& s) noexcept - : m_stream(nullptr), m_buffer(s) + /// a lexer from a buffer with given length + lexer(const lexer_char_t* buff, const size_t len) noexcept + : m_stream(nullptr), m_buffer(), m_content(buff) { - m_content = reinterpret_cast(m_buffer.c_str()); assert(m_content != nullptr); m_start = m_cursor = m_content; - m_limit = m_content + s.size(); + m_limit = m_content + len; } - /// constructor with a given stream - explicit lexer(std::istream* s) noexcept - : m_stream(s), m_buffer() + /// a lexer from a string literal + explicit lexer(const typename string_t::value_type* buff) noexcept + : m_stream(nullptr), m_buffer(), + m_content(reinterpret_cast(buff)) + { + assert(m_content != nullptr); + m_start = m_cursor = m_content; + m_limit = m_content + strlen(buff); + } + + /// a lexer from an input stream + explicit lexer(std::istream& s) + : m_stream(&s), m_buffer() { - assert(m_stream != nullptr); std::getline(*m_stream, m_buffer); m_content = reinterpret_cast(m_buffer.c_str()); assert(m_content != nullptr); @@ -8853,17 +8861,41 @@ basic_json_parser_63: class parser { public: - /// constructor for strings + /// a parser reading from a string literal + parser(const typename string_t::value_type* buff, parser_callback_t cb = nullptr) noexcept + : callback(cb), m_lexer(buff) + { + // read first token + get_token(); + } + + /// a parser reading from a string container parser(const string_t& s, const parser_callback_t cb = nullptr) noexcept - : callback(cb), m_lexer(s) + : callback(cb), + m_lexer(reinterpret_cast(s.c_str()), s.size()) { // read first token get_token(); } /// a parser reading from an input stream - parser(std::istream& _is, const parser_callback_t cb = nullptr) noexcept - : callback(cb), m_lexer(&_is) + parser(std::istream& is, const parser_callback_t cb = nullptr) noexcept + : callback(cb), m_lexer(is) + { + // read first token + get_token(); + } + + /// a parser reading from a container with continguous storage + template ::iterator_category, std::random_access_iterator_tag>::value + , int>::type + = 0> + parser(IteratorType first, IteratorType last, const parser_callback_t cb = nullptr) noexcept + : callback(cb), + m_lexer(reinterpret_cast(&(*first)), + static_cast(std::distance(first, last))) { // read first token get_token(); diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 0d8ffa155..b8f646af1 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -3960,7 +3960,7 @@ class basic_json @return Iterator following the last removed element. If the iterator @a pos refers to the last element, the `end()` iterator is returned. - @tparam InteratorType an @ref iterator or @ref const_iterator + @tparam IteratorType an @ref iterator or @ref const_iterator @post Invalidates iterators and references at or after the point of the erase, including the `end()` iterator. @@ -3982,7 +3982,7 @@ class basic_json @liveexample{The example shows the result of `erase()` for different JSON types.,erase__IteratorType} - @sa @ref erase(InteratorType, InteratorType) -- removes the elements in + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in the given range @sa @ref erase(const typename object_t::key_type&) -- removes the element from an object at the given key @@ -3991,13 +3991,13 @@ class basic_json @since version 1.0.0 */ - template ::value or - std::is_same::value + std::is_same::value or + std::is_same::value , int>::type = 0> - InteratorType erase(InteratorType pos) + IteratorType erase(IteratorType pos) { // make sure iterator fits the current value if (this != pos.m_object) @@ -4005,7 +4005,7 @@ class basic_json throw std::domain_error("iterator does not fit current value"); } - InteratorType result = end(); + IteratorType result = end(); switch (m_type) { @@ -4069,7 +4069,7 @@ class basic_json @return Iterator following the last removed element. If the iterator @a second refers to the last element, the `end()` iterator is returned. - @tparam InteratorType an @ref iterator or @ref const_iterator + @tparam IteratorType an @ref iterator or @ref const_iterator @post Invalidates iterators and references at or after the point of the erase, including the `end()` iterator. @@ -4092,7 +4092,7 @@ class basic_json @liveexample{The example shows the result of `erase()` for different JSON types.,erase__IteratorType_IteratorType} - @sa @ref erase(InteratorType) -- removes the element at a given position + @sa @ref erase(IteratorType) -- removes the element at a given position @sa @ref erase(const typename object_t::key_type&) -- removes the element from an object at the given key @sa @ref erase(const size_type) -- removes the element from an array at @@ -4100,13 +4100,13 @@ class basic_json @since version 1.0.0 */ - template ::value or - std::is_same::value + std::is_same::value or + std::is_same::value , int>::type = 0> - InteratorType erase(InteratorType first, InteratorType last) + IteratorType erase(IteratorType first, IteratorType last) { // make sure iterator fits the current value if (this != first.m_object or this != last.m_object) @@ -4114,7 +4114,7 @@ class basic_json throw std::domain_error("iterators do not fit current value"); } - InteratorType result = end(); + IteratorType result = end(); switch (m_type) { @@ -4186,8 +4186,8 @@ class basic_json @liveexample{The example shows the effect of `erase()`.,erase__key_type} - @sa @ref erase(InteratorType) -- removes the element at a given position - @sa @ref erase(InteratorType, InteratorType) -- removes the elements in + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in the given range @sa @ref erase(const size_type) -- removes the element from an array at the given index @@ -4223,8 +4223,8 @@ class basic_json @liveexample{The example shows the effect of `erase()`.,erase__size_type} - @sa @ref erase(InteratorType) -- removes the element at a given position - @sa @ref erase(InteratorType, InteratorType) -- removes the elements in + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in the given range @sa @ref erase(const typename object_t::key_type&) -- removes the element from an object at the given key @@ -7504,21 +7504,29 @@ class basic_json /// the char type to use in the lexer using lexer_char_t = unsigned char; - /// constructor with a given buffer - explicit lexer(const string_t& s) noexcept - : m_stream(nullptr), m_buffer(s) + /// a lexer from a buffer with given length + lexer(const lexer_char_t* buff, const size_t len) noexcept + : m_stream(nullptr), m_buffer(), m_content(buff) { - m_content = reinterpret_cast(m_buffer.c_str()); assert(m_content != nullptr); m_start = m_cursor = m_content; - m_limit = m_content + s.size(); + m_limit = m_content + len; } - /// constructor with a given stream - explicit lexer(std::istream* s) noexcept - : m_stream(s), m_buffer() + /// a lexer from a string literal + explicit lexer(const typename string_t::value_type* buff) noexcept + : m_stream(nullptr), m_buffer(), + m_content(reinterpret_cast(buff)) + { + assert(m_content != nullptr); + m_start = m_cursor = m_content; + m_limit = m_content + strlen(buff); + } + + /// a lexer from an input stream + explicit lexer(std::istream& s) + : m_stream(&s), m_buffer() { - assert(m_stream != nullptr); std::getline(*m_stream, m_buffer); m_content = reinterpret_cast(m_buffer.c_str()); assert(m_content != nullptr); @@ -8150,17 +8158,41 @@ class basic_json class parser { public: - /// constructor for strings + /// a parser reading from a string literal + parser(const typename string_t::value_type* buff, parser_callback_t cb = nullptr) noexcept + : callback(cb), m_lexer(buff) + { + // read first token + get_token(); + } + + /// a parser reading from a string container parser(const string_t& s, const parser_callback_t cb = nullptr) noexcept - : callback(cb), m_lexer(s) + : callback(cb), + m_lexer(reinterpret_cast(s.c_str()), s.size()) { // read first token get_token(); } /// a parser reading from an input stream - parser(std::istream& _is, const parser_callback_t cb = nullptr) noexcept - : callback(cb), m_lexer(&_is) + parser(std::istream& is, const parser_callback_t cb = nullptr) noexcept + : callback(cb), m_lexer(is) + { + // read first token + get_token(); + } + + /// a parser reading from a container with continguous storage + template ::iterator_category, std::random_access_iterator_tag>::value + , int>::type + = 0> + parser(IteratorType first, IteratorType last, const parser_callback_t cb = nullptr) noexcept + : callback(cb), + m_lexer(reinterpret_cast(&(*first)), + static_cast(std::distance(first, last))) { // read first token get_token(); diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index fe0055034..c59186eff 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -32,6 +32,8 @@ SOFTWARE. #include "json.hpp" using nlohmann::json; +#include + TEST_CASE("parser class") { SECTION("parse") @@ -743,11 +745,47 @@ TEST_CASE("parser class") } } - SECTION("copy constructor") + SECTION("constructing from continguous containers") { - 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})); + SECTION("from std::vector") + { + std::vector v = {'t', 'r', 'u', 'e'}; + CHECK (json::parser(std::begin(v), std::end(v)).parse() == json(true)); + } + + SECTION("from std::array") + { + std::array v { {'t', 'r', 'u', 'e'} }; + CHECK (json::parser(std::begin(v), std::end(v)).parse() == json(true)); + } + + SECTION("from array") + { + uint8_t v[] = {'t', 'r', 'u', 'e'}; + CHECK (json::parser(std::begin(v), std::end(v)).parse() == json(true)); + } + + SECTION("from char literal") + { + CHECK (json::parser("true").parse() == json(true)); + } + + SECTION("from std::string") + { + std::string v = {'t', 'r', 'u', 'e'}; + CHECK (json::parser(std::begin(v), std::end(v)).parse() == json(true)); + } + + SECTION("from std::initializer_list") + { + std::initializer_list v = {'t', 'r', 'u', 'e', '\0'}; + CHECK (json::parser(std::begin(v), std::end(v)).parse() == json(true)); + } + + SECTION("from std::valarray") + { + std::valarray v = {'t', 'r', 'u', 'e'}; + CHECK (json::parser(std::begin(v), std::end(v)).parse() == json(true)); + } } } From 01386b3977bc474c3c9febd7c596863bed9b57ef Mon Sep 17 00:00:00 2001 From: Niels Date: Sun, 14 Aug 2016 17:23:15 +0200 Subject: [PATCH 36/77] cleanup --- src/json.hpp | 127 ++++++++++++++++++--------------- src/json.hpp.re2c | 107 ++++++++++++++------------- test/src/unit-class_parser.cpp | 2 +- 3 files changed, 127 insertions(+), 109 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 752493d02..8342b0e1f 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -37,6 +37,7 @@ SOFTWARE. #include #include #include +#include #include #include #include @@ -7506,7 +7507,7 @@ class basic_json /// a lexer from a buffer with given length lexer(const lexer_char_t* buff, const size_t len) noexcept - : m_stream(nullptr), m_buffer(), m_content(buff) + : m_content(buff) { assert(m_content != nullptr); m_start = m_cursor = m_content; @@ -7515,29 +7516,19 @@ class basic_json /// a lexer from a string literal explicit lexer(const typename string_t::value_type* buff) noexcept - : m_stream(nullptr), m_buffer(), - m_content(reinterpret_cast(buff)) - { - assert(m_content != nullptr); - m_start = m_cursor = m_content; - m_limit = m_content + strlen(buff); - } + : lexer(reinterpret_cast(buff), strlen(buff)) + {} /// a lexer from an input stream explicit lexer(std::istream& s) - : m_stream(&s), m_buffer() + : m_stream(&s), m_line_buffer() { - std::getline(*m_stream, m_buffer); - m_content = reinterpret_cast(m_buffer.c_str()); - assert(m_content != nullptr); - m_start = m_cursor = m_content; - m_limit = m_content + m_buffer.size(); + // fill buffer + fill_line_buffer(); } - /// default constructor - lexer() = default; - // switch off unwanted functions + lexer() = delete; lexer(const lexer&) = delete; lexer operator=(const lexer&) = delete; @@ -7690,7 +7681,7 @@ class basic_json infinite sequence of whitespace or byte-order-marks. This contradicts the assumption of finite input, q.e.d. */ - token_type scan() noexcept + token_type scan() { while (true) { @@ -7742,7 +7733,7 @@ class basic_json }; if ((m_limit - m_cursor) < 5) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(); // LCOV_EXCL_LINE; } yych = *m_cursor; if (yybm[0 + yych] & 32) @@ -7876,7 +7867,7 @@ basic_json_parser_6: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(); // LCOV_EXCL_LINE; } yych = *m_cursor; if (yybm[0 + yych] & 32) @@ -7946,7 +7937,7 @@ basic_json_parser_15: m_marker = ++m_cursor; if ((m_limit - m_cursor) < 3) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(); // LCOV_EXCL_LINE; } yych = *m_cursor; if (yybm[0 + yych] & 64) @@ -8039,7 +8030,7 @@ basic_json_parser_31: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(); // LCOV_EXCL_LINE; } yych = *m_cursor; basic_json_parser_32: @@ -8076,7 +8067,7 @@ basic_json_parser_36: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(); // LCOV_EXCL_LINE; } yych = *m_cursor; if (yych <= 'e') @@ -8220,7 +8211,7 @@ basic_json_parser_43: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(); // LCOV_EXCL_LINE; } yych = *m_cursor; if (yych <= '@') @@ -8256,7 +8247,7 @@ basic_json_parser_44: m_marker = ++m_cursor; if ((m_limit - m_cursor) < 3) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(); // LCOV_EXCL_LINE; } yych = *m_cursor; if (yych <= 'D') @@ -8297,7 +8288,7 @@ basic_json_parser_47: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(); // LCOV_EXCL_LINE; } yych = *m_cursor; if (yych <= '/') @@ -8339,7 +8330,7 @@ basic_json_parser_54: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(); // LCOV_EXCL_LINE; } yych = *m_cursor; if (yych <= '@') @@ -8393,7 +8384,7 @@ basic_json_parser_60: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(); // LCOV_EXCL_LINE; } yych = *m_cursor; if (yych <= '@') @@ -8434,7 +8425,7 @@ basic_json_parser_63: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + fill_line_buffer(); // LCOV_EXCL_LINE; } yych = *m_cursor; if (yych <= '@') @@ -8472,30 +8463,57 @@ basic_json_parser_63: return last_token_type; } - /// append data from the stream to the internal buffer - void yyfill() noexcept + /*! + @brief append data from the stream to the line buffer + + This function is called by the scan() function when the end of the + buffer (`m_limit`) is reached and the `m_cursor` pointer cannot be + incremented without leaving the limits of the line buffer. Note re2c + decides when to call this function. + + @pre + p p p p p p u u u u u x . . . . . . + ^ ^ ^ ^ + m_content m_start | m_limit + m_cursor + + @post + u u u u u x x x x x x x . . . . . . + ^ ^ ^ + | m_cursor m_limit + m_start + m_content + */ + void fill_line_buffer() { + // no stream is used or end of file is reached if (m_stream == nullptr or not * m_stream) { return; } + // number of processed characters (p) const auto offset_start = m_start - m_content; + // offset for m_marker wrt. to m_start const auto offset_marker = m_marker - m_start; + // number of unprocessed characters (u) const auto offset_cursor = m_cursor - m_start; - m_buffer.erase(0, static_cast(offset_start)); + // delete processed characters from line buffer + m_line_buffer.erase(0, static_cast(offset_start)); + // read next line from input stream std::string line; - assert(m_stream != nullptr); std::getline(*m_stream, line); - m_buffer += "\n" + line; // add line with newline symbol + // add line with newline symbol to the line buffer + m_line_buffer += "\n" + line; - m_content = reinterpret_cast(m_buffer.c_str()); + // set pointers + m_content = reinterpret_cast(m_line_buffer.c_str()); assert(m_content != nullptr); m_start = m_content; m_marker = m_start + offset_marker; m_cursor = m_start + offset_cursor; - m_limit = m_start + m_buffer.size() - 1; + m_limit = m_start + m_line_buffer.size() - 1; } /// return string representation of last read token @@ -8837,8 +8855,8 @@ basic_json_parser_63: private: /// optional input stream std::istream* m_stream = nullptr; - /// the buffer - string_t m_buffer; + /// line buffer buffer for m_stream + string_t m_line_buffer {}; /// the buffer pointer const lexer_char_t* m_content = nullptr; /// pointer to the beginning of the current symbol @@ -8862,29 +8880,20 @@ basic_json_parser_63: { public: /// a parser reading from a string literal - parser(const typename string_t::value_type* buff, parser_callback_t cb = nullptr) noexcept + parser(const typename string_t::value_type* buff, parser_callback_t cb = nullptr) : callback(cb), m_lexer(buff) - { - // read first token - get_token(); - } + {} /// a parser reading from a string container - parser(const string_t& s, const parser_callback_t cb = nullptr) noexcept + parser(const string_t& s, const parser_callback_t cb = nullptr) : callback(cb), m_lexer(reinterpret_cast(s.c_str()), s.size()) - { - // read first token - get_token(); - } + {} /// a parser reading from an input stream - parser(std::istream& is, const parser_callback_t cb = nullptr) noexcept + parser(std::istream& is, const parser_callback_t cb = nullptr) : callback(cb), m_lexer(is) - { - // read first token - get_token(); - } + {} /// a parser reading from a container with continguous storage template ::iterator_category, std::random_access_iterator_tag>::value , int>::type = 0> - parser(IteratorType first, IteratorType last, const parser_callback_t cb = nullptr) noexcept + parser(IteratorType first, IteratorType last, const parser_callback_t cb = nullptr) : callback(cb), m_lexer(reinterpret_cast(&(*first)), static_cast(std::distance(first, last))) - { - // read first token - get_token(); - } + {} /// public parser interface basic_json parse() { + // read first token + get_token(); + basic_json result = parse_internal(true); result.assert_invariant(); @@ -9108,7 +9117,7 @@ basic_json_parser_63: } /// get next token from lexer - typename lexer::token_type get_token() noexcept + typename lexer::token_type get_token() { last_token = m_lexer.scan(); return last_token; diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index b8f646af1..7ff4e1faa 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -37,6 +37,7 @@ SOFTWARE. #include #include #include +#include #include #include #include @@ -7506,7 +7507,7 @@ class basic_json /// a lexer from a buffer with given length lexer(const lexer_char_t* buff, const size_t len) noexcept - : m_stream(nullptr), m_buffer(), m_content(buff) + : m_content(buff) { assert(m_content != nullptr); m_start = m_cursor = m_content; @@ -7515,29 +7516,19 @@ class basic_json /// a lexer from a string literal explicit lexer(const typename string_t::value_type* buff) noexcept - : m_stream(nullptr), m_buffer(), - m_content(reinterpret_cast(buff)) - { - assert(m_content != nullptr); - m_start = m_cursor = m_content; - m_limit = m_content + strlen(buff); - } + : lexer(reinterpret_cast(buff), strlen(buff)) + {} /// a lexer from an input stream explicit lexer(std::istream& s) - : m_stream(&s), m_buffer() + : m_stream(&s), m_line_buffer() { - std::getline(*m_stream, m_buffer); - m_content = reinterpret_cast(m_buffer.c_str()); - assert(m_content != nullptr); - m_start = m_cursor = m_content; - m_limit = m_content + m_buffer.size(); + // fill buffer + fill_line_buffer(); } - /// default constructor - lexer() = default; - // switch off unwanted functions + lexer() = delete; lexer(const lexer&) = delete; lexer operator=(const lexer&) = delete; @@ -7690,7 +7681,7 @@ class basic_json infinite sequence of whitespace or byte-order-marks. This contradicts the assumption of finite input, q.e.d. */ - token_type scan() noexcept + token_type scan() { while (true) { @@ -7706,7 +7697,7 @@ class basic_json re2c:define:YYCURSOR = m_cursor; re2c:define:YYLIMIT = m_limit; re2c:define:YYMARKER = m_marker; - re2c:define:YYFILL = "yyfill(); // LCOV_EXCL_LINE"; + re2c:define:YYFILL = "fill_line_buffer(); // LCOV_EXCL_LINE"; re2c:yyfill:parameter = 0; re2c:indent:string = " "; re2c:indent:top = 1; @@ -7769,30 +7760,57 @@ class basic_json return last_token_type; } - /// append data from the stream to the internal buffer - void yyfill() noexcept + /*! + @brief append data from the stream to the line buffer + + This function is called by the scan() function when the end of the + buffer (`m_limit`) is reached and the `m_cursor` pointer cannot be + incremented without leaving the limits of the line buffer. Note re2c + decides when to call this function. + + @pre + p p p p p p u u u u u x . . . . . . + ^ ^ ^ ^ + m_content m_start | m_limit + m_cursor + + @post + u u u u u x x x x x x x . . . . . . + ^ ^ ^ + | m_cursor m_limit + m_start + m_content + */ + void fill_line_buffer() { + // no stream is used or end of file is reached if (m_stream == nullptr or not * m_stream) { return; } + // number of processed characters (p) const auto offset_start = m_start - m_content; + // offset for m_marker wrt. to m_start const auto offset_marker = m_marker - m_start; + // number of unprocessed characters (u) const auto offset_cursor = m_cursor - m_start; - m_buffer.erase(0, static_cast(offset_start)); + // delete processed characters from line buffer + m_line_buffer.erase(0, static_cast(offset_start)); + // read next line from input stream std::string line; - assert(m_stream != nullptr); std::getline(*m_stream, line); - m_buffer += "\n" + line; // add line with newline symbol + // add line with newline symbol to the line buffer + m_line_buffer += "\n" + line; - m_content = reinterpret_cast(m_buffer.c_str()); + // set pointers + m_content = reinterpret_cast(m_line_buffer.c_str()); assert(m_content != nullptr); m_start = m_content; m_marker = m_start + offset_marker; m_cursor = m_start + offset_cursor; - m_limit = m_start + m_buffer.size() - 1; + m_limit = m_start + m_line_buffer.size() - 1; } /// return string representation of last read token @@ -8134,8 +8152,8 @@ class basic_json private: /// optional input stream std::istream* m_stream = nullptr; - /// the buffer - string_t m_buffer; + /// line buffer buffer for m_stream + string_t m_line_buffer {}; /// the buffer pointer const lexer_char_t* m_content = nullptr; /// pointer to the beginning of the current symbol @@ -8159,29 +8177,20 @@ class basic_json { public: /// a parser reading from a string literal - parser(const typename string_t::value_type* buff, parser_callback_t cb = nullptr) noexcept + parser(const typename string_t::value_type* buff, parser_callback_t cb = nullptr) : callback(cb), m_lexer(buff) - { - // read first token - get_token(); - } + {} /// a parser reading from a string container - parser(const string_t& s, const parser_callback_t cb = nullptr) noexcept + parser(const string_t& s, const parser_callback_t cb = nullptr) : callback(cb), m_lexer(reinterpret_cast(s.c_str()), s.size()) - { - // read first token - get_token(); - } + {} /// a parser reading from an input stream - parser(std::istream& is, const parser_callback_t cb = nullptr) noexcept + parser(std::istream& is, const parser_callback_t cb = nullptr) : callback(cb), m_lexer(is) - { - // read first token - get_token(); - } + {} /// a parser reading from a container with continguous storage template ::iterator_category, std::random_access_iterator_tag>::value , int>::type = 0> - parser(IteratorType first, IteratorType last, const parser_callback_t cb = nullptr) noexcept + parser(IteratorType first, IteratorType last, const parser_callback_t cb = nullptr) : callback(cb), m_lexer(reinterpret_cast(&(*first)), static_cast(std::distance(first, last))) - { - // read first token - get_token(); - } + {} /// public parser interface basic_json parse() { + // read first token + get_token(); + basic_json result = parse_internal(true); result.assert_invariant(); @@ -8405,7 +8414,7 @@ class basic_json } /// get next token from lexer - typename lexer::token_type get_token() noexcept + typename lexer::token_type get_token() { last_token = m_lexer.scan(); return last_token; diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index c59186eff..35a76c101 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -784,7 +784,7 @@ TEST_CASE("parser class") SECTION("from std::valarray") { - std::valarray v = {'t', 'r', 'u', 'e'}; + std::valarray v = {'t', 'r', 'u', 'e', '\0'}; CHECK (json::parser(std::begin(v), std::end(v)).parse() == json(true)); } } From c9e5d56c9ce209a03fd6685ab187ffcca0263956 Mon Sep 17 00:00:00 2001 From: Niels Date: Sun, 14 Aug 2016 17:30:53 +0200 Subject: [PATCH 37/77] fix for MSVC --- test/src/unit-class_parser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index 35a76c101..c7429dd41 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -749,7 +749,7 @@ TEST_CASE("parser class") { SECTION("from std::vector") { - std::vector v = {'t', 'r', 'u', 'e'}; + std::vector v = {'t', 'r', 'u', 'e', '\0'}; CHECK (json::parser(std::begin(v), std::end(v)).parse() == json(true)); } From 7b42c973bdf512e69a6dec0add21b083ac660dad Mon Sep 17 00:00:00 2001 From: Niels Date: Sun, 14 Aug 2016 17:34:58 +0200 Subject: [PATCH 38/77] fix for MSVC --- test/src/unit-class_parser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index c7429dd41..1a4b0a7c6 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -755,7 +755,7 @@ TEST_CASE("parser class") SECTION("from std::array") { - std::array v { {'t', 'r', 'u', 'e'} }; + std::array v { {'t', 'r', 'u', 'e', '\0'} }; CHECK (json::parser(std::begin(v), std::end(v)).parse() == json(true)); } From 4871e3941575cec382895e171831146d78a68975 Mon Sep 17 00:00:00 2001 From: Niels Date: Sun, 14 Aug 2016 17:52:21 +0200 Subject: [PATCH 39/77] spell fix --- src/json.hpp | 2 +- src/json.hpp.re2c | 2 +- test/src/unit-class_parser.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 8342b0e1f..90bd097fb 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -8895,7 +8895,7 @@ basic_json_parser_63: : callback(cb), m_lexer(is) {} - /// a parser reading from a container with continguous storage + /// a parser reading from a container with contiguous storage template ::iterator_category, std::random_access_iterator_tag>::value diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 7ff4e1faa..8a9a6c1f6 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -8192,7 +8192,7 @@ class basic_json : callback(cb), m_lexer(is) {} - /// a parser reading from a container with continguous storage + /// a parser reading from a container with contiguous storage template ::iterator_category, std::random_access_iterator_tag>::value diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index 1a4b0a7c6..863cec520 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -745,7 +745,7 @@ TEST_CASE("parser class") } } - SECTION("constructing from continguous containers") + SECTION("constructing from contiguous containers") { SECTION("from std::vector") { From 92ee1d56ebc2df0e9a4b16c089c266d622f372d1 Mon Sep 17 00:00:00 2001 From: Niels Date: Sun, 14 Aug 2016 21:59:41 +0200 Subject: [PATCH 40/77] cleanup --- src/json.hpp | 9 ++-- src/json.hpp.re2c | 9 ++-- test/src/unit-class_lexer.cpp | 84 +++++++++++++++++++++++------------ 3 files changed, 62 insertions(+), 40 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 90bd097fb..a96851581 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -7514,11 +7514,6 @@ class basic_json m_limit = m_content + len; } - /// a lexer from a string literal - explicit lexer(const typename string_t::value_type* buff) noexcept - : lexer(reinterpret_cast(buff), strlen(buff)) - {} - /// a lexer from an input stream explicit lexer(std::istream& s) : m_stream(&s), m_line_buffer() @@ -8881,7 +8876,9 @@ basic_json_parser_63: public: /// a parser reading from a string literal parser(const typename string_t::value_type* buff, parser_callback_t cb = nullptr) - : callback(cb), m_lexer(buff) + : callback(cb), + m_lexer(reinterpret_cast(buff), + strlen(buff)) {} /// a parser reading from a string container diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 8a9a6c1f6..82b6a123f 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -7514,11 +7514,6 @@ class basic_json m_limit = m_content + len; } - /// a lexer from a string literal - explicit lexer(const typename string_t::value_type* buff) noexcept - : lexer(reinterpret_cast(buff), strlen(buff)) - {} - /// a lexer from an input stream explicit lexer(std::istream& s) : m_stream(&s), m_line_buffer() @@ -8178,7 +8173,9 @@ class basic_json public: /// a parser reading from a string literal parser(const typename string_t::value_type* buff, parser_callback_t cb = nullptr) - : callback(cb), m_lexer(buff) + : callback(cb), + m_lexer(reinterpret_cast(buff), + strlen(buff)) {} /// a parser reading from a string container diff --git a/test/src/unit-class_lexer.cpp b/test/src/unit-class_lexer.cpp index 708a8cbfa..7cece21c6 100644 --- a/test/src/unit-class_lexer.cpp +++ b/test/src/unit-class_lexer.cpp @@ -38,43 +38,67 @@ TEST_CASE("lexer class") { 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); + CHECK(json::lexer(reinterpret_cast("["), + 1).scan() == json::lexer::token_type::begin_array); + CHECK(json::lexer(reinterpret_cast("]"), + 1).scan() == json::lexer::token_type::end_array); + CHECK(json::lexer(reinterpret_cast("{"), + 1).scan() == json::lexer::token_type::begin_object); + CHECK(json::lexer(reinterpret_cast("}"), + 1).scan() == json::lexer::token_type::end_object); + CHECK(json::lexer(reinterpret_cast(","), + 1).scan() == json::lexer::token_type::value_separator); + CHECK(json::lexer(reinterpret_cast(":"), + 1).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); + CHECK(json::lexer(reinterpret_cast("null"), + 4).scan() == json::lexer::token_type::literal_null); + CHECK(json::lexer(reinterpret_cast("true"), + 4).scan() == json::lexer::token_type::literal_true); + CHECK(json::lexer(reinterpret_cast("false"), + 5).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); + CHECK(json::lexer(reinterpret_cast("0"), + 1).scan() == json::lexer::token_type::value_number); + CHECK(json::lexer(reinterpret_cast("1"), + 1).scan() == json::lexer::token_type::value_number); + CHECK(json::lexer(reinterpret_cast("2"), + 1).scan() == json::lexer::token_type::value_number); + CHECK(json::lexer(reinterpret_cast("3"), + 1).scan() == json::lexer::token_type::value_number); + CHECK(json::lexer(reinterpret_cast("4"), + 1).scan() == json::lexer::token_type::value_number); + CHECK(json::lexer(reinterpret_cast("5"), + 1).scan() == json::lexer::token_type::value_number); + CHECK(json::lexer(reinterpret_cast("6"), + 1).scan() == json::lexer::token_type::value_number); + CHECK(json::lexer(reinterpret_cast("7"), + 1).scan() == json::lexer::token_type::value_number); + CHECK(json::lexer(reinterpret_cast("8"), + 1).scan() == json::lexer::token_type::value_number); + CHECK(json::lexer(reinterpret_cast("9"), + 1).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); + CHECK(json::lexer(reinterpret_cast(" "), + 1).scan() == json::lexer::token_type::end_of_input); + CHECK(json::lexer(reinterpret_cast("\t"), + 1).scan() == json::lexer::token_type::end_of_input); + CHECK(json::lexer(reinterpret_cast("\n"), + 1).scan() == json::lexer::token_type::end_of_input); + CHECK(json::lexer(reinterpret_cast("\r"), + 1).scan() == json::lexer::token_type::end_of_input); + CHECK(json::lexer(reinterpret_cast(" \t\n\r\n\t "), + 7).scan() == json::lexer::token_type::end_of_input); } } @@ -100,7 +124,11 @@ TEST_CASE("lexer class") { for (int c = 1; c < 128; ++c) { - auto s = std::string(1, c); + // create string from the ASCII code + const auto s = std::string(1, c); + // store scan() result + const auto res = json::lexer(reinterpret_cast(s.c_str()), + 1).scan(); switch (c) { @@ -122,7 +150,7 @@ TEST_CASE("lexer class") case ('8'): case ('9'): { - CHECK(json::lexer(s.c_str()).scan() != json::lexer::token_type::parse_error); + CHECK(res != json::lexer::token_type::parse_error); break; } @@ -132,14 +160,14 @@ TEST_CASE("lexer class") case ('\n'): case ('\r'): { - CHECK(json::lexer(s.c_str()).scan() == json::lexer::token_type::end_of_input); + CHECK(res == 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); + CHECK(res == json::lexer::token_type::parse_error); break; } } From dfc2c1abe56a74a498ddac72f107ce2e60b78674 Mon Sep 17 00:00:00 2001 From: Niels Date: Sun, 14 Aug 2016 23:38:20 +0200 Subject: [PATCH 41/77] added assertion for contiguous memory --- src/json.hpp | 8 +++++++- src/json.hpp.re2c | 8 +++++++- test/src/unit-class_parser.cpp | 14 +++++++------- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index a96851581..c2542682d 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -8902,7 +8902,13 @@ basic_json_parser_63: : callback(cb), m_lexer(reinterpret_cast(&(*first)), static_cast(std::distance(first, last))) - {} + { + int i = 0; + assert(std::accumulate(first, last, true, [&i, &first](bool res, decltype(*first) val) + { + return res and (val == *(std::next(std::addressof(*first), i++))); + })); + } /// public parser interface basic_json parse() diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 82b6a123f..20eb6e589 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -8199,7 +8199,13 @@ class basic_json : callback(cb), m_lexer(reinterpret_cast(&(*first)), static_cast(std::distance(first, last))) - {} + { + int i = 0; + assert(std::accumulate(first, last, true, [&i, &first](bool res, decltype(*first) val) + { + return res and (val == *(std::next(std::addressof(*first), i++))); + })); + } /// public parser interface basic_json parse() diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index 863cec520..b6d6a6157 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -750,42 +750,42 @@ TEST_CASE("parser class") SECTION("from std::vector") { std::vector v = {'t', 'r', 'u', 'e', '\0'}; - CHECK (json::parser(std::begin(v), std::end(v)).parse() == json(true)); + CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true)); } SECTION("from std::array") { std::array v { {'t', 'r', 'u', 'e', '\0'} }; - CHECK (json::parser(std::begin(v), std::end(v)).parse() == json(true)); + CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true)); } SECTION("from array") { uint8_t v[] = {'t', 'r', 'u', 'e'}; - CHECK (json::parser(std::begin(v), std::end(v)).parse() == json(true)); + CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true)); } SECTION("from char literal") { - CHECK (json::parser("true").parse() == json(true)); + CHECK(json::parser("true").parse() == json(true)); } SECTION("from std::string") { std::string v = {'t', 'r', 'u', 'e'}; - CHECK (json::parser(std::begin(v), std::end(v)).parse() == json(true)); + CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true)); } SECTION("from std::initializer_list") { std::initializer_list v = {'t', 'r', 'u', 'e', '\0'}; - CHECK (json::parser(std::begin(v), std::end(v)).parse() == json(true)); + CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true)); } SECTION("from std::valarray") { std::valarray v = {'t', 'r', 'u', 'e', '\0'}; - CHECK (json::parser(std::begin(v), std::end(v)).parse() == json(true)); + CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true)); } } } From ca80a71c2852207e08c99ea016b7a0ef271d69bf Mon Sep 17 00:00:00 2001 From: Niels Date: Mon, 15 Aug 2016 21:45:49 +0200 Subject: [PATCH 42/77] added notes from the CII Best Practices badge --- .github/CONTRIBUTING.md | 5 +++++ README.md | 1 + src/json.hpp | 4 ++-- src/json.hpp.re2c | 4 ++-- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 66420e9ac..2b4160810 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -6,6 +6,10 @@ This project started as a little excuse to exercise some of the cool new C++11 f To make it as easy as possible for you to contribute and for me to keep an overview, here are a few guidelines which should help us avoid all kinds of unnecessary work or disappointment. And of course, this document is subject to discussion, so please [create an issue](https://github.com/nlohmann/json/issues/new) or a pull request if you find a way to improve it! +## Private reports + +Usually, all issues are tracked publicly on [Github](https://github.com/nlohmann/json/issues). If you want to make a private report (e.g., for a vulnerability or to attach an example that is not meant to be publisheed), please send an email to . + ## Prerequisites Please [create an issue](https://github.com/nlohmann/json/issues/new), assuming one does not already exist, and describe your concern. Note you need a [GitHub account](https://github.com/signup/free) for this. @@ -57,6 +61,7 @@ Please understand that I cannot accept pull requests changing only file `src/jso ## Note - If you open a pull request, the code will be automatically tested with [Valgrind](http://valgrind.org)'s Memcheck tool to detect memory leaks. Please be aware that the execution with Valgrind _may_ in rare cases yield different behavior than running the code directly. This can result in failing unit tests which run successfully without Valgrind. +- There is a Makefile target `make pretty` which runs [Artistic Style](http://astyle.sourceforge.net) to fix indentation. If possible, run it before opening the pull request. Otherwise, we shall run it afterward. ## Please don't diff --git a/README.md b/README.md index de70912b4..73a03363b 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/nlohmann/json/master/LICENSE.MIT) [![Github Releases](https://img.shields.io/github/release/nlohmann/json.svg)](https://github.com/nlohmann/json/releases) [![Github Issues](https://img.shields.io/github/issues/nlohmann/json.svg)](http://github.com/nlohmann/json/issues) +[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/289/badge)](https://bestpractices.coreinfrastructure.org/projects/289) ## Design goals diff --git a/src/json.hpp b/src/json.hpp index 5ab8ec264..ddf37eb2e 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -9484,7 +9484,7 @@ basic_json_parser_63: } /// split the string input to reference tokens - static std::vector split(std::string reference_string) + static std::vector split(const std::string& reference_string) { std::vector result; @@ -10212,7 +10212,7 @@ basic_json_parser_63: */ static basic_json diff(const basic_json& source, const basic_json& target, - std::string path = "") + const std::string& path = "") { // the patch basic_json result(value_t::array); diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 0d8ffa155..740c768c5 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -8781,7 +8781,7 @@ class basic_json } /// split the string input to reference tokens - static std::vector split(std::string reference_string) + static std::vector split(const std::string& reference_string) { std::vector result; @@ -9509,7 +9509,7 @@ class basic_json */ static basic_json diff(const basic_json& source, const basic_json& target, - std::string path = "") + const std::string& path = "") { // the patch basic_json result(value_t::array); From 5e67f7af01440b6f91e4aae5bfc792202f6cec8f Mon Sep 17 00:00:00 2001 From: Niels Date: Mon, 15 Aug 2016 22:44:14 +0200 Subject: [PATCH 43/77] added a first version of a parser for #290 --- src/json.hpp | 93 +++++++++++++++++++++++++++---- src/json.hpp.re2c | 93 +++++++++++++++++++++++++++---- test/src/unit-deserialization.cpp | 50 ++++++++++++++++- 3 files changed, 215 insertions(+), 21 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 03c162533..6bafd0072 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -5962,6 +5962,16 @@ class basic_json return parser(s, cb).parse(); } + /*! + @brief deserialize from string literal + @copydoc parse(const string_t&, const parser_callback_t) + */ + static basic_json parse(const typename string_t::value_type* s, + const parser_callback_t cb = nullptr) + { + return parser(s, cb).parse(); + } + /*! @brief deserialize from stream @@ -6001,6 +6011,75 @@ class basic_json return parser(i, cb).parse(); } + /*! + @brief deserialize from a container with contiguous storage + + This function reads from a nonempty iterator range of a container with + contiguous storage of 1-byte values. Compatible container types include + `std::vector`, `std::string`, `std::array`, `std::valarray`, and + `std::initializer_list`. Furthermore, C-style arrays can be used with + `std::begin()`/`std::end()`. User-defined containers can be used as long + as they implement random-access iterators and a contiguous storage. + + @pre The iterator range is contiguous. Violating this precondition yields + undefined behavior. **This precondition is enforced with an assertion.** + @pre Each element in the range has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with an assertion.** + @pre The iterator range is nonempty. Violating this precondition yields + undefined behavior. **This precondition is enforced with an assertion.** + + @warning There is no way to enforce the preconditions at compile-time. If + the function is called with noncompliant iterators, the behavior + is undefined and will most liekely yield segmentation violation. + + @param[in] first begin of the range to parse (included) + @param[in] last end of the range to parse (excluded) + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @return result of the deserialization + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @todo Example and references. + + @since version 2.0.3 + */ + template ::iterator_category, std::random_access_iterator_tag>::value + , int>::type + = 0> + static basic_json parse(IteratorType first, IteratorType last, + const parser_callback_t cb = nullptr) + { + // assertion to check that the iterator range is indeed contiguous, + // see http://stackoverflow.com/a/35008842/266378 for more discussion + assert(std::accumulate(first, last, std::make_pair(true, 0), + [&first](std::pair res, decltype(*first) val) + { + res.first &= (val == *(std::next(std::addressof(*first), res.second++))); + return res; + }).first); + + // assertion to check that each element is 1 byte long + assert(std::all_of(first, last, [](decltype(*first) val) + { + return sizeof(val) == 1; + })); + + // assertion that the iterator range is not empty + assert(std::distance(first, last) > 0); + + return parser(first, last, cb).parse(); + } + /*! @brief deserialize from stream @@ -8875,10 +8954,10 @@ basic_json_parser_63: { public: /// a parser reading from a string literal - parser(const typename string_t::value_type* buff, parser_callback_t cb = nullptr) + parser(const typename string_t::value_type* buff, + const parser_callback_t cb = nullptr) : callback(cb), - m_lexer(reinterpret_cast(buff), - strlen(buff)) + m_lexer(reinterpret_cast(buff), strlen(buff)) {} /// a parser reading from a string container @@ -8902,13 +8981,7 @@ basic_json_parser_63: : callback(cb), m_lexer(reinterpret_cast(&(*first)), static_cast(std::distance(first, last))) - { - int i = 0; - assert(std::accumulate(first, last, true, [&i, &first](bool res, decltype(*first) val) - { - return res and (val == *(std::next(std::addressof(*first), i++))); - })); - } + {} /// public parser interface basic_json parse() diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 58724e911..87f7aabcf 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -5962,6 +5962,16 @@ class basic_json return parser(s, cb).parse(); } + /*! + @brief deserialize from string literal + @copydoc parse(const string_t&, const parser_callback_t) + */ + static basic_json parse(const typename string_t::value_type* s, + const parser_callback_t cb = nullptr) + { + return parser(s, cb).parse(); + } + /*! @brief deserialize from stream @@ -6001,6 +6011,75 @@ class basic_json return parser(i, cb).parse(); } + /*! + @brief deserialize from a container with contiguous storage + + This function reads from a nonempty iterator range of a container with + contiguous storage of 1-byte values. Compatible container types include + `std::vector`, `std::string`, `std::array`, `std::valarray`, and + `std::initializer_list`. Furthermore, C-style arrays can be used with + `std::begin()`/`std::end()`. User-defined containers can be used as long + as they implement random-access iterators and a contiguous storage. + + @pre The iterator range is contiguous. Violating this precondition yields + undefined behavior. **This precondition is enforced with an assertion.** + @pre Each element in the range has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with an assertion.** + @pre The iterator range is nonempty. Violating this precondition yields + undefined behavior. **This precondition is enforced with an assertion.** + + @warning There is no way to enforce the preconditions at compile-time. If + the function is called with noncompliant iterators, the behavior + is undefined and will most liekely yield segmentation violation. + + @param[in] first begin of the range to parse (included) + @param[in] last end of the range to parse (excluded) + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @return result of the deserialization + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @todo Example and references. + + @since version 2.0.3 + */ + template ::iterator_category, std::random_access_iterator_tag>::value + , int>::type + = 0> + static basic_json parse(IteratorType first, IteratorType last, + const parser_callback_t cb = nullptr) + { + // assertion to check that the iterator range is indeed contiguous, + // see http://stackoverflow.com/a/35008842/266378 for more discussion + assert(std::accumulate(first, last, std::make_pair(true, 0), + [&first](std::pair res, decltype(*first) val) + { + res.first &= (val == *(std::next(std::addressof(*first), res.second++))); + return res; + }).first); + + // assertion to check that each element is 1 byte long + assert(std::all_of(first, last, [](decltype(*first) val) + { + return sizeof(val) == 1; + })); + + // assertion that the iterator range is not empty + assert(std::distance(first, last) > 0); + + return parser(first, last, cb).parse(); + } + /*! @brief deserialize from stream @@ -8172,10 +8251,10 @@ class basic_json { public: /// a parser reading from a string literal - parser(const typename string_t::value_type* buff, parser_callback_t cb = nullptr) + parser(const typename string_t::value_type* buff, + const parser_callback_t cb = nullptr) : callback(cb), - m_lexer(reinterpret_cast(buff), - strlen(buff)) + m_lexer(reinterpret_cast(buff), strlen(buff)) {} /// a parser reading from a string container @@ -8199,13 +8278,7 @@ class basic_json : callback(cb), m_lexer(reinterpret_cast(&(*first)), static_cast(std::distance(first, last))) - { - int i = 0; - assert(std::accumulate(first, last, true, [&i, &first](bool res, decltype(*first) val) - { - return res and (val == *(std::next(std::addressof(*first), i++))); - })); - } + {} /// public parser interface basic_json parse() diff --git a/test/src/unit-deserialization.cpp b/test/src/unit-deserialization.cpp index 781885746..0e5e21229 100644 --- a/test/src/unit-deserialization.cpp +++ b/test/src/unit-deserialization.cpp @@ -31,6 +31,8 @@ SOFTWARE. #include "json.hpp" using nlohmann::json; +#include + TEST_CASE("deserialization") { SECTION("stream") @@ -41,13 +43,20 @@ TEST_CASE("deserialization") CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); } - SECTION("string") + SECTION("string literal") { 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("string_t") + { + json::string_t 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; @@ -70,4 +79,43 @@ TEST_CASE("deserialization") { CHECK("[\"foo\",1,2,3,false,{\"one\":1}]"_json == json({"foo", 1, 2, 3, false, {{"one", 1}}})); } + + SECTION("contiguous containers") + { + SECTION("from std::vector") + { + std::vector v = {'t', 'r', 'u', 'e', '\0'}; + CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); + } + + SECTION("from std::array") + { + std::array v { {'t', 'r', 'u', 'e', '\0'} }; + CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); + } + + SECTION("from array") + { + uint8_t v[] = {'t', 'r', 'u', 'e'}; + CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); + } + + SECTION("from std::string") + { + std::string v = {'t', 'r', 'u', 'e'}; + CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); + } + + SECTION("from std::initializer_list") + { + std::initializer_list v = {'t', 'r', 'u', 'e', '\0'}; + CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); + } + + SECTION("from std::valarray") + { + std::valarray v = {'t', 'r', 'u', 'e', '\0'}; + CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); + } + } } From a78eaa27b5bfdc000ef97391f6664cb317e5cbc9 Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 17 Aug 2016 21:31:59 +0200 Subject: [PATCH 44/77] fixed unit tests --- test/src/unit-class_parser.cpp | 2 +- test/src/unit-deserialization.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index b6d6a6157..775952b92 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -761,7 +761,7 @@ TEST_CASE("parser class") SECTION("from array") { - uint8_t v[] = {'t', 'r', 'u', 'e'}; + uint8_t v[] = {'t', 'r', 'u', 'e', '\0'}; CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true)); } diff --git a/test/src/unit-deserialization.cpp b/test/src/unit-deserialization.cpp index 0e5e21229..965d1d075 100644 --- a/test/src/unit-deserialization.cpp +++ b/test/src/unit-deserialization.cpp @@ -96,7 +96,7 @@ TEST_CASE("deserialization") SECTION("from array") { - uint8_t v[] = {'t', 'r', 'u', 'e'}; + uint8_t v[] = {'t', 'r', 'u', 'e', '\0'}; CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); } From 35f22e85962fb1b325ba595b7d696449fdd0c435 Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 17 Aug 2016 21:38:19 +0200 Subject: [PATCH 45/77] checking MSVC compiler flags --- test/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 782d5b53f..9fe0327c8 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -38,8 +38,8 @@ add_executable(${JSON_UNITTEST_TARGET_NAME} set_target_properties(${JSON_UNITTEST_TARGET_NAME} PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED ON - COMPILE_DEFINITIONS "$<$:_SCL_SECURE_NO_WARNINGS>" - COMPILE_OPTIONS "$<$:/EHsc;$<$:/Od>>" + #COMPILE_DEFINITIONS "$<$:_SCL_SECURE_NO_WARNINGS>" + COMPILE_OPTIONS "$<$:/EHsc /Wall;$<$:/Od>>" ) target_include_directories(${JSON_UNITTEST_TARGET_NAME} PRIVATE "src") From c0922c7aac3ffacbfcde50eddb45ce23006e5b1e Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 17 Aug 2016 21:43:28 +0200 Subject: [PATCH 46/77] /Wall --- test/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9fe0327c8..540d9db19 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -39,7 +39,7 @@ set_target_properties(${JSON_UNITTEST_TARGET_NAME} PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED ON #COMPILE_DEFINITIONS "$<$:_SCL_SECURE_NO_WARNINGS>" - COMPILE_OPTIONS "$<$:/EHsc /Wall;$<$:/Od>>" + COMPILE_OPTIONS "$<$:/EHsc;/Wall$<$:/Od>>" ) target_include_directories(${JSON_UNITTEST_TARGET_NAME} PRIVATE "src") From f40f81c87eb9cfb7b3983e8d71c9ec5ba82db87d Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 17 Aug 2016 21:44:53 +0200 Subject: [PATCH 47/77] forgot a semicolon --- test/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 540d9db19..febb06958 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -39,7 +39,7 @@ set_target_properties(${JSON_UNITTEST_TARGET_NAME} PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED ON #COMPILE_DEFINITIONS "$<$:_SCL_SECURE_NO_WARNINGS>" - COMPILE_OPTIONS "$<$:/EHsc;/Wall$<$:/Od>>" + COMPILE_OPTIONS "$<$:/EHsc;/Wall;$<$:/Od>>" ) target_include_directories(${JSON_UNITTEST_TARGET_NAME} PRIVATE "src") From 628a5eae500b8320aec3f0fbb4a5eba48d320e03 Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 17 Aug 2016 22:33:26 +0200 Subject: [PATCH 48/77] reset build file --- test/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index febb06958..782d5b53f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -38,8 +38,8 @@ add_executable(${JSON_UNITTEST_TARGET_NAME} set_target_properties(${JSON_UNITTEST_TARGET_NAME} PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED ON - #COMPILE_DEFINITIONS "$<$:_SCL_SECURE_NO_WARNINGS>" - COMPILE_OPTIONS "$<$:/EHsc;/Wall;$<$:/Od>>" + COMPILE_DEFINITIONS "$<$:_SCL_SECURE_NO_WARNINGS>" + COMPILE_OPTIONS "$<$:/EHsc;$<$:/Od>>" ) target_include_directories(${JSON_UNITTEST_TARGET_NAME} PRIVATE "src") From 039cedaf8e2884d136b70aae1f3a2b80640b2583 Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 17 Aug 2016 23:14:28 +0200 Subject: [PATCH 49/77] changes to address #295 --- src/json.hpp | 6 ++++-- src/json.hpp.re2c | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index ddf37eb2e..a8289a495 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -8892,7 +8892,8 @@ basic_json_parser_63: { case lexer::token_type::begin_object: { - if (keep and (not callback or (keep = callback(depth++, parse_event_t::object_start, result)))) + if (keep and (not callback + or ((keep = callback(depth++, parse_event_t::object_start, result)) != 0))) { // explicitly set result to object to cope with {} result.m_type = value_t::object; @@ -8970,7 +8971,8 @@ basic_json_parser_63: case lexer::token_type::begin_array: { - if (keep and (not callback or (keep = callback(depth++, parse_event_t::array_start, result)))) + if (keep and (not callback + or ((keep = callback(depth++, parse_event_t::array_start, result)) != 0))) { // explicitly set result to object to cope with [] result.m_type = value_t::array; diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 740c768c5..ffa20f39a 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -8189,7 +8189,8 @@ class basic_json { case lexer::token_type::begin_object: { - if (keep and (not callback or (keep = callback(depth++, parse_event_t::object_start, result)))) + if (keep and (not callback + or ((keep = callback(depth++, parse_event_t::object_start, result)) != 0))) { // explicitly set result to object to cope with {} result.m_type = value_t::object; @@ -8267,7 +8268,8 @@ class basic_json case lexer::token_type::begin_array: { - if (keep and (not callback or (keep = callback(depth++, parse_event_t::array_start, result)))) + if (keep and (not callback + or ((keep = callback(depth++, parse_event_t::array_start, result)) != 0))) { // explicitly set result to object to cope with [] result.m_type = value_t::array; From d2564c6100062476182006fc9e3ba34d2078facf Mon Sep 17 00:00:00 2001 From: Niels Date: Thu, 18 Aug 2016 18:29:19 +0200 Subject: [PATCH 50/77] added cppcheck target for travis --- .travis.yml | 14 ++++++++++++++ Makefile | 3 +-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ad1d12d97..bd806cb89 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,6 +41,20 @@ matrix: after_success: - valgrind --error-exitcode=1 --leak-check=full test/json_unit + # cppcheck + + - os: linux + compiler: gcc + env: + - COMPILER=g++-4.9 + - SPECIAL=cppcheck + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: [g++-4.9, cppcheck] + after_success: + - make cppcheck + # Coveralls (http://gronlier.fr/blog/2015/01/adding-code-coverage-to-your-c-project/) - os: linux diff --git a/Makefile b/Makefile index f08d5cf37..b53d8d3dc 100644 --- a/Makefile +++ b/Makefile @@ -64,8 +64,7 @@ fuzz: test/src/fuzz.cpp src/json.hpp # call cppcheck on the main header file cppcheck: - cppcheck --enable=all --inconclusive --std=c++11 src/json.hpp - + cppcheck --enable=warning --inconclusive --force --std=c++11 src/json.hpp --error-exitcode=1 ########################################################################## # maintainer targets From 0cf7ebaa57f83935398f466907e322062c167a4b Mon Sep 17 00:00:00 2001 From: Niels Date: Thu, 18 Aug 2016 18:43:27 +0200 Subject: [PATCH 51/77] mentioning the CII --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 73a03363b..3015e860a 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ There are myriads of [JSON](http://json.org) libraries out there, and each may e - **Trivial integration**. Our whole code consists of a single header file [`json.hpp`](https://github.com/nlohmann/json/blob/develop/src/json.hpp). That's it. No library, no subproject, no dependencies, no complex build system. The class is written in vanilla C++11. All in all, everything should require no adjustment of your compiler flags or project settings. -- **Serious testing**. Our class is heavily [unit-tested](https://github.com/nlohmann/json/blob/master/test/src/unit.cpp) and covers [100%](https://coveralls.io/r/nlohmann/json) of the code, including all exceptional behavior. Furthermore, we checked with [Valgrind](http://valgrind.org) that there are no memory leaks. +- **Serious testing**. Our class is heavily [unit-tested](https://github.com/nlohmann/json/blob/master/test/src/unit.cpp) and covers [100%](https://coveralls.io/r/nlohmann/json) of the code, including all exceptional behavior. Furthermore, we checked with [Valgrind](http://valgrind.org) that there are no memory leaks. To maintain high quality, the project is following the [Core Infrastructure Initiative (CII) best practices](https://bestpractices.coreinfrastructure.org/projects/289). Other aspects were not so important to us: From eef80590037f0aa4d1d100ad4495378082954849 Mon Sep 17 00:00:00 2001 From: Niels Date: Sat, 20 Aug 2016 20:29:33 +0200 Subject: [PATCH 52/77] allowing parsing from contiguous containers --- src/json.hpp | 51 ++++++++++++----- src/json.hpp.re2c | 51 ++++++++++++----- test/src/unit-deserialization.cpp | 94 ++++++++++++++++++++++--------- 3 files changed, 144 insertions(+), 52 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index f10e5960a..8a1c9a777 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -5956,11 +5956,13 @@ class basic_json @since version 1.0.0 */ + /* static basic_json parse(const string_t& s, const parser_callback_t cb = nullptr) { return parser(s, cb).parse(); } + */ /*! @brief deserialize from string literal @@ -6012,10 +6014,10 @@ class basic_json } /*! - @brief deserialize from a container with contiguous storage + @brief deserialize from a iterator range with contiguous storage - This function reads from a nonempty iterator range of a container with - contiguous storage of 1-byte values. Compatible container types include + This function reads from an iterator range of a container with contiguous + storage of 1-byte values. Compatible container types include `std::vector`, `std::string`, `std::array`, `std::valarray`, and `std::initializer_list`. Furthermore, C-style arrays can be used with `std::begin()`/`std::end()`. User-defined containers can be used as long @@ -6025,9 +6027,7 @@ class basic_json undefined behavior. **This precondition is enforced with an assertion.** @pre Each element in the range has a size of 1 byte. Violating this precondition yields undefined behavior. **This precondition is enforced - with an assertion.** - @pre The iterator range is nonempty. Violating this precondition yields - undefined behavior. **This precondition is enforced with an assertion.** + with a static assertion.** @warning There is no way to enforce the preconditions at compile-time. If the function is called with noncompliant iterators, the behavior @@ -6053,7 +6053,9 @@ class basic_json */ template ::iterator_category, std::random_access_iterator_tag>::value + std::is_base_of< + std::random_access_iterator_tag, + typename std::iterator_traits::iterator_category>::value , int>::type = 0> static basic_json parse(IteratorType first, IteratorType last, @@ -6069,17 +6071,40 @@ class basic_json }).first); // assertion to check that each element is 1 byte long - assert(std::all_of(first, last, [](decltype(*first) val) - { - return sizeof(val) == 1; - })); + static_assert(sizeof(typename std::iterator_traits::value_type) == 1, + "each element in the iterator range must have the size of 1 byte"); - // assertion that the iterator range is not empty - assert(std::distance(first, last) > 0); + // if iterator range is empty, create a parser with an empty string + // to generate "unexpected EOF" error message + if (std::distance(first, last) <= 0) + { + return parser("").parse(); + } return parser(first, last, cb).parse(); } + template()))>::iterator_category>::value + , int>::type = 0> + static basic_json parse(const ContiguousContainer& c, + const parser_callback_t cb = nullptr) + { + // delegate the call to the iterator-range parse overload + return parse(std::begin(c), std::end(c), cb); + } + + template + static basic_json parse(T (&array)[N], + const parser_callback_t cb = nullptr) + { + // delegate the call to the iterator-range parse overload + return parse(std::begin(array), std::end(array), cb); + } + /*! @brief deserialize from stream diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index b1f5a6334..cb51f557c 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -5956,11 +5956,13 @@ class basic_json @since version 1.0.0 */ + /* static basic_json parse(const string_t& s, const parser_callback_t cb = nullptr) { return parser(s, cb).parse(); } + */ /*! @brief deserialize from string literal @@ -6012,10 +6014,10 @@ class basic_json } /*! - @brief deserialize from a container with contiguous storage + @brief deserialize from a iterator range with contiguous storage - This function reads from a nonempty iterator range of a container with - contiguous storage of 1-byte values. Compatible container types include + This function reads from an iterator range of a container with contiguous + storage of 1-byte values. Compatible container types include `std::vector`, `std::string`, `std::array`, `std::valarray`, and `std::initializer_list`. Furthermore, C-style arrays can be used with `std::begin()`/`std::end()`. User-defined containers can be used as long @@ -6025,9 +6027,7 @@ class basic_json undefined behavior. **This precondition is enforced with an assertion.** @pre Each element in the range has a size of 1 byte. Violating this precondition yields undefined behavior. **This precondition is enforced - with an assertion.** - @pre The iterator range is nonempty. Violating this precondition yields - undefined behavior. **This precondition is enforced with an assertion.** + with a static assertion.** @warning There is no way to enforce the preconditions at compile-time. If the function is called with noncompliant iterators, the behavior @@ -6053,7 +6053,9 @@ class basic_json */ template ::iterator_category, std::random_access_iterator_tag>::value + std::is_base_of< + std::random_access_iterator_tag, + typename std::iterator_traits::iterator_category>::value , int>::type = 0> static basic_json parse(IteratorType first, IteratorType last, @@ -6069,17 +6071,40 @@ class basic_json }).first); // assertion to check that each element is 1 byte long - assert(std::all_of(first, last, [](decltype(*first) val) - { - return sizeof(val) == 1; - })); + static_assert(sizeof(typename std::iterator_traits::value_type) == 1, + "each element in the iterator range must have the size of 1 byte"); - // assertion that the iterator range is not empty - assert(std::distance(first, last) > 0); + // if iterator range is empty, create a parser with an empty string + // to generate "unexpected EOF" error message + if (std::distance(first, last) <= 0) + { + return parser("").parse(); + } return parser(first, last, cb).parse(); } + template()))>::iterator_category>::value + , int>::type = 0> + static basic_json parse(const ContiguousContainer& c, + const parser_callback_t cb = nullptr) + { + // delegate the call to the iterator-range parse overload + return parse(std::begin(c), std::end(c), cb); + } + + template + static basic_json parse(T (&array)[N], + const parser_callback_t cb = nullptr) + { + // delegate the call to the iterator-range parse overload + return parse(std::begin(array), std::end(array), cb); + } + /*! @brief deserialize from stream diff --git a/test/src/unit-deserialization.cpp b/test/src/unit-deserialization.cpp index 965d1d075..b681e3df3 100644 --- a/test/src/unit-deserialization.cpp +++ b/test/src/unit-deserialization.cpp @@ -82,40 +82,82 @@ TEST_CASE("deserialization") SECTION("contiguous containers") { - SECTION("from std::vector") + SECTION("directly") { - std::vector v = {'t', 'r', 'u', 'e', '\0'}; - CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); + SECTION("from std::vector") + { + std::vector v = {'t', 'r', 'u', 'e', '\0'}; + CHECK(json::parse(v) == json(true)); + } + + SECTION("from std::array") + { + std::array v { {'t', 'r', 'u', 'e', '\0'} }; + CHECK(json::parse(v) == json(true)); + } + + SECTION("from array") + { + uint8_t v[] = {'t', 'r', 'u', 'e', '\0'}; + CHECK(json::parse(v) == json(true)); + } + + SECTION("from std::string") + { + std::string v = {'t', 'r', 'u', 'e'}; + CHECK(json::parse(v) == json(true)); + } + + SECTION("from std::initializer_list") + { + std::initializer_list v = {'t', 'r', 'u', 'e', '\0'}; + CHECK(json::parse(v) == json(true)); + } } - SECTION("from std::array") + SECTION("via iterator range") { - std::array v { {'t', 'r', 'u', 'e', '\0'} }; - CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); - } + SECTION("from std::vector") + { + std::vector v = {'t', 'r', 'u', 'e', '\0'}; + CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); + } - SECTION("from array") - { - uint8_t v[] = {'t', 'r', 'u', 'e', '\0'}; - CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); - } + SECTION("from std::array") + { + std::array v { {'t', 'r', 'u', 'e', '\0'} }; + CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); + } - SECTION("from std::string") - { - std::string v = {'t', 'r', 'u', 'e'}; - CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); - } + SECTION("from array") + { + uint8_t v[] = {'t', 'r', 'u', 'e', '\0'}; + CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); + } - SECTION("from std::initializer_list") - { - std::initializer_list v = {'t', 'r', 'u', 'e', '\0'}; - CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); - } + SECTION("from std::string") + { + std::string v = {'t', 'r', 'u', 'e'}; + CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); + } - SECTION("from std::valarray") - { - std::valarray v = {'t', 'r', 'u', 'e', '\0'}; - CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); + SECTION("from std::initializer_list") + { + std::initializer_list v = {'t', 'r', 'u', 'e', '\0'}; + CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); + } + + SECTION("from std::valarray") + { + std::valarray v = {'t', 'r', 'u', 'e', '\0'}; + CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); + } + + SECTION("with empty range") + { + std::vector v; + CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument); + } } } } From b4571360df0294f7e824aced8e13f062f3414493 Mon Sep 17 00:00:00 2001 From: Niels Date: Sun, 21 Aug 2016 12:35:40 +0200 Subject: [PATCH 53/77] more on #290 --- README.md | 2 +- .../parse__array__parser_callback_t.cpp | 28 ++++ .../parse__array__parser_callback_t.link | 1 + .../parse__array__parser_callback_t.output | 20 +++ ...contiguouscontainer__parser_callback_t.cpp | 13 ++ ...ontiguouscontainer__parser_callback_t.link | 1 + ...tiguouscontainer__parser_callback_t.output | 6 + ...parse__iteratortype__parser_callback_t.cpp | 13 ++ ...arse__iteratortype__parser_callback_t.link | 1 + ...se__iteratortype__parser_callback_t.output | 6 + .../parse__string__parser_callback_t.cpp | 4 +- .../parse__string__parser_callback_t.link | 2 +- src/json.hpp | 133 ++++++++++++------ src/json.hpp.re2c | 133 ++++++++++++------ test/src/unit-class_parser.cpp | 30 ++-- test/src/unit-deserialization.cpp | 6 + 16 files changed, 298 insertions(+), 101 deletions(-) create mode 100644 doc/examples/parse__array__parser_callback_t.cpp create mode 100644 doc/examples/parse__array__parser_callback_t.link create mode 100644 doc/examples/parse__array__parser_callback_t.output create mode 100644 doc/examples/parse__contiguouscontainer__parser_callback_t.cpp create mode 100644 doc/examples/parse__contiguouscontainer__parser_callback_t.link create mode 100644 doc/examples/parse__contiguouscontainer__parser_callback_t.output create mode 100644 doc/examples/parse__iteratortype__parser_callback_t.cpp create mode 100644 doc/examples/parse__iteratortype__parser_callback_t.link create mode 100644 doc/examples/parse__iteratortype__parser_callback_t.output diff --git a/README.md b/README.md index 3015e860a..9893fedd2 100644 --- a/README.md +++ b/README.md @@ -511,7 +511,7 @@ To compile and run the tests, you need to execute $ make check =============================================================================== -All tests passed (8905099 assertions in 32 test cases) +All tests passed (8905119 assertions in 32 test cases) ``` For more information, have a look at the file [.travis.yml](https://github.com/nlohmann/json/blob/master/.travis.yml). diff --git a/doc/examples/parse__array__parser_callback_t.cpp b/doc/examples/parse__array__parser_callback_t.cpp new file mode 100644 index 000000000..8e086d200 --- /dev/null +++ b/doc/examples/parse__array__parser_callback_t.cpp @@ -0,0 +1,28 @@ +#include + +using json = nlohmann::json; + +int main() +{ + // a JSON text + char text[] = 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] + } + } + )"; + + // parse and serialize JSON + json j_complete = json::parse(text); + std::cout << std::setw(4) << j_complete << "\n\n"; +} diff --git a/doc/examples/parse__array__parser_callback_t.link b/doc/examples/parse__array__parser_callback_t.link new file mode 100644 index 000000000..a1d3cd349 --- /dev/null +++ b/doc/examples/parse__array__parser_callback_t.link @@ -0,0 +1 @@ +online \ No newline at end of file diff --git a/doc/examples/parse__array__parser_callback_t.output b/doc/examples/parse__array__parser_callback_t.output new file mode 100644 index 000000000..62bb85863 --- /dev/null +++ b/doc/examples/parse__array__parser_callback_t.output @@ -0,0 +1,20 @@ +{ + "Image": { + "Animated": false, + "Height": 600, + "IDs": [ + 116, + 943, + 234, + 38793 + ], + "Thumbnail": { + "Height": 125, + "Url": "http://www.example.com/image/481989943", + "Width": 100 + }, + "Title": "View from 15th Floor", + "Width": 800 + } +} + diff --git a/doc/examples/parse__contiguouscontainer__parser_callback_t.cpp b/doc/examples/parse__contiguouscontainer__parser_callback_t.cpp new file mode 100644 index 000000000..5a339079f --- /dev/null +++ b/doc/examples/parse__contiguouscontainer__parser_callback_t.cpp @@ -0,0 +1,13 @@ +#include + +using json = nlohmann::json; + +int main() +{ + // a JSON text given as std::vector + std::vector text = {'[', '1', ',', '2', ',', '3', ']', '\0'}; + + // parse and serialize JSON + json j_complete = json::parse(text); + std::cout << std::setw(4) << j_complete << "\n\n"; +} diff --git a/doc/examples/parse__contiguouscontainer__parser_callback_t.link b/doc/examples/parse__contiguouscontainer__parser_callback_t.link new file mode 100644 index 000000000..57d6dc3a9 --- /dev/null +++ b/doc/examples/parse__contiguouscontainer__parser_callback_t.link @@ -0,0 +1 @@ +online \ No newline at end of file diff --git a/doc/examples/parse__contiguouscontainer__parser_callback_t.output b/doc/examples/parse__contiguouscontainer__parser_callback_t.output new file mode 100644 index 000000000..74633e808 --- /dev/null +++ b/doc/examples/parse__contiguouscontainer__parser_callback_t.output @@ -0,0 +1,6 @@ +[ + 1, + 2, + 3 +] + diff --git a/doc/examples/parse__iteratortype__parser_callback_t.cpp b/doc/examples/parse__iteratortype__parser_callback_t.cpp new file mode 100644 index 000000000..3f723c5fa --- /dev/null +++ b/doc/examples/parse__iteratortype__parser_callback_t.cpp @@ -0,0 +1,13 @@ +#include + +using json = nlohmann::json; + +int main() +{ + // a JSON text given as std::vector + std::vector text = {'[', '1', ',', '2', ',', '3', ']', '\0'}; + + // parse and serialize JSON + json j_complete = json::parse(text.begin(), text.end()); + std::cout << std::setw(4) << j_complete << "\n\n"; +} diff --git a/doc/examples/parse__iteratortype__parser_callback_t.link b/doc/examples/parse__iteratortype__parser_callback_t.link new file mode 100644 index 000000000..63f58fe64 --- /dev/null +++ b/doc/examples/parse__iteratortype__parser_callback_t.link @@ -0,0 +1 @@ +online \ No newline at end of file diff --git a/doc/examples/parse__iteratortype__parser_callback_t.output b/doc/examples/parse__iteratortype__parser_callback_t.output new file mode 100644 index 000000000..74633e808 --- /dev/null +++ b/doc/examples/parse__iteratortype__parser_callback_t.output @@ -0,0 +1,6 @@ +[ + 1, + 2, + 3 +] + diff --git a/doc/examples/parse__string__parser_callback_t.cpp b/doc/examples/parse__string__parser_callback_t.cpp index 62982ca6f..0a4f3b539 100644 --- a/doc/examples/parse__string__parser_callback_t.cpp +++ b/doc/examples/parse__string__parser_callback_t.cpp @@ -5,7 +5,7 @@ using json = nlohmann::json; int main() { // a JSON text - std::string text = R"( + auto text = R"( { "Image": { "Width": 800, @@ -44,4 +44,4 @@ int main() // parse (with callback) and serialize JSON json j_filtered = json::parse(text, cb); std::cout << std::setw(4) << j_filtered << '\n'; -} \ No newline at end of file +} diff --git a/doc/examples/parse__string__parser_callback_t.link b/doc/examples/parse__string__parser_callback_t.link index 1ad3b7199..292046b6b 100644 --- a/doc/examples/parse__string__parser_callback_t.link +++ b/doc/examples/parse__string__parser_callback_t.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/src/json.hpp b/src/json.hpp index 8a1c9a777..0675a9399 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -951,7 +951,7 @@ class basic_json With a parser callback function, the result of parsing a JSON text can be influenced. When passed to @ref parse(std::istream&, const - parser_callback_t) or @ref parse(const string_t&, const parser_callback_t), + parser_callback_t) or @ref parse(const char*, const parser_callback_t), it is called on certain events (passed as @ref parse_event_t via parameter @a event) with a set recursion depth @a depth and context JSON value @a parsed. The return value of the callback function is a boolean @@ -994,7 +994,7 @@ class basic_json skipped completely or replaced by an empty discarded object. @sa @ref parse(std::istream&, parser_callback_t) or - @ref parse(const string_t&, parser_callback_t) for examples + @ref parse(const char*, parser_callback_t) for examples @since version 1.0.0 */ @@ -5933,9 +5933,9 @@ class basic_json /// @{ /*! - @brief deserialize from string + @brief deserialize from string literal - @param[in] s string to read a serialized JSON value from + @param[in] s string literal to read a serialized JSON value from @param[in] cb a parser callback function of type @ref parser_callback_t which is used to control the deserialization by filtering unwanted values (optional) @@ -5947,6 +5947,8 @@ class basic_json @a cb has a super-linear complexity. @note A UTF-8 byte order mark is silently ignored. + @note String containers like `std::string` or @ref string_t can be parsed + with @ref parse(const ContiguousContainer&, const parser_callback_t) @liveexample{The example below demonstrates the `parse()` function with and without callback function.,parse__string__parser_callback_t} @@ -5954,24 +5956,47 @@ class basic_json @sa @ref parse(std::istream&, const parser_callback_t) for a version that reads from an input stream - @since version 1.0.0 + @since version 1.0.0 (originally for @ref string_t) */ - /* - static basic_json parse(const string_t& s, + static basic_json parse(const char* s, const parser_callback_t cb = nullptr) { return parser(s, cb).parse(); } - */ /*! - @brief deserialize from string literal - @copydoc parse(const string_t&, const parser_callback_t) + @brief deserialize from an array + + This function reads from an array of 1-byte values. + + @pre Each element of the container has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with a static assertion.** + + @param[in] array array to read from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @return result of the deserialization + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `parse()` function reading + from an array.,parse__array__parser_callback_t} + + @since version 2.0.3 */ - static basic_json parse(const typename string_t::value_type* s, + template + static basic_json parse(T (&array)[N], const parser_callback_t cb = nullptr) { - return parser(s, cb).parse(); + // delegate the call to the iterator-range parse overload + return parse(std::begin(array), std::end(array), cb); } /*! @@ -5993,7 +6018,7 @@ class basic_json @liveexample{The example below demonstrates the `parse()` function with and without callback function.,parse__istream__parser_callback_t} - @sa @ref parse(const string_t&, const parser_callback_t) for a version + @sa @ref parse(const char*, const parser_callback_t) for a version that reads from a string @since version 1.0.0 @@ -6014,7 +6039,7 @@ class basic_json } /*! - @brief deserialize from a iterator range with contiguous storage + @brief deserialize from an iterator range with contiguous storage This function reads from an iterator range of a container with contiguous storage of 1-byte values. Compatible container types include @@ -6029,13 +6054,14 @@ class basic_json precondition yields undefined behavior. **This precondition is enforced with a static assertion.** - @warning There is no way to enforce the preconditions at compile-time. If - the function is called with noncompliant iterators, the behavior - is undefined and will most liekely yield segmentation violation. + @warning There is no way to enforce all preconditions at compile-time. If + the function is called with noncompliant iterators and with + assertions switched off, the behavior is undefined and will most + likely yield segmentation violation. - @param[in] first begin of the range to parse (included) - @param[in] last end of the range to parse (excluded) - @param[in] cb a parser callback function of type @ref parser_callback_t + @param[in] first begin of the range to parse (included) + @param[in] last end of the range to parse (excluded) + @param[in] cb a parser callback function of type @ref parser_callback_t which is used to control the deserialization by filtering unwanted values (optional) @@ -6047,7 +6073,8 @@ class basic_json @note A UTF-8 byte order mark is silently ignored. - @todo Example and references. + @liveexample{The example below demonstrates the `parse()` function reading + from an iterator range.,parse__iteratortype__parser_callback_t} @since version 2.0.3 */ @@ -6084,6 +6111,45 @@ class basic_json return parser(first, last, cb).parse(); } + /*! + @brief deserialize from a container with contiguous storage + + This function reads from a container with contiguous storage of 1-byte + values. Compatible container types include `std::vector`, `std::string`, + `std::array`, and `std::initializer_list`. User-defined containers can be + used as long as they implement random-access iterators and a contiguous + storage. + + @pre The container storage is contiguous. Violating this precondition + yields undefined behavior. **This precondition is enforced with an + assertion.** + @pre Each element of the container has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with a static assertion.** + + @warning There is no way to enforce all preconditions at compile-time. If + the function is called with a noncompliant container and with + assertions switched off, the behavior is undefined and will most + likely yield segmentation violation. + + @param[in] c container to read from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @return result of the deserialization + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `parse()` function reading + from a contiguous container.,parse__contiguouscontainer__parser_callback_t} + + @since version 2.0.3 + */ template - static basic_json parse(T (&array)[N], - const parser_callback_t cb = nullptr) - { - // delegate the call to the iterator-range parse overload - return parse(std::begin(array), std::end(array), cb); - } - /*! @brief deserialize from stream @@ -6158,7 +6216,7 @@ class basic_json Returns the type name as string to be used in error messages - usually to indicate that a function was called on a wrong JSON type. - @return basically a string representation of a the @ref m_type member + @return basically a string representation of a the @a m_type member @complexity Constant. @@ -7626,7 +7684,7 @@ class basic_json fill_line_buffer(); } - // switch off unwanted functions + // switch off unwanted functions (due to pointer members) lexer() = delete; lexer(const lexer&) = delete; lexer operator=(const lexer&) = delete; @@ -8979,24 +9037,17 @@ basic_json_parser_63: { public: /// a parser reading from a string literal - parser(const typename string_t::value_type* buff, - const parser_callback_t cb = nullptr) + parser(const char* buff, const parser_callback_t cb = nullptr) : callback(cb), m_lexer(reinterpret_cast(buff), strlen(buff)) {} - /// a parser reading from a string container - parser(const string_t& s, const parser_callback_t cb = nullptr) - : callback(cb), - m_lexer(reinterpret_cast(s.c_str()), s.size()) - {} - /// a parser reading from an input stream parser(std::istream& is, const parser_callback_t cb = nullptr) : callback(cb), m_lexer(is) {} - /// a parser reading from a container with contiguous storage + /// a parser reading from an iterator range with contiguous storage template ::iterator_category, std::random_access_iterator_tag>::value @@ -10560,7 +10611,7 @@ if no parse error occurred. */ inline nlohmann::json operator "" _json(const char* s, std::size_t) { - return nlohmann::json::parse(reinterpret_cast(s)); + return nlohmann::json::parse(s); } /*! diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index cb51f557c..f56c5d3fa 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -951,7 +951,7 @@ class basic_json With a parser callback function, the result of parsing a JSON text can be influenced. When passed to @ref parse(std::istream&, const - parser_callback_t) or @ref parse(const string_t&, const parser_callback_t), + parser_callback_t) or @ref parse(const char*, const parser_callback_t), it is called on certain events (passed as @ref parse_event_t via parameter @a event) with a set recursion depth @a depth and context JSON value @a parsed. The return value of the callback function is a boolean @@ -994,7 +994,7 @@ class basic_json skipped completely or replaced by an empty discarded object. @sa @ref parse(std::istream&, parser_callback_t) or - @ref parse(const string_t&, parser_callback_t) for examples + @ref parse(const char*, parser_callback_t) for examples @since version 1.0.0 */ @@ -5933,9 +5933,9 @@ class basic_json /// @{ /*! - @brief deserialize from string + @brief deserialize from string literal - @param[in] s string to read a serialized JSON value from + @param[in] s string literal to read a serialized JSON value from @param[in] cb a parser callback function of type @ref parser_callback_t which is used to control the deserialization by filtering unwanted values (optional) @@ -5947,6 +5947,8 @@ class basic_json @a cb has a super-linear complexity. @note A UTF-8 byte order mark is silently ignored. + @note String containers like `std::string` or @ref string_t can be parsed + with @ref parse(const ContiguousContainer&, const parser_callback_t) @liveexample{The example below demonstrates the `parse()` function with and without callback function.,parse__string__parser_callback_t} @@ -5954,24 +5956,47 @@ class basic_json @sa @ref parse(std::istream&, const parser_callback_t) for a version that reads from an input stream - @since version 1.0.0 + @since version 1.0.0 (originally for @ref string_t) */ - /* - static basic_json parse(const string_t& s, + static basic_json parse(const char* s, const parser_callback_t cb = nullptr) { return parser(s, cb).parse(); } - */ /*! - @brief deserialize from string literal - @copydoc parse(const string_t&, const parser_callback_t) + @brief deserialize from an array + + This function reads from an array of 1-byte values. + + @pre Each element of the container has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with a static assertion.** + + @param[in] array array to read from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @return result of the deserialization + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `parse()` function reading + from an array.,parse__array__parser_callback_t} + + @since version 2.0.3 */ - static basic_json parse(const typename string_t::value_type* s, + template + static basic_json parse(T (&array)[N], const parser_callback_t cb = nullptr) { - return parser(s, cb).parse(); + // delegate the call to the iterator-range parse overload + return parse(std::begin(array), std::end(array), cb); } /*! @@ -5993,7 +6018,7 @@ class basic_json @liveexample{The example below demonstrates the `parse()` function with and without callback function.,parse__istream__parser_callback_t} - @sa @ref parse(const string_t&, const parser_callback_t) for a version + @sa @ref parse(const char*, const parser_callback_t) for a version that reads from a string @since version 1.0.0 @@ -6014,7 +6039,7 @@ class basic_json } /*! - @brief deserialize from a iterator range with contiguous storage + @brief deserialize from an iterator range with contiguous storage This function reads from an iterator range of a container with contiguous storage of 1-byte values. Compatible container types include @@ -6029,13 +6054,14 @@ class basic_json precondition yields undefined behavior. **This precondition is enforced with a static assertion.** - @warning There is no way to enforce the preconditions at compile-time. If - the function is called with noncompliant iterators, the behavior - is undefined and will most liekely yield segmentation violation. + @warning There is no way to enforce all preconditions at compile-time. If + the function is called with noncompliant iterators and with + assertions switched off, the behavior is undefined and will most + likely yield segmentation violation. - @param[in] first begin of the range to parse (included) - @param[in] last end of the range to parse (excluded) - @param[in] cb a parser callback function of type @ref parser_callback_t + @param[in] first begin of the range to parse (included) + @param[in] last end of the range to parse (excluded) + @param[in] cb a parser callback function of type @ref parser_callback_t which is used to control the deserialization by filtering unwanted values (optional) @@ -6047,7 +6073,8 @@ class basic_json @note A UTF-8 byte order mark is silently ignored. - @todo Example and references. + @liveexample{The example below demonstrates the `parse()` function reading + from an iterator range.,parse__iteratortype__parser_callback_t} @since version 2.0.3 */ @@ -6084,6 +6111,45 @@ class basic_json return parser(first, last, cb).parse(); } + /*! + @brief deserialize from a container with contiguous storage + + This function reads from a container with contiguous storage of 1-byte + values. Compatible container types include `std::vector`, `std::string`, + `std::array`, and `std::initializer_list`. User-defined containers can be + used as long as they implement random-access iterators and a contiguous + storage. + + @pre The container storage is contiguous. Violating this precondition + yields undefined behavior. **This precondition is enforced with an + assertion.** + @pre Each element of the container has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with a static assertion.** + + @warning There is no way to enforce all preconditions at compile-time. If + the function is called with a noncompliant container and with + assertions switched off, the behavior is undefined and will most + likely yield segmentation violation. + + @param[in] c container to read from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @return result of the deserialization + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `parse()` function reading + from a contiguous container.,parse__contiguouscontainer__parser_callback_t} + + @since version 2.0.3 + */ template - static basic_json parse(T (&array)[N], - const parser_callback_t cb = nullptr) - { - // delegate the call to the iterator-range parse overload - return parse(std::begin(array), std::end(array), cb); - } - /*! @brief deserialize from stream @@ -6158,7 +6216,7 @@ class basic_json Returns the type name as string to be used in error messages - usually to indicate that a function was called on a wrong JSON type. - @return basically a string representation of a the @ref m_type member + @return basically a string representation of a the @a m_type member @complexity Constant. @@ -7626,7 +7684,7 @@ class basic_json fill_line_buffer(); } - // switch off unwanted functions + // switch off unwanted functions (due to pointer members) lexer() = delete; lexer(const lexer&) = delete; lexer operator=(const lexer&) = delete; @@ -8276,24 +8334,17 @@ class basic_json { public: /// a parser reading from a string literal - parser(const typename string_t::value_type* buff, - const parser_callback_t cb = nullptr) + parser(const char* buff, const parser_callback_t cb = nullptr) : callback(cb), m_lexer(reinterpret_cast(buff), strlen(buff)) {} - /// a parser reading from a string container - parser(const string_t& s, const parser_callback_t cb = nullptr) - : callback(cb), - m_lexer(reinterpret_cast(s.c_str()), s.size()) - {} - /// a parser reading from an input stream parser(std::istream& is, const parser_callback_t cb = nullptr) : callback(cb), m_lexer(is) {} - /// a parser reading from a container with contiguous storage + /// a parser reading from an iterator range with contiguous storage template ::iterator_category, std::random_access_iterator_tag>::value @@ -9857,7 +9908,7 @@ if no parse error occurred. */ inline nlohmann::json operator "" _json(const char* s, std::size_t) { - return nlohmann::json::parse(reinterpret_cast(s)); + return nlohmann::json::parse(s); } /*! diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index 775952b92..5cae3071c 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -477,7 +477,7 @@ TEST_CASE("parser class") case ('r'): case ('t'): { - CHECK_NOTHROW(json::parser(s).parse()); + CHECK_NOTHROW(json::parser(s.c_str()).parse()); break; } @@ -490,8 +490,8 @@ TEST_CASE("parser class") // 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 '\"'"); + CHECK_THROWS_AS(json::parser(s.c_str()).parse(), std::invalid_argument); + CHECK_THROWS_WITH(json::parser(s.c_str()).parse(), "parse error - unexpected '\"'"); break; } } @@ -549,22 +549,22 @@ TEST_CASE("parser class") 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()); + CHECK_NOTHROW(json::parser(s1.c_str()).parse()); + CHECK_NOTHROW(json::parser(s2.c_str()).parse()); + CHECK_NOTHROW(json::parser(s3.c_str()).parse()); + CHECK_NOTHROW(json::parser(s4.c_str()).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_AS(json::parser(s1.c_str()).parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser(s2.c_str()).parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser(s3.c_str()).parse(), std::invalid_argument); + CHECK_THROWS_AS(json::parser(s4.c_str()).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 '\"'"); + CHECK_THROWS_WITH(json::parser(s1.c_str()).parse(), "parse error - unexpected '\"'"); + CHECK_THROWS_WITH(json::parser(s2.c_str()).parse(), "parse error - unexpected '\"'"); + CHECK_THROWS_WITH(json::parser(s3.c_str()).parse(), "parse error - unexpected '\"'"); + CHECK_THROWS_WITH(json::parser(s4.c_str()).parse(), "parse error - unexpected '\"'"); } } } diff --git a/test/src/unit-deserialization.cpp b/test/src/unit-deserialization.cpp index b681e3df3..6e2c78130 100644 --- a/test/src/unit-deserialization.cpp +++ b/test/src/unit-deserialization.cpp @@ -113,6 +113,12 @@ TEST_CASE("deserialization") std::initializer_list v = {'t', 'r', 'u', 'e', '\0'}; CHECK(json::parse(v) == json(true)); } + + SECTION("empty container") + { + std::vector v; + CHECK_THROWS_AS(json::parse(v), std::invalid_argument); + } } SECTION("via iterator range") From 585a39a2357dc54ae10afb6f948755dcf6395e6b Mon Sep 17 00:00:00 2001 From: Niels Date: Sun, 21 Aug 2016 14:39:54 +0200 Subject: [PATCH 54/77] improved branch coverage --- test/src/unit-allocator.cpp | 150 +++++++++++++++++++++++++++++++++++- 1 file changed, 146 insertions(+), 4 deletions(-) diff --git a/test/src/unit-allocator.cpp b/test/src/unit-allocator.cpp index dcf8aa35a..948446b9c 100644 --- a/test/src/unit-allocator.cpp +++ b/test/src/unit-allocator.cpp @@ -28,13 +28,14 @@ SOFTWARE. #include "catch.hpp" +#define private public #include "json.hpp" using nlohmann::json; // special test case to check if memory is leaked if constructor throws template -struct my_allocator : std::allocator +struct bad_allocator : std::allocator { template void construct(T*, Args&& ...) @@ -48,16 +49,157 @@ TEST_CASE("bad_alloc") SECTION("bad_alloc") { // create JSON type using the throwing allocator - using my_json = nlohmann::basic_json; + bad_allocator>; // creating an object should throw - CHECK_THROWS_AS(my_json j(my_json::value_t::object), std::bad_alloc); + CHECK_THROWS_AS(bad_json j(bad_json::value_t::object), std::bad_alloc); + } +} + +bool next_allocation_fails = false; + +template +struct my_allocator : std::allocator +{ + template + void construct(T* p, Args&& ... args) + { + if (next_allocation_fails) + { + throw std::bad_alloc(); + } + else + { + ::new(reinterpret_cast(p)) T(std::forward(args)...); + } + } +}; + +TEST_CASE("controlled bad_alloc") +{ + // create JSON type using the throwing allocator + using my_json = nlohmann::basic_json; + + SECTION("class json_value") + { + SECTION("json_value(value_t)") + { + SECTION("object") + { + next_allocation_fails = false; + auto t = my_json::value_t::object; + CHECK_NOTHROW(my_json::json_value j(t)); + next_allocation_fails = true; + CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc); + next_allocation_fails = false; + } + SECTION("array") + { + next_allocation_fails = false; + auto t = my_json::value_t::array; + CHECK_NOTHROW(my_json::json_value j(t)); + next_allocation_fails = true; + CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc); + next_allocation_fails = false; + } + SECTION("string") + { + next_allocation_fails = false; + auto t = my_json::value_t::string; + CHECK_NOTHROW(my_json::json_value j(t)); + next_allocation_fails = true; + CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc); + next_allocation_fails = false; + } + } + + SECTION("json_value(const string_t&)") + { + next_allocation_fails = false; + my_json::string_t v("foo"); + CHECK_NOTHROW(my_json::json_value j(v)); + next_allocation_fails = true; + CHECK_THROWS_AS(my_json::json_value j(v), std::bad_alloc); + next_allocation_fails = false; + } + + /* + SECTION("json_value(const object_t&)") + { + next_allocation_fails = false; + my_json::object_t v {{"foo", "bar"}}; + CHECK_NOTHROW(my_json::json_value j(v)); + next_allocation_fails = true; + CHECK_THROWS_AS(my_json::json_value j(v), std::bad_alloc); + next_allocation_fails = false; + } + */ + /* + SECTION("json_value(const array_t&)") + { + next_allocation_fails = false; + my_json::array_t v = {"foo", "bar", "baz"}; + CHECK_NOTHROW(my_json::json_value j(v)); + next_allocation_fails = true; + CHECK_THROWS_AS(my_json::json_value j(v), std::bad_alloc); + next_allocation_fails = false; + } + */ + } + + SECTION("class basic_json") + { + SECTION("basic_json(const CompatibleObjectType&)") + { + next_allocation_fails = false; + std::map v {{"foo", "bar"}}; + CHECK_NOTHROW(my_json j(v)); + next_allocation_fails = true; + CHECK_THROWS_AS(my_json j(v), std::bad_alloc); + next_allocation_fails = false; + } + + SECTION("basic_json(const CompatibleArrayType&)") + { + next_allocation_fails = false; + std::vector v {"foo", "bar", "baz"}; + CHECK_NOTHROW(my_json j(v)); + next_allocation_fails = true; + CHECK_THROWS_AS(my_json j(v), std::bad_alloc); + next_allocation_fails = false; + } + + SECTION("basic_json(const typename string_t::value_type*)") + { + next_allocation_fails = false; + CHECK_NOTHROW(my_json v("foo")); + next_allocation_fails = true; + CHECK_THROWS_AS(my_json v("foo"), std::bad_alloc); + next_allocation_fails = false; + } + + SECTION("basic_json(const typename string_t::value_type*)") + { + next_allocation_fails = false; + std::string s("foo"); + CHECK_NOTHROW(my_json v(s)); + next_allocation_fails = true; + CHECK_THROWS_AS(my_json v(s), std::bad_alloc); + next_allocation_fails = false; + } } } From aa7f5ad8b123177067fb0b943866425d22e20013 Mon Sep 17 00:00:00 2001 From: Niels Date: Sun, 21 Aug 2016 21:48:15 +0200 Subject: [PATCH 55/77] minor changes --- test/src/unit-allocator.cpp | 93 ++++++++++++++++++++++++------------- 1 file changed, 61 insertions(+), 32 deletions(-) diff --git a/test/src/unit-allocator.cpp b/test/src/unit-allocator.cpp index 948446b9c..9ad162c1e 100644 --- a/test/src/unit-allocator.cpp +++ b/test/src/unit-allocator.cpp @@ -63,7 +63,9 @@ TEST_CASE("bad_alloc") } } -bool next_allocation_fails = false; +bool next_construct_fails = false; +bool next_destroy_fails = false; +bool next_deallocate_fails = false; template struct my_allocator : std::allocator @@ -71,8 +73,9 @@ struct my_allocator : std::allocator template void construct(T* p, Args&& ... args) { - if (next_allocation_fails) + if (next_construct_fails) { + next_construct_fails = false; throw std::bad_alloc(); } else @@ -80,6 +83,32 @@ struct my_allocator : std::allocator ::new(reinterpret_cast(p)) T(std::forward(args)...); } } + + void deallocate(T* p, std::size_t n) + { + if (next_deallocate_fails) + { + next_deallocate_fails = false; + throw std::bad_alloc(); + } + else + { + std::allocator::deallocate(p, n); + } + } + + void destroy(T* p) + { + if (next_destroy_fails) + { + next_destroy_fails = false; + throw std::bad_alloc(); + } + else + { + p->~T(); + } + } }; TEST_CASE("controlled bad_alloc") @@ -100,63 +129,63 @@ TEST_CASE("controlled bad_alloc") { SECTION("object") { - next_allocation_fails = false; + next_construct_fails = false; auto t = my_json::value_t::object; CHECK_NOTHROW(my_json::json_value j(t)); - next_allocation_fails = true; + next_construct_fails = true; CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc); - next_allocation_fails = false; + next_construct_fails = false; } SECTION("array") { - next_allocation_fails = false; + next_construct_fails = false; auto t = my_json::value_t::array; CHECK_NOTHROW(my_json::json_value j(t)); - next_allocation_fails = true; + next_construct_fails = true; CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc); - next_allocation_fails = false; + next_construct_fails = false; } SECTION("string") { - next_allocation_fails = false; + next_construct_fails = false; auto t = my_json::value_t::string; CHECK_NOTHROW(my_json::json_value j(t)); - next_allocation_fails = true; + next_construct_fails = true; CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc); - next_allocation_fails = false; + next_construct_fails = false; } } SECTION("json_value(const string_t&)") { - next_allocation_fails = false; + next_construct_fails = false; my_json::string_t v("foo"); CHECK_NOTHROW(my_json::json_value j(v)); - next_allocation_fails = true; + next_construct_fails = true; CHECK_THROWS_AS(my_json::json_value j(v), std::bad_alloc); - next_allocation_fails = false; + next_construct_fails = false; } /* SECTION("json_value(const object_t&)") { - next_allocation_fails = false; + next_construct_fails = false; my_json::object_t v {{"foo", "bar"}}; CHECK_NOTHROW(my_json::json_value j(v)); - next_allocation_fails = true; + next_construct_fails = true; CHECK_THROWS_AS(my_json::json_value j(v), std::bad_alloc); - next_allocation_fails = false; + next_construct_fails = false; } */ /* SECTION("json_value(const array_t&)") { - next_allocation_fails = false; + next_construct_fails = false; my_json::array_t v = {"foo", "bar", "baz"}; CHECK_NOTHROW(my_json::json_value j(v)); - next_allocation_fails = true; + next_construct_fails = true; CHECK_THROWS_AS(my_json::json_value j(v), std::bad_alloc); - next_allocation_fails = false; + next_construct_fails = false; } */ } @@ -165,41 +194,41 @@ TEST_CASE("controlled bad_alloc") { SECTION("basic_json(const CompatibleObjectType&)") { - next_allocation_fails = false; + next_construct_fails = false; std::map v {{"foo", "bar"}}; CHECK_NOTHROW(my_json j(v)); - next_allocation_fails = true; + next_construct_fails = true; CHECK_THROWS_AS(my_json j(v), std::bad_alloc); - next_allocation_fails = false; + next_construct_fails = false; } SECTION("basic_json(const CompatibleArrayType&)") { - next_allocation_fails = false; + next_construct_fails = false; std::vector v {"foo", "bar", "baz"}; CHECK_NOTHROW(my_json j(v)); - next_allocation_fails = true; + next_construct_fails = true; CHECK_THROWS_AS(my_json j(v), std::bad_alloc); - next_allocation_fails = false; + next_construct_fails = false; } SECTION("basic_json(const typename string_t::value_type*)") { - next_allocation_fails = false; + next_construct_fails = false; CHECK_NOTHROW(my_json v("foo")); - next_allocation_fails = true; + next_construct_fails = true; CHECK_THROWS_AS(my_json v("foo"), std::bad_alloc); - next_allocation_fails = false; + next_construct_fails = false; } SECTION("basic_json(const typename string_t::value_type*)") { - next_allocation_fails = false; + next_construct_fails = false; std::string s("foo"); CHECK_NOTHROW(my_json v(s)); - next_allocation_fails = true; + next_construct_fails = true; CHECK_THROWS_AS(my_json v(s), std::bad_alloc); - next_allocation_fails = false; + next_construct_fails = false; } } } From 94331a355d5fc2032810af7160504addcf86f783 Mon Sep 17 00:00:00 2001 From: Niels Date: Sun, 21 Aug 2016 21:50:13 +0200 Subject: [PATCH 56/77] removed LCOV_EXCL_LINE --- src/json.hpp | 22 +++++++++++----------- src/json.hpp.re2c | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index a8289a495..b5e24206b 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -7734,7 +7734,7 @@ class basic_json }; if ((m_limit - m_cursor) < 5) { - yyfill(); // LCOV_EXCL_LINE; + yyfill(); } yych = *m_cursor; if (yybm[0 + yych] & 32) @@ -7868,7 +7868,7 @@ basic_json_parser_6: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + yyfill(); } yych = *m_cursor; if (yybm[0 + yych] & 32) @@ -7938,7 +7938,7 @@ basic_json_parser_15: m_marker = ++m_cursor; if ((m_limit - m_cursor) < 3) { - yyfill(); // LCOV_EXCL_LINE; + yyfill(); } yych = *m_cursor; if (yybm[0 + yych] & 64) @@ -8031,7 +8031,7 @@ basic_json_parser_31: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + yyfill(); } yych = *m_cursor; basic_json_parser_32: @@ -8068,7 +8068,7 @@ basic_json_parser_36: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + yyfill(); } yych = *m_cursor; if (yych <= 'e') @@ -8212,7 +8212,7 @@ basic_json_parser_43: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + yyfill(); } yych = *m_cursor; if (yych <= '@') @@ -8248,7 +8248,7 @@ basic_json_parser_44: m_marker = ++m_cursor; if ((m_limit - m_cursor) < 3) { - yyfill(); // LCOV_EXCL_LINE; + yyfill(); } yych = *m_cursor; if (yych <= 'D') @@ -8289,7 +8289,7 @@ basic_json_parser_47: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + yyfill(); } yych = *m_cursor; if (yych <= '/') @@ -8331,7 +8331,7 @@ basic_json_parser_54: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + yyfill(); } yych = *m_cursor; if (yych <= '@') @@ -8385,7 +8385,7 @@ basic_json_parser_60: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + yyfill(); } yych = *m_cursor; if (yych <= '@') @@ -8426,7 +8426,7 @@ basic_json_parser_63: ++m_cursor; if (m_limit <= m_cursor) { - yyfill(); // LCOV_EXCL_LINE; + yyfill(); } yych = *m_cursor; if (yych <= '@') diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index ffa20f39a..fdc006387 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -7698,7 +7698,7 @@ class basic_json re2c:define:YYCURSOR = m_cursor; re2c:define:YYLIMIT = m_limit; re2c:define:YYMARKER = m_marker; - re2c:define:YYFILL = "yyfill(); // LCOV_EXCL_LINE"; + re2c:define:YYFILL = "yyfill()"; re2c:yyfill:parameter = 0; re2c:indent:string = " "; re2c:indent:top = 1; From 1e896eb91ef940cda23a4bb49a302ed227c442d7 Mon Sep 17 00:00:00 2001 From: Niels Date: Sun, 21 Aug 2016 22:38:56 +0200 Subject: [PATCH 57/77] improved code coverage --- test/src/unit-class_parser.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index fe0055034..259daf849 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -445,6 +445,10 @@ TEST_CASE("parser class") 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_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(), @@ -457,6 +461,14 @@ TEST_CASE("parser class") "parse error - unexpected '\"'"); CHECK_THROWS_WITH(json::parser("\"\\u012\"").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) From 79fd4dfbd861f0397bf47316a13876f5a3fd07b3 Mon Sep 17 00:00:00 2001 From: Niels Date: Mon, 22 Aug 2016 20:40:25 +0200 Subject: [PATCH 58/77] do not build for special cases --- .travis.yml | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index bd806cb89..053bd4360 100644 --- a/.travis.yml +++ b/.travis.yml @@ -237,24 +237,28 @@ install: ################ script: - # show OS/compiler version - - uname -a - - $CXX --version + - if [[ "${SPECIAL}" != "" ]]; then - # compile - - make + # show OS/compiler version + uname -a + $CXX --version - # execute unit tests - - test/json_unit "*" + # compile + make - # check if homebrew works (only checks develop branch) - - if [ `which brew` ]; then - brew update ; - brew tap nlohmann/json ; - brew install nlohmann_json --HEAD ; - brew test nlohmann_json ; + # execute unit tests + test/json_unit "*" + + # check if homebrew works (only checks develop branch) + if [ `which brew` ]; then + brew update ; + brew tap nlohmann/json ; + brew install nlohmann_json --HEAD ; + brew test nlohmann_json ; fi + fi + #language: cpp # #dist: trusty From 71af209ea90875fd4dc2a10fa6a8b11f800a911a Mon Sep 17 00:00:00 2001 From: Niels Date: Mon, 22 Aug 2016 20:44:36 +0200 Subject: [PATCH 59/77] fix for YAML file --- .travis.yml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 053bd4360..606e13893 100644 --- a/.travis.yml +++ b/.travis.yml @@ -236,19 +236,18 @@ install: # build script # ################ -script: - - if [[ "${SPECIAL}" != "" ]]; then - +script: | + if [[ "${SPECIAL}" != "" ]]; then # show OS/compiler version uname -a $CXX --version - + # compile make - + # execute unit tests test/json_unit "*" - + # check if homebrew works (only checks develop branch) if [ `which brew` ]; then brew update ; @@ -256,7 +255,6 @@ script: brew install nlohmann_json --HEAD ; brew test nlohmann_json ; fi - fi #language: cpp From 29a4ef6c5cc06f25cf1cb3cf836fc9b4b170a50b Mon Sep 17 00:00:00 2001 From: Niels Date: Mon, 22 Aug 2016 20:50:12 +0200 Subject: [PATCH 60/77] no execution for special cases --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 606e13893..9ddb21072 100644 --- a/.travis.yml +++ b/.travis.yml @@ -237,7 +237,7 @@ install: ################ script: | - if [[ "${SPECIAL}" != "" ]]; then + if [[ "${SPECIAL}" == "" ]]; then # show OS/compiler version uname -a $CXX --version From b688119aa480d81153cee037e27ca2933cda15d1 Mon Sep 17 00:00:00 2001 From: Niels Date: Mon, 22 Aug 2016 20:54:32 +0200 Subject: [PATCH 61/77] reverted .travis file --- .travis.yml | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9ddb21072..bd806cb89 100644 --- a/.travis.yml +++ b/.travis.yml @@ -236,26 +236,24 @@ install: # build script # ################ -script: | - if [[ "${SPECIAL}" == "" ]]; then - # show OS/compiler version - uname -a - $CXX --version - - # compile - make - - # execute unit tests - test/json_unit "*" - - # check if homebrew works (only checks develop branch) - if [ `which brew` ]; then - brew update ; - brew tap nlohmann/json ; - brew install nlohmann_json --HEAD ; - brew test nlohmann_json ; +script: + # show OS/compiler version + - uname -a + - $CXX --version + + # compile + - make + + # execute unit tests + - test/json_unit "*" + + # check if homebrew works (only checks develop branch) + - if [ `which brew` ]; then + brew update ; + brew tap nlohmann/json ; + brew install nlohmann_json --HEAD ; + brew test nlohmann_json ; fi - fi #language: cpp # From a79d634ccbbbccfacf5ada18d5fe2a1ce6ab338a Mon Sep 17 00:00:00 2001 From: Niels Date: Mon, 22 Aug 2016 21:40:07 +0200 Subject: [PATCH 62/77] integrated proposals for #290 --- .../parse__array__parser_callback_t.cpp | 28 -- .../parse__array__parser_callback_t.link | 1 - .../parse__array__parser_callback_t.output | 20 -- src/json.hpp | 291 ++++++------------ src/json.hpp.re2c | 291 ++++++------------ 5 files changed, 200 insertions(+), 431 deletions(-) delete mode 100644 doc/examples/parse__array__parser_callback_t.cpp delete mode 100644 doc/examples/parse__array__parser_callback_t.link delete mode 100644 doc/examples/parse__array__parser_callback_t.output diff --git a/doc/examples/parse__array__parser_callback_t.cpp b/doc/examples/parse__array__parser_callback_t.cpp deleted file mode 100644 index 8e086d200..000000000 --- a/doc/examples/parse__array__parser_callback_t.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include - -using json = nlohmann::json; - -int main() -{ - // a JSON text - char text[] = 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] - } - } - )"; - - // parse and serialize JSON - json j_complete = json::parse(text); - std::cout << std::setw(4) << j_complete << "\n\n"; -} diff --git a/doc/examples/parse__array__parser_callback_t.link b/doc/examples/parse__array__parser_callback_t.link deleted file mode 100644 index a1d3cd349..000000000 --- a/doc/examples/parse__array__parser_callback_t.link +++ /dev/null @@ -1 +0,0 @@ -online \ No newline at end of file diff --git a/doc/examples/parse__array__parser_callback_t.output b/doc/examples/parse__array__parser_callback_t.output deleted file mode 100644 index 62bb85863..000000000 --- a/doc/examples/parse__array__parser_callback_t.output +++ /dev/null @@ -1,20 +0,0 @@ -{ - "Image": { - "Animated": false, - "Height": 600, - "IDs": [ - 116, - 943, - 234, - 38793 - ], - "Thumbnail": { - "Height": 125, - "Url": "http://www.example.com/image/481989943", - "Width": 100 - }, - "Title": "View from 15th Floor", - "Width": 800 - } -} - diff --git a/src/json.hpp b/src/json.hpp index 5a35b16d7..ac5c78b31 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -1165,11 +1165,9 @@ class basic_json @since version 1.0.0 */ - template ::value and - std::is_constructible::value, int>::type - = 0> + template::value and + std::is_constructible::value, int>::type = 0> basic_json(const CompatibleObjectType& val) : m_type(value_t::object) { @@ -1230,16 +1228,14 @@ class basic_json @since version 1.0.0 */ - template ::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value and - std::is_constructible::value, int>::type - = 0> + template::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + std::is_constructible::value, int>::type = 0> basic_json(const CompatibleArrayType& val) : m_type(value_t::array) { @@ -1325,10 +1321,8 @@ class basic_json @since version 1.0.0 */ - template ::value, int>::type - = 0> + template::value, int>::type = 0> basic_json(const CompatibleStringType& val) : basic_json(string_t(val)) { @@ -1378,12 +1372,9 @@ class basic_json @since version 1.0.0 */ - template::value) - and std::is_same::value - , int>::type - = 0> + template::value) and + std::is_same::value, int>::type = 0> basic_json(const number_integer_t val) noexcept : m_type(value_t::number_integer), m_value(val) { @@ -1447,13 +1438,11 @@ class basic_json @since version 1.0.0 */ - template::value and std::numeric_limits::is_integer and std::numeric_limits::is_signed, - CompatibleNumberIntegerType>::type - = 0> + CompatibleNumberIntegerType>::type = 0> basic_json(const CompatibleNumberIntegerType val) noexcept : m_type(value_t::number_integer), m_value(static_cast(val)) @@ -1478,12 +1467,9 @@ class basic_json @since version 2.0.0 */ - template::value) - and std::is_same::value - , int>::type - = 0> + template::value) and + std::is_same::value, int>::type = 0> basic_json(const number_unsigned_t val) noexcept : m_type(value_t::number_unsigned), m_value(val) { @@ -1510,13 +1496,11 @@ class basic_json @since version 2.0.0 */ - template ::value and - std::numeric_limits::is_integer and - not std::numeric_limits::is_signed, - CompatibleNumberUnsignedType>::type - = 0> + template::value and + std::numeric_limits::is_integer and + not std::numeric_limits::is_signed, + CompatibleNumberUnsignedType>::type = 0> basic_json(const CompatibleNumberUnsignedType val) noexcept : m_type(value_t::number_unsigned), m_value(static_cast(val)) @@ -1592,11 +1576,9 @@ class basic_json @since version 1.0.0 */ - template::value and - std::is_floating_point::value>::type - > + std::is_floating_point::value>::type> basic_json(const CompatibleNumberFloatType val) noexcept : basic_json(number_float_t(val)) { @@ -1863,12 +1845,9 @@ class basic_json @since version 1.0.0 */ - template ::value or - std::is_same::value - , int>::type - = 0> + template::value or + std::is_same::value, int>::type = 0> basic_json(InputIT first, InputIT last) { assert(first.m_object != nullptr); @@ -2616,11 +2595,9 @@ class basic_json ////////////////// /// get an object (explicit) - template ::value and - std::is_convertible::value - , int>::type = 0> + template::value and + std::is_convertible::value, int>::type = 0> T get_impl(T*) const { if (is_object()) @@ -2647,14 +2624,12 @@ class basic_json } /// get an array (explicit) - template ::value and - not std::is_same::value and - not std::is_arithmetic::value and - not std::is_convertible::value and - not has_mapped_type::value - , int>::type = 0> + template::value and + not std::is_same::value and + not std::is_arithmetic::value and + not std::is_convertible::value and + not has_mapped_type::value, int>::type = 0> T get_impl(T*) const { if (is_array()) @@ -2674,11 +2649,9 @@ class basic_json } /// get an array (explicit) - template ::value and - not std::is_same::value - , int>::type = 0> + template::value and + not std::is_same::value, int>::type = 0> std::vector get_impl(std::vector*) const { if (is_array()) @@ -2699,11 +2672,9 @@ class basic_json } /// get an array (explicit) - template ::value and - not has_mapped_type::value - , int>::type = 0> + template::value and + not has_mapped_type::value, int>::type = 0> T get_impl(T*) const { if (is_array()) @@ -2730,10 +2701,8 @@ class basic_json } /// get a string (explicit) - template ::value - , int>::type = 0> + template::value, int>::type = 0> T get_impl(T*) const { if (is_string()) @@ -2747,10 +2716,8 @@ class basic_json } /// get a number (explicit) - template::value - , int>::type = 0> + template::value, int>::type = 0> T get_impl(T*) const { switch (m_type) @@ -2939,10 +2906,8 @@ class basic_json @since version 1.0.0 */ - template::value - , int>::type = 0> + template::value, int>::type = 0> ValueType get() const { return get_impl(static_cast(nullptr)); @@ -2975,10 +2940,8 @@ class basic_json @since version 1.0.0 */ - template::value - , int>::type = 0> + template::value, int>::type = 0> PointerType get() noexcept { // delegate the call to get_ptr @@ -2989,10 +2952,8 @@ class basic_json @brief get a pointer value (explicit) @copydoc get() */ - template::value - , int>::type = 0> + template::value, int>::type = 0> constexpr const PointerType get() const noexcept { // delegate the call to get_ptr @@ -3025,10 +2986,8 @@ class basic_json @since version 1.0.0 */ - template::value - , int>::type = 0> + template::value, int>::type = 0> PointerType get_ptr() noexcept { // get the type of the PointerType (remove pointer and const) @@ -3054,11 +3013,9 @@ class basic_json @brief get a pointer value (implicit) @copydoc get_ptr() */ - template::value - and std::is_const::type>::value - , int>::type = 0> + template::value and + std::is_const::type>::value, int>::type = 0> constexpr const PointerType get_ptr() const noexcept { // get the type of the PointerType (remove pointer and const) @@ -3106,10 +3063,8 @@ class basic_json @since version 1.1.0 */ - template::value - , int>::type = 0> + template::value, int>::type = 0> ReferenceType get_ref() { // delegate call to get_ref_impl @@ -3120,11 +3075,9 @@ class basic_json @brief get a reference value (implicit) @copydoc get_ref() */ - template::value - and std::is_const::type>::value - , int>::type = 0> + template::value and + std::is_const::type>::value, int>::type = 0> ReferenceType get_ref() const { // delegate call to get_ref_impl @@ -3159,10 +3112,9 @@ class basic_json @since version 1.0.0 */ - template < typename ValueType, typename - std::enable_if < - not std::is_pointer::value - and not std::is_same::value + template < typename ValueType, typename std::enable_if < + not std::is_pointer::value and + not std::is_same::value #ifndef _MSC_VER // Fix for issue #167 operator<< abiguity under VS2015 and not std::is_same>::value #endif @@ -3752,10 +3704,8 @@ class basic_json @since version 1.0.0 */ - template ::value - , int>::type = 0> + template::value, int>::type = 0> ValueType value(const typename object_t::key_type& key, ValueType default_value) const { // at only works for objects @@ -3828,10 +3778,8 @@ class basic_json @since version 2.0.2 */ - template ::value - , int>::type = 0> + template::value, int>::type = 0> ValueType value(const json_pointer& ptr, ValueType default_value) const { // at only works for objects @@ -3992,12 +3940,10 @@ class basic_json @since version 1.0.0 */ - template ::value or - std::is_same::value - , int>::type - = 0> + template::value or + std::is_same::value, int>::type + = 0> IteratorType erase(IteratorType pos) { // make sure iterator fits the current value @@ -4101,12 +4047,10 @@ class basic_json @since version 1.0.0 */ - template ::value or - std::is_same::value - , int>::type - = 0> + template::value or + std::is_same::value, int>::type + = 0> IteratorType erase(IteratorType first, IteratorType last) { // make sure iterator fits the current value @@ -5935,6 +5879,7 @@ class basic_json /*! @brief deserialize from string literal + @tparam CharT character/literal type with size of 1 byte @param[in] s string literal to read a serialized JSON value from @param[in] cb a parser callback function of type @ref parser_callback_t which is used to control the deserialization by filtering unwanted values @@ -5958,45 +5903,12 @@ class basic_json @since version 1.0.0 (originally for @ref string_t) */ - static basic_json parse(const char* s, + template::value and sizeof(CharT) == 1, int>::type = 0> + static basic_json parse(const CharT* s, const parser_callback_t cb = nullptr) { - return parser(s, cb).parse(); - } - - /*! - @brief deserialize from an array - - This function reads from an array of 1-byte values. - - @pre Each element of the container has a size of 1 byte. Violating this - precondition yields undefined behavior. **This precondition is enforced - with a static assertion.** - - @param[in] array array to read from - @param[in] cb a parser callback function of type @ref parser_callback_t - which is used to control the deserialization by filtering unwanted values - (optional) - - @return result of the deserialization - - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. The complexity can be higher if the parser callback function - @a cb has a super-linear complexity. - - @note A UTF-8 byte order mark is silently ignored. - - @liveexample{The example below demonstrates the `parse()` function reading - from an array.,parse__array__parser_callback_t} - - @since version 2.0.3 - */ - template - static basic_json parse(T (&array)[N], - const parser_callback_t cb = nullptr) - { - // delegate the call to the iterator-range parse overload - return parse(std::begin(array), std::end(array), cb); + return parser(reinterpret_cast(s), cb).parse(); } /*! @@ -6059,6 +5971,7 @@ class basic_json assertions switched off, the behavior is undefined and will most likely yield segmentation violation. + @tparam IteratorType iterator of container with contiguous storage @param[in] first begin of the range to parse (included) @param[in] last end of the range to parse (excluded) @param[in] cb a parser callback function of type @ref parser_callback_t @@ -6078,13 +5991,10 @@ class basic_json @since version 2.0.3 */ - template ::iterator_category>::value - , int>::type - = 0> + template::iterator_category>::value, int>::type = 0> static basic_json parse(IteratorType first, IteratorType last, const parser_callback_t cb = nullptr) { @@ -6132,6 +6042,7 @@ class basic_json assertions switched off, the behavior is undefined and will most likely yield segmentation violation. + @tparam ContiguousContainer container type with contiguous storage @param[in] c container to read from @param[in] cb a parser callback function of type @ref parser_callback_t which is used to control the deserialization by filtering unwanted values @@ -6150,11 +6061,10 @@ class basic_json @since version 2.0.3 */ - template()))>::iterator_category>::value + typename std::iterator_traits()))>::iterator_category>::value , int>::type = 0> static basic_json parse(const ContiguousContainer& c, const parser_callback_t cb = nullptr) @@ -9048,11 +8958,10 @@ basic_json_parser_63: {} /// a parser reading from an iterator range with contiguous storage - template ::iterator_category, std::random_access_iterator_tag>::value - , int>::type - = 0> + template::iterator_category, std::random_access_iterator_tag>::value + , int>::type + = 0> parser(IteratorType first, IteratorType last, const parser_callback_t cb = nullptr) : callback(cb), m_lexer(reinterpret_cast(&(*first)), @@ -10569,7 +10478,7 @@ namespace std @since version 1.0.0 */ -template <> +template<> inline void swap(nlohmann::json& j1, nlohmann::json& j2) noexcept( is_nothrow_move_constructible::value and @@ -10580,7 +10489,7 @@ inline void swap(nlohmann::json& j1, } /// hash value for JSON objects -template <> +template<> struct hash { /*! diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index a5b8a14f5..dffb11aff 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -1165,11 +1165,9 @@ class basic_json @since version 1.0.0 */ - template ::value and - std::is_constructible::value, int>::type - = 0> + template::value and + std::is_constructible::value, int>::type = 0> basic_json(const CompatibleObjectType& val) : m_type(value_t::object) { @@ -1230,16 +1228,14 @@ class basic_json @since version 1.0.0 */ - template ::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value and - std::is_constructible::value, int>::type - = 0> + template::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + std::is_constructible::value, int>::type = 0> basic_json(const CompatibleArrayType& val) : m_type(value_t::array) { @@ -1325,10 +1321,8 @@ class basic_json @since version 1.0.0 */ - template ::value, int>::type - = 0> + template::value, int>::type = 0> basic_json(const CompatibleStringType& val) : basic_json(string_t(val)) { @@ -1378,12 +1372,9 @@ class basic_json @since version 1.0.0 */ - template::value) - and std::is_same::value - , int>::type - = 0> + template::value) and + std::is_same::value, int>::type = 0> basic_json(const number_integer_t val) noexcept : m_type(value_t::number_integer), m_value(val) { @@ -1447,13 +1438,11 @@ class basic_json @since version 1.0.0 */ - template::value and std::numeric_limits::is_integer and std::numeric_limits::is_signed, - CompatibleNumberIntegerType>::type - = 0> + CompatibleNumberIntegerType>::type = 0> basic_json(const CompatibleNumberIntegerType val) noexcept : m_type(value_t::number_integer), m_value(static_cast(val)) @@ -1478,12 +1467,9 @@ class basic_json @since version 2.0.0 */ - template::value) - and std::is_same::value - , int>::type - = 0> + template::value) and + std::is_same::value, int>::type = 0> basic_json(const number_unsigned_t val) noexcept : m_type(value_t::number_unsigned), m_value(val) { @@ -1510,13 +1496,11 @@ class basic_json @since version 2.0.0 */ - template ::value and - std::numeric_limits::is_integer and - not std::numeric_limits::is_signed, - CompatibleNumberUnsignedType>::type - = 0> + template::value and + std::numeric_limits::is_integer and + not std::numeric_limits::is_signed, + CompatibleNumberUnsignedType>::type = 0> basic_json(const CompatibleNumberUnsignedType val) noexcept : m_type(value_t::number_unsigned), m_value(static_cast(val)) @@ -1592,11 +1576,9 @@ class basic_json @since version 1.0.0 */ - template::value and - std::is_floating_point::value>::type - > + std::is_floating_point::value>::type> basic_json(const CompatibleNumberFloatType val) noexcept : basic_json(number_float_t(val)) { @@ -1863,12 +1845,9 @@ class basic_json @since version 1.0.0 */ - template ::value or - std::is_same::value - , int>::type - = 0> + template::value or + std::is_same::value, int>::type = 0> basic_json(InputIT first, InputIT last) { assert(first.m_object != nullptr); @@ -2616,11 +2595,9 @@ class basic_json ////////////////// /// get an object (explicit) - template ::value and - std::is_convertible::value - , int>::type = 0> + template::value and + std::is_convertible::value, int>::type = 0> T get_impl(T*) const { if (is_object()) @@ -2647,14 +2624,12 @@ class basic_json } /// get an array (explicit) - template ::value and - not std::is_same::value and - not std::is_arithmetic::value and - not std::is_convertible::value and - not has_mapped_type::value - , int>::type = 0> + template::value and + not std::is_same::value and + not std::is_arithmetic::value and + not std::is_convertible::value and + not has_mapped_type::value, int>::type = 0> T get_impl(T*) const { if (is_array()) @@ -2674,11 +2649,9 @@ class basic_json } /// get an array (explicit) - template ::value and - not std::is_same::value - , int>::type = 0> + template::value and + not std::is_same::value, int>::type = 0> std::vector get_impl(std::vector*) const { if (is_array()) @@ -2699,11 +2672,9 @@ class basic_json } /// get an array (explicit) - template ::value and - not has_mapped_type::value - , int>::type = 0> + template::value and + not has_mapped_type::value, int>::type = 0> T get_impl(T*) const { if (is_array()) @@ -2730,10 +2701,8 @@ class basic_json } /// get a string (explicit) - template ::value - , int>::type = 0> + template::value, int>::type = 0> T get_impl(T*) const { if (is_string()) @@ -2747,10 +2716,8 @@ class basic_json } /// get a number (explicit) - template::value - , int>::type = 0> + template::value, int>::type = 0> T get_impl(T*) const { switch (m_type) @@ -2939,10 +2906,8 @@ class basic_json @since version 1.0.0 */ - template::value - , int>::type = 0> + template::value, int>::type = 0> ValueType get() const { return get_impl(static_cast(nullptr)); @@ -2975,10 +2940,8 @@ class basic_json @since version 1.0.0 */ - template::value - , int>::type = 0> + template::value, int>::type = 0> PointerType get() noexcept { // delegate the call to get_ptr @@ -2989,10 +2952,8 @@ class basic_json @brief get a pointer value (explicit) @copydoc get() */ - template::value - , int>::type = 0> + template::value, int>::type = 0> constexpr const PointerType get() const noexcept { // delegate the call to get_ptr @@ -3025,10 +2986,8 @@ class basic_json @since version 1.0.0 */ - template::value - , int>::type = 0> + template::value, int>::type = 0> PointerType get_ptr() noexcept { // get the type of the PointerType (remove pointer and const) @@ -3054,11 +3013,9 @@ class basic_json @brief get a pointer value (implicit) @copydoc get_ptr() */ - template::value - and std::is_const::type>::value - , int>::type = 0> + template::value and + std::is_const::type>::value, int>::type = 0> constexpr const PointerType get_ptr() const noexcept { // get the type of the PointerType (remove pointer and const) @@ -3106,10 +3063,8 @@ class basic_json @since version 1.1.0 */ - template::value - , int>::type = 0> + template::value, int>::type = 0> ReferenceType get_ref() { // delegate call to get_ref_impl @@ -3120,11 +3075,9 @@ class basic_json @brief get a reference value (implicit) @copydoc get_ref() */ - template::value - and std::is_const::type>::value - , int>::type = 0> + template::value and + std::is_const::type>::value, int>::type = 0> ReferenceType get_ref() const { // delegate call to get_ref_impl @@ -3159,10 +3112,9 @@ class basic_json @since version 1.0.0 */ - template < typename ValueType, typename - std::enable_if < - not std::is_pointer::value - and not std::is_same::value + template < typename ValueType, typename std::enable_if < + not std::is_pointer::value and + not std::is_same::value #ifndef _MSC_VER // Fix for issue #167 operator<< abiguity under VS2015 and not std::is_same>::value #endif @@ -3752,10 +3704,8 @@ class basic_json @since version 1.0.0 */ - template ::value - , int>::type = 0> + template::value, int>::type = 0> ValueType value(const typename object_t::key_type& key, ValueType default_value) const { // at only works for objects @@ -3828,10 +3778,8 @@ class basic_json @since version 2.0.2 */ - template ::value - , int>::type = 0> + template::value, int>::type = 0> ValueType value(const json_pointer& ptr, ValueType default_value) const { // at only works for objects @@ -3992,12 +3940,10 @@ class basic_json @since version 1.0.0 */ - template ::value or - std::is_same::value - , int>::type - = 0> + template::value or + std::is_same::value, int>::type + = 0> IteratorType erase(IteratorType pos) { // make sure iterator fits the current value @@ -4101,12 +4047,10 @@ class basic_json @since version 1.0.0 */ - template ::value or - std::is_same::value - , int>::type - = 0> + template::value or + std::is_same::value, int>::type + = 0> IteratorType erase(IteratorType first, IteratorType last) { // make sure iterator fits the current value @@ -5935,6 +5879,7 @@ class basic_json /*! @brief deserialize from string literal + @tparam CharT character/literal type with size of 1 byte @param[in] s string literal to read a serialized JSON value from @param[in] cb a parser callback function of type @ref parser_callback_t which is used to control the deserialization by filtering unwanted values @@ -5958,45 +5903,12 @@ class basic_json @since version 1.0.0 (originally for @ref string_t) */ - static basic_json parse(const char* s, + template::value and sizeof(CharT) == 1, int>::type = 0> + static basic_json parse(const CharT* s, const parser_callback_t cb = nullptr) { - return parser(s, cb).parse(); - } - - /*! - @brief deserialize from an array - - This function reads from an array of 1-byte values. - - @pre Each element of the container has a size of 1 byte. Violating this - precondition yields undefined behavior. **This precondition is enforced - with a static assertion.** - - @param[in] array array to read from - @param[in] cb a parser callback function of type @ref parser_callback_t - which is used to control the deserialization by filtering unwanted values - (optional) - - @return result of the deserialization - - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. The complexity can be higher if the parser callback function - @a cb has a super-linear complexity. - - @note A UTF-8 byte order mark is silently ignored. - - @liveexample{The example below demonstrates the `parse()` function reading - from an array.,parse__array__parser_callback_t} - - @since version 2.0.3 - */ - template - static basic_json parse(T (&array)[N], - const parser_callback_t cb = nullptr) - { - // delegate the call to the iterator-range parse overload - return parse(std::begin(array), std::end(array), cb); + return parser(reinterpret_cast(s), cb).parse(); } /*! @@ -6059,6 +5971,7 @@ class basic_json assertions switched off, the behavior is undefined and will most likely yield segmentation violation. + @tparam IteratorType iterator of container with contiguous storage @param[in] first begin of the range to parse (included) @param[in] last end of the range to parse (excluded) @param[in] cb a parser callback function of type @ref parser_callback_t @@ -6078,13 +5991,10 @@ class basic_json @since version 2.0.3 */ - template ::iterator_category>::value - , int>::type - = 0> + template::iterator_category>::value, int>::type = 0> static basic_json parse(IteratorType first, IteratorType last, const parser_callback_t cb = nullptr) { @@ -6132,6 +6042,7 @@ class basic_json assertions switched off, the behavior is undefined and will most likely yield segmentation violation. + @tparam ContiguousContainer container type with contiguous storage @param[in] c container to read from @param[in] cb a parser callback function of type @ref parser_callback_t which is used to control the deserialization by filtering unwanted values @@ -6150,11 +6061,10 @@ class basic_json @since version 2.0.3 */ - template()))>::iterator_category>::value + typename std::iterator_traits()))>::iterator_category>::value , int>::type = 0> static basic_json parse(const ContiguousContainer& c, const parser_callback_t cb = nullptr) @@ -8345,11 +8255,10 @@ class basic_json {} /// a parser reading from an iterator range with contiguous storage - template ::iterator_category, std::random_access_iterator_tag>::value - , int>::type - = 0> + template::iterator_category, std::random_access_iterator_tag>::value + , int>::type + = 0> parser(IteratorType first, IteratorType last, const parser_callback_t cb = nullptr) : callback(cb), m_lexer(reinterpret_cast(&(*first)), @@ -9866,7 +9775,7 @@ namespace std @since version 1.0.0 */ -template <> +template<> inline void swap(nlohmann::json& j1, nlohmann::json& j2) noexcept( is_nothrow_move_constructible::value and @@ -9877,7 +9786,7 @@ inline void swap(nlohmann::json& j1, } /// hash value for JSON objects -template <> +template<> struct hash { /*! From 1d66ab9f7a5898267ebecefe5c2ba4ee0ffec613 Mon Sep 17 00:00:00 2001 From: Niels Date: Tue, 23 Aug 2016 22:38:05 +0200 Subject: [PATCH 63/77] fixed lexer issue which required null byte at the end of contiguous storage containers #290 --- src/json.hpp | 49 +++++++++++++++++++++---------- src/json.hpp.re2c | 49 +++++++++++++++++++++---------- test/src/unit-class_parser.cpp | 10 +++---- test/src/unit-deserialization.cpp | 18 ++++++------ 4 files changed, 82 insertions(+), 44 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index ac5c78b31..0e25f746e 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -8538,6 +8538,13 @@ basic_json_parser_63: incremented without leaving the limits of the line buffer. Note re2c decides when to call this function. + If the lexer reads from contiguous storage, there is no trailing null + byte. Therefore, this function must make sure to add these padding + null bytes. + + If the lexer reads from an input stream, this function reads the next + line of the input. + @pre p p p p p p u u u u u x . . . . . . ^ ^ ^ ^ @@ -8553,26 +8560,38 @@ basic_json_parser_63: */ void fill_line_buffer() { - // no stream is used or end of file is reached - if (m_stream == nullptr or not * m_stream) - { - return; - } - // number of processed characters (p) const auto offset_start = m_start - m_content; // offset for m_marker wrt. to m_start - const auto offset_marker = m_marker - m_start; + const auto offset_marker = (m_marker == nullptr) ? 0 : m_marker - m_start; // number of unprocessed characters (u) const auto offset_cursor = m_cursor - m_start; - // delete processed characters from line buffer - m_line_buffer.erase(0, static_cast(offset_start)); - // read next line from input stream - std::string line; - std::getline(*m_stream, line); - // add line with newline symbol to the line buffer - m_line_buffer += "\n" + line; + // no stream is used or end of file is reached + if (m_stream == nullptr or not * m_stream) + { + // copy unprocessed characters to line buffer + m_line_buffer.clear(); + for (m_cursor = m_start; m_cursor != m_limit; ++m_cursor) + { + m_line_buffer.append(1, static_cast(*m_cursor)); + } + + // append 5 characters (size of longest keyword "false") to + // make sure that there is sufficient space between m_cursor + // and m_limit + m_line_buffer.append(5, '\0'); + } + else + { + // delete processed characters from line buffer + m_line_buffer.erase(0, static_cast(offset_start)); + // read next line from input stream + std::string line; + std::getline(*m_stream, line); + // add line with newline symbol to the line buffer + m_line_buffer += "\n" + line; + } // set pointers m_content = reinterpret_cast(m_line_buffer.c_str()); @@ -8580,7 +8599,7 @@ basic_json_parser_63: m_start = m_content; m_marker = m_start + offset_marker; m_cursor = m_start + offset_cursor; - m_limit = m_start + m_line_buffer.size() - 1; + m_limit = m_start + m_line_buffer.size(); } /// return string representation of last read token diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index dffb11aff..089517bb1 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -7835,6 +7835,13 @@ class basic_json incremented without leaving the limits of the line buffer. Note re2c decides when to call this function. + If the lexer reads from contiguous storage, there is no trailing null + byte. Therefore, this function must make sure to add these padding + null bytes. + + If the lexer reads from an input stream, this function reads the next + line of the input. + @pre p p p p p p u u u u u x . . . . . . ^ ^ ^ ^ @@ -7850,26 +7857,38 @@ class basic_json */ void fill_line_buffer() { - // no stream is used or end of file is reached - if (m_stream == nullptr or not * m_stream) - { - return; - } - // number of processed characters (p) const auto offset_start = m_start - m_content; // offset for m_marker wrt. to m_start - const auto offset_marker = m_marker - m_start; + const auto offset_marker = (m_marker == nullptr) ? 0 : m_marker - m_start; // number of unprocessed characters (u) const auto offset_cursor = m_cursor - m_start; - // delete processed characters from line buffer - m_line_buffer.erase(0, static_cast(offset_start)); - // read next line from input stream - std::string line; - std::getline(*m_stream, line); - // add line with newline symbol to the line buffer - m_line_buffer += "\n" + line; + // no stream is used or end of file is reached + if (m_stream == nullptr or not * m_stream) + { + // copy unprocessed characters to line buffer + m_line_buffer.clear(); + for (m_cursor = m_start; m_cursor != m_limit; ++m_cursor) + { + m_line_buffer.append(1, static_cast(*m_cursor)); + } + + // append 5 characters (size of longest keyword "false") to + // make sure that there is sufficient space between m_cursor + // and m_limit + m_line_buffer.append(5, '\0'); + } + else + { + // delete processed characters from line buffer + m_line_buffer.erase(0, static_cast(offset_start)); + // read next line from input stream + std::string line; + std::getline(*m_stream, line); + // add line with newline symbol to the line buffer + m_line_buffer += "\n" + line; + } // set pointers m_content = reinterpret_cast(m_line_buffer.c_str()); @@ -7877,7 +7896,7 @@ class basic_json m_start = m_content; m_marker = m_start + offset_marker; m_cursor = m_start + offset_cursor; - m_limit = m_start + m_line_buffer.size() - 1; + m_limit = m_start + m_line_buffer.size(); } /// return string representation of last read token diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index 32a6ac8f3..6fcf947de 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -761,19 +761,19 @@ TEST_CASE("parser class") { SECTION("from std::vector") { - std::vector v = {'t', 'r', 'u', 'e', '\0'}; + std::vector v = {'t', 'r', 'u', 'e'}; CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true)); } SECTION("from std::array") { - std::array v { {'t', 'r', 'u', 'e', '\0'} }; + std::array v { {'t', 'r', 'u', 'e'} }; CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true)); } SECTION("from array") { - uint8_t v[] = {'t', 'r', 'u', 'e', '\0'}; + uint8_t v[] = {'t', 'r', 'u', 'e'}; CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true)); } @@ -790,13 +790,13 @@ TEST_CASE("parser class") SECTION("from std::initializer_list") { - std::initializer_list v = {'t', 'r', 'u', 'e', '\0'}; + std::initializer_list v = {'t', 'r', 'u', 'e'}; CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true)); } SECTION("from std::valarray") { - std::valarray v = {'t', 'r', 'u', 'e', '\0'}; + std::valarray v = {'t', 'r', 'u', 'e'}; CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true)); } } diff --git a/test/src/unit-deserialization.cpp b/test/src/unit-deserialization.cpp index 6e2c78130..dcd7c2721 100644 --- a/test/src/unit-deserialization.cpp +++ b/test/src/unit-deserialization.cpp @@ -86,19 +86,19 @@ TEST_CASE("deserialization") { SECTION("from std::vector") { - std::vector v = {'t', 'r', 'u', 'e', '\0'}; + std::vector v = {'t', 'r', 'u', 'e'}; CHECK(json::parse(v) == json(true)); } SECTION("from std::array") { - std::array v { {'t', 'r', 'u', 'e', '\0'} }; + std::array v { {'t', 'r', 'u', 'e'} }; CHECK(json::parse(v) == json(true)); } SECTION("from array") { - uint8_t v[] = {'t', 'r', 'u', 'e', '\0'}; + uint8_t v[] = {'t', 'r', 'u', 'e'}; CHECK(json::parse(v) == json(true)); } @@ -110,7 +110,7 @@ TEST_CASE("deserialization") SECTION("from std::initializer_list") { - std::initializer_list v = {'t', 'r', 'u', 'e', '\0'}; + std::initializer_list v = {'t', 'r', 'u', 'e'}; CHECK(json::parse(v) == json(true)); } @@ -125,19 +125,19 @@ TEST_CASE("deserialization") { SECTION("from std::vector") { - std::vector v = {'t', 'r', 'u', 'e', '\0'}; + std::vector v = {'t', 'r', 'u', 'e'}; CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); } SECTION("from std::array") { - std::array v { {'t', 'r', 'u', 'e', '\0'} }; + std::array v { {'t', 'r', 'u', 'e'} }; CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); } SECTION("from array") { - uint8_t v[] = {'t', 'r', 'u', 'e', '\0'}; + uint8_t v[] = {'t', 'r', 'u', 'e'}; CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); } @@ -149,13 +149,13 @@ TEST_CASE("deserialization") SECTION("from std::initializer_list") { - std::initializer_list v = {'t', 'r', 'u', 'e', '\0'}; + std::initializer_list v = {'t', 'r', 'u', 'e'}; CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); } SECTION("from std::valarray") { - std::valarray v = {'t', 'r', 'u', 'e', '\0'}; + std::valarray v = {'t', 'r', 'u', 'e'}; CHECK(json::parse(std::begin(v), std::end(v)) == json(true)); } From 8b833c452a8ef13487968681518beacfe2204ce7 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 24 Aug 2016 07:12:56 +0200 Subject: [PATCH 64/77] experiment: changed order of parse functions --- src/json.hpp | 70 ++++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 0e25f746e..3c1898a8a 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -5876,41 +5876,6 @@ class basic_json /// @name deserialization /// @{ - /*! - @brief deserialize from string literal - - @tparam CharT character/literal type with size of 1 byte - @param[in] s string literal to read a serialized JSON value from - @param[in] cb a parser callback function of type @ref parser_callback_t - which is used to control the deserialization by filtering unwanted values - (optional) - - @return result of the deserialization - - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. The complexity can be higher if the parser callback function - @a cb has a super-linear complexity. - - @note A UTF-8 byte order mark is silently ignored. - @note String containers like `std::string` or @ref string_t can be parsed - with @ref parse(const ContiguousContainer&, const parser_callback_t) - - @liveexample{The example below demonstrates the `parse()` function with - and without callback function.,parse__string__parser_callback_t} - - @sa @ref parse(std::istream&, const parser_callback_t) for a version that - reads from an input stream - - @since version 1.0.0 (originally for @ref string_t) - */ - template::value and sizeof(CharT) == 1, int>::type = 0> - static basic_json parse(const CharT* s, - const parser_callback_t cb = nullptr) - { - return parser(reinterpret_cast(s), cb).parse(); - } - /*! @brief deserialize from stream @@ -6073,6 +6038,41 @@ class basic_json return parse(std::begin(c), std::end(c), cb); } + /*! + @brief deserialize from string literal + + @tparam CharT character/literal type with size of 1 byte + @param[in] s string literal to read a serialized JSON value from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @return result of the deserialization + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + @note String containers like `std::string` or @ref string_t can be parsed + with @ref parse(const ContiguousContainer&, const parser_callback_t) + + @liveexample{The example below demonstrates the `parse()` function with + and without callback function.,parse__string__parser_callback_t} + + @sa @ref parse(std::istream&, const parser_callback_t) for a version that + reads from an input stream + + @since version 1.0.0 (originally for @ref string_t) + */ + template::value and sizeof(CharT) == 1, int>::type = 0> + static basic_json parse(const CharT* s, + const parser_callback_t cb = nullptr) + { + return parser(reinterpret_cast(s), cb).parse(); + } + /*! @brief deserialize from stream From 04c6c886ebfbff0a05dbd4db2048ab5a317dc15f Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 24 Aug 2016 21:02:33 +0200 Subject: [PATCH 65/77] another experiment --- .../parse__array__parser_callback_t.cpp | 28 +++++ .../parse__array__parser_callback_t.link | 1 + .../parse__array__parser_callback_t.output | 20 ++++ src/json.hpp | 108 ++++++++++++------ src/json.hpp.re2c | 44 ++++++- 5 files changed, 163 insertions(+), 38 deletions(-) create mode 100644 doc/examples/parse__array__parser_callback_t.cpp create mode 100644 doc/examples/parse__array__parser_callback_t.link create mode 100644 doc/examples/parse__array__parser_callback_t.output diff --git a/doc/examples/parse__array__parser_callback_t.cpp b/doc/examples/parse__array__parser_callback_t.cpp new file mode 100644 index 000000000..8e086d200 --- /dev/null +++ b/doc/examples/parse__array__parser_callback_t.cpp @@ -0,0 +1,28 @@ +#include + +using json = nlohmann::json; + +int main() +{ + // a JSON text + char text[] = 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] + } + } + )"; + + // parse and serialize JSON + json j_complete = json::parse(text); + std::cout << std::setw(4) << j_complete << "\n\n"; +} diff --git a/doc/examples/parse__array__parser_callback_t.link b/doc/examples/parse__array__parser_callback_t.link new file mode 100644 index 000000000..3389916f5 --- /dev/null +++ b/doc/examples/parse__array__parser_callback_t.link @@ -0,0 +1 @@ +online \ No newline at end of file diff --git a/doc/examples/parse__array__parser_callback_t.output b/doc/examples/parse__array__parser_callback_t.output new file mode 100644 index 000000000..62bb85863 --- /dev/null +++ b/doc/examples/parse__array__parser_callback_t.output @@ -0,0 +1,20 @@ +{ + "Image": { + "Animated": false, + "Height": 600, + "IDs": [ + 116, + 943, + 234, + 38793 + ], + "Thumbnail": { + "Height": 125, + "Url": "http://www.example.com/image/481989943", + "Width": 100 + }, + "Title": "View from 15th Floor", + "Width": 800 + } +} + diff --git a/src/json.hpp b/src/json.hpp index 3c1898a8a..3fdcbaa1b 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -5876,6 +5876,78 @@ class basic_json /// @name deserialization /// @{ + /*! + @brief deserialize from an array + + This function reads from an array of 1-byte values. + + @pre Each element of the container has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with a static assertion.** + + @param[in] array array to read from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @return result of the deserialization + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `parse()` function reading + from an array.,parse__array__parser_callback_t} + + @since version 2.0.3 + */ + template + static basic_json parse(T (&array)[N], + const parser_callback_t cb = nullptr) + { + // delegate the call to the iterator-range parse overload + return parse(std::begin(array), std::end(array), cb); + } + + /*! + @brief deserialize from string literal + + @tparam CharT character/literal type with size of 1 byte + @param[in] s string literal to read a serialized JSON value from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @return result of the deserialization + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + @note String containers like `std::string` or @ref string_t can be parsed + with @ref parse(const ContiguousContainer&, const parser_callback_t) + + @liveexample{The example below demonstrates the `parse()` function with + and without callback function.,parse__string__parser_callback_t} + + @sa @ref parse(std::istream&, const parser_callback_t) for a version that + reads from an input stream + + @since version 1.0.0 (originally for @ref string_t) + */ + template::value and + std::is_integral::type>::value and + sizeof(std::remove_pointer) == 1, int>::type = 0> + static basic_json parse(const CharPT s, + const parser_callback_t cb = nullptr) + { + return parser(reinterpret_cast(s), cb).parse(); + } + /*! @brief deserialize from stream @@ -6027,6 +6099,7 @@ class basic_json @since version 2.0.3 */ template::value and std::is_base_of< std::random_access_iterator_tag, typename std::iterator_traits()))>::iterator_category>::value @@ -6038,41 +6111,6 @@ class basic_json return parse(std::begin(c), std::end(c), cb); } - /*! - @brief deserialize from string literal - - @tparam CharT character/literal type with size of 1 byte - @param[in] s string literal to read a serialized JSON value from - @param[in] cb a parser callback function of type @ref parser_callback_t - which is used to control the deserialization by filtering unwanted values - (optional) - - @return result of the deserialization - - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. The complexity can be higher if the parser callback function - @a cb has a super-linear complexity. - - @note A UTF-8 byte order mark is silently ignored. - @note String containers like `std::string` or @ref string_t can be parsed - with @ref parse(const ContiguousContainer&, const parser_callback_t) - - @liveexample{The example below demonstrates the `parse()` function with - and without callback function.,parse__string__parser_callback_t} - - @sa @ref parse(std::istream&, const parser_callback_t) for a version that - reads from an input stream - - @since version 1.0.0 (originally for @ref string_t) - */ - template::value and sizeof(CharT) == 1, int>::type = 0> - static basic_json parse(const CharT* s, - const parser_callback_t cb = nullptr) - { - return parser(reinterpret_cast(s), cb).parse(); - } - /*! @brief deserialize from stream diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 089517bb1..5107e0564 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -5876,6 +5876,41 @@ class basic_json /// @name deserialization /// @{ + /*! + @brief deserialize from an array + + This function reads from an array of 1-byte values. + + @pre Each element of the container has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with a static assertion.** + + @param[in] array array to read from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @return result of the deserialization + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `parse()` function reading + from an array.,parse__array__parser_callback_t} + + @since version 2.0.3 + */ + template + static basic_json parse(T (&array)[N], + const parser_callback_t cb = nullptr) + { + // delegate the call to the iterator-range parse overload + return parse(std::begin(array), std::end(array), cb); + } + /*! @brief deserialize from string literal @@ -5903,9 +5938,11 @@ class basic_json @since version 1.0.0 (originally for @ref string_t) */ - template::value and sizeof(CharT) == 1, int>::type = 0> - static basic_json parse(const CharT* s, + template::value and + std::is_integral::type>::value and + sizeof(std::remove_pointer) == 1, int>::type = 0> + static basic_json parse(const CharPT s, const parser_callback_t cb = nullptr) { return parser(reinterpret_cast(s), cb).parse(); @@ -6062,6 +6099,7 @@ class basic_json @since version 2.0.3 */ template::value and std::is_base_of< std::random_access_iterator_tag, typename std::iterator_traits()))>::iterator_category>::value From bd3bd37e961c90dd32c9a0166eec88edd2785d1a Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 24 Aug 2016 21:29:28 +0200 Subject: [PATCH 66/77] removed coverity badge (build is broken, see #299) --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 3015e860a..f03b31253 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,6 @@ [![Build Status](https://travis-ci.org/nlohmann/json.svg?branch=master)](https://travis-ci.org/nlohmann/json) [![Build Status](https://ci.appveyor.com/api/projects/status/1acb366xfyg3qybk?svg=true)](https://ci.appveyor.com/project/nlohmann/json) [![Coverage Status](https://img.shields.io/coveralls/nlohmann/json.svg)](https://coveralls.io/r/nlohmann/json) -[![Coverity Scan Build Status](https://scan.coverity.com/projects/5550/badge.svg)](https://scan.coverity.com/projects/nlohmann-json) [![Try online](https://img.shields.io/badge/try-online-blue.svg)](http://melpon.org/wandbox/permlink/p5o4znPnGHJpDVqN) [![Documentation](https://img.shields.io/badge/docs-doxygen-blue.svg)](http://nlohmann.github.io/json) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/nlohmann/json/master/LICENSE.MIT) From 87dea32e8162d2b17968a5d9fe8dcb87fa8efc54 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 24 Aug 2016 21:31:11 +0200 Subject: [PATCH 67/77] using AppVeyor badge for develop branch --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f03b31253..bf1276388 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ [![JSON for Modern C++](https://raw.githubusercontent.com/nlohmann/json/master/doc/json.gif)](https://github.com/nlohmann/json/releases) [![Build Status](https://travis-ci.org/nlohmann/json.svg?branch=master)](https://travis-ci.org/nlohmann/json) -[![Build Status](https://ci.appveyor.com/api/projects/status/1acb366xfyg3qybk?svg=true)](https://ci.appveyor.com/project/nlohmann/json) +[![Build Status](https://ci.appveyor.com/api/projects/status/1acb366xfyg3qybk/branch/develop?svg=true)](https://ci.appveyor.com/project/nlohmann/json) [![Coverage Status](https://img.shields.io/coveralls/nlohmann/json.svg)](https://coveralls.io/r/nlohmann/json) [![Try online](https://img.shields.io/badge/try-online-blue.svg)](http://melpon.org/wandbox/permlink/p5o4znPnGHJpDVqN) [![Documentation](https://img.shields.io/badge/docs-doxygen-blue.svg)](http://nlohmann.github.io/json) From e0ff1a837c889e73eada0105758fd903d06c3cf7 Mon Sep 17 00:00:00 2001 From: Thomas Braun Date: Thu, 25 Aug 2016 00:24:33 +0200 Subject: [PATCH 68/77] unit-constructor1.cpp: Fix floating point truncation warning --- test/src/unit-constructor1.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/src/unit-constructor1.cpp b/test/src/unit-constructor1.cpp index 87728648a..8a26738bd 100644 --- a/test/src/unit-constructor1.cpp +++ b/test/src/unit-constructor1.cpp @@ -709,7 +709,7 @@ TEST_CASE("constructors") SECTION("float") { - float n = 42.23; + float n = 42.23f; json j(n); CHECK(j.type() == json::value_t::number_float); CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float)); From 29c5f32d4287a49f59fcd65b7ffacd7f154b03aa Mon Sep 17 00:00:00 2001 From: Niels Date: Mon, 29 Aug 2016 22:36:33 +0200 Subject: [PATCH 69/77] fixed performance degradation (#272) --- Makefile | 3 +++ src/json.hpp | 3 ++- src/json.hpp.re2c | 3 ++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index b53d8d3dc..4c96bad7f 100644 --- a/Makefile +++ b/Makefile @@ -66,6 +66,9 @@ fuzz: test/src/fuzz.cpp src/json.hpp cppcheck: cppcheck --enable=warning --inconclusive --force --std=c++11 src/json.hpp --error-exitcode=1 +clang_sanitize: clean + CXX=clang++ CXXFLAGS="-g -O2 -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer" $(MAKE) + ########################################################################## # maintainer targets ########################################################################## diff --git a/src/json.hpp b/src/json.hpp index b5e24206b..d6f8925fc 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -2232,7 +2232,8 @@ class basic_json { std::stringstream ss; // fix locale problems - ss.imbue(std::locale(std::locale(), new DecimalSeparator)); + const static std::locale loc(std::locale(), new DecimalSeparator); + ss.imbue(loc); // 6, 15 or 16 digits of precision allows round-trip IEEE 754 // string->float->string, string->double->string or string->long diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index fdc006387..75fc27e5a 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -2232,7 +2232,8 @@ class basic_json { std::stringstream ss; // fix locale problems - ss.imbue(std::locale(std::locale(), new DecimalSeparator)); + const static std::locale loc(std::locale(), new DecimalSeparator); + ss.imbue(loc); // 6, 15 or 16 digits of precision allows round-trip IEEE 754 // string->float->string, string->double->string or string->long From 6e6e1c9b1fec37b3f400ce347195666701d3910b Mon Sep 17 00:00:00 2001 From: Niels Date: Tue, 30 Aug 2016 18:26:07 +0200 Subject: [PATCH 70/77] implemented idea from #290 --- src/json.hpp | 2 +- src/json.hpp.re2c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 0aa45af52..8ac262e69 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -5942,7 +5942,7 @@ class basic_json template::value and std::is_integral::type>::value and - sizeof(std::remove_pointer) == 1, int>::type = 0> + sizeof(typename std::remove_pointer::type) == 1, int>::type = 0> static basic_json parse(const CharPT s, const parser_callback_t cb = nullptr) { diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index dbc5dec3b..3b6764580 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -5942,7 +5942,7 @@ class basic_json template::value and std::is_integral::type>::value and - sizeof(std::remove_pointer) == 1, int>::type = 0> + sizeof(typename std::remove_pointer::type) == 1, int>::type = 0> static basic_json parse(const CharPT s, const parser_callback_t cb = nullptr) { From a485aa8d272f3656c1fb5840ce100f3288f7e2da Mon Sep 17 00:00:00 2001 From: Niels Date: Tue, 30 Aug 2016 23:44:15 +0200 Subject: [PATCH 71/77] cleanup and improvement of branch coverage --- src/json.hpp | 218 ++++++++++--------------- src/json.hpp.re2c | 218 ++++++++++--------------- test/src/unit-class_const_iterator.cpp | 16 ++ test/src/unit-deserialization.cpp | 104 ++++++++---- 4 files changed, 265 insertions(+), 291 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index d6f8925fc..2f0f02d51 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -1164,11 +1164,10 @@ class basic_json @since version 1.0.0 */ - template ::value and - std::is_constructible::value, int>::type - = 0> + template::value and + std::is_constructible::value, int>::type + = 0> basic_json(const CompatibleObjectType& val) : m_type(value_t::object) { @@ -1229,16 +1228,15 @@ class basic_json @since version 1.0.0 */ - template ::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value and - std::is_constructible::value, int>::type - = 0> + template::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + std::is_constructible::value, int>::type + = 0> basic_json(const CompatibleArrayType& val) : m_type(value_t::array) { @@ -1324,10 +1322,9 @@ class basic_json @since version 1.0.0 */ - template ::value, int>::type - = 0> + template::value, int>::type + = 0> basic_json(const CompatibleStringType& val) : basic_json(string_t(val)) { @@ -1377,8 +1374,7 @@ class basic_json @since version 1.0.0 */ - template::value) and std::is_same::value , int>::type @@ -1477,8 +1473,7 @@ class basic_json @since version 2.0.0 */ - template::value) and std::is_same::value , int>::type @@ -1509,13 +1504,11 @@ class basic_json @since version 2.0.0 */ - template ::value and - std::numeric_limits::is_integer and - not std::numeric_limits::is_signed, - CompatibleNumberUnsignedType>::type - = 0> + template::value and + std::numeric_limits::is_integer and + not std::numeric_limits::is_signed, CompatibleNumberUnsignedType>::type + = 0> basic_json(const CompatibleNumberUnsignedType val) noexcept : m_type(value_t::number_unsigned), m_value(static_cast(val)) @@ -1591,11 +1584,9 @@ class basic_json @since version 1.0.0 */ - template::value and - std::is_floating_point::value>::type - > + std::is_floating_point::value>::type> basic_json(const CompatibleNumberFloatType val) noexcept : basic_json(number_float_t(val)) { @@ -1672,8 +1663,8 @@ class basic_json @since version 1.0.0 */ basic_json(std::initializer_list init, - bool type_deduction = true, - value_t manual_type = value_t::array) + const bool type_deduction = true, + const value_t manual_type = value_t::array) { // check if each element is an array with two elements whose first // element is a string @@ -1862,12 +1853,9 @@ class basic_json @since version 1.0.0 */ - template ::value or - std::is_same::value - , int>::type - = 0> + template::value or + std::is_same::value>::type> basic_json(InputIT first, InputIT last) { assert(first.m_object != nullptr); @@ -2616,11 +2604,10 @@ class basic_json ////////////////// /// get an object (explicit) - template ::value and - std::is_convertible::value - , int>::type = 0> + template::value and + std::is_convertible::value + , int>::type = 0> T get_impl(T*) const { if (is_object()) @@ -2647,14 +2634,13 @@ class basic_json } /// get an array (explicit) - template ::value and - not std::is_same::value and - not std::is_arithmetic::value and - not std::is_convertible::value and - not has_mapped_type::value - , int>::type = 0> + template::value and + not std::is_same::value and + not std::is_arithmetic::value and + not std::is_convertible::value and + not has_mapped_type::value + , int>::type = 0> T get_impl(T*) const { if (is_array()) @@ -2674,11 +2660,10 @@ class basic_json } /// get an array (explicit) - template ::value and - not std::is_same::value - , int>::type = 0> + template::value and + not std::is_same::value + , int>::type = 0> std::vector get_impl(std::vector*) const { if (is_array()) @@ -2699,11 +2684,10 @@ class basic_json } /// get an array (explicit) - template ::value and - not has_mapped_type::value - , int>::type = 0> + template::value and + not has_mapped_type::value + , int>::type = 0> T get_impl(T*) const { if (is_array()) @@ -2730,10 +2714,9 @@ class basic_json } /// get a string (explicit) - template ::value - , int>::type = 0> + template::value + , int>::type = 0> T get_impl(T*) const { if (is_string()) @@ -2747,10 +2730,8 @@ class basic_json } /// get a number (explicit) - template::value - , int>::type = 0> + template::value, int>::type = 0> T get_impl(T*) const { switch (m_type) @@ -2939,10 +2920,8 @@ class basic_json @since version 1.0.0 */ - template::value - , int>::type = 0> + template::value>::type> ValueType get() const { return get_impl(static_cast(nullptr)); @@ -2975,10 +2954,8 @@ class basic_json @since version 1.0.0 */ - template::value - , int>::type = 0> + template::value>::type> PointerType get() noexcept { // delegate the call to get_ptr @@ -2989,10 +2966,8 @@ class basic_json @brief get a pointer value (explicit) @copydoc get() */ - template::value - , int>::type = 0> + template::value>::type> constexpr const PointerType get() const noexcept { // delegate the call to get_ptr @@ -3025,10 +3000,8 @@ class basic_json @since version 1.0.0 */ - template::value - , int>::type = 0> + template::value>::type> PointerType get_ptr() noexcept { // get the type of the PointerType (remove pointer and const) @@ -3054,11 +3027,9 @@ class basic_json @brief get a pointer value (implicit) @copydoc get_ptr() */ - template::value - and std::is_const::type>::value - , int>::type = 0> + and std::is_const::type>::value>::type> constexpr const PointerType get_ptr() const noexcept { // get the type of the PointerType (remove pointer and const) @@ -3106,10 +3077,8 @@ class basic_json @since version 1.1.0 */ - template::value - , int>::type = 0> + template::value>::type> ReferenceType get_ref() { // delegate call to get_ref_impl @@ -3120,11 +3089,9 @@ class basic_json @brief get a reference value (implicit) @copydoc get_ref() */ - template::value - and std::is_const::type>::value - , int>::type = 0> + and std::is_const::type>::value>::type> ReferenceType get_ref() const { // delegate call to get_ref_impl @@ -3159,14 +3126,13 @@ class basic_json @since version 1.0.0 */ - template < typename ValueType, typename - std::enable_if < + template < typename ValueType, typename = typename std::enable_if < not std::is_pointer::value and not std::is_same::value #ifndef _MSC_VER // Fix for issue #167 operator<< abiguity under VS2015 and not std::is_same>::value #endif - , int >::type = 0 > + >::type > operator ValueType() const { // delegate the call to get<>() const @@ -3752,10 +3718,8 @@ class basic_json @since version 1.0.0 */ - template ::value - , int>::type = 0> + template::value>::type> ValueType value(const typename object_t::key_type& key, ValueType default_value) const { // at only works for objects @@ -3828,10 +3792,8 @@ class basic_json @since version 2.0.2 */ - template ::value - , int>::type = 0> + template::value>::type> ValueType value(const json_pointer& ptr, ValueType default_value) const { // at only works for objects @@ -3992,12 +3954,9 @@ class basic_json @since version 1.0.0 */ - template ::value or - std::is_same::value - , int>::type - = 0> + template::value or + std::is_same::value>::type> InteratorType erase(InteratorType pos) { // make sure iterator fits the current value @@ -4101,12 +4060,9 @@ class basic_json @since version 1.0.0 */ - template ::value or - std::is_same::value - , int>::type - = 0> + template::value or + std::is_same::value>::type> InteratorType erase(InteratorType first, InteratorType last) { // make sure iterator fits the current value @@ -6267,7 +6223,7 @@ class basic_json const unsigned int current_indent = 0) const { // variable to hold indentation for recursive calls - unsigned int new_indent = current_indent; + auto new_indent = current_indent; switch (m_type) { @@ -7561,7 +7517,7 @@ class basic_json const std::size_t codepoint2 = 0) { // calculate the code point from the given code points - std::size_t codepoint = codepoint1; + auto codepoint = codepoint1; // check if codepoint1 is a high surrogate if (codepoint1 >= 0xD800 and codepoint1 <= 0xDBFF) @@ -9524,7 +9480,7 @@ basic_json_parser_63: auto reference_token = reference_string.substr(start, slash - start); // check reference tokens are properly escaped - for (size_t pos = reference_token.find_first_of("~"); + for (auto pos = reference_token.find_first_of("~"); pos != std::string::npos; pos = reference_token.find_first_of("~", pos + 1)) { @@ -9569,7 +9525,7 @@ basic_json_parser_63: assert(not f.empty()); for ( - size_t pos = s.find(f); // find first occurrence of f + auto pos = s.find(f); // find first occurrence of f pos != std::string::npos; // make sure f was found s.replace(pos, f.size(), t), // replace with t pos = s.find(f, pos + t.size()) // find next occurrence of f @@ -9969,7 +9925,7 @@ basic_json_parser_63: else { // make sure the top element of the pointer exists - json_pointer top_pointer = ptr.top(); + auto top_pointer = ptr.top(); if (top_pointer != ptr) { basic_json& x = result.at(top_pointer); @@ -10377,7 +10333,7 @@ namespace std @since version 1.0.0 */ -template <> +template<> inline void swap(nlohmann::json& j1, nlohmann::json& j2) noexcept( is_nothrow_move_constructible::value and @@ -10388,7 +10344,7 @@ inline void swap(nlohmann::json& j1, } /// hash value for JSON objects -template <> +template<> struct hash { /*! diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 75fc27e5a..28532a8d6 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -1164,11 +1164,10 @@ class basic_json @since version 1.0.0 */ - template ::value and - std::is_constructible::value, int>::type - = 0> + template::value and + std::is_constructible::value, int>::type + = 0> basic_json(const CompatibleObjectType& val) : m_type(value_t::object) { @@ -1229,16 +1228,15 @@ class basic_json @since version 1.0.0 */ - template ::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value and - std::is_constructible::value, int>::type - = 0> + template::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + std::is_constructible::value, int>::type + = 0> basic_json(const CompatibleArrayType& val) : m_type(value_t::array) { @@ -1324,10 +1322,9 @@ class basic_json @since version 1.0.0 */ - template ::value, int>::type - = 0> + template::value, int>::type + = 0> basic_json(const CompatibleStringType& val) : basic_json(string_t(val)) { @@ -1377,8 +1374,7 @@ class basic_json @since version 1.0.0 */ - template::value) and std::is_same::value , int>::type @@ -1477,8 +1473,7 @@ class basic_json @since version 2.0.0 */ - template::value) and std::is_same::value , int>::type @@ -1509,13 +1504,11 @@ class basic_json @since version 2.0.0 */ - template ::value and - std::numeric_limits::is_integer and - not std::numeric_limits::is_signed, - CompatibleNumberUnsignedType>::type - = 0> + template::value and + std::numeric_limits::is_integer and + not std::numeric_limits::is_signed, CompatibleNumberUnsignedType>::type + = 0> basic_json(const CompatibleNumberUnsignedType val) noexcept : m_type(value_t::number_unsigned), m_value(static_cast(val)) @@ -1591,11 +1584,9 @@ class basic_json @since version 1.0.0 */ - template::value and - std::is_floating_point::value>::type - > + std::is_floating_point::value>::type> basic_json(const CompatibleNumberFloatType val) noexcept : basic_json(number_float_t(val)) { @@ -1672,8 +1663,8 @@ class basic_json @since version 1.0.0 */ basic_json(std::initializer_list init, - bool type_deduction = true, - value_t manual_type = value_t::array) + const bool type_deduction = true, + const value_t manual_type = value_t::array) { // check if each element is an array with two elements whose first // element is a string @@ -1862,12 +1853,9 @@ class basic_json @since version 1.0.0 */ - template ::value or - std::is_same::value - , int>::type - = 0> + template::value or + std::is_same::value>::type> basic_json(InputIT first, InputIT last) { assert(first.m_object != nullptr); @@ -2616,11 +2604,10 @@ class basic_json ////////////////// /// get an object (explicit) - template ::value and - std::is_convertible::value - , int>::type = 0> + template::value and + std::is_convertible::value + , int>::type = 0> T get_impl(T*) const { if (is_object()) @@ -2647,14 +2634,13 @@ class basic_json } /// get an array (explicit) - template ::value and - not std::is_same::value and - not std::is_arithmetic::value and - not std::is_convertible::value and - not has_mapped_type::value - , int>::type = 0> + template::value and + not std::is_same::value and + not std::is_arithmetic::value and + not std::is_convertible::value and + not has_mapped_type::value + , int>::type = 0> T get_impl(T*) const { if (is_array()) @@ -2674,11 +2660,10 @@ class basic_json } /// get an array (explicit) - template ::value and - not std::is_same::value - , int>::type = 0> + template::value and + not std::is_same::value + , int>::type = 0> std::vector get_impl(std::vector*) const { if (is_array()) @@ -2699,11 +2684,10 @@ class basic_json } /// get an array (explicit) - template ::value and - not has_mapped_type::value - , int>::type = 0> + template::value and + not has_mapped_type::value + , int>::type = 0> T get_impl(T*) const { if (is_array()) @@ -2730,10 +2714,9 @@ class basic_json } /// get a string (explicit) - template ::value - , int>::type = 0> + template::value + , int>::type = 0> T get_impl(T*) const { if (is_string()) @@ -2747,10 +2730,8 @@ class basic_json } /// get a number (explicit) - template::value - , int>::type = 0> + template::value, int>::type = 0> T get_impl(T*) const { switch (m_type) @@ -2939,10 +2920,8 @@ class basic_json @since version 1.0.0 */ - template::value - , int>::type = 0> + template::value>::type> ValueType get() const { return get_impl(static_cast(nullptr)); @@ -2975,10 +2954,8 @@ class basic_json @since version 1.0.0 */ - template::value - , int>::type = 0> + template::value>::type> PointerType get() noexcept { // delegate the call to get_ptr @@ -2989,10 +2966,8 @@ class basic_json @brief get a pointer value (explicit) @copydoc get() */ - template::value - , int>::type = 0> + template::value>::type> constexpr const PointerType get() const noexcept { // delegate the call to get_ptr @@ -3025,10 +3000,8 @@ class basic_json @since version 1.0.0 */ - template::value - , int>::type = 0> + template::value>::type> PointerType get_ptr() noexcept { // get the type of the PointerType (remove pointer and const) @@ -3054,11 +3027,9 @@ class basic_json @brief get a pointer value (implicit) @copydoc get_ptr() */ - template::value - and std::is_const::type>::value - , int>::type = 0> + and std::is_const::type>::value>::type> constexpr const PointerType get_ptr() const noexcept { // get the type of the PointerType (remove pointer and const) @@ -3106,10 +3077,8 @@ class basic_json @since version 1.1.0 */ - template::value - , int>::type = 0> + template::value>::type> ReferenceType get_ref() { // delegate call to get_ref_impl @@ -3120,11 +3089,9 @@ class basic_json @brief get a reference value (implicit) @copydoc get_ref() */ - template::value - and std::is_const::type>::value - , int>::type = 0> + and std::is_const::type>::value>::type> ReferenceType get_ref() const { // delegate call to get_ref_impl @@ -3159,14 +3126,13 @@ class basic_json @since version 1.0.0 */ - template < typename ValueType, typename - std::enable_if < + template < typename ValueType, typename = typename std::enable_if < not std::is_pointer::value and not std::is_same::value #ifndef _MSC_VER // Fix for issue #167 operator<< abiguity under VS2015 and not std::is_same>::value #endif - , int >::type = 0 > + >::type > operator ValueType() const { // delegate the call to get<>() const @@ -3752,10 +3718,8 @@ class basic_json @since version 1.0.0 */ - template ::value - , int>::type = 0> + template::value>::type> ValueType value(const typename object_t::key_type& key, ValueType default_value) const { // at only works for objects @@ -3828,10 +3792,8 @@ class basic_json @since version 2.0.2 */ - template ::value - , int>::type = 0> + template::value>::type> ValueType value(const json_pointer& ptr, ValueType default_value) const { // at only works for objects @@ -3992,12 +3954,9 @@ class basic_json @since version 1.0.0 */ - template ::value or - std::is_same::value - , int>::type - = 0> + template::value or + std::is_same::value>::type> InteratorType erase(InteratorType pos) { // make sure iterator fits the current value @@ -4101,12 +4060,9 @@ class basic_json @since version 1.0.0 */ - template ::value or - std::is_same::value - , int>::type - = 0> + template::value or + std::is_same::value>::type> InteratorType erase(InteratorType first, InteratorType last) { // make sure iterator fits the current value @@ -6267,7 +6223,7 @@ class basic_json const unsigned int current_indent = 0) const { // variable to hold indentation for recursive calls - unsigned int new_indent = current_indent; + auto new_indent = current_indent; switch (m_type) { @@ -7561,7 +7517,7 @@ class basic_json const std::size_t codepoint2 = 0) { // calculate the code point from the given code points - std::size_t codepoint = codepoint1; + auto codepoint = codepoint1; // check if codepoint1 is a high surrogate if (codepoint1 >= 0xD800 and codepoint1 <= 0xDBFF) @@ -8821,7 +8777,7 @@ class basic_json auto reference_token = reference_string.substr(start, slash - start); // check reference tokens are properly escaped - for (size_t pos = reference_token.find_first_of("~"); + for (auto pos = reference_token.find_first_of("~"); pos != std::string::npos; pos = reference_token.find_first_of("~", pos + 1)) { @@ -8866,7 +8822,7 @@ class basic_json assert(not f.empty()); for ( - size_t pos = s.find(f); // find first occurrence of f + auto pos = s.find(f); // find first occurrence of f pos != std::string::npos; // make sure f was found s.replace(pos, f.size(), t), // replace with t pos = s.find(f, pos + t.size()) // find next occurrence of f @@ -9266,7 +9222,7 @@ class basic_json else { // make sure the top element of the pointer exists - json_pointer top_pointer = ptr.top(); + auto top_pointer = ptr.top(); if (top_pointer != ptr) { basic_json& x = result.at(top_pointer); @@ -9674,7 +9630,7 @@ namespace std @since version 1.0.0 */ -template <> +template<> inline void swap(nlohmann::json& j1, nlohmann::json& j2) noexcept( is_nothrow_move_constructible::value and @@ -9685,7 +9641,7 @@ inline void swap(nlohmann::json& j1, } /// hash value for JSON objects -template <> +template<> struct hash { /*! diff --git a/test/src/unit-class_const_iterator.cpp b/test/src/unit-class_const_iterator.cpp index 4908b5ce9..b64e7e3c2 100644 --- a/test/src/unit-class_const_iterator.cpp +++ b/test/src/unit-class_const_iterator.cpp @@ -64,6 +64,22 @@ TEST_CASE("const_iterator class") json::const_iterator it2(&j); it2 = it; } + + SECTION("copy constructor from non-const iterator") + { + SECTION("create from uninitialized iterator") + { + const json::iterator it {}; + json::const_iterator cit(it); + } + + SECTION("create from initialized iterator") + { + json j; + const json::iterator it = j.begin(); + json::const_iterator cit(it); + } + } } SECTION("initialization") diff --git a/test/src/unit-deserialization.cpp b/test/src/unit-deserialization.cpp index 781885746..9d4c064fa 100644 --- a/test/src/unit-deserialization.cpp +++ b/test/src/unit-deserialization.cpp @@ -33,41 +33,87 @@ using nlohmann::json; TEST_CASE("deserialization") { - SECTION("stream") + SECTION("successful deserialization") { - 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("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") + { + json::string_t 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}}})); + } } - SECTION("string") + SECTION("successful deserialization") { - 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("stream") + { + std::stringstream ss; + ss << "[\"foo\",1,2,3,false,{\"one\":1}"; + CHECK_THROWS_AS(json::parse(ss), std::invalid_argument); + CHECK_THROWS_WITH(json::parse(ss), "parse error - unexpected end of input"); + } - 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("string") + { + json::string_t s = "[\"foo\",1,2,3,false,{\"one\":1}"; + CHECK_THROWS_AS(json::parse(s), std::invalid_argument); + CHECK_THROWS_WITH(json::parse(s), "parse error - unexpected end of input; expected ']'"); + } - 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("operator<<") + { + std::stringstream ss; + ss << "[\"foo\",1,2,3,false,{\"one\":1}"; + json j; + CHECK_THROWS_AS(j << ss, std::invalid_argument); + CHECK_THROWS_WITH(j << ss, "parse error - unexpected end of input"); + } - SECTION("user-defined string literal") - { - CHECK("[\"foo\",1,2,3,false,{\"one\":1}]"_json == json({"foo", 1, 2, 3, false, {{"one", 1}}})); + SECTION("operator>>") + { + std::stringstream ss; + ss << "[\"foo\",1,2,3,false,{\"one\":1}"; + json j; + CHECK_THROWS_AS(ss >> j, std::invalid_argument); + CHECK_THROWS_WITH(ss >> j, "parse error - unexpected end of input"); + } + + SECTION("user-defined string literal") + { + CHECK_THROWS_AS("[\"foo\",1,2,3,false,{\"one\":1}"_json, std::invalid_argument); + CHECK_THROWS_WITH("[\"foo\",1,2,3,false,{\"one\":1}"_json, + "parse error - unexpected end of input; expected ']'"); + } } } From 941714c99c0ab9150b6ca3e451c8d2ffcafe0be3 Mon Sep 17 00:00:00 2001 From: Niels Date: Tue, 30 Aug 2016 23:49:06 +0200 Subject: [PATCH 72/77] template changes did not work with MSVC --- src/json.hpp.re2c | 218 ++++++++++++++++++++++++++++------------------ 1 file changed, 131 insertions(+), 87 deletions(-) diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 28532a8d6..75fc27e5a 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -1164,10 +1164,11 @@ class basic_json @since version 1.0.0 */ - template::value and - std::is_constructible::value, int>::type - = 0> + template ::value and + std::is_constructible::value, int>::type + = 0> basic_json(const CompatibleObjectType& val) : m_type(value_t::object) { @@ -1228,15 +1229,16 @@ class basic_json @since version 1.0.0 */ - template::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value and - std::is_constructible::value, int>::type - = 0> + template ::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + std::is_constructible::value, int>::type + = 0> basic_json(const CompatibleArrayType& val) : m_type(value_t::array) { @@ -1322,9 +1324,10 @@ class basic_json @since version 1.0.0 */ - template::value, int>::type - = 0> + template ::value, int>::type + = 0> basic_json(const CompatibleStringType& val) : basic_json(string_t(val)) { @@ -1374,7 +1377,8 @@ class basic_json @since version 1.0.0 */ - template::value) and std::is_same::value , int>::type @@ -1473,7 +1477,8 @@ class basic_json @since version 2.0.0 */ - template::value) and std::is_same::value , int>::type @@ -1504,11 +1509,13 @@ class basic_json @since version 2.0.0 */ - template::value and - std::numeric_limits::is_integer and - not std::numeric_limits::is_signed, CompatibleNumberUnsignedType>::type - = 0> + template ::value and + std::numeric_limits::is_integer and + not std::numeric_limits::is_signed, + CompatibleNumberUnsignedType>::type + = 0> basic_json(const CompatibleNumberUnsignedType val) noexcept : m_type(value_t::number_unsigned), m_value(static_cast(val)) @@ -1584,9 +1591,11 @@ class basic_json @since version 1.0.0 */ - template::value and - std::is_floating_point::value>::type> + std::is_floating_point::value>::type + > basic_json(const CompatibleNumberFloatType val) noexcept : basic_json(number_float_t(val)) { @@ -1663,8 +1672,8 @@ class basic_json @since version 1.0.0 */ basic_json(std::initializer_list init, - const bool type_deduction = true, - const value_t manual_type = value_t::array) + bool type_deduction = true, + value_t manual_type = value_t::array) { // check if each element is an array with two elements whose first // element is a string @@ -1853,9 +1862,12 @@ class basic_json @since version 1.0.0 */ - template::value or - std::is_same::value>::type> + template ::value or + std::is_same::value + , int>::type + = 0> basic_json(InputIT first, InputIT last) { assert(first.m_object != nullptr); @@ -2604,10 +2616,11 @@ class basic_json ////////////////// /// get an object (explicit) - template::value and - std::is_convertible::value - , int>::type = 0> + template ::value and + std::is_convertible::value + , int>::type = 0> T get_impl(T*) const { if (is_object()) @@ -2634,13 +2647,14 @@ class basic_json } /// get an array (explicit) - template::value and - not std::is_same::value and - not std::is_arithmetic::value and - not std::is_convertible::value and - not has_mapped_type::value - , int>::type = 0> + template ::value and + not std::is_same::value and + not std::is_arithmetic::value and + not std::is_convertible::value and + not has_mapped_type::value + , int>::type = 0> T get_impl(T*) const { if (is_array()) @@ -2660,10 +2674,11 @@ class basic_json } /// get an array (explicit) - template::value and - not std::is_same::value - , int>::type = 0> + template ::value and + not std::is_same::value + , int>::type = 0> std::vector get_impl(std::vector*) const { if (is_array()) @@ -2684,10 +2699,11 @@ class basic_json } /// get an array (explicit) - template::value and - not has_mapped_type::value - , int>::type = 0> + template ::value and + not has_mapped_type::value + , int>::type = 0> T get_impl(T*) const { if (is_array()) @@ -2714,9 +2730,10 @@ class basic_json } /// get a string (explicit) - template::value - , int>::type = 0> + template ::value + , int>::type = 0> T get_impl(T*) const { if (is_string()) @@ -2730,8 +2747,10 @@ class basic_json } /// get a number (explicit) - template::value, int>::type = 0> + template::value + , int>::type = 0> T get_impl(T*) const { switch (m_type) @@ -2920,8 +2939,10 @@ class basic_json @since version 1.0.0 */ - template::value>::type> + template::value + , int>::type = 0> ValueType get() const { return get_impl(static_cast(nullptr)); @@ -2954,8 +2975,10 @@ class basic_json @since version 1.0.0 */ - template::value>::type> + template::value + , int>::type = 0> PointerType get() noexcept { // delegate the call to get_ptr @@ -2966,8 +2989,10 @@ class basic_json @brief get a pointer value (explicit) @copydoc get() */ - template::value>::type> + template::value + , int>::type = 0> constexpr const PointerType get() const noexcept { // delegate the call to get_ptr @@ -3000,8 +3025,10 @@ class basic_json @since version 1.0.0 */ - template::value>::type> + template::value + , int>::type = 0> PointerType get_ptr() noexcept { // get the type of the PointerType (remove pointer and const) @@ -3027,9 +3054,11 @@ class basic_json @brief get a pointer value (implicit) @copydoc get_ptr() */ - template::value - and std::is_const::type>::value>::type> + and std::is_const::type>::value + , int>::type = 0> constexpr const PointerType get_ptr() const noexcept { // get the type of the PointerType (remove pointer and const) @@ -3077,8 +3106,10 @@ class basic_json @since version 1.1.0 */ - template::value>::type> + template::value + , int>::type = 0> ReferenceType get_ref() { // delegate call to get_ref_impl @@ -3089,9 +3120,11 @@ class basic_json @brief get a reference value (implicit) @copydoc get_ref() */ - template::value - and std::is_const::type>::value>::type> + and std::is_const::type>::value + , int>::type = 0> ReferenceType get_ref() const { // delegate call to get_ref_impl @@ -3126,13 +3159,14 @@ class basic_json @since version 1.0.0 */ - template < typename ValueType, typename = typename std::enable_if < + template < typename ValueType, typename + std::enable_if < not std::is_pointer::value and not std::is_same::value #ifndef _MSC_VER // Fix for issue #167 operator<< abiguity under VS2015 and not std::is_same>::value #endif - >::type > + , int >::type = 0 > operator ValueType() const { // delegate the call to get<>() const @@ -3718,8 +3752,10 @@ class basic_json @since version 1.0.0 */ - template::value>::type> + template ::value + , int>::type = 0> ValueType value(const typename object_t::key_type& key, ValueType default_value) const { // at only works for objects @@ -3792,8 +3828,10 @@ class basic_json @since version 2.0.2 */ - template::value>::type> + template ::value + , int>::type = 0> ValueType value(const json_pointer& ptr, ValueType default_value) const { // at only works for objects @@ -3954,9 +3992,12 @@ class basic_json @since version 1.0.0 */ - template::value or - std::is_same::value>::type> + template ::value or + std::is_same::value + , int>::type + = 0> InteratorType erase(InteratorType pos) { // make sure iterator fits the current value @@ -4060,9 +4101,12 @@ class basic_json @since version 1.0.0 */ - template::value or - std::is_same::value>::type> + template ::value or + std::is_same::value + , int>::type + = 0> InteratorType erase(InteratorType first, InteratorType last) { // make sure iterator fits the current value @@ -6223,7 +6267,7 @@ class basic_json const unsigned int current_indent = 0) const { // variable to hold indentation for recursive calls - auto new_indent = current_indent; + unsigned int new_indent = current_indent; switch (m_type) { @@ -7517,7 +7561,7 @@ class basic_json const std::size_t codepoint2 = 0) { // calculate the code point from the given code points - auto codepoint = codepoint1; + std::size_t codepoint = codepoint1; // check if codepoint1 is a high surrogate if (codepoint1 >= 0xD800 and codepoint1 <= 0xDBFF) @@ -8777,7 +8821,7 @@ class basic_json auto reference_token = reference_string.substr(start, slash - start); // check reference tokens are properly escaped - for (auto pos = reference_token.find_first_of("~"); + for (size_t pos = reference_token.find_first_of("~"); pos != std::string::npos; pos = reference_token.find_first_of("~", pos + 1)) { @@ -8822,7 +8866,7 @@ class basic_json assert(not f.empty()); for ( - auto pos = s.find(f); // find first occurrence of f + size_t pos = s.find(f); // find first occurrence of f pos != std::string::npos; // make sure f was found s.replace(pos, f.size(), t), // replace with t pos = s.find(f, pos + t.size()) // find next occurrence of f @@ -9222,7 +9266,7 @@ class basic_json else { // make sure the top element of the pointer exists - auto top_pointer = ptr.top(); + json_pointer top_pointer = ptr.top(); if (top_pointer != ptr) { basic_json& x = result.at(top_pointer); @@ -9630,7 +9674,7 @@ namespace std @since version 1.0.0 */ -template<> +template <> inline void swap(nlohmann::json& j1, nlohmann::json& j2) noexcept( is_nothrow_move_constructible::value and @@ -9641,7 +9685,7 @@ inline void swap(nlohmann::json& j1, } /// hash value for JSON objects -template<> +template <> struct hash { /*! From afba1d3fcb8921885b7169f8355a4e472d9b64f2 Mon Sep 17 00:00:00 2001 From: Niels Date: Tue, 30 Aug 2016 23:51:14 +0200 Subject: [PATCH 73/77] forgot to run re2c --- src/json.hpp | 218 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 131 insertions(+), 87 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 2f0f02d51..d6f8925fc 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -1164,10 +1164,11 @@ class basic_json @since version 1.0.0 */ - template::value and - std::is_constructible::value, int>::type - = 0> + template ::value and + std::is_constructible::value, int>::type + = 0> basic_json(const CompatibleObjectType& val) : m_type(value_t::object) { @@ -1228,15 +1229,16 @@ class basic_json @since version 1.0.0 */ - template::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value and - not std::is_same::value and - std::is_constructible::value, int>::type - = 0> + template ::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + std::is_constructible::value, int>::type + = 0> basic_json(const CompatibleArrayType& val) : m_type(value_t::array) { @@ -1322,9 +1324,10 @@ class basic_json @since version 1.0.0 */ - template::value, int>::type - = 0> + template ::value, int>::type + = 0> basic_json(const CompatibleStringType& val) : basic_json(string_t(val)) { @@ -1374,7 +1377,8 @@ class basic_json @since version 1.0.0 */ - template::value) and std::is_same::value , int>::type @@ -1473,7 +1477,8 @@ class basic_json @since version 2.0.0 */ - template::value) and std::is_same::value , int>::type @@ -1504,11 +1509,13 @@ class basic_json @since version 2.0.0 */ - template::value and - std::numeric_limits::is_integer and - not std::numeric_limits::is_signed, CompatibleNumberUnsignedType>::type - = 0> + template ::value and + std::numeric_limits::is_integer and + not std::numeric_limits::is_signed, + CompatibleNumberUnsignedType>::type + = 0> basic_json(const CompatibleNumberUnsignedType val) noexcept : m_type(value_t::number_unsigned), m_value(static_cast(val)) @@ -1584,9 +1591,11 @@ class basic_json @since version 1.0.0 */ - template::value and - std::is_floating_point::value>::type> + std::is_floating_point::value>::type + > basic_json(const CompatibleNumberFloatType val) noexcept : basic_json(number_float_t(val)) { @@ -1663,8 +1672,8 @@ class basic_json @since version 1.0.0 */ basic_json(std::initializer_list init, - const bool type_deduction = true, - const value_t manual_type = value_t::array) + bool type_deduction = true, + value_t manual_type = value_t::array) { // check if each element is an array with two elements whose first // element is a string @@ -1853,9 +1862,12 @@ class basic_json @since version 1.0.0 */ - template::value or - std::is_same::value>::type> + template ::value or + std::is_same::value + , int>::type + = 0> basic_json(InputIT first, InputIT last) { assert(first.m_object != nullptr); @@ -2604,10 +2616,11 @@ class basic_json ////////////////// /// get an object (explicit) - template::value and - std::is_convertible::value - , int>::type = 0> + template ::value and + std::is_convertible::value + , int>::type = 0> T get_impl(T*) const { if (is_object()) @@ -2634,13 +2647,14 @@ class basic_json } /// get an array (explicit) - template::value and - not std::is_same::value and - not std::is_arithmetic::value and - not std::is_convertible::value and - not has_mapped_type::value - , int>::type = 0> + template ::value and + not std::is_same::value and + not std::is_arithmetic::value and + not std::is_convertible::value and + not has_mapped_type::value + , int>::type = 0> T get_impl(T*) const { if (is_array()) @@ -2660,10 +2674,11 @@ class basic_json } /// get an array (explicit) - template::value and - not std::is_same::value - , int>::type = 0> + template ::value and + not std::is_same::value + , int>::type = 0> std::vector get_impl(std::vector*) const { if (is_array()) @@ -2684,10 +2699,11 @@ class basic_json } /// get an array (explicit) - template::value and - not has_mapped_type::value - , int>::type = 0> + template ::value and + not has_mapped_type::value + , int>::type = 0> T get_impl(T*) const { if (is_array()) @@ -2714,9 +2730,10 @@ class basic_json } /// get a string (explicit) - template::value - , int>::type = 0> + template ::value + , int>::type = 0> T get_impl(T*) const { if (is_string()) @@ -2730,8 +2747,10 @@ class basic_json } /// get a number (explicit) - template::value, int>::type = 0> + template::value + , int>::type = 0> T get_impl(T*) const { switch (m_type) @@ -2920,8 +2939,10 @@ class basic_json @since version 1.0.0 */ - template::value>::type> + template::value + , int>::type = 0> ValueType get() const { return get_impl(static_cast(nullptr)); @@ -2954,8 +2975,10 @@ class basic_json @since version 1.0.0 */ - template::value>::type> + template::value + , int>::type = 0> PointerType get() noexcept { // delegate the call to get_ptr @@ -2966,8 +2989,10 @@ class basic_json @brief get a pointer value (explicit) @copydoc get() */ - template::value>::type> + template::value + , int>::type = 0> constexpr const PointerType get() const noexcept { // delegate the call to get_ptr @@ -3000,8 +3025,10 @@ class basic_json @since version 1.0.0 */ - template::value>::type> + template::value + , int>::type = 0> PointerType get_ptr() noexcept { // get the type of the PointerType (remove pointer and const) @@ -3027,9 +3054,11 @@ class basic_json @brief get a pointer value (implicit) @copydoc get_ptr() */ - template::value - and std::is_const::type>::value>::type> + and std::is_const::type>::value + , int>::type = 0> constexpr const PointerType get_ptr() const noexcept { // get the type of the PointerType (remove pointer and const) @@ -3077,8 +3106,10 @@ class basic_json @since version 1.1.0 */ - template::value>::type> + template::value + , int>::type = 0> ReferenceType get_ref() { // delegate call to get_ref_impl @@ -3089,9 +3120,11 @@ class basic_json @brief get a reference value (implicit) @copydoc get_ref() */ - template::value - and std::is_const::type>::value>::type> + and std::is_const::type>::value + , int>::type = 0> ReferenceType get_ref() const { // delegate call to get_ref_impl @@ -3126,13 +3159,14 @@ class basic_json @since version 1.0.0 */ - template < typename ValueType, typename = typename std::enable_if < + template < typename ValueType, typename + std::enable_if < not std::is_pointer::value and not std::is_same::value #ifndef _MSC_VER // Fix for issue #167 operator<< abiguity under VS2015 and not std::is_same>::value #endif - >::type > + , int >::type = 0 > operator ValueType() const { // delegate the call to get<>() const @@ -3718,8 +3752,10 @@ class basic_json @since version 1.0.0 */ - template::value>::type> + template ::value + , int>::type = 0> ValueType value(const typename object_t::key_type& key, ValueType default_value) const { // at only works for objects @@ -3792,8 +3828,10 @@ class basic_json @since version 2.0.2 */ - template::value>::type> + template ::value + , int>::type = 0> ValueType value(const json_pointer& ptr, ValueType default_value) const { // at only works for objects @@ -3954,9 +3992,12 @@ class basic_json @since version 1.0.0 */ - template::value or - std::is_same::value>::type> + template ::value or + std::is_same::value + , int>::type + = 0> InteratorType erase(InteratorType pos) { // make sure iterator fits the current value @@ -4060,9 +4101,12 @@ class basic_json @since version 1.0.0 */ - template::value or - std::is_same::value>::type> + template ::value or + std::is_same::value + , int>::type + = 0> InteratorType erase(InteratorType first, InteratorType last) { // make sure iterator fits the current value @@ -6223,7 +6267,7 @@ class basic_json const unsigned int current_indent = 0) const { // variable to hold indentation for recursive calls - auto new_indent = current_indent; + unsigned int new_indent = current_indent; switch (m_type) { @@ -7517,7 +7561,7 @@ class basic_json const std::size_t codepoint2 = 0) { // calculate the code point from the given code points - auto codepoint = codepoint1; + std::size_t codepoint = codepoint1; // check if codepoint1 is a high surrogate if (codepoint1 >= 0xD800 and codepoint1 <= 0xDBFF) @@ -9480,7 +9524,7 @@ basic_json_parser_63: auto reference_token = reference_string.substr(start, slash - start); // check reference tokens are properly escaped - for (auto pos = reference_token.find_first_of("~"); + for (size_t pos = reference_token.find_first_of("~"); pos != std::string::npos; pos = reference_token.find_first_of("~", pos + 1)) { @@ -9525,7 +9569,7 @@ basic_json_parser_63: assert(not f.empty()); for ( - auto pos = s.find(f); // find first occurrence of f + size_t pos = s.find(f); // find first occurrence of f pos != std::string::npos; // make sure f was found s.replace(pos, f.size(), t), // replace with t pos = s.find(f, pos + t.size()) // find next occurrence of f @@ -9925,7 +9969,7 @@ basic_json_parser_63: else { // make sure the top element of the pointer exists - auto top_pointer = ptr.top(); + json_pointer top_pointer = ptr.top(); if (top_pointer != ptr) { basic_json& x = result.at(top_pointer); @@ -10333,7 +10377,7 @@ namespace std @since version 1.0.0 */ -template<> +template <> inline void swap(nlohmann::json& j1, nlohmann::json& j2) noexcept( is_nothrow_move_constructible::value and @@ -10344,7 +10388,7 @@ inline void swap(nlohmann::json& j1, } /// hash value for JSON objects -template<> +template <> struct hash { /*! From 442058f8edd62a3a95cb6c8c37c0763877e125e7 Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 31 Aug 2016 17:07:35 +0200 Subject: [PATCH 74/77] interface cleanup --- doc/examples/basic_json.cpp | 12 ----- doc/examples/basic_json.link | 1 - doc/examples/basic_json.output | 1 - doc/examples/basic_json__istream.cpp | 4 +- doc/examples/basic_json__istream.link | 2 +- doc/examples/basic_json__nullptr_t.cpp | 9 ++-- doc/examples/basic_json__nullptr_t.link | 2 +- doc/examples/basic_json__nullptr_t.output | 1 + src/json.hpp | 65 +++++++++-------------- src/json.hpp.re2c | 65 +++++++++-------------- 10 files changed, 62 insertions(+), 100 deletions(-) delete mode 100644 doc/examples/basic_json.cpp delete mode 100644 doc/examples/basic_json.link delete mode 100644 doc/examples/basic_json.output diff --git a/doc/examples/basic_json.cpp b/doc/examples/basic_json.cpp deleted file mode 100644 index 0f36e4f84..000000000 --- a/doc/examples/basic_json.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include - -using json = nlohmann::json; - -int main() -{ - // create a JSON value with default null value - json j; - - // serialize the JSON null value - std::cout << j << '\n'; -} diff --git a/doc/examples/basic_json.link b/doc/examples/basic_json.link deleted file mode 100644 index e5c17c93a..000000000 --- a/doc/examples/basic_json.link +++ /dev/null @@ -1 +0,0 @@ -online \ No newline at end of file diff --git a/doc/examples/basic_json.output b/doc/examples/basic_json.output deleted file mode 100644 index 19765bd50..000000000 --- a/doc/examples/basic_json.output +++ /dev/null @@ -1 +0,0 @@ -null diff --git a/doc/examples/basic_json__istream.cpp b/doc/examples/basic_json__istream.cpp index 71f16ed34..32885b22c 100644 --- a/doc/examples/basic_json__istream.cpp +++ b/doc/examples/basic_json__istream.cpp @@ -27,7 +27,8 @@ int main() ss << text; // create JSON from stream - json j_complete(ss); + json j_complete(ss); // deprecated! + // shall be replaced by: json j_complete = json::parse(ss); std::cout << std::setw(4) << j_complete << "\n\n"; @@ -51,5 +52,6 @@ int main() // create JSON from stream (with callback) json j_filtered(ss, cb); + // shall be replaced by: json j_filtered = json::parse(ss, cb); std::cout << std::setw(4) << j_filtered << '\n'; } \ No newline at end of file diff --git a/doc/examples/basic_json__istream.link b/doc/examples/basic_json__istream.link index 20d1033c3..eb165e2fd 100644 --- a/doc/examples/basic_json__istream.link +++ b/doc/examples/basic_json__istream.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/basic_json__nullptr_t.cpp b/doc/examples/basic_json__nullptr_t.cpp index 426afabc6..d0156d537 100644 --- a/doc/examples/basic_json__nullptr_t.cpp +++ b/doc/examples/basic_json__nullptr_t.cpp @@ -4,9 +4,12 @@ using json = nlohmann::json; int main() { - // create a JSON null value - json j(nullptr); + // implicitly create a JSON null value + json j1; + + // explicitly create a JSON null value + json j2(nullptr); // serialize the JSON null value - std::cout << j << '\n'; + std::cout << j1 << '\n' << j2 << '\n'; } diff --git a/doc/examples/basic_json__nullptr_t.link b/doc/examples/basic_json__nullptr_t.link index 7e9177522..f911caa5c 100644 --- a/doc/examples/basic_json__nullptr_t.link +++ b/doc/examples/basic_json__nullptr_t.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/basic_json__nullptr_t.output b/doc/examples/basic_json__nullptr_t.output index 19765bd50..c1e4b6c17 100644 --- a/doc/examples/basic_json__nullptr_t.output +++ b/doc/examples/basic_json__nullptr_t.output @@ -1 +1,2 @@ null +null diff --git a/src/json.hpp b/src/json.hpp index d6f8925fc..d755f7129 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -73,6 +73,15 @@ SOFTWARE. #pragma GCC diagnostic ignored "-Wfloat-equal" #endif +// allow for portable deprecation warnings +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #define JSON_DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) + #define JSON_DEPRECATED __declspec(deprecated) +#else + #define JSON_DEPRECATED +#endif + /*! @brief namespace for Niels Lohmann @see https://github.com/nlohmann @@ -1057,40 +1066,10 @@ class basic_json } /*! - @brief create a null object (implicitly) + @brief create a null object - Create a `null` JSON value. This is the implicit version of the `null` - value constructor as it takes no parameters. - - @note The class invariant is satisfied, because it poses no requirements - for null values. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this constructor never throws - exceptions. - - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is constant. - - As postcondition, it holds: `basic_json().empty() == true`. - - @liveexample{The following code shows the constructor for a `null` JSON - value.,basic_json} - - @sa @ref basic_json(std::nullptr_t) -- create a `null` value - - @since version 1.0.0 - */ - basic_json() = default; - - /*! - @brief create a null object (explicitly) - - Create a `null` JSON value. This is the explicitly version of the `null` - value constructor as it takes a null pointer as parameter. It allows to - create `null` values by explicitly assigning a `nullptr` to a JSON value. + Create a `null` JSON value. It either takes a null pointer as parameter + (explicitly creating `null`) or no parameter (implicitly creating `null`). The passed null pointer itself is not read -- it is only used to choose the right constructor. @@ -1099,15 +1078,12 @@ class basic_json @exceptionsafety No-throw guarantee: this constructor never throws exceptions. - @liveexample{The following code shows the constructor with null pointer - parameter.,basic_json__nullptr_t} - - @sa @ref basic_json() -- default constructor (implicitly creating a `null` - value) + @liveexample{The following code shows the constructor with and without a + null pointer parameter.,basic_json__nullptr_t} @since version 1.0.0 */ - basic_json(std::nullptr_t) noexcept + basic_json(std::nullptr_t = nullptr) noexcept : basic_json(value_t::null) { assert_invariant(); @@ -1971,12 +1947,21 @@ class basic_json @note A UTF-8 byte order mark is silently ignored. + @deprecated This constructor is deprecated and will be removed in version + 3.0.0 to unify the interface of the library. Deserialization will be + done by stream operators or by calling one of the `parse` functions, + e.g. @ref parse(std::istream&, const parser_callback_t). That is, calls + like `json j(i);` for an input stream @a i need to be replaced by + `json j = json::parse(i);`. See the example below. + @liveexample{The example below demonstrates constructing a JSON value from a `std::stringstream` with and without callback function.,basic_json__istream} - @since version 2.0.0 + @since version 2.0.0, deprecated in version 2.0.3, to be removed in + version 3.0.0 */ + JSON_DEPRECATED explicit basic_json(std::istream& i, const parser_callback_t cb = nullptr) { *this = parser(i, cb).parse(); diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 75fc27e5a..75b38d964 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -73,6 +73,15 @@ SOFTWARE. #pragma GCC diagnostic ignored "-Wfloat-equal" #endif +// allow for portable deprecation warnings +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #define JSON_DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) + #define JSON_DEPRECATED __declspec(deprecated) +#else + #define JSON_DEPRECATED +#endif + /*! @brief namespace for Niels Lohmann @see https://github.com/nlohmann @@ -1057,40 +1066,10 @@ class basic_json } /*! - @brief create a null object (implicitly) + @brief create a null object - Create a `null` JSON value. This is the implicit version of the `null` - value constructor as it takes no parameters. - - @note The class invariant is satisfied, because it poses no requirements - for null values. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this constructor never throws - exceptions. - - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is constant. - - As postcondition, it holds: `basic_json().empty() == true`. - - @liveexample{The following code shows the constructor for a `null` JSON - value.,basic_json} - - @sa @ref basic_json(std::nullptr_t) -- create a `null` value - - @since version 1.0.0 - */ - basic_json() = default; - - /*! - @brief create a null object (explicitly) - - Create a `null` JSON value. This is the explicitly version of the `null` - value constructor as it takes a null pointer as parameter. It allows to - create `null` values by explicitly assigning a `nullptr` to a JSON value. + Create a `null` JSON value. It either takes a null pointer as parameter + (explicitly creating `null`) or no parameter (implicitly creating `null`). The passed null pointer itself is not read -- it is only used to choose the right constructor. @@ -1099,15 +1078,12 @@ class basic_json @exceptionsafety No-throw guarantee: this constructor never throws exceptions. - @liveexample{The following code shows the constructor with null pointer - parameter.,basic_json__nullptr_t} - - @sa @ref basic_json() -- default constructor (implicitly creating a `null` - value) + @liveexample{The following code shows the constructor with and without a + null pointer parameter.,basic_json__nullptr_t} @since version 1.0.0 */ - basic_json(std::nullptr_t) noexcept + basic_json(std::nullptr_t = nullptr) noexcept : basic_json(value_t::null) { assert_invariant(); @@ -1971,12 +1947,21 @@ class basic_json @note A UTF-8 byte order mark is silently ignored. + @deprecated This constructor is deprecated and will be removed in version + 3.0.0 to unify the interface of the library. Deserialization will be + done by stream operators or by calling one of the `parse` functions, + e.g. @ref parse(std::istream&, const parser_callback_t). That is, calls + like `json j(i);` for an input stream @a i need to be replaced by + `json j = json::parse(i);`. See the example below. + @liveexample{The example below demonstrates constructing a JSON value from a `std::stringstream` with and without callback function.,basic_json__istream} - @since version 2.0.0 + @since version 2.0.0, deprecated in version 2.0.3, to be removed in + version 3.0.0 */ + JSON_DEPRECATED explicit basic_json(std::istream& i, const parser_callback_t cb = nullptr) { *this = parser(i, cb).parse(); From 58cbf4b3ef23d913528140fc0b1b861464f0dfb5 Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 31 Aug 2016 18:23:46 +0200 Subject: [PATCH 75/77] added another test case --- test/src/unit-deserialization.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/src/unit-deserialization.cpp b/test/src/unit-deserialization.cpp index 49ae7b4be..d94632133 100644 --- a/test/src/unit-deserialization.cpp +++ b/test/src/unit-deserialization.cpp @@ -148,6 +148,18 @@ TEST_CASE("deserialization") CHECK(json::parse(v) == json(true)); } + SECTION("from chars") + { + uint8_t *v = new uint8_t[5]; + v[0] = 't'; + v[1] = 'r'; + v[2] = 'u'; + v[3] = 'e'; + v[4] = '\0'; + CHECK(json::parse(v) == json(true)); + delete[] v; + } + SECTION("from std::string") { std::string v = {'t', 'r', 'u', 'e'}; From 740b66f225457b2e2ef5d059c61f6db7e153fc14 Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 31 Aug 2016 20:23:21 +0200 Subject: [PATCH 76/77] cleanup --- .github/CONTRIBUTING.md | 10 ++-------- README.md | 4 +++- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 2b4160810..04a7cc1cc 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -41,16 +41,10 @@ There are currently two files which need to be edited: 2. [`test/src/unit.cpp`](https://github.com/nlohmann/json/blob/master/test/unit.cpp) - This contains the [Catch](https://github.com/philsquared/Catch) unit tests which currently cover [100 %](https://coveralls.io/github/nlohmann/json) of the library's code. - If you add or change a feature, please also add a unit test to this file. The unit tests can be compiled with + If you add or change a feature, please also add a unit test to this file. The unit tests can be compiled and executed with ```sh - make - ``` - - and can be executed with - - ```sh - ./json_unit + make check ``` The test cases are also executed with several different compilers on [Travis](https://travis-ci.org/nlohmann/json) once you open a pull request. diff --git a/README.md b/README.md index c14534080..5b0c96527 100644 --- a/README.md +++ b/README.md @@ -492,6 +492,8 @@ I deeply appreciate the help of the following people. - [Mário Feroldi](https://github.com/thelostt) fixed a small typo. - [duncanwerner](https://github.com/duncanwerner) found a really embarrassing performance regression in the 2.0.0 release. - [Damien](https://github.com/dtoma) fixed one of the last conversion warnings. +- [Thomas Braun](https://github.com/t-b) fixed a warning in a test case. +- [Théo DELRIEU](https://github.com/theodelrieu) patiently and constructively oversaw the long way toward [iterator-range parsing](https://github.com/nlohmann/json/issues/290). Thanks a lot for helping out! @@ -510,7 +512,7 @@ To compile and run the tests, you need to execute $ make check =============================================================================== -All tests passed (8905119 assertions in 32 test cases) +All tests passed (8905154 assertions in 35 test cases) ``` For more information, have a look at the file [.travis.yml](https://github.com/nlohmann/json/blob/master/.travis.yml). From df9fd6237e9e4cd81695f89868dda58e105891c2 Mon Sep 17 00:00:00 2001 From: Niels Date: Wed, 31 Aug 2016 21:27:23 +0200 Subject: [PATCH 77/77] release 2.0.3 --- CMakeLists.txt | 2 +- ChangeLog.md | 12 +++++++++++- README.md | 2 +- doc/Doxyfile | 2 +- doc/examples/README.link | 2 +- doc/index.md | 2 +- src/json.hpp | 2 +- src/json.hpp.re2c | 2 +- test/reports/2016-08-29-fuzz/exec_speed.png | Bin 0 -> 28144 bytes test/reports/2016-08-29-fuzz/fuzz.tiff | Bin 0 -> 235588 bytes test/reports/2016-08-29-fuzz/high_freq.png | Bin 0 -> 26251 bytes test/reports/2016-08-29-fuzz/index.html | 10 ++++++++++ test/reports/2016-08-29-fuzz/low_freq.png | Bin 0 -> 11752 bytes test/src/fuzz.cpp | 2 +- test/src/unit-algorithms.cpp | 2 +- test/src/unit-allocator.cpp | 2 +- test/src/unit-capacity.cpp | 2 +- test/src/unit-class_const_iterator.cpp | 2 +- test/src/unit-class_iterator.cpp | 2 +- test/src/unit-class_lexer.cpp | 2 +- test/src/unit-class_parser.cpp | 2 +- test/src/unit-comparison.cpp | 2 +- test/src/unit-concepts.cpp | 2 +- test/src/unit-constructor1.cpp | 2 +- test/src/unit-constructor2.cpp | 2 +- test/src/unit-convenience.cpp | 2 +- test/src/unit-conversions.cpp | 2 +- test/src/unit-deserialization.cpp | 4 ++-- test/src/unit-element_access1.cpp | 2 +- test/src/unit-element_access2.cpp | 2 +- test/src/unit-inspection.cpp | 2 +- test/src/unit-iterator_wrapper.cpp | 2 +- test/src/unit-iterators1.cpp | 2 +- test/src/unit-iterators2.cpp | 2 +- test/src/unit-json_patch.cpp | 2 +- test/src/unit-json_pointer.cpp | 2 +- test/src/unit-modifiers.cpp | 2 +- test/src/unit-pointer_access.cpp | 2 +- test/src/unit-readme.cpp | 2 +- test/src/unit-reference_access.cpp | 2 +- test/src/unit-regression.cpp | 2 +- test/src/unit-serialization.cpp | 2 +- test/src/unit-testsuites.cpp | 2 +- test/src/unit-unicode.cpp | 2 +- test/src/unit.cpp | 2 +- 45 files changed, 61 insertions(+), 41 deletions(-) create mode 100644 test/reports/2016-08-29-fuzz/exec_speed.png create mode 100644 test/reports/2016-08-29-fuzz/fuzz.tiff create mode 100644 test/reports/2016-08-29-fuzz/high_freq.png create mode 100644 test/reports/2016-08-29-fuzz/index.html create mode 100644 test/reports/2016-08-29-fuzz/low_freq.png diff --git a/CMakeLists.txt b/CMakeLists.txt index 72802b29b..0b2d54c4a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.0) # define the project -project(nlohmann_json VERSION 2.0.2 LANGUAGES CXX) +project(nlohmann_json VERSION 2.0.3 LANGUAGES CXX) enable_testing() diff --git a/ChangeLog.md b/ChangeLog.md index 8062f3b31..dd1b37966 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,7 +1,17 @@ # Change Log All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). -## [v2.0.2](https://github.com/nlohmann/json/releases/tag/v2.0.2) (2016-07-30) +## [v2.0.3](https://github.com/nlohmann/json/releases/tag/v2.0.3) (2016-08-31) +[Full Changelog](https://github.com/nlohmann/json/compare/v2.0.2...v2.0.3) + +- Support for iterator-range parsing [\#290](https://github.com/nlohmann/json/issues/290) + +- warning C4706: assignment within conditional expression [\#295](https://github.com/nlohmann/json/issues/295) +- Horribly inconsistent behavior between const/non-const reference in operator \[\] \(\) [\#289](https://github.com/nlohmann/json/issues/289) + +- unit-constructor1.cpp: Fix floating point truncation warning [\#300](https://github.com/nlohmann/json/pull/300) ([t-b](https://github.com/t-b)) + +## [v2.0.2](https://github.com/nlohmann/json/releases/tag/v2.0.2) (2016-07-31) [Full Changelog](https://github.com/nlohmann/json/compare/v2.0.1...v2.0.2) - value\(\) does not work with \_json\_pointer types [\#283](https://github.com/nlohmann/json/issues/283) diff --git a/README.md b/README.md index 5b0c96527..1c62e1374 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Build Status](https://travis-ci.org/nlohmann/json.svg?branch=master)](https://travis-ci.org/nlohmann/json) [![Build Status](https://ci.appveyor.com/api/projects/status/1acb366xfyg3qybk/branch/develop?svg=true)](https://ci.appveyor.com/project/nlohmann/json) [![Coverage Status](https://img.shields.io/coveralls/nlohmann/json.svg)](https://coveralls.io/r/nlohmann/json) -[![Try online](https://img.shields.io/badge/try-online-blue.svg)](http://melpon.org/wandbox/permlink/p5o4znPnGHJpDVqN) +[![Try online](https://img.shields.io/badge/try-online-blue.svg)](http://melpon.org/wandbox/permlink/zkFPohcXJ2TFuAyq) [![Documentation](https://img.shields.io/badge/docs-doxygen-blue.svg)](http://nlohmann.github.io/json) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/nlohmann/json/master/LICENSE.MIT) [![Github Releases](https://img.shields.io/github/release/nlohmann/json.svg)](https://github.com/nlohmann/json/releases) diff --git a/doc/Doxyfile b/doc/Doxyfile index e0055d651..17cbd1a3e 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -5,7 +5,7 @@ #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = "JSON for Modern C++" -PROJECT_NUMBER = 2.0.2 +PROJECT_NUMBER = 2.0.3 PROJECT_BRIEF = PROJECT_LOGO = OUTPUT_DIRECTORY = . diff --git a/doc/examples/README.link b/doc/examples/README.link index 0e87a79e6..a2d8a68ca 100644 --- a/doc/examples/README.link +++ b/doc/examples/README.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/index.md b/doc/index.md index cf32a7a55..ba62dfab4 100644 --- a/doc/index.md +++ b/doc/index.md @@ -268,4 +268,4 @@ The container functions known from STL have been extended to support the differe @author [Niels Lohmann](http://nlohmann.me) @see https://github.com/nlohmann/json to download the source code -@version 2.0.2 +@version 2.0.3 diff --git a/src/json.hpp b/src/json.hpp index 1a07e46c8..25a9dbcf7 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.3 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 300337a21..7ca046eaf 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.3 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/reports/2016-08-29-fuzz/exec_speed.png b/test/reports/2016-08-29-fuzz/exec_speed.png new file mode 100644 index 0000000000000000000000000000000000000000..e40e3414a8721acefec76bbf7a35e472968247eb GIT binary patch literal 28144 zcmc$`Wl$Vpw=OzZa3{EXAh>&QcMB3UxVyW%2lwC-2=4CgE`tOM?tVJ^+xy&eZq@yL zrwWGY>1y6y>s|J&XLW>vh zXj|I;FEyGS%eG2QdrzXx$X6+m#M{0p5+{My)-LB;olp-V6fQrvfbjH#>L{n99?$G#7l>+eb(%{}e^ta?}bOVT}|(Cf|IL5`R0{no zRpzcu(}wHCc8|yPp9GF+X&Wdl_6uIVP*1Z2Z8O*xSkFWzSJK79L-UOJPh}1cj({(ussE1% z@bhaz+vbk`o}5t7t3ps>M7Z-$D(20YJ4XB;_lIEKEJ>bVmg8Xx2R%5u-H$p z#c{y0;Sa{Ze|AKVfi4A>{qa*-NzQ7kOT~m~q2><;9Zr7!f8xI?Sv%Ehb+%wswroBR zXBvw9kMNp{-FIDO%ft+?`dypo>#Ulm-|mSnC)V$O!0XeQ)?cK7uh_t{FqoZc%wR<5%q!z$hDWaTI1-^w zKpC2P9wsA+RDq>{KKRbt>uPIj0d1C)l+@K-2cZfM|8J48?Ar5YTP;?$*n>sNVA2iu z-a*B`y!Yu3rgCv~e-^a^icu7N+`G8A2!ui8<>PyQ`t_bsJiUO@Vg2>u8{Jt|U^m?SI`d_cz-QAd&7)m+gThG@@t-5+`2KWm* zBxp$ODviP!^9Jpn&^QET%R{0B6f;jd>?Sie#Q)gcpt_uG*}_)N&d$IN(P^?3aNLtxrA@i< zN(vD0u{?L=&BjYflyq#mm8JDQV+P9(NB_tLHFNO<2P6>Rnz3SliMWYFS^O|-)+v%&#T3GS#Fh;-M;7F4fga1JL2T}l+34& zlMUy8X@Vo|ol;7>p!zN`HWkeDQSPCD?7<~Xs~3^h47}tfpR64mIJdOKWj2`w2nhx< zpt`ymFyJH_MFn~JnAq6H#>Uv#ScG1LCOLU|B3@^6aBoQ0`|J78(2(2N`s2bc-xS>? z%Z4x%qKu3T1Zc>A+o08Yh1Jo=-rl|^5JnBR<+~%*=tnmOCA7Yd;^IGmQZh3#FmTTR z8@;(yl$ZD5&nYgZ#&{d#>U20;=fPjt+S*!N{J|-M?A^CfD{0nL-5frpq@R11> zH%q zx#HaSmxHdSg^sIbb-CJAK8XYJ1ltSJmms$vRFkxL}X)aEk}s~LLua~ zA0l##FTK2UD4dZ}U7Vkv7B59mrfYE{1Jx5j`*)oS57DW+@nkE6_4Rbk@&yjr25-x( z+E^|!$l>o;&fdpv??$=*R8NU_3=hGfD3x6t-cPzE7PJhW%6NTUIc(f@zndujf@L1{ zvtUFY&NS=7>&brX1}p6M#p`D8S@?njVIRcbA8B^V;%6DD)>C`rU1xi*R6npgr1TVp zQJ$Y?ER!bKx_V{FwF>(VigF9NSsJKemPUTTca(l^&ugP7lG5Y%)+_kjvZ?Z$Lx!4~ zO(PN2$WLeymnt~b3jS5k!jO{hU=#iC5oV*=s(U|O2WL|z0WtBPsYu9a%LexKnVDMa zmEUh~z9~(*o!%{tjT*Y6fI62pH#>cv@4#TNR-LKO)d&sOx;>PCp=6w^t81}5wPt~? z16`Rr*@@0|-#ltvSCdkdD%DDt-+QKACl5C_Zq&eJu8j&bpckWjrle)S8BKEi~E_Y3rT3TC=`^Cc9F+e9ba@iBaTAUEqH`g6*& zZr$_tmaG`wL9y(Gz$dtW1r}xegJ=?C<1LhK;3sr>6UmM9e7*aw7uHwk@qr+3ZVI5ad+gc~!sq~~?IGY|zh>@`4BPq!y59gOKh)=6^3N`P{fAjLV> z0h@fQ%YFUZdQx3cUT$e?>wDI6aOhsq*vLtUGMB7bty`;IWl^X`UbCPUTR%VEQ+jD} zbYHMAScFxQ`uF~EB-syl7&S9(-K+~CLyxCIE@WyFn+a_+sn{bJmywR+7d4)=D*I#I zZp}q_IM#3TJ0n%nINH)|F=W_8i_(ikx0aw4IOfWywLNR142E*z+U3e4q8=0Z?CnTG zN5y!dP_n@}7?Rdbu`Zn354O<1yGV@50A>39mDY&i@6jNL)sst@5E_9g4On&{BpRAI z#lU{u!_m6?@g0tA2z=e_Lj2T`Rpxm}h>{)j&C-9)fl&1ACtzST_gH^2S+I9L(CzKy z2|ABtC-vzGCfDuux}lGeKkY*{V+jvAEXUxc zZAVfRGki+jlYZlOM)7*u&h5`rG@tgncUw>UJ_oy=q*hA3k?KIm@v)hDCKPa*;N~6IYL4?vx)ZY!w8|$B498>d~LU0q$>*r-xk*X+DX>3VMKqNtwy=fu|~;$OZaP!%(r z(G`fO+DKav9bPF<$x6J))&D#FeQ$+ypTgMKwyguKNBQK5Q zZLH3p-cv`OzNA2YFG*$^InG%b3L#Kp+(WC##{G?Zl>)_1#HG0@Riqn)-b#~h~WzrRt zvK(nI>hp)mxgUO=YY5$jaJ<>XBfp;*E%sOIGTs7iXsXA^esQ@|>)uOQW!$_k@1p(W z;oSBZviSSeB;HE4gTlV8xpBriVNwWpfd2iXuJMgC3{e$l`<1?MCLz~ z5PJSpAY{<8YYGRp5(?A(A^x4jju@7(iu2{#!H=}y`Jv!0L8IMe_VyhqV_f^JgL#nQ zzQbbUiDNv{!}olFBO~!#R&)ANivCH0!Y-Szg!d<};@U4_LDXD3qZs8@<+n31&W=zeZ;4sGs{X3t_k4GdcCde1x)qzn|b) zZbclbg7yjWApty$(Z|vSD0jDRAx)C4%QBZ&@|&1o_EK)XYP;H<2c?Z0VZhGzaGUSIQnF^$u zcHX-L-iDM(pOR@cvt%4E)jsh6&Rj=ZyIQx!h%<8}S5#D#fq{Y2`+mdi{^myMq`j>z zGjF@pI}sZ^w-bIsc|;q0fk3L1qmos z$bnuhsboR}7J4CBwhxksE|sI&CXemr_$1u7;pov1A?K)BgWP8k?8{_p;evEOTue3< zQXdevHAGQmakcK1<#dBJthk!r2e$X^FFNO5xL9!ue*Ak5TaV1H$>D;RRnJSf7=7$} z_YOt6W()JyY1w;LBYfpi29K$#!zizb3UaPY?7UwtY8}HD!|cagf-u;%3p^Jz|9P6u zztzLkd}H=15!OemwYX`xp6Ti1C!}oyxFkrZ-&bygildDssGiwix#chIh{-3!vGBig z;g?{H40pb?yDiVuBE+?aRs*5S)jP6EfA^_2j%g>MVfN9>2)N?I<+FK14<_54{Pt!D zE+V*c>#Ng<`L)q>7=$&$1n42)R zhsoZPFl-7cqjieLpi<$7*P(p6vj8^pEo3UWuv}31C+Di6hB% z3t23x2bEeiuDDZML)#w`W(oH-FK(msenobhf9QgZj-dwGe?V@fgDy<78>A436&6}- z|L{&}G!1LSEPgqF3pz0)JBcT`{m`G63mJ`n&vg)poWAt&u}nTmoj!7Sfz z*Y(v$(z|(8>-YB&pp2gty^idS!?vT zHr-kN?1t#?aTbIy2#u$Cj7pVO zxw_dZpaB02xbjN9#oS_*4q&Mgl9Ek8Noz8Ojj^z~>3fjv#>vUq*wAo$bK~sl>U+@x zYf%{4U3IPU_Os$^WL;K>cT1az?HxUOiVx+UWx~-WU*hBU)1Fy3=}b#_Gu2i?9Pbr4 zS27YGcRg10_{E?=_rmhaqBkR?4vI92R)v9L{l^LIqYoa3x~7PJVJ~=G)>jigiX`^N zM@vbk4_G=~)byc#7<~{c`#vQ@we7^k$3JPRnY7_I5!>#uh%E-LJSXkv$J`c-{L&Bw zbuYj_TC$8Ieg@lR54DEqVp$O9#*pKtFockQijZa#Uxh~>6iWa7{X&JZ@bK_C$#UcoV*h0MhrfJFS@^ZPZ9W_)4Hwjals zTOigRDWO?xbQ$Xy8TV>8ipALn-VTlvYFs_s=NZ{W<`0GIheaJxzeYsG0SIl+cjuk!F&J|lq1)<3raD$>yUL!2& zpgb6JnAtxvn>X|Kv3PSL)M1-4D8y24TBzZlQ6vEKe1wgd6^G^C^T0tgh)^x{+#D6vW!q96EEQxN1=Pghm zSXo*Eami`vo7>E8^KfZ(N5_U`gY9~AZCzc`qy(*)NssZIfTOmen~F?lG{epj+=A`p zmsz%)#6_|kk>II;M?NOo6Rp48E0$2%uXNDI9lmvKviT09TA&udq3sr(qU;1JrU<+K zB}d6kjn9n~&`BLv{k@PqX=cPyEF+=>uQeEGC+Jd_;yNW;Zq zO7mK3@vC6ci0VlC?DuMP7zHtAovz9AVN?yvhJtu5OBP zuEMntiKKo;7)hprttiWw>Y09qJW9Q7Zy&u#T9;6SQ%QXo&>QUspDblKd(Nh`0C;!N z_0Lyg&^^F0W^?AlUAo&9+LxqhwO$f&?ocwJK@qqi7H*4;xv<#XhqH4udCILG5|Vwn zi(?r={;`R{&>Q-cSl}gVr6bSfebv zmAOP}2?v9+BB`nKwTs!fSw<&>&>y`d4Y*_uUjpOm3g&mmbjiTnps&uqPbudHiY?{T zVX!hPm>}(>79qps!BH&BNYYfe6*GK+)l58V-(Ex6j5uat!l5u_j1}G*iTeprOL1j9 ziZ)?blQzy%HZrJB^B6G>ObbV{s)Z2zu&yV7P?G@N`t2(ZYz(z-qye|TeKdi^{1G^} ze-UVL_e4X|5no3WUI4k@y0mSrPmWU1I z2a^PugqAxg!kqYEU-plXvGu2Pxphl)sI9qXB=x!bAtHn*~EN6MkWb&<~#Lwg+{>r4AA) z=#kt|!Bm%T@73ewm36N;RI79zHH~8i3L(f-6~G^sU#!sjdSih&JLA3v%3?-SBzAi# zJdoEMbB7?oZlgeTgRiCJ91^`m?ZW`$$ht>R(^Al5Dhl)UBQVi)?qN zYxg@*Oo_B5b1lY0vESNU0f_W$y~PPY+JJV8G(ifHv$DtlqD)uY_W`+8>67Tc?zhy` zBb+BOpVblcLy1XoyzSe>o&{-D!qcrGj&074!~;aG1jETcXcc~vzy0-V;42*ON2XTs zFs4Duy%ML$y$a_poU^=Oo@HxrG48@{oN48c2dt+KxS^jq*>guPsY&T3v`4LZS49fX zP`{Rj8?($I!x*=nP|adNr8x)?X;r(2XVb3Y)^y1$n(XA?10_khh^!n0k#BhD<2ocE$cN>Y2WItswv%t&wg{5afc9+qz z!1!T7VZup(bA$Pi%+SEN-slRrZfFZBY2n*9+-7wcE-<@?M8eD&tguY+e%zq*?~W2L zf4L#cCRA+A589)_AYIMx6`0wLViC-(bj^Ji3I(Tm7hZPO2%gB^=nph4@^;?n!A5X{ zmn~tkyK%jEd%uwffrt?SR{($0Ni*dX?j4;gb{`?9I!OA*8Bv@t+1|H>bzUT+6rE1fY zM571tX>|vE++O(LU2fLskx#+9bE15kP*;*IB~a;t0X?)N2s^uPXKm+#K=-T7?fj43 z4%Bv0s@`h71tyQP#pTWba8R$y_jNZ;2xYrF04h5>o6_=hwPCHv&hPbh$*$|wb-$Ec z)YHk)G}6;5Wu1zPo&tb^fUE+?`^d&EfRc*mn}`a*N_x3|wBi2C&Bz0-*Jrrl^ZF*j zof7+=ROjh&#Cb057Kv2ybsSd?Da)%T?fR~`yL*V7pcO5W`?g8_OeQWfCP{BPV>`xg zRE!UKOT!A3W^_Rc?8{0*SeiMxi|ReLa_PTPoTe(=AykFLNHrh~JOsGBWGaszO$l4O zH>46HBKkv^NPS|VFtb=_MsSSrvP60nKQ&uzX@f3;FLE%sroeKp!aA+E!Sa6uJ{|zO zjg-fZmnUSz{fajL*M^fJP`SUD+iBte%3leo6W1&_!3O@Xr+cq10d}(c_q4H<>Vq@n zqE#D5#gf z?nO_$`bdLx{UuPr5pcY*eI7g(5}50T@dL{xL#kq{bEq~j{9vq$M5gw@V7v)+oWoKD2+_CRr)C^{9fMC;Kr$Y33>nbT|k{5B%mnBE|(2e-h3z?j$Y^@ z4g0OB|KS3t&`q$M-pP% zb!*1T6xlSTtw{%;dP-6F^&`g@JVBj#F^uaOKiq4>k~0^*a-nEEuC zY^9Rbmi^#{y*9s#kuy>Ph<&9`ynakMY}h5yREhFZE7o+uAtmu82Dyvmq}bG{baQs- zS~Z<`G!o#}-j)#VlGGRCHaL)(58mEq3%*%;I{M}U6wskWu49X|h+MZYi8djqrkz;y zjlaHVNZaA2U>e&EHz^ix)SRAsqx|O%%prY#1(x8VcY{hMHptga$Tqz~hKG|uZsF5i zsW^pA;7~E;xCX0n)nFs120Ze*j6tlP$Otwv68>48Kd6mXAYsdeYT!I;D#!Pbz9pYq z7;0WR1u?y(tm=zNh<&5pSqoDw+!}^Zk$fimp}KFucV-z8VI(){3L^iZU5}d?Nj_fQ zP4DZ8nVA{+Oim6?P7IQ?f88S%k78YUdE{@wQi8Zoj6o_AfJ)03)@Bk-7yO558h`1;!thqgO<=GOE@aBR^mi|G9m262`ZFI zBuOK;HsXL}*YqUUrms`g{+sX~{8{z$W`Au|IE@h|%NYX#O|XVR@kg?o*zLA1*^G!m zy2A?Yb9~m|;s^kptXfk2XB z<*csDVGJ*ZF|AIHE9yc;M-QwwDRfLyA3lty46n110bdM5SMUX&p|+bSScsaCVEHzj zTIpMaIpl5Mf=nnql*-NYB_-4dV7cx6-i>mxugu61KJp0!`Bu)@3jpp=ZIT`rh5rNAb_(}Im?0i*01TkpXW>r?~Xf!|q zc@MLrP6i05=V^fQtFNF(-pSsT@Ot8_UR=|p)7*FtvvuemtLpJ1T11I^@8@4bM-I^B z5#=)KjrjU8t@;HTVM<8v*SzhQI2aKz3&7@mUkY2t4qHf>Rv($%sI(g_g8SeDCwJ~* zz6fM49nNpG2UvRF;G(yR)v>`DQV?}>SeLb`obAO<7W7D_#x`UmTgZ3R4E(i3-Z$x=}Bk>vMr=;1$D!8@l*N|F#`c z9m%#B#4^rd|E%p8_kmd>f29A^M!Y&Mn42%BlAT$jPbuUBQI@bRGw2Jj7^=^_hvm)c zEl3jSs%m{3-&r>R#OY>gadELz@T{cpKP|fSTYAmvuT_in%IUpA#HIQ<8~}69?Q+jCIv}o;d))Fz&Z?E;0ZQ59Z%PZ#YP-QB zhkO!9Kc%L-EuN70KHL7!W&2srBQ?``;6~Ox`uM}~%6&l%=t8&w?&~OPOzBwKRtpqJ z<+KL-@m?4b?aFmtv|0Id)vlJ6m7h}@5${dy}RqQHgXsbj~&B@{nL$?@XXQk#ZefiaM+koo;*}ng0 zuO|T=QW`tFw;*T(_^(_(3Ru;@c1Ekpw?`M$fb{&2exUMleU|UAQV$EFDMN{wbSm+O z!b;k5nx)&;PKgR!ikBAE~`N;X%e^09DNxYQ%%C6)|V- zXfLco57mD&;rl}%szS*e4>9E$gI5P}i`N$Kh>aC4+>=jdge*FHMomfX8j74Pp8k~R z!vMk*lg7_F7fgW?vHTC zRB)JH^QVpx*XYBAS*Y+~6l5dbSiPYD2gOsgS24G3^yM1EwM`d_!eUQtGgOgERj7a4 z^I+T4Y)bUd1J3XC;=Q4-s{m!H(P4?o1PGD+&hg1H^{~cSRt zxH6RNzHnmz^roh3lnzqrUsGIQ*Ou@Qhc)ZCEbEwsUb7iXQF-;?V4cd}S6muSyw>bJ z@fTi!hC6hHN^VVKR0_MUE!Dw+t`toyi2gZYH3f`RG16=nOJShOEQ)NusE?W=!XA<;+^DAE}N~abI0BNc~~3zjBa$BbWLxiy~SB$>MmUXxt!#2AF@3Z7?HxrI@ok zW}q@s^ux1+$ag^ww3{V0@$i%pCN2!*FpB&Xr|D}n+nQh;e2P5+IBC&PE~E63Rg6qV zZz?RTlhIR0Jd8I0_R0%T$NYUn?#2Vk^OPx=SdRHxevgac8-?QX0cbR4L>pdd9hqK> zk+Q-YXc5uS%=nQW#wiXQ-vCC%|4Qj^wg$>WNI>Wx23beUoL|c?#{ex3c5E#Jgv^E- zJhUjqMPN-6+NvLGPei{De=C8{U^g_ns;+se%9yUY2AbnVyclp#BQ?UQa2kpsKjwQ) zzkDPQ7^dXeulJ-A%SjN2t)Q--#{dk17m9qQ23RY>j8RA@(DdnMD;Pdu&m4XTmH0i!-e49 zgy{(GPTZu0d_QtU^S_r@d$qs3Bk4im{1SpQ7xuPl=}( zN%qyJ{$Mla(0n1G2-v=&z3>=#?t#IOQ z?kjw(t3nAgRztO~3;L7&>OVdRhbAw*YVDG?`5n2NqH1|7cT?D7b=WG;L;JG^FOI^# z^eE5xQU9jFzCH;N1n6JO&X!f+RgAHe!XFI5*s;y~l=SF)yZfUB<36&FYmx(mIcfn7 z{$Xg#n#k0p>zOoTU6;-_O7i8^fAwZhu@>nr^uyfEA_*eO<(!!Z(nX!k;n$GY*XC`} zq4MOTubsr&e=@?L()2&IGtOuXH8bt9NAmf(L5EHDY)%`bzx_3^GdG7XN)|UyGeKLg zAk^d7GPh$cSDYH*>0cNFqX|kjjWskk_884nOwD~~Un3Z=*R%Y-y|lzB5cWx7j@3T^ zp1V>aOtc`DQz!dVvo+h0wSdX`)cN2^R@Nd6xE0af6X2%S@`TjjFTA~=E&gQGdoqC7 zGnmWW;8y9#R4kmiqYouJAe`j2SyjDG_`SVn2+N^G(yIEXa4nrd2OXyTC$Iq8N?n+q zwa%^iOaUdP1CS$h$Lr_L)K~8>A>yp%VadIZze6x z_Kawez0MB{dP#c17q!fp=)C_*T=#Eu>Opfp{; zwi=@?XGd^xaD5|L(xkwtZbjIT72)Wq`>QN)!k;v}C0#zCwL$S-?V)-~ke!*9e-N zW`JDX36S;xS|ti9s@+o zRBHrg=GPSh%F{__1bShZ%;uzivFOc&8v>%~g8T4jUseTNl)WK!>aKNORN9$d(*3Za z?e@&bM6zn$BuYnLYG)_PeeBZhVX^#}8u3loD(;et^8C%b`aTPmOy>^ak=F?CnbG8& zu;wJyFsOA~H4BRdswuOgZ_o=tqjJMxmi6gLzIX9-P5VsqMTIYg`7=Yhq1kPl5FhbH)A(^wzE*BRZJ27dR zD;hdLhHdVt$(ofK<&f8Pc|Z946cHk0OQWbYYjLp|@?aD_=Jz>R^gzjOdp5~yiJ{*f1ck|L#pCgkBs~(OT$v*| zs15XRi^A<&*k(fW%o9<4`04~k&qlCb0aIvGYvN5 z&%_`M$2d>Gv#sNqC@3Slh4ncA@EkxKe}B90va_@-7@~uyS+NFmXJuzcLQJezV<2p1 zMw8v46h4|<8y^f-pxJ=QIjc{Wz+`-A{v#2nC zXm#lV6spmu#TURceF4YK>=!ay%J4B4`=Cp^&UsgSw=OQ-`WT5VJehi-SkAgm1%s%b zA&un+W+kySMuw$KUgRTBIK8*q{y_QNq-%MaQfDy{e!CXUtR#^%;e2gWzC zu$bbXr?OTI#Bk`BpZPI0Uf_m1#!gfzX+}ynM48i=lwXo=86?{m~Lh z!i&oPd&h7cHF882SUk5-uWv+pKmP;Qi2lpxaG$BBBM79&mdglkffBIXotP1Gq@p46 zec30lX*p0Z@Vov^`@REEfEt+b~KamBnvl01k zzk?>XDpsm>sE*mBpJDR&ZT1u6vjcJvwR8f4A?1y6c9hC>4$8hkXFWHTjB2ZM&iiBS zijKF2aC-})W(VQhOsCAv->FNq%QbF-b~0lvuB8k)Y_v;1Dj=~MsV7_ZUVYwHc%h?ssWg_e8`D?DQAvczJj2 z%B&B=^n;3W&g7x*2>}YnnBiA2*`Nl`5!?v<;60w87xu(oAN<_EIHD0O1|s(Z!U|j< zU}o;oI)+|lhQ2<*8d0R@vE>~yzzTq5ROoO%nr-*^oojJ_zNN3M+|zY+HO9oG*S>7P zybcV0c-{R~#!KPD@jmnjN4!2&YaN%u@MS8liM2ltb8kfMhfwxOl^@>G4A^nzwV0V| zG&~Ml|E-B(ASIGa@o2ZTnk-aCJ@EcviH{2NhCg^nTNpbUJIvQ^woAMyV%=P3?r+M- z5*oYJI%8@U5*1JOmswhx#PI!}R@m1%^;cXF5L&23hrRozvcC&=+ce={zVye5+V2pg zh)u?m!w9_$U zQ&6Q=PDIla>>Y(rU<_G_1RrU5mhgKhTMY`-m|aG5SJYd^w*yh*HqyK^ECpH6Y1m62 zN)x-}pxX1BPRO4n`w4 zCl*3OQxgfdy?DcpXgVjH}?$gOH%oANQI@O2QnH)j?C5A{;m3_Oe?xwZ7 zn=wX~dLbfF8Oyf%l2aAD;YQ|=i|&Na3V70C z5Q32O0e`7dh!9kt=|+k&Aq`kFdf5I_4zb;Sxbae7k$~(|&XcelOyHCrB!Em}EHvp6 zbBC zSK(wu>OX)(cY7nM&4-yb<2uc;txUkNZe-MgxzasUrGA`diE#g^Y`3ku_nI_owf3+g zvPaZ!kjw_-kka!0_MGeY_NZR*Q<3SV2i5nYA5-zO*6(1y+W$;N-An5`o4hEJHrHM@%-AgIJgkZm% zwv^57%wt;&jQfiuJ)2Qi#aVPDfMt-_<{he2RD^cz9Vl5g7_Mp2T#ARYe>oEAHh`|P zkmySA9Z5dGdVX(WsZ&~Qp`P^ZdZL#%`58kbMj?bh0?Ba~Edwgsy_mIWHTlmxl}_bP zCxs4Sa)&x9lRrAPs3Z=drZ!jU2;TA9HtQcfvkzk| zc84KA&-dp`=ImRKlWx#UQ&~L5O>0NF|C#WTB}hSk5kK=b|0T>|XLP1&w$&IkOZ{AD zv)uG!%~=bX&I^I}mo#T?z)PI%xs*)Y_&XTvMK|_3UH;;rex3Bo`oaX^-Jr5v_3(`s zhvd>R?`2=pJ@6)+{C0SSoyS*n*twD5@y>yX)tl>5F6>fGbLUKC*0hT2=W?>Mlc*!# ztqXSE^af0zF9v;%{_v}+O6(*qd&l|@CbetI0KFun>sx6Y2bI@(f|_>0j-jaUB#h0& zB*fuD>=8J`N=>d1oGOoV2m`f%v16iRmE*6!Yr~X${iJTQsGPBq^+tl}Vr1 z)2}#+i+V3i@~+Vl>Q_~Qv9=W2rlZA*B2tJ9!mW3RIa2Q=WcQ|C>FERHl%wcrA8+$! zf?J-rRhsh9;VP8~^(!28HcG^6bP&6^JDpk}=@e1F>g5s+N2C^sNI3fb=|984qFELS z>!I~SXs(C`?W}eJ^E5%iJGU~F7?Kp#yt1oeXF(-QYF&6VrGj^&em0}knZ;uV`+W8R=avO)>IgO`#y zvA3JI1EL|bDZ&8TI@PzePYn$XVEeQ;kNwe)6$=@hbPWAy2UD?0C?US96hpB}lr%-2 z#4-xC*UyEY3H|CGiqs~BPIC1e&rCc2ESnw{(~Ik=Xie&+j0%0*H*PpU+?UL4Ypc^m zL9n&MlD=kP;w*Bkx;E-8Jx5UM^kEl;L5<|^)Jiu^NtjL6Bpmt3aAB&v$h=XnM^^NN zp8QklW&g=!HLC+d968=$~DL#a*1>g0Q)w}HTi+C zpw^UUU>NPeOR{ytw)G%dBv}T9w{K%}L;@31xNC|_SZ@Yq6<##(8s)}Qp#N9vR#eYz zR^0i2L0SiyO-_1sbw)W~rWF^3CnG(EI|r&bo4+w0WB+yLeKb6FkR7FpdT-Z5?KpGh zEiOAbpO^9IP2*9;}s#n#k8Xp%{Olk=*RFA zk-3{#xVfmg8?Tfb3Os7o3dM8RmQuy`jGKle#tgsW-~0ctHo+~i&1*RwoTf4H4uiQr zbC#+yqPYM_;JY@gmYZlJ!SmO(W!SFfyB4Kri;cWOQZ{SA7YwQEV@ou(4i-YNW>u_f=Zp8=EataQ zB_s=cw(0Z{-Q(uj?*zxS;yg?KJu-5?{d=+Li57|GvH3YX~AO+SDsZOXUN|wd@7P0&Y*s`L=N32RT8PVtmDU>87FG z_6<`!m{;h2a+5uG>|)WQW-XDI@=^cJfj6I5k>VPS>D7bL&k^NAi)~%r%+S%0>F&)O z>ymYyJs3Y|#*{6x?jif=`Kty#O^c$0Q9V@Gj(9hl%sa-AJ_IdW+{@`>zPF|ut)p8~j^ zg`8TF1x=<*p-?qxl`p?1gALOajO+3Q$B!0mcW=h+!hnQ^U0`6yuPZ!fqysyu!Sv}q z+5J4;=S&*^DjERKAPl1CoVsw{iLyX7mYp-329!(EK7c=Ef%=0e$ev+PH~(!B^!7b% z)Nk#^0pW$yUQ5-imK0Y?&+f+Xb8zM#ognL$^%2kO>YL~A?1cs z0)Ni@jCN2wILAxj=l?egHZATsTD0h8{}2BhJ^r_3D7;7IVUFi^zHRJY(P+3|Dr&y0 zCWfpo258)2O794*7izyZFu;fh+l=-IQ!IEY71Ey-E?pe{jCO91PASZG1Y48#ZP^O9 z9|dyj_-b%XG|5!+#s_u%3GjZ^z~-xB-PhRpQKv%@q|6xFHem{Y1v*|{Z3~tg%?`!@ zSinJwRp2OgF>C4rXLKGuJB8}!^;bh@w0L*a?bAy-Q?gfwBeyz@YCr2#zxE#Dm1xdP z!54|n;iv46UYB>V-z}u2?krxS2E6&gMdsDSYS2_YrU*@v>g!96;qFM(oYV;Q-5rb^ zWt_1~TV&g_zIZSM?8QDv349gpsCNeCnLd=5CXtWm#A_ptEDl$?0)4qJeXp}cvcMd6 zVATKh+yK|p)qyx*uJ`{M#^xbEqZnqwa|qniKVTX{*BxQPbCGZ~Lxm4!nx3i~wPs@w z&?nGN?f;Hr-Z_q5QA)Ts_0(fh8|RhJm8_K8rCXPaexSGShUv*6sR(D8Z(uD=9Na&2 za$Cwj%c1(f)=y`4v%pXE=FNFIUhMUUb}4XE3idiNvqB2xc5ZT--dYPvuj3*8Ix~h; z^r}`|fgo{->SU5BRwz={$(A%oIR|Uh{=X|ZCOVjlqmo=fV450WTM-ixz25%v>u@@l z#L@Ro`2QXUzYV_+q@*DP*mIb7kVb7}L;7wln>6}nw<%eUYYI_(_ zp2oJ+wl2EV6JHWPugga2*5hMm7Sir<{kfv<1NMV)c?HSaZraVv+w?+u@@~| zB$gtDUYkrN|8om9&7=2#`1hMvFO|J?6V*V}FG%N|YOi6Za_ zg|m(S)!A9bMY*jF9~Bf)0hJJ?q#LA>Mrou&as+7!=^hLkk?!tNkZxv6rz0&bLrKSg zFfcIh8up3(oPExhcRs)kznOd1z2aI|{#O%b6MjDMg6ES#jqQA^pQy(+gT^&N%3qtC z&5IvJL1E3mEvv!^&Bzq-JltKq`|;Op{9BHdHArLf&*JZRc(M8a*w3lpJ#f-?>VKeue?$?Se>?S89x#&-sfMQ^<}VOZ$&)1Y(?io?A}C-S6AMZN?9&x^_1dhJZH z(BtJ(XurVLWYiid3PCzsbr+XAWwP1IR*}_2of?!7U0yHM`ZZh6>b16)%BcqzA`|Je|HQ616fjUUX+9+w zHhw&o+x6Da)S@J9K(@B7!|J5y>3(PtxruXQ3Z^hGym^0acU{BfMaYaGQL>=mxec4y zr6mRphL9dG;bUuw(Km4qf$^tp=)!S%#7D zr(ua3)KL1$l^DX0g@>Iu-r4|x_8urZO zpUJP9jOdGOGW$+BmeY;;*j#)qTWe>9_GsL&C>=Zc!>)PHs8+*wDhD|w_LNAU?V5y7 zdeqC@(3k8kG#(a3k*AEsE)PZ|wmGWPyCd+%UYSh3BY#jZ`aI6nb)dfqQg0c%CUJl( zupz;Ea3F=33I9;dko?>MqMCC4j{E(>rDux#ncaj&=3VacTXEj^6e*ar5UA%tV!owz9>0sDzE^CCfM$%vMi1XV1|= zR;5M7rT4trE7jo=BRN@rP|l{|+l-~o^735G6=RK9I3Lwgv!87ovs7+X0r@nGJ5+1# zO^IB_DWG0l@?7lo=!2_2fiA0+KE0d+O*SbH4luL_;o$D8VmN84%+FlDCdL2_sBlEl zI3QiM(ER>=rq+1=TP=w_wEDVRc~2!6RQeg$6iKefN^E?NH9DEQosDKtsVz&4ds`%9 zBwxagC%$Vz{7atqQEmUNtpuNn&3kGV^7@N!+N8 zfjE2htzLK4DMPu+;yC@Z4BU>LI3w@n*If2GXrmBnlD>SMkf-f^mA$=E^G+&9HcKs9 zT97H5-P22WE#QC1UYS+&x%#sc{YPq3K7Os&1yIOS+)BR3OQ+({q)gazRWpoN3w1A= z^gwPojTCur@ovn1EHqv)HEL@Llmux2sUE-vswQI5QGSFjGAG9~>1IU*OD35qLE$SG<&| zn?#s-4a6GT>l2!KFk-eWQ#PyMEb|0#3m1;>DPug>U34IxzHQ@9L(?A7w(N+k=ns`SZL7fS?2DMkAgY> znENgzst=VQSnG6FT9-#PzO&<36WHGkC+=6A3cW90G)fA=v^5^w*kF%>XzHQqNTwJh zE7HCxX3dA|Qiz9JXti>&C~qe#)d~e3I4@}Y@sRO~`P^-25@wq{PMNTc!Sv)?muf-u zvq6{J2R85c^QdBYC_?r}N{3QsMA=#NUIXJtF`0CUq>%ZWEW@BZr`wr{(eLXcAhPT8 zUWW@4Ct2uE?=b9qw;JJ*NyBtfh&uY%v!47bV7&8=k|KSv&{U9OONf~Ex#f*bLDP9r z|B%>j!L2gR@$5XYcN=DtBv5$=rFboomgg8}n3k>i%EmK4Z%r~AW@_2YCJq+*CS(>^ z$oa654|b=EeQ)-E&($k&fHIS-OZyPOU35*EEE$5A!ky;vw3Td8sh~oLKdMa1*(N|n z*9+x!r%B7Q`hL^vL8g(9(VtZ;257@<>m7f5SwO-iA%M(-M|8*HqE>EvZqA{`K*(X{ z$K<5qE&RZu=B$+n9e)G!UU-j+PySNe%&Xs1()(+P?ZjnS_F0r81H$H-mMKA1RM5`~axHMTl@s*%$#!ETm0fW=_D{++i;QV9m7O}oqgzS&|I_kJ% z&3!M;kSy2Va>`f?JSDKGKlN?B^+0f!JpM9Z1^wwIp8%!7-_k`vMWFXmPqb0aXl&;x zO5Wd1Zp{WF)#!7Mv2k3OPfBQhPMv?EZ!xl!&$x5Kjk?0wfaA3~?zJ{jRH$;(TE-@@ zx8guXC3f{uY^C>i5zXp`-BLxH53hp%rWpMX^=I{tjxeAKu-kNyczLseI*Co+YyBNp zQ-;nf`KIJ4WusxQ2kpRAK@(&qQY)UtvV~Sv3g@>K%o3Qlqi`aDJV0MvzLTH9VPaQ4p(O!Jr0AXB37lR|Y0xzom)KrbU#@if zuBS)_E|Dn%*2`K8{UZdo#Vo!y75rq%7&1&g;!d`8W`SGgR&Fn|HeW`2lgb?L`}*Dc zAf`#&p1$3{^}^S8##&`NNnK0piC&5&c|12kwG1Qu`vS@jsseHFI2SIwK@$7bXqlvm z&8O2bQB8#5n+h%hPWIy|Uth!#$<7k;UT{y&NdfmnSzB7;x0BwJw?XH&9A9EYPgnMl zy3PwO*>-`eb#j4)d?lG*B~Ai$q0$kxMgiO?`N%thr!!5Hm3~%Ze*{?ZlnH>vI59pR z)NXpR8qcoxN%s|(hJ~`bozX-nqc3GT1wwyHzA47j^9EOyG zWU$_iz#9{)1!{|j-nbJN`>pGQS4_}Ti-%W$%?I&#oNhD1l2^!!EEi2zO(Bxx=#l4* zF!ByuWn`{0%0O>8yA3Fut8B#E%#yChxIIu$j;w#S-=61JV#8T-P3xv%$n>2kNeC3R{PpYC%=3dnefi>rR{GY@ z$|J{Lj(iwbVn5#Uw5x4&PPJ0<`7W-gCAy}GB-J$9Gh8XiG-$QpqUF27S**Bc%)p_z zgC!)i2np<#E6mZ6y-6r*HHnEi_?Tx@D70S{U)pBKEHbFPlI80t7g9>!qq{A_CPpt+ z)`jz zU((nwtoUwL=tjd_Jw?FYJLa)lsI<>wMUkSL>~OyaECRg8X(0eN$H4k&f$Mks(8ghv z<>0Vw4Z|OQGoJ(BdpF0Evgt)rKiDOK&8TSgJuF03*BURVvT0=6%MBJVjF;^9l)N5n z#eWc*uqs-5C%!Fl6@S&>d-EIKWJJSben=KoGR23|n4HGGG;yEbTXpkLKs#FjHp=72 zjiAE2tE&spi74KTtj$!s>@G8I0??GkrY68iSx87on0AC2i?BhA@U&?4&NRxGZOV%t zdh~XR#QDyrUKV*Dzon&1%&`TEu5@~Jc1yuCS<->d@men~1Xbks@82&eDFHug9p_uI zMc-~9IpW*5Z=&1v?RJ9Ypd{|}_f|RV+X%4 z1GA(iw7v%K%a2)vzENzsv9bC(9jG+>{n*3fi+(jI4f9PJYU8_?pHmd%1A##3qO<&M z`BMP5>CX20x9S*LC)23xFH<_xQ3s zL2{xUaw7}k`=Et*u+^G#J;l#Tnec-DNAclR1Sr@CC1sdNIMf}GJalw)1cZbnfkOma zZ&vaje58AUS;ti#%YuO%a>EB4{uTWJ$)Yqy4w3aUOQwKQC>PDT?j8k7BY|d;eSkRI6C~68!Rt zq)@64Fk?<*5`r!P|MA>y5GAnkbk^SaSy(s`?cE|}+G>|)#P$HkI{rXvJ*FltkE2yp zp+1tCh!dDMc#e{ny~1++EoL>nW{Mx*4nRe~JaApw^5ZfCK&-$)Prtc}BTXZz_0)nc zGDQ$VxNlBUptE~YYFf(P0EFtzphd?U?+}_agn#fEee8 z=(vspJMQG(bU*cIMN-{41gR86goHrSO6q*nYzOEC!^9Ht*|#d6hC7QFr-CRR>;H0Z zP@vJKbx2O#TcVl#Wzf7P&?3XPbc36VB|JwbtQ(%d&AC*^Nv_9mW3b=;5?5LMvDutc zlhM&!9G!o$r5EE^4#x*6l-JPnx9*?FwSIZjW&sW%;5kbDLo{-c0MWSq;S@`hw4bU5 z6bG=VL5&Wevkd#qOixc^L4sg5f+Vwyj0~_gi6o9`?X{(W4*Vf;YFhMFU#$=ct&Mj4 z)8n%3|>ouY0s8z*L zkj4Pq&^z^ugjJKxz(We;2n?h%*yIviN;ga&+V{474T_-n>=l=ZrkLDb zX7ZKGBW+ESyl*s{!flnbd$X%XPUF_m{Q2)XKoLEFZve3gpsWltV`DXDodox5ybsrb zFAOvd zhpUt35IjR+e!w}7YxUe4IltX)I9WL?CsdLsVdeZJiY2IGpSHIbo3`%@7!SPnZi38U zuv=gS(Oym7CateK_ z>rQ!mKST-wqo-(J{+Nza3R^vQqK3OrN1Y2#A;sr^BTqKckbxsYHMNkwI{T6d-&jJlYJBjw|L56q&by2n5p~?VzX_IXmS5X)@igDMD z9k7fQPA;hg1_lmU^K)}YPDgZ_p`$v@uH# zqx&o&^K~i&nz%I4Z)-H%>ujsgjb?oZpeL z!x6~TaTcbOMDJc{=K;{Q?;D4}sk{^f)CDvYv(O&ANW_@CJ(c@p#XqFzmW1aYXF>%U zT^&T{vXilc+7RTx2y|g}O0&AscRL;fXB<7y^6QrtV+~P@ec38MuN#^yf|Hk;2d@_r z5&{anuC{huTpY+*0OAV3MxCCxH_cjBQQW**IyN;ivHDurh)b_6yEjV$fI=cB748a=}@L8*SmSF#%D2Mp3r~_9sc$GX^qhEy~7y~>ti#}Sh?8t+5 zgD=CGlpFSlzugG1x2mcicNIX^my?r|g9AVvuYsi#Lx}}d_QXpPq1gMy zlaA&GhiZ^95+^EZ^zP9OaxNAUg7asSUJ{vVXB1;zYQ3b3ji_4bm|U&~ROn8qHGc|du;N9g74 zHZu!NF!+c|l@YFYk3fwW&?uMjd2Y7D;+rm34x0Shd+DmlwAU<(G;;+0p6mlWD!X5= zoM&8~-k%&-#dJHZu!>aYH*%tHFMUUfodInHy!LJGR-8>h7CRnPnx=XwF^T6mBAtJI z6`Zt(w&BpKiE#AaY1&Nlhn$4oWd;cin4B@78NufLk9QZ2pY#?r{r(k&qTz0&qHRBO z>4gch`29}jJ0|zzj>SQ|u6lbh+f7$+oEZ{35&vygKg9OI8xHB8elwY@l9I6e{7#3u zSNthJc?Vk0yLazEPcWjNu(|zx-itfIz!n3THpu;bC1quxEWKu-BxH|Q3Y5v#X0vKp zc$o(7{)QZX!yHGdmcf|K1lZJwDk`E)laA(mB2AM3e2L(5|J0&SF}_7_RsR=-Fe* zS703i&=eGwIlaBL#mUXR8qCJdE>5lX=ft8yC}uzYKa(nkTa}7}f|ZR8QR^p>X;Qu# z{6TK<`*-!WfA;9U-;Ct>@yiwy_-7~h;9z=63L@Cj(sI=H5cB~MUsseZK-bfHJ!iX8 zDZ5E=1PIaP6a9lz6hR=t>|&DMAR&1Nc)jiSJ)$@j$-^;dBpmFhO~BdF7td~WYjVpy zbVx<&K8AgXyZmKRwwrQDTmj$l=F<}}JhA*@Vn$k8 zF6*PL|D1KLKwAP}`=5kU!cI0r>aqfCxf8!9CuXLONX9CdMP`{@gM_kWg zm~)@C;ruE>=;4?ih}nYeVWHqzD)j77Z-`k9f(F3s@p4FJVxpdbfj6LJFR1^=9{Lo5 z8}0dx52K+e!yvH$j~COdOA~lC-n`$(4pe7Zesq!~n+$+(Q_ZO2ML zBwh7DmN?X3gQyb6^(EgBAZ7y$-uUuYO6YpyLVEeI~z zlRqDe(a*Onlbvz+2sjH<2f<=xW@ZMKJq+YI4kgO0C$v{?{`~n9cnvaEyXgIV0r41^ zawa|b3N$pKPXSvG;oQ)+ushr2A!PT1_QX zrR63qeLHjYPPq*Y`W>hda+qoR)rHx#?!`x`gEO`53&4s0GZ;i0W%oAG7#7xtcW5xH zO4p;IvAwQG+r|z)&d!>pV_{ABnst;37WB~}W44DV-yBD=rwFz5$AXpuvwkqjz+QejJuoL;rH*vlKjHL-~a)eP^(ppd^Vj#Z+1Y)kxL|Ev~zmvH#ZFJ zyxnU-JwH0?irTku`r5GwsBtDeF8Ljf$SYT_fEAd>GH1>IF(P8`$)AQ!!ZG zn`!ksQ#BiC6q2JK>=U}j@&m$0tS03XFL;a^oGqN4D|2&S0)r7bTeu77{&sRXLhf$p z)8pCD(9q6s@_tT}o>ini2bsh43!Z>>Qz+2HLVecb6vRX^k_4oCiiEw*lEnC+z-S9> zMXsH7wYA%GEvLzejL72%(5m^?S!@0h?UJxCf^Bj=14M*e?!-pTq@8U|}n3z$#fK>GW`O+nR?--n$9>N#F%Gwnif^0|aFBJqOMfuRVe~LaxSu^JwCaii$^{ zbw$O+2XA#YsM%Os6LXo0dF`9GoyO@yzJQYzfcp(vS5@T#nEsU!R{FntqUq5dgWkNX zWZ|swN(P8kQ9if5z2K!87&znsIKY5-o^AC@&GB@w5tSq*_E>yKT0XL7U|_(+#AH^H zG`|DtDv2L?Fnb2StTAY6Y67!pey6;+`1T{exOU7WIJn{3`>)#ly|K?_t&D|p~JMb~?`uh5lV`H86-3@BM3T~5}JvlqqX5I#egb1c?0dDk= zb=>LutPlaBn9kF)^OF;As9v#lm)Rqz5LtNfm@T&9E=o(pG*&DRMGhh(BJ3wC^8sTv z%oA#J%BRMKJZ`9m$Y`=z^>%VU{~}f2)dGXTDl20WJToRXIN8~i1yY}|wbs=G)tGeUgZ3Pb6RFoP>AbAkxl9{8;7FhlzR4%tzbw)oy-2dFy)HGWu zzoe`TsE+p|Nm6f~;$_!7k)l2wzM2wIi&ur%Lj`r;jdZlA+l4Zc}qm8L&3x1VBj{uG1 zGatRi(_mDFRZT#dgCroE-T|6}dy|!4{=^39HZXR69~|s9>6M#2JIvWwUx%J<2|*_o z_q4UO^IWf9N6<+4)`OTCaDjns1wV3PboAix@cJSM<0>elk@OERXi4&~=cMeqfZP^5 zPen;t{hUL)1Kz^EM5uWi&ZFYww0CxTgfh;AAAus3kvkA$V?U6_y?_F7SI#}LOt_U` zs#YHpwIgqGJ&NDT+`Pk_cjyiV4fYobnSBW9a8X5t)3#xro2x6opkO6OsWw|fU_7XK z$vE_42g}2_)=nR|ufg82zCOI>dL44eyHNtf^|AyJ9VH=HvdM!h+LAx$X7@moS-83V zR}2XwBO?>z;~QqTUkh)#H-(3Vp`ntTdol99v&WX@vWm%uq|l$Ih9a4 z2v(8VYu%m)WB&d7YoJVmdQ{+C1U|09#;)`iXryG%W%Hnco15Bf@i)jgcv1(!{#->}*q~;iRjkd($OvNCz+V#P@8m+W0vwdLuD+McodgHt z>~`+(hH`Uod_cZ3w6ajo4B~(;Y z=(3SDI|ql4E@CLYJl8uN^d^;_Q>V8hJ;7CqC(eC~k`R8Q)oWEj=0UOFSd0S zA}Ze8DxRwViTvlgLWM&zci3;!(2Sk^d2tP~da49GZb>)&^^(C9 z7#JA9k7OYU6Yh?ce4@mB``TUzdGI>u1ruD*09s}9;|}A$dKmukK4~!oV|ZZ;%^vpa z^Fyy?X#}awaFQw0694pg|21pU?teaei|bO)?crNC2fGXHz#H?~q$Oo~YkPa#njhHB zNGi~^d%?t!o|=2rN7#{&Z#F+lz&Uu>(j(CVj<4wN>jR3VFeMP|va+&JnB=#;C3w`Z zBKSlv!Up)95TKWB5SOgu7Vdf^Sb#%*Krp31oh2+RjLWz&ZufWwV!3kKcl-pjyZEnT zvcB|%L-|(&6gzKl6m)}N!lc+MTD}(utU-t)bx8$lS>fQYya^S>{>H)k+vNJ+I{*LA bO)x|{OR1L(?L)h<-=-+5`m{{u#k>CpQ2=!C literal 0 HcmV?d00001 diff --git a/test/reports/2016-08-29-fuzz/fuzz.tiff b/test/reports/2016-08-29-fuzz/fuzz.tiff new file mode 100644 index 0000000000000000000000000000000000000000..fd11fd725d8675e515abab2552c55e9d46d244c3 GIT binary patch literal 235588 zcmb@tc{r4B`2T&)!i=$vee5%`hY+%xv1iMcEi@uqNcP=5_I(RcmLZ{%DEeqMwn)*~ zN?96fvXn}qRFB{H*^lG-^ZDyOj{C1`JFoLPU+4R6Z4KlBBIgkV@I=7I3wKH5e^GQV zW>rLo&Dcm;v%yJGAkWm>u4_3;PrS(7$6iaa+*+p8;=KJsp9dbtDy)1Rv>M(7pQy6& zb9lJCn`l6_^>@^k;?Fm0un%y2bY7$SRI_8?Y3&Q3xjgYbAzC=(6XvSh z=&X3JPl;7jC|aQOZ4pa`)}^c zUP97Y&0kBKErEaCdkmIOt^Io@{qTJ}Kn3i{YlZx>=1K0f0F-k__y5 z(-=Ths;Ql_B4}FNaY@vyc!xqTFHtL&I+?v(Eo<8TBwTc`xgi-p6sAxp`GOo_)rM=b z+W~NLmLw1#dg4BQmjPRjCsK$U2``95HfpWk2pSGCbU&CiqJD5cvxAUa=CY1u=%n$E zrz)o=F(-F{xXd&&01O3T+o`NSJSJnE!RKAyl-(|Y0GWtVfh+&H0Hj=AYiCQ z08FHaTXRj5fst;H!#}GHy0kzT_3yI4RsRSLK}KkTONJUmRB0>(L|>lq9L*?#k=tD% zTs{{kyO`V3x5(;eE7jkCR|5y0NmKF>FsoddyA{#}YkL>0F_TZLPr3sX-PU^C$A+gW+o8ejkEQR|VmW%Kd8hBwFA zCNy8GvGbTuSOm0iF_DTQ2rPQ9Q+?k**ct-A&F^i_cD%ZiH@ZLK;PXLeAe8h92ucg) z0pX8!JNc<0%|61+1)Ab4b%QTvFG9>J8NLvTWrLJL8?pz?b(3kBk-INv{VFnW;KKN% z6uYsV!wrUSw^RuUWO_wX?dr6>?*s`2+VWQVb9$ zme~eSX8?RRb;jQf+1L6YU>U$5GA}?cYGz-UQ8XFwd#_P_I|}w@{AavtkU9-`{8W4$ zF&8zk5$*aW{YIeMG~d_syF;SgNv>~yj755u>nrB=G3}3jfyiVrAU?Q=Lyqe zdFQBrFKt1pHRjY(CA$U3vi}F+J0bg7<^^P!EkM)2sAM_~5V?^62w(XW`e->=AYF|g ziQ4LlebjO_q_%neT;uKA8+ahkbW!-FZ8#2@1#HDrihv#(+Db|6F{UpXLonsfCIAj} z!s~?Re|K+7yhi1QzNaL16)({*6^T5@!|n<{c_(%Z0#hDxI{;-e9x-Qj`c0T;VSQq? zuXhr$l*Ofa2}KEYTuSlGYrreiiZ!(z^~8CDNz>c`;i~_e%LE51pHleledkGA3t)!$U)?#~u?=&)q)6UkO(V z9&O4T{!)YJjVZ*X-Dd#05LVPbU$8;Pv}&um?-4*e67I@qp1RuSc4<3~^Tvq_oK=p* zOYIyI{p0+)^XOtt2n0jG0>E8tku(s@3cJ83(cX>S%Hx9Gx@o_nU&2a6r2U62=cZKv z{`$$YMMWvwDc3f2uJ1+mZqFWOlt(cwYf>pRpvBaZlGE<4`COAvFw12Lmy!n1#kQ*R zvg!qls)Q;+lG^2IB@BfQn90zBM8wT9`&K85vB9HnUtAD0lG|Wgs8S;W{wr3BA$Q&g zJ)4eMuOI?J1l}`b%B#6N?9G{%mC?tXpFATd=m%o8(zF{TpI2g}&s!SBA3sm$zjE0m z{^gG!TB}dqRWRQB48NP*F-3n;B4U|g%x>hD%k9ejlFx9`KBVb{U&lJw0B8HEt{@$k zc>h@j-Y-rPLaY@m+8#?87hw6I^(=_+@E(8hfi@A;vG=k8*w4vzuk-9KrTSU;l3{iMEBb`p^aW81WaI*31iRpN#1FH`t|~Tp?7f<*%u7-YXS?#bLBPwl85L{LRK_ zI)>xwIvEC{V&lLPUR^(tV2zbfz2%ow&=6H>ka@ zGB|0kYua`Pz&2e5xp>9S?yy;$4y>8jwzdrv^u4~5Q9SN;%6VfTL^^W)>O|;w8;8Wu zTk4~arY@*1-%>XhoRV*JTz55wn0V{o_&0Kv@~Yr*00oVA^KYEBypyl|Ab&aB=n|V8 z+kLN?Y+~9OzfzGoJc^A^;n*Y{Xg&v-CH40dGrvlS5pN%hOC@b&(*Q5xdNUeI84!vO zQWo9I=7WGb+6|9^;*W{-QC3i1I72usu9W)q>>ZU3fYgks2mT5c#AlmBw(t2#ZGeRk zF?-h5x6qlB!)sPAbC91vHK97@TwB#2d{+Wy!~Ls zZ@iC!LFM$&M_V$MnIgnuQ0axJ+2J{HU)o}xtvL0nk8~=>FK$>apHDDp*aA-66M>kl zRr?w)e1O?YweX%M-~+nS+wL7sQ4`6Qkj~N5p+W?W+`)HGVk=sC`|PICpaJ7Y18Pj6 zR{(;HLa1t}UvqghNriQ#Z*THPn2W+n?w$&Soo%MUMhF%MG8yE40LLrV^eNj}kc^~R zD$a18_(RYmuMsA1{Jk}Wtz07*i?qI(CO{ACvuMNhrWN&EG;j&d7zXen<*5eQgbq3o zMM}hTN@$G$m86xjqe2Hbn6pw#NuMb33mOXEHd_+bDycQ2-$Yq}@G-?#D)g`JEJ*amVMrjH#U zpptn-jSal%Be)$m3qbcY?6h8)zz5yQ$)_6+MQwg0mCmy#S>B5!g2k$jE*BI7?m(Vh zMcQ9HH~yr~XPNJdrn&>nR}HDGTHhGzCSYny)l1*&cI(N~)}kuiZmFneU(Mn{{aIx* z8+$Ob)sl&@Z4-S`q!T{(pKUa|x#84@$j^V4(|^)n*LMnDZflc8Vysb7U7j0rbywa9 zo^MebSh&YyIJjMuFFRw{=D>oLWs(2#`j@BkCIKqf$)IQrp@0< z&Y=aeESX}`-yqET|8A`Q3FSu+0}JuP$@Mlmq(7jD9fsv4kS$?t;_##I3%wS*DS>caxZUW(A(>mpYA1Y+Bj z=>>w#G=kN49K){Wf^ribOc8*&abrIdpcD-GeJ+5s>l!yL3^(uG+mg-`zplnL^lXVS z9DNQg2mlWuunq(v<=S}bykX=NR%8$hH3}INvvbUJ=dR5UMd=@f?z@~mfYX0>nE&e- zH$M;wKB`~F`T5VO9Rk@sn7WF2y*hCoH%nVV7v;0Sxmau~S^8prf58&&^3J^;zO%eL zyEXpx*zmj$6KDT} zcQ`g0O~2joeRC(|?J-Tjo-oZCv%}RWgw)4w^B{|D0EmDMrU=ovJwlgP&T;=d78ODL z`*IZd@#ld1 zB_2K;OOWI+vqeD9Ig9Bxdta^Gxh>ZlAg|=VIvV;lBv!Uw{iyHOQEC*ejRvsIOSUgq zl^+1mh|D30*|aN$z26wpe~iI%kqSZxxOVO~!ub{f3J1q>6zW(J`=BF$EsFRv3oq#V9>I6nCL>tr*5t zW*$?5;j$q2B7h|^*+EawDQ)5bR;iqGLE4`xJAlCeA8Et$Eh0U`W<)$9uqupg%;5MB z1OFTmo`e2#qud0EA3%qAbM#D|83_+w8S=h_9+P%BJU@Aoi1X}>Np&?QVksL{?gf#p z>g@T)hGdvwhmtz4s^*RcKB!!-h2|AuVtjeu^hlS$tErf`Y0Smp9Je%UiJ=9%{xXOGhP*-=|#2*0F<)ln6m&Y`Gpc5XhS8x6iw|P1K~DQ zAVlz;_c|v;EDeN7{tebl@cPzG!E)SLg^^49h!0RwNureIXE0g5qYSnl6c!?i*U#BkSkZ`SE<~brJNLtqZvZwfY}SJ9w1~t%>xH5aIaP3~lKese zcO)~8>|smZMs36a>{|@mg41m{8HwDAhV-Z8mVaEdw+EoW{ z5BJX39_nvBs4$Noi-^-7Jv+Z}_C3;B$aPEYar+T)#_*b^@z2vgycNp=oO(|xl0JGqN-XHeA3 zpda>te=4~2Uc`eBmXkeJM3`0SNXQ~qcz1`peE;IqHCKj`_rfy)-bQYX7plZptYpAM zRaPbC@Z_%wc#YT-Q1)aXUf0Zb7^yI|XP}WC+9ok%RY?_mlT?1fbRj{&2ey$M{foy? zjg7V*P9(yD4*2UpSWga`Z;hMS;NbN*fd)&2C$L6qQ;0Y}LL3*n4~}Y4NWii;!NqZe zkzxMy6T#7}=lD)UvHy%i zo=7rd@cL=yXTvB++B`ad4S3B8L4fP^8wcnaCk?Ptee>S(s9ct8Bj0+{%M#gx;oj!J zM42;F`Rn+v1^n;})MyI4RR!wkIF0)j{F>ZmaC}+3HA4+8FwII!2&r2XRwQ!&u=+v$ zX#qmur1u)p%0NIpyrMEfz?duAJik?+E7~53nD!(1vJaQT92)@0NQp5^_&JUtPb5N4 za1&LF&8rSBul#@Q}&K$PV1yH zu78tQ zRiq)oTqLhX;M>YP0A6@abVmMnHtK}$5wC@F>Ry%QN1b++#hlc=%k?NtaJ}4Ww`&M$ z)T#5@$>$#=-@KabAD4eiBLaUdzFCKIb0*V&aWwYCF$?xdGh57X^=JG0Q>`ZEJcM!pML3Q@c;)*C1bhllEFW0yh_L@E4}!=AX8bCT?^2skNZo zPDNB(ka>z`NX6<|Z}|T5ykxMEKr@m2w{22XN|vJzQv%g+SR|pz#A&aQB<*orV70?a z3vZpDW zG6rC$&3UHU4IkV$IRZ24a2e+H7!jEoNj)_(Y4kD%< z!(E!l3mQ$u=llu08Md&n2#TaIIp!mA`bXw_rPJ_I3WT_&$-yWL^vdBbEaD>)*<&nN zvL1Y_8=u6y$|vylUezYle|dU-PrM~*aA{Zk>5x05AWf72d-py*4J3FBy(Vl9EU0o0 zq)RPUXR}@$P1yOu*7v#OYs_MSAwKF<6XesgYf`zhSd&JiD#xZi{F@% z=AMQC&!dYB%O(92USfKmvQK_`G4L+&+Pgp3--qSCzn+AhYwLwnMO~Y_$28gJyC}vv zq6^J@KY#JWzJGec{$W+X%YPB^3plp?pwq((sM?4R3%K5#R)RRs+p{=R1MzPXSdc6e z_fK*9gDU#pi78P>w5W@RIO&a^-%f_vPXEuv!IM8^Z1IR~x(EP?4>IC9cxAO?f^o7hdIo>+tZJ__`NHyCi3{1Oo5G9?1`iYg#++SY-z z)&{tlNDxpc0)LwTIQ7<8njav_3mEX}*`o@{u=863uSVo&fQX$b4G6^>P$Ag}H$CYq zQcciZt_xG24)%ezd+SXy8!E26htxx+1MC(V;utC?g4xWLtRCn8ay|TkfwUMEF|OPS zSf`K0U75L&d&21W>e@@&XglU>QXIKhnCdvdl8)JEU~EBS5)Ta(hKqLKOXwoV_$0cu z0j2z}2{UgEu20;46Sn{%1|}S0E!z$e4jYbt0!hC5G+^Rv`@3~E>G6bCA=Zb)SE{O(MVCE9$rBW;s6+mPZ2kg2&Tb6GP zu%+MtmvL57O1EA1lQFKv4Qn2N@^L&-L zx+-^{xR`;w{sd4gLdJKK?bJMkb@3sg;M^z~R;vRL}v9@zdBlvzJ#r{ zE99!uI~B9PL3Oe@szo>Caw5#1JhP6pd3R*w=I%^ES=E-Lqm|+bWEly(0o)Sj40FXt z&W-+>p}}@M@)uRY_Hx4 zFX&j~U8kVa{I>5`J&jkjY65e8_g`&Pr#{@c`kCkQjawU!wUr8;1Jrjj8?|n)Ytw2@ z&gI|Pj+1vPyNRDx%Q5>Mrs~FxR0bY(yjf~eYDQ4O#A(No9l&R)x+`=JiTy8|c42HY zTnNCVjW2)IwAj4nPi#1Wb)cD|wzCz(0QXcV2+q1r1ZLSqc~;?qS}fcSf#EHx+88eg zpv28zB%Cgx!l2+R?3kD=1eyfr6aJY^{76jpoB5geFLP->DYM#w+Ef6Gpo z{#MJgYZwRu$+S?QNwx(zWiGmiF*=y)CmgsUrg8R`9CU5LA;FvPK*AH z!K5i4=|XRaVB&lRRf{3dTuKQ5iSZTyVOmWe-;x-H58cuGv$nA!BQSKU;# z7B&IJ3Kj%S^2~C)VggCBfTbsG83{a*5a{Q!b`NMjEJ>DLycazpYXiYJN6l95&TQQF=^O<+L+)I(pMY z$LHFU%F9~(sDOHt8%DE1K`(Ms>s#I}QOgQOZt2>jDCoQFzrb#$$uKVl&%ELYNh!C-)I>h!jLznL@dji{Q3(#1XIp_Le02l0 z+-E!|-rhgou$tgHw#_IW@EA4-H+?uq*B zDI3=133lF^a`(%R%D8;%#w$M-hlVoS!n;B@uRKH%d^C^URu5^N+Ku4#o|ZqJm3F&aYLD-@pLKN|HGX)Q4Er7Hyz+g}vQnl!Rq|R?dn76pP}fJ( zx^#jJH(DCaHFiJbY^3@sDGmbA!1{8-)qLwnZ^eqEFr%ivJaM3zpBqir8 zEotgX`**k18@bdpq2f%32%diT){EXXarYjnxVeY~^HYW^a%o96X>K9sF~-T{S0X?; zgS$69rB>ujt*@-l=WY%82lMaaldkZ5M79u(m>&@hGVx;oPIJ(KRR$k68PODffNU#Z zKYXsM^KttzDs7Z+*fDWQ7+wNwY+ z4(&VMmCS>;F7{cA^}oS>qht5RsIr;9NC^>FiT7L?w?aT4=b30fggV}>_&MLyV_XO? zDX|GC)4rh2-D5wH=kEGYA0RwJ31JeJ3B-uUQY1|W>@gO zF#>SujdQ}6^ta~TbP3&B1g!aCN`K}0r+%uety@&z)Jm`CxmOq9k$nd3;sp>{=ABW; z?~l?K1Z06DCOs2$IH+ce&@pNO<(uY275*>T=6Cu}_6^>yVg(9>mrhS;Q8m2c-i z)%kep;vkEs>gfi)@!4GXY+{Hi-_O~yOMIMA6+ke25`ZnD^rahM+OEPvz!l^rNc4#r zUi9-%DA%H)~mCr18A#1Uj_)iJ>jfKV$GDaUAg~$`)X!66N z4l<}LaW@T{xfl}Owb{5ZWT=Ym6-T-NrY)W;G09wu3C_7+YfcQt{9HYuTY5HeNzL~f ztU)cK8NlbG_%5VeT=c&_2E_^5CtcX#s`6?@cE1J0)tK{9 z%{c>E&kPnBu)GF-eomG7@)WE9DBv_hc?ne;RirQJX~qk02eRT$rfYg1Qr^_>2avyC z_mDaBLt&(?`lEBlGeYeVm3%<;QJVizNt_R|Sy#q~RVJ(-0=h`sX+(vw%;k+*mzWHi z3rmGn30)nrtpEXTD2JZOl95U?H<C@9rV~?wP+g zvRRPsoEb5qMV2@)v~_z(7a4elWXuOZbOy~00wm-xk*0HSIsR+ztV*B4@jfdg9~Ipf zD8u?K(y&G5HtBmW8=+UWyP*vriR1>HG2+%GUMLx*m62Kr3=_m}663k(7OB%Gv#|B7bfzuHtjBW)AlCzs&I?_{-wrho6D1)*rU_fOywa<^}w@sGN9 z0Ff6?RMVo6nb5|Z21B^icA#kfzOSE?1kbpwphv=F7ld{85`hr_M-z^u*}h4&CADC= zbIhNj$bJ~KVLC4Md6%(L1w88&CXEPS%6dgWSXV93be4yfO3G4j9>_y+Rk>;VCk{M> zn@sA+ZTy2k7>(u4z=1kngICY2x2fSI7@9mzJ53^XrsA^T5WgntrL46ulz9Snn;k&; z$OF!FP3Gb?B0PH?ih1;y=z*o5%vB3I_K9+PFCJBi&%j$_!5IklHBDBFqElc_XG+z~ zg1hjZ1SL1u60%+3kY<~a|7G-(T1j^*gsdA@FeWSi;c?XSAp-*(^9 z@Ymf((a$@oN~)wwth6Wz+PU;VCR@$nZ8l|WZx@u zVC4d=0`^rDxJhak2FLZ(?n{wmeITHbCf@_&c{u#)W}d=rqqb*t)R;_{`%KEF=^!N~ zFF;Dofvd`Kz}@M2!|Ng>DJ@>+ULJRrtg#KxQQjdhLn=Tq6uOs=iTZFq-@Mq<@e-{N zW~LTlX8)-GDPXzh3WaI{q=X{9G3!;{VA_r0pr*TF#ofK1dsFxCAMC-FpS+yTwcPAZ ze?ogLk$t~Z2|{{5x5^bae%zlXdmHU-<8#!ocd~c(^-Hx!SuLlGTP&f3LluB^l_+L_ ziOq!9Uc6y;!1y#W!HGp-$HK8ulr4$T|x=u$H(4L;_z;nknOR z=(@5;Uiz8_oU(rMaBN2k0G}F$X@nGRGy?wz1E7p%I;Z>DTP!u=BC|%VwU@3`~fDdVunDZSE zjbWaor$KhZILXui2@`S~v$7>|&);pO%e3l<)r&VIqPn3+O;!hgPYe+`l!gF0q-Gvq z-=uat_)q zopIm8jRkbk)9L00%XKOK05PQYup;U9UK0zR3B=Apnn@)bEaV&QjAv{)57=}3lgYbS zOh;;0 zxi{PJJJ>Tw?RP>oCi6w{=_~H(i$w^3qSg==7z!~H?W<|0Gy4zgg-Yh<1_0PHDSIz&{%#y#j`w?>XpB?Wx+--Cd&-veL9JM- z)9zvPojK=q6Gm6&s*tB7xIKy#TY9c%*Ea)00|@Su znP_k1_ZHvg4Mc^3Sr&6P{&F!MiLw56WWI4d?hO3%&A`Untj}7qvB*ake*4gekoq>u zzb99ChaqgCkwQkjl^%EEX=PJ6WqFANoWEq@<5Sh6va4VGk#UpqSL&N+H%sJkj+X_u%- z1U_Ph%lgJyN)fl)3Z`gT!1{yK3Ilz3^AsenVP+6O>){X=6xf|nHMzBr_aZiZ0=Wy&e7RhJ1xeJit=#e`f>zA{o zH9kDk`tU{v>2%H0JVMo04@o)NDxD2?5#q##W}uxQ;M_jCO?>bbtsLbuf717vC20R1 zTK&&aUwag9LdG3uC?Oap`N%%?9^2w%2zVU>p`N@80;o_8?1H%Vm%o?=GuVP3YJsxz z{?#7LSn$TIaBZ@x$Ccx%RK!Q3ZB5`)Cw%P=8r)xT>M{Mdo%H=ZgGgB`_+SSzXc03=rT_%S;vuh%te`pJ9IEgSMgg zOQ&L#F=w$cnZiO$Y%mBgt($7I$l=qQP1B1$AXvmGXZm!AfJxSpCM%4v5*kD|q8@28 zx#$@TJ2v1{5j8_9nRLY-b9$uyu)inn zvUfK(raLcYB%1kkft%e=lybN+*_ZizcqSAh0+{yAp97Px=(dE|7=XH42>=>EC;)_D zH!cD?yB*8#eN=I*v{S4>P^@!#tbfjaLdkv+egE#Y{nyp|>(X(sn{fh{W6iF|lAp#o zSM1j|#ST_;AG;az_FlZ)&bxnCLCpxn*Yw`w#&w}%2L6rLag7G#lJ}7}-e*M{6k)b= zFI{gaA?hz;@EjbrtnTy=9(a$;Pgo7*qHygGq z>SlI1b+yYk0S6GL`dI0SgOc1x9U(-Pkw!yxOjoi9@J{!)2Ed8g*c9Ey5BtiI>F>o> zA$o%@a>qfI8E5%ZGl|@$4d*;glO4NG)k*Wr4(Fj5lkHP`T%n)60M!>Fd?}M0B(i6e zHVvFLpHj9Zv1?vTv_zbSWwPruGR7(4(j90>77bwN2r)>sC1MXI))EK^^u08qsx5^C zKt-Ofx^RI}EuJ|nVF&nRbKnu?J6fXoTGIic@UlH?Ac|J)%H3ZE=UXPJOhzmZxwyG8 z5cqw`1u$Q>g5eL?#1|7J3Oq0R|K9qCNbOzts&GbV@Sg0F z?91bv-!(?>Z?T#JwPqmMB6k@70JqfUnRM|2C=s(6n15T`(r-;D2tYHl}38HkBfsyU9Zl3sHL$HXM z-Qp2(b8g=3BZ@piZ3)!{?XKLC=*fiZdFN6a&`&1tAfakio3?Rc6p@xCvRWJkxY98{ zl@3>|g7X>~*2c!l?k^*Gd1?6?&kc%1r@6gjJ+8xnMJGU8=9uN1-KSr20`YAx?QQ|y z{*!t<)|6gZWei$YPOEygCj;;_ydwh(^pH92kVTXVK|Ce8op)}>EoQdn#6-X3MF?}U~x8czKLIIg#B>Jaz8ZE*OoByBvFH+i1f z8)m02{o-3zzfNC?;&kTg9d?vqy`$ctg1IBnFq5S4oj9hJ)qu~vjhdy}7a|X};Yzm^ z-(>aUVW-uPt_>wfnyqq$CYnl215EWq@2%Uf0>`_WZ~s#G)ZmL#80u8a4P!jC6O2h= z7w5LRlJ4T4cbvViZ4Y+zrOK=5+Y)vMsf3|>dgi-;GyJ3@2Zg~5c$Rv^0289VOJqPX z#q^yci<8eGuA7L}>yo|n#o+UKN(2yOUZ#p*Qe^>uEsn*%XG-o&=og`5i2iRj{yFXy z(kJVK-ZNgjkE?Cgu?6+b>lWpWH`jBT8m+EImA3i6G_AVtXn7hJ_N0esjGjE{=T^~} zD0G}Yb3tzYVfz$B9 z)V1(D`E7CPMvcs^+h(||)3m;BYKO1d=z zaJLLeVHSK8)9KG}M!55A3k~WS^m!4ZD~lQg#=nfEQM>@xL)0L8L4*h*3X0zt6T1Y| z83~+}k6|n@E-M&SLzJrm(p}^=&Ki@DG9Ie3m&kL-Y4=nxDIxGLeyYm-DlJuvzQ(g8 zE16U5ruMw~huG3`VqvO641@(pTJE@n@*Xfx(FDs_Al{%0GtcHVSPsfBTGov!SQ=-C z(sHCy=@%iGka+)LG1oI|3_u`L^%-XIfXT$lHVo{gR+g3b-bi8l?;|vT(53O8G5eIx zmbv}A?3>;b$d#hJ^zNGYkyYo4uu<4b(5tHx?pd49~6TTqqYyt^(UMyxgyx zT$VkkCRN&DFp<_yB$TT#KeS@TrIz=vR5GS3iZP13%v%NiZ_YVc2VAjET~x|WS{ zX|eEE_r{Exf;uxM_?_u{TIy1dwkrBHVdZg>mC|zJ<xz!(&C#3T7T4dNT(DT1l54me zSZTk$W5%OmgzIy!P6bi1An%ik5u52vQYl*=HvEDloCX z*he0|)nD@vzJYoEbn5b3n@x{vol&2cH3Yp|3JbM!jBdNY%EwKO3-=|B?y9Afj4v7k zK_qDK|5MD<0Qwj>kT0=$kJ~&=ETz3OtWChZHJ9IfJ?epz!m2IzRi=P zmyd%&TFNzcI`$m@R&d3ArRsZQP{`ltB>7+MAIq;hrU@LB0>s?K=;lReu|OBn{xPax;x+Ze&%dY zaCPT{-Ngu@$H@j;a|2yJrB`NWY9m6bryl+E-I|-}j=3nh^5kdZkNKJTo_y;cUD40~ ze3&1DW*gdGY=W`o9+w{PMrf8w|yY zOD!KXm3`4a_A<6sef8jD_4oN?dYrh)qlCfhJM*=ZakZz{|M=bcG5^#q?yT45pQg^; z`46-4wUM8HiQoHK^N*1rp1%E8W?=7wxXte`#ozuOQqm6wYl-4Fc3uh$|EfK^nONJo z`}gDA@3{{fiT?)n{>iKye5lWp6F+u;BHAAYL~SN{t9zx-AaaUS}OoY_;GQSlznK1M(Nzx-CmH}?M=`mG)$9RLGy zD>`Dg4Zu)D5Gs3N_8$$)#ss4kDX`5xRo3NW>M4S)9y04tQy_k-3$71=EJ=-S4wZkX zTEDS!Anx~RJsoy&nwxOgq=B!CfBe)4#6G^DWr`mP#9@b4CoIzNQoKR}-mdO%vcAfs zDhv<-y3Ra{@a4!0l^9`Zv!O+FWbHj8MhfL{pV)@XW1ACd0jy7=n>(fe0q%u0#E47V zf^)Tgwg3T0=0JjCdsR8P27=v7F!o)xN)sm|?L{f)&;awQMQ|;#k^xIesJcJ<+kmbd zY+^TA7zqAR3}{#vH8C!hbi;+B=?HxSSGMtDu}g=iieSF!Qt|Xo5{Xc3zFgv}R%0hu zX0cN8F5;P&N~NhM8Fl@MDM;w1(s=Peg8MiC4%+Hs=Kq3#n9{;FY#%EC!6W!Z(eHO7uUn4aazIjsCSIsaROy~6^CjT&kWX3+=$E~6zrZG@A$ zm3~%aYqBQmr$9faB<{P_!c8w#!K+U) zKv3BWEouu~kfvkKzx_-WcE@z0g5wms``YW+0%QnVMw6rRJ!EFnZK0?5uyUU?7@@YpIpdz-00eh6oN+Sx5 zK@1_b6yC6h?9z~eU^xnAb^miY##75+4=(RlZzM_7Qk{?StsU9+?+Z)FSaPfT)t?fN`gMF?p6!`LT0c zQ9RO(4Fjx|mtjq*Q=ij-VvNW;vx&>j+A=MIwYm^6WNW6j9Gy|)lp*G<;%P+~E5I}@ zeMC8{y+5{q3a-YLH4}SLPf{VCq}k?&qj7~aP!wC5MS`mu8aKe)#)->T_15`76W*F&- zM8e^v_3&4`=|X`IA|+sK(?}k5KtSIj9yPsrV*u&YS!M~{EY-rEs3Dnr)}+-4!#GM%xZ-6&L9Y{-4Q1_)eJ zV7u`lOCYNZE2UpaGRveo_kL9CT)s{d7TCBj!%Rw?2!3saS*{*%nOM*-Tsp3aApJ2(RgJtGy zYQ&uLrM_)H+!$2>3|(J{xiM%czz)~fPXaJU15_vwI4Lfnb`IA>i*daKa$%pjD$jsSTV^3KAC}ry2AF4N4$IWjxf-O+;Ip23z8O>jcKpv zR!$p+3lxviuW>Af>3hB4S6FH2yj3S{+$E#;=P! zMU-ZO?cpn4uv)TwQS2bdhT0N-+M8?^b=_+dNtutgGKE-g!Wbv%Z+QarQ$xRs-C>16 zu)JXm%v_v%YX$2yZ-5#CoZcVAM>Db2@3Pp$zZb>YL`RXIUsJ)Q(x)V|KefM9@-TJPR81XS&r!DlV6Cy62{7;XqO!as)c&DmpbBD+4FhI=37I$jfDQ#qv+ zSRo)^t^rbq4mCP%FbX{ArkPXfX=72tYCdeFxh!1_rEsh;Z`xGtYNnm`Su2SBM3ecm z+VL)O$`uuJ?HydM(r3In1Pf()lX1RH`=iH&r?Gbt$kZBRnXSkM=&to7CZnqnR`pj(_8Yja+1mz>ib1{Cv0e!d7<_7OIx|C6fWNN4nC}0{NZCy}q1TLqRN%q}H%x2sGc^5! z>bfmsv~%@)gi)C@4H=2Y!6=4o*8q{VxYaCcxYarAn2JV;-ge4|$25}J+OGto*~GDE z&&5-uO2*Qcqn=c$c#$iaU-cFDk0guf>lR*?uW@c~%-`c6zd#jJHCZmA+#Su@zQ%aZ zCIy$UP6Cv@Z_FuLWXP|sA9hTlA@peY!@+Q8SO3pOjqI)^ypA*%tULn``}vIfRJ1I5 z4!IHeW1OSY#&EiBpyOPj!y%hmX<`O^dYDi?y&P4ild+pMZ8Y9EcsIZC{!wXbe&1vN z)2EAT-^;w$r>MH1b+7$I>X-lk)~NABw$L<$EV6h=i%72MU^VNI@bA!a!3*AMjseU= zap`2he5CWJd3fUZ0uvoD4JFDx{>c@(!%T}ugaRg=^-}yHbY(3i;zUX%d)=jbSWAy+MY!t`o3Mpv6l@CTrXU;_N|CUwn+vUr%4(DQ8VeQz<*spy6}^oRpf z6$V)cLl9~(I*2Q_tcr?#p^!xXOGSO?ZjD!TW zDuZHZ41^+WdvfemJM1lmF&?Eokryzel)?2qk6~hEH?ko88g#-Lxb#KtwJ9h=6pv}J zhhf@#7&fz#=;%a5VlgDY3Yh3?tPJ_ZP;^lkh6IDifrZT|aO3r$HEn;k5d_j0{r|Re z7eo32wo-yTq;Pp10B@NrPXNlh+bRNW`J`zal_{J+fx`B}+CSRN00bdXml?p+k*V4R zFn2l5FN*Qgh@QI!fDxlFlW7Kr5L&sfv^#$eaGn8|sQ_-EXwND5Y^E6PrqiT$0620W zv_h2VE^0+C4YcZVv|)}%p5FxU4hsy)2+(a2QXAMFfKrs{TRYshyKRX97EAV)Qwruk zFUpkbqy4>ctMm*ETBh6>cwDHh+8o2b!FYTdbJ}4x8e5f^T0V6!#9EFm{~g=@cN~VW zMuBl9I4As45FpMd*MLA0W2%OEJMugUfgay$y?BVIih&?$9_nQWomLQI0E|<{yRrvX zYi~$qTnDDfCfH#bjxvfV%QiS-|v z@#rVKHP@RP2A7UAs&e*kq&@%)*L#;IN)-nadpTDs-G-lO7Hk_7%Pc?Zwxi=N5(GH~ zLn$H6iC@vt3AfZt+Y_gORTi4r>4=RfY>nly(Hz?9xSev$)vI3f;&AKj8Jh**P(>9D z76(i&nwCtRS)&8zjxjVrXPP$U+q*&yXpxz8vpLlbE%_c!z~f`qupk7Y#4|y za~M~nrk{IZ!ipUArPB-O5i--;k^H1RjGy{&pCpX+q_Yliwv^7-@z3cBFRB990Jqh0)6 zk!$@TpDi`DyD4c^J^JV)wqvSvI0bdC+zJZ)@FX*R#%vekoH=uX!DNS`nHq+KRa9_1EwjY7b>A1B z0cT22J>?m0eD(4pLxG$}LUJMc$m|tA6A(%4HI5gCynM>kY1;fwh1mJ>t4t&I1>XTCSGFU zuy&{zWEq7bV0<^T=c57j9QVRC-qA7~X64_*NoQQvq%6%V{6oYRPm6c&z!tM47Mmn* z60Wz7CwZ8~w`Xumu##@39J(d#X#J?(t;%K2oR8~g{nGqgx#x2)+hf9p zN9v2_YO~kQ3yz=7ZcfLQ!!GzReI5H=EffqH&<)gu9w7>R@HnDwat<@(70Yk+)ki zooXw6;kRP>s8V@QBtXaE#q&kEDKZA~7xsJh$Xv9e$hh6(bVxEItFoW(d3V;$Mnb5f<-BcWY+~Ns(FdB9)hLbiqKlS=p`vvDU_XXp6i=8yg zDCa<&z95#lK*-81_cV+KNDZ$W;W_#_?GWyFm@%+aMq~6BN_7`UK?V(jIBa}Kt)l@` zQ;$B>U^&>hezfUBd+yBiIryJ~gHI_2H>D!D+CPeQlxa&uDBt*K+f_-v{ys!Q+Hy?b z<}vi@n_@SW)hLHKY3F4YEWo*O{IAN96GC$|0tB@%m}lyf*NgB7U~dN~NHuj~)q9D8 ztI6n&DM(2p@TPu9je)Ok5fgf5a?h5x=Q$7n`CiHBixnSxfRQQ+3Wr@zR|f)E!BF^M z&q($^_~3CmPx*KOKmwwu+$BY+7@(Lc2!`9`UjMRZTm6>6*3q`>~+Rdv2xb zK5xd}FpHOKKKt@L3%YCChX2r^77iQ*u=w}2gKu8l{6b7fREK* zaTug_GDrL8muvs@M!fnmb?M7$#+QG=d%`6fQT{M?W^qQA{fTg%6$yzO&@COr;BA?T zc7*0_qa3e<=#q<&M=(T&TayzR=TAL!;@P`?;7*hE-6SC>6t%Z(`b5C{;-%u}Np9Jy zgV=Gxivi4V=Xv?@MC}N<9913Q^`M)bg=VF8FeMqCsTgCPC_Lc8qt;{wAB+R=Jt19E z0DAE}_hBU`lcVxPpGhIFLJE%5;84n*PhoZtu_D6@&fB9EI+J-QKdiz7i6P^SZ)X9^ zFsZE^?Gye5B+@HZMV7unsE^_Lh8bqC3}buYWz!aOfAO>5ng|S+WV8%pcP)dE)J&qU z<7hm&`t`|%hB_I131o=r^o;<{>p#I5KY(TsW&b>e(B4k}I$;j_?2P&CtV@4K`-_%m z?TDR+c)c^1d;l67arngd4^iJYzkf73`9nPB2NCgvdB(o-C+1R}_2_-Kre4jT9z2ot zb4OJ+L=In9O$&n-EY+cV^X zsB8M4KX2r`*nB7J#+?^s``z>;ff-o5>n8NaXVDE^zj2+EFNnW#dl$w@2%IG6{Y%W%`n3u<(TjQJ;AL`#*jKGZMMF+?Q%TzPnz9OMuE8?dU1+Tm?9ZnZ@r7*Q*FAxx5~Vm2a)~tZ_y!vI-&skqg-=4 zqdeJ&nk~0k#{qQ|@h#fcI4!@TH%4aOVze+XU}AJGZy~;GYY9kUoL;uTdgxV$I-A#0c0v!({Vc*cp7_#f6NZMvF-a<_rclNnlG)5p~Y? zx>3=w6C4s4t(2pM86;kuiHtNp{%`_-MEaU?=4li>(81m5CNR%=+Sj2jR(b zWK}AhMuNGZz*E>R5PV#*4up??EABv334DN`O%Vc!u}SP-^ZJLUfJ)A8D}qLa!I7#M zy`!!*c1&QU19(?8fM%E{H(^Z;i=weXeer%b_1<_0X)!6q9syidfQziI34m2lWQ`RS zXvjY52PEkjXT*QxXenvQPE_h`rkm^_bqKRiOgQ`FhhbGr0|-{BXc z`Tt>$wVm*JDf%ql?`7$y)MGRM!A`w={3oA#rp&oCc(&|B_3=5e{7YeTWo>N{t#)jX zR?CP@U;z?do$>HRX?K*2^Sf{WQD%_f8o(gjUx|kOJ!_9%`~4jfPnU&hi>I!(&wu~a z%a}i`^f8l_pf!2nAr;RZGv5>JiK_XK)1E9>0LLIt!##S0;+s`<>8OQk1dj;}mNJ)~Y!u2?mUvL;oFxF4dy zNxGck^Fm>M@7Jo6GLN^U3zVL0RbAJ5y7j`mXe74ki}~2L48P69cJ&_T=Q}da^t0EF z7cFqa&v(Bs#8NT8GSkU-4@`S{H?-0qMU`lMQ@;=SZ|srx%78EyVC(@GQ&lEl&U()33D0{YrYU=h?SEatEh({?taf2L5?lu=4Y7?e$Op{3|!M^QYtS*WxgA z;zVp@3R-xOoFuo1(0kZ3qHs70>G$*zO~*Ny|LYroHrd$6jl@E>k5Hn`zmEYBU+})^ z0jZF40K=WP#Q5aqmmYf7jgarCJf4FS*w{;{&(;J+|&H8Dq&^yZ9MvrKCJ8K0G+`i!U`KaS;>Yn$= zzH)cI0IQG!-4N?174F(0PP5he@97g4-JO3pCbk&9ADFFlkBV}hy>IjZxmXf=B*w-2 znaKz1wQBckr(I@W?|X4?xkh+2>7e%~^A7_*Yu!JmIoPip2t~nI9(?JPBRGpy8}8WW ze={j_GFGdZg7qFQIj-JoR;t|+4IWYFT<6T~)=3n?kuy9HHcI_Onh)p)wc?6%UA&Llyd;o`0iE%MmA|2sD@ z!}RsJLipO;FMzXJYzO56@WS3|2wus32P9_FPsK#L*;r&aD?iVk+DawWxviDV#qY0k zZShzyJ<@%&!@KR!$I`i-v_Ah1uZ=Pv! zHO2zy(ZoZz#XNfI?q?wBg=32P-}-Jz#4VaCx>ILl?K-y463BWY40&>=0h9`%)R@NslJ*tDX3n4IT$Ypu?uVWGE~(oW$JN0s zDfPM{k76Q^;Rj<{Pij585o;p&Y}Mjkj1&YDJ0(W|3A{8G>|Si+qoU0%!qxU3fy)gE zwitPp#dknR+4Wv}22B8{!9-}-h{i$x?^|$!+9gt*L@*u7aK6#S_IEn3EoP{J5Uv2$e^ zvWLRFNn~|C2(jvNiTK!iJUTdJg&#IwE|+r);M$hVVlb7W>-BPll6WWWgNW zMaE$+?$ZF~>QQRw>IoPxRJ~>iu;_%arnSwN^QTi!YjRq5o(N}Tj?z?Xxqzg%Lzvng zrMUi*T*(4Ab!Uf`c)g~nRp7p!Ii&9Ca>XO;v}bOlXWJBVM_j7F_i5dG^X|F3;}K|) z+$p+FJLrA6{x4NEzO&bY{j9%S8^!4`faMvB$6K$2mkjseboeMp0d1vGJcDiBTP7@l z4T$v2y-~LJv2QF9VDpO$-n$I@KG%+=%cS@3e8BpATRo;Toz=d3YHg|0)?@u?HGQ6uREkIgIz*>&AX%&fTM#^YSc`Ae%1W0g%xOkhMq(-}@SV+f#!_7l@HC+OOOdS02zd&xwX> z&n(?P-X_}Aedx}tPelAlJH3=j2j!}r+^)-|de@R2*8DZ|%nBnHqiiVMH{` zGcRmGzPUXdH8`R}3nc0_`S(^V%Z5TNmkr4VFOA(p?7X;Dl-z@nN4LUVWQ(TuLMVCq zr%;p@qu9?-cx6@}v9|_^=eiZ+i}mP{(L*dGoA{@g9wgWv zVS|8t#331Uz)d(I8t^O}7YSv)E~Z7Ee0o{7!KToH^R<7iL@z?oex#&-;T(ZMwNmhF zm6seSmq@>)jj02D5)GOFZSf`%Miv>i^uh-RV9m3ePe~{)`xi_GDZu)Y&huD`5u z*-XJm`5B4s8InZ->5UAcE1d?1FfF?1$+8EIVGyjTYbE>OFNW*7pUYugxW+(eZ@Ih1 zinH@Tp$Q3|S7n-HIUP@w6e;8vp&j9E3@yj28MqX5{?x^l@0E3+NN*Iv&a?>%n81>$ z8@e_fW%TUPB1wm9;LbH{*)B&;(@>Dy1M zLxI}a%({0GY}60&AMe%tN7WuB+!9}sKAs=btD|!&B>Bmg2Gtd_D~qRky`M&$-xGoz z0Q`h|5>2gUAD;W0QWm-85Y3fXiwAzB)9x6k5$v1ftfwm%LjZwcAwnz@u436zhDE)^< z5&O$7xhhJH#L)f(N(-djE{OS;I#huNK^b|vv7^cFEB+;Ik17KLgQx3~du&%XVVsbj zR-?yx$F=IcSBCc2zHrVx66ic}?dYSTrqH@>0exD`9%cLJB|XW|DYV-*8dcA5At(;K0G=I_~3bJIR;JD zzRSCYr&Zy|>!_qSrchfQWpBl4LFgmdQBNRufO)LQ8-vBn<|U*vpg65)`7G8WkgUe# zC7B(ar;~K{&bThktts6L$xo~-;j{;hBeMEb@C`4!m$rK~98DLTUk7mleUzXFI0VHWaIA=e5e@@0PF5Z98uYMnRKKc-rE|SNQ8+&;N;8;2yqsci- z2eLDs0}zw78(zYWx)>?n#v8!C_?4;2OB%odBp<&MzXejz#^+o2#~t(fgaZ*jAjB?n z?VQ+5G^0b2sS0`$&Ps+_auuEeAfhMZZhhswyIb#cp5NQIzIct?X&sCW047m?}N=cYJ%NQL0lMW&U2IzsSzO1i~Uw3!gr zHaQJbWoR359z`Y^B?wwYr?UfOP$V<~KwTE2U0osiw%RmNNufz}C`0>U@q z%JeNI8kmy6|3kU}eHIlF@uP3-YB?w zlN>>?Cc!Jq#~evWsP9sN5HXhb)Jo!1albV!S()T|Cm!0Tls_p4_E!d5lfSiIyjVYEj@pb+e9l$C z5-%QJVyf;n4hH1CF1Ssyk3J(qU)TW5*R+bFn`IQ!@OJojHVzWKONK zqC1>YO7?XZ=j9Wj|trBkX~AB~no5PX&If!wjY zbY|Q=ujq77^txlV_o*}I?JaV#eh;#G#&Vk#^Bj58Qi=Ha%I4*D=oi*9Uc!}s7 z2P&-e?w-bvx=hM8sy(`sit9{TaF@q(M`uESR4u(-9O5Xt31=~i0T@oX z$^QV@$f|Bm5rI5v3w>UmCIRZ()NUam=|FJpXF*O0#Kr^LB#^9!C8gV8I*Wjf;!DD z1+eb$7QHJS;_eQWtMR_uadn6FNGkWfjO&UV;P^nPL^macgH@Zj9c(%9qb%e)J8r2B zAx(BkG{FlnB@Uj|90fveulhkEpGV1=LQaask;?n#KlWSfIjr!;QT3{WOc3QY->}G| z>1pYS*Rm74*@uMdUUaKZIx9_{(8dJsnXD@4;C<(!;JVc7I}DO8++BNcPfL z@D$;8>(&G)Ew^~>O98!JGWNXA4jRrVZd4K9fELX_yMH&+B)J?yR7~S|UTE<`@JPPs zoC_Em)UWJJue3aBA4&5`zqNJlJ)07yEbq9C5i>@4Q-y2dDPZ!5ng>e{$yMWtE-dHkkvpBR% ze`}#~Otj&xCe7F9n@@AyvHRCw1$VsC>hZCe$VE*y+%Lxb<>PCWl1TMNwaa>5F*F(9 zW3pU!vso5b{hiV!>#Lf>Px8ugNI)tIHd^U49=XAb#P-Z_$q8j+aXyp4rhOP{O5n4FrN!;Q-Sf)=*Ig4z zy@VccBXu3;^)RZq8<=;2`xSlWIl$tad4%A+psxE(312cJh`lFh`r`7c$nq}z`CqtG z2twNMC6_$QlA69GL|D==awY*2D9k(I%!RIHml|A1NXYxs$KPj#=6x36E0bL80sc=O zEJP z!*dK{xrVhGQpY^R4RKk8Y43x$N`gZ6FQ<`~Wy5%GhRf{b$9!+)D-*<1P$Hpv(4Awb z&66z(ZE_avS^Fj*qkNwCO7x{sZZ2fdSUeKZioo6+*&I{V5p#2Am6HrH-eT#+GB7&w zP{##W53)eMs9P|EUp@eKC$N*}%2C`r)0iKG*`~930C|hiaw2p0^reXcjP{erhFfhB zp1dp(D591&j#j}V7;EMzR-DqLn*v6#ZPg0Kvk+iA-IfvcDJk$%Avn?W>O}vYmG5ul zl;7rT#xwL@!YL72oOfArEi~z&V}CbeNTllNPxN9AayN948s98(k(=VlNXiv`!Gf4W z=*=x)@)`nrpo@NRjVPZTqqQA28(+HO)caxh#7SwzlRDreX@v24=Sx_j=#2W8Je@C1 z{~){<0Ek>j`^&T*4rXWQ`a8fi#5YFY(TvkBVEoEL=|?|bjX5L!m4W|yL4rAYjW6$J zM0rNUwYwjOK*XEg);~X(nZ|~F;utbgA~qGzRfAW4s(zEm)P5HEe(VI-#0kZa^1oBc zwvF@3p1GguR+a+*6Dh5u3AKD+6eb!9xEY^R*xY5ZP4;toJGa&ZMft<9Q}0 zGh3ep!cIiJ`*d#hQ!_ZxJHN62!-?+;dwz2e941zmWej=%j08JB?k0O#EH;4|$8W;4 zvjS){%pUXD4+k(IYf()fF}52~FFxCT{&Es|Q1#c#DA|`MvX(v{^o%MQsF))bAp35{CVsblguzabn$hP9 z3OOi{RRwFWlDPK%Sjo?0Wy1t!B9jwiM&!Jt?|N}iGqEQ|W0`vtt!^njFPI|TD_AXA~jYf{;@g@n>K2zy#3KPChI-X59&>v)nvpjDPc$Rz`r_X`gBZJI%rTbMnYv%9nMrm^ z=0nhaSw0G^qI?+`vIRIJG}7i+G=w2ig8-KCe^??4S~a19fA*CA?HjhV*_`zE1wHX3TBguRrPVW5kmTGRPZ<1~LoHH?Z+S$Oz;A{}R!EnlYCTx4rzY zW(-f_{{27V5Ajr=dwRTU?$iCNf6?g7hp)equ*dB%n@Mgpn{2XFI>!8@2y(@|RT%SY zzow(uV58GVorH+X452}T)0C~n8+B2SxAW%4`dQMc^z0z43^FhNkmWBPx%fh9P>BmeCN zemw8<@`(}-f2qS`Ilel-CGK=9t$9BVg6V&x&Xu)pwMZZLp92z)li{(o8pN%$T75vx zjho6zqU)dTetqVRc>YS_n+t89j(UmT-${LY;nVN;Z;ht2o>Ptf;xZQTE~1%> zcli8c>F>O*Y2_{ne^*Od2>))Bw=BVDQ?y*6?RuEB{MTxd@79a$ij932iayjWUE3Jl$ zgR_-<>?U?s!J!btg#>^qIxMsDH%U6A8H7ns-sRyeCb5nJxU3O%x)!~Ced9s+t^M#g zH7nJ#YDX7fU$htGgpgqW9oN|-cjjojsc)}#wn)7{DIHU*p@V_3R{<9^B5?CmP}-xR zlE@6Tp2Bu9TqWfc=F{ze1SUt8Y#<<%F6{_l1WlV=z?)7D129{OzA)wVC1q@1&pz}b zR)OALBU11Bekomm4-LSv^hNn`F3zWfb3#DGC)d4Om)iqX6yKag-{*CQZ}t0SF!9+4)eHpFtBwna zjn7pEi5%O4>;N~}oE#9$Q#Wp65-_d&q{xBOS=eNBeAYMuBOq7IP)sV20^w$Hwh-X$ zcq1b$@RNf_Euckn@MTVe{M7x*Md|nhgDk*@XjHN44-x>N4&I-*NF20M@%*->;@QmKdz+Zuer9d`-DN z-)4z8QlJLF*n1!rIGMWulr!@+hyfV?eXL*nelY_@w0PAln!RvV;Fo*pY`Qok|zh*WKxpN5%*$G9zoEeT=u zXwH;Q)=>MU>k}5TF-Pod>cx07bcHcazYlP8l%chhoIYqDT1i@-&|VJudyW>qoY!O6 zt*rC1#75o2V_aI6FNQCTL|^Kdf|o%G@SdT3oUW6!8`DP!si~6v>y#z}Wo{hL;Yxm$ zHz=5iLS05(2y{Ae4se-1rXQCJ0)B;^%C?uiRb;7m_!)>a{)f8^K?tiG5N&TTGGk=0 zxS^D(b2Dn-rX{CLCZni<3gGfQZ+urYC-2L%-w$TsUa!2AtY5acsoTsh@$E|hDAWV% z!#wYNPwkmBP&wqgf?|nu<8(jne>lE-F~}H(zQ}QTG@=~d6>l(aU*iO` z$N=1f(*Q6*_i#XhBB>F2GA)nF@tj-@KmXT(GIVqfK*W*-E20iu*}bW59X6ap)mGv# z>QYHMS9Sw@pm&y!E)Z(Fj}$9ah1+%zheM^Qih#9~N_f@|so(HY&KylBM31k#rZ*lg ziOM@n4*f?i1PNezRpRjEs~-mTuyPB52A2gA+<*410CCEc(c+CFY)C_Z$5@LxDScxb zGs_;7xN^W;!8~|{mw4<|)`J8Eg2X`jLXsc=_GssC-BlgiW~QW#jdKYo7>CYM2pg3{ z#QC}u)x5L(SHdqNj&f#!jE|kx9z~v<{xxCsk&W(HA_>nWpNy#-E&L)G(8 zrCl6gZJcrB7e1E!py{U^zHs7wGm2Wf5-@PgQt*=WnxAbyvCxD;1!A;Sg=<~D08RO< zK=#sNT1xM;$!k`iZA$T%;}$lrE-3T#Cke2-vg+}2u>P1nMS(s(b`Fa;oYTAzjUlk| zz`_%P?bD!bIa>R777uDqGyv(0#d+}|`gq2nC)tFni$Y==O|ZqZ%V0d?oiw&jKB^9n z8|py94FLfNl7{)=V!ZyGC)PFi69;~1^}!-pe4U9JKF3S_rNUa~VYy8#rK}PskVSP^QN^1ul1P2#O#~tR4|{`q zY*}HkUz~>zx9D)1-GgN}0@$4B*jKd9AhA6WJq|sw9{JJ!c~@s^m&MCpP&jr~c^pGN z5K8%X=@#$DZY<#o{HhkV-4Ib|jth{2afYk<#LD}G`1=g1JBMOPgY~g{B zvtZqZP;S9@k}wdlZRdst$X!zwOxz^_$9AwBy-8ob%TK zkcPdft;a7c55s}_Us4t~tGws(6%)kwH+3!^byb9PNjTdVbDR!xr^GF)dN3R|+e}ZV zquNR7EumUXsu=v~0%oDUfOJ^bQg4ONW96JL%!T^iqVavEW$kEmyUS_7FQRasTkX1n zrrY3FXVbTX#-@Arb4GwdPdaRWaWe@Rkwq3JG1v-^9fV+$OBVZW;bQY`qp!7I*bL|Z zwX&0|W-`D72x5upRn?AfxOkTp`ZncYdjW`&6;LL!6v5M8@dd?uwyp%5(AV#dn>|}5 z4+#hki5v7g!9pha2+O&!Tyr*%a2uel%BKhDn%{Usyew1}3r7KAlJK|va0di64}fi~ zi$T&um-Cy0CE?pj@||VW&t?5$=hrMt!W-R@vruC-d8SR};e~Wd?lspPM3xADel*>lj7LC8@ zWY-i^Ev43bukn0;w0;KfB=*Y5#411%L+B}{?v)L75Gm;)LR(@>rv%uAh@}um>L`rh z*0M zj7ap6aG{-8-O(z_pj^CIj8!HV&pic4WA25TFk-;Dm=w_yJ;H<*(fYl8RKSBZA-prY z!K+8S`d6m!?;p0joT!|eyadm*GSUB%+wfs`OfP@85mM>4`;L`IygeVGBhx4!e_ljIux{^}q&hyY)*pedIs30GMq}NT_yS*KqT~$Pu2} z#!bVj^VR|3 zZ6yy0iz!Z{og{FPE0kRcRT_2YqkIG&)Tqm`#n zw<*{3&VV>C=Q7XV_iip?I!P-M-xghJX{lyGl{tTr{{s&Z`VO~f1)%l7pnG^`pgBDF ztndr+VYF)hUO0ht19E^r+$*yDwOG zBnJ-r9MwOC|7NguUWAwM`7%WQR-Z0cw|crS9Z&2cQiU}8ZsJ=U_wp0Z6MbuX9O zCc=NR0ahYiKLm@T1F+1!dq)(;Rn(NGKyW}GSLcEZH2ccMIY1=Xt&GNc9@gA5wrkSX z{H-wSTm8~-La-uXhWkt)dE<}$^nj9>R`Jf@b8z=KssHLb9^n&riX&OY8<#KmSNpceqo&>zm#laqj)xM9B?82dqrZ^Hb)3^nm?M*-0RlP}MbbMn z{lp}xtRW0#zwxwZ3Gn6PR>gCF9}f_#U*|(mQ%0}f92BGzpTgTuyZbvtOT+M&q5Xpv zn=+@q6b|WO=K6fdfPi?ce7yk69!b>mtr^mg+ngd*y3|y}U!CP_HPHj`&VC^nB8=+|X!hSLfZ=_;cu~ zsRywXMJdHT>qBAvZTVYboPbP>OMAJykNBnNuq~+0_tU$73MEivZ(ed+o&?eM!9OKyP!RVMWzGzh1MQ7;#wq+_bQDXG zCz4it*ue16e8!M!WrC-1jUHx%s8 zCQ0i9O%fLs;rD$olY=)&le)yIb<)%4cvQ{<*s_-vD_k|P`Zxwe%b<&~aQ27UF2D~f zuldc?30pp9scJIftx1@-4MRQEoeDk22I?8okG^sNL;(IbKn(~caliuWzcPByZH8=S zJ~WU#EC}Z$=+g;SD;iHR-tV7dNYTJRe6at=%yA1(UE5>s~TrvU8819WL- zfc$x@ud@s*)df!`5uckh4Bhlk5pvlAeX2rteI|c$p z31CM3+ybmI1EXkPc>Y}9{RfH?lb%GCVZEv=`L$v_!sgD+YV!V41p?}+EtJ=h03W2| z2C#}Rl>7j{wc?oyMGD|Xiw^xtY)Q$~BRun)<+ZUsy0|* z3E-jq3BkZhZ^W?Fj@+$oGqunT-IohX!ZnxD?KS^?%e_}b`CV0q_1^Z@u&t4P!0YhhW(Hade`#{QTG1!=N!Jc(UzC-NNRWiVI9E7zl%1iY6I9&)BuM?M?&UYtJ5}hA1^1+ zUB|N7s)_&@0wyLezi1iH=|IRn1au@zf){CwMU3-FTDcdG4kZd^jeojWXuP+K_|TkD z#3yg?fs^){F08C6=VQlYE* zVm3jL%OW@#c|&^~J)tbqFp?|)als1<1z<{l6$(B3Tg4?u6t=2z(!Zpvi`-eX&1y2>Lc9dE6nO)_(E~u_p$q63RZ-*2k1#^vsxqK=A;WQthSs zs`HmlbsfsTIDn52QwUQLRx<2-#2JG3TTqR*jyqj*I8rPB2^Sdc;oSJ4CIFO~^1C+P zKfUI1!m~B|H2aE={E}48d~{9_CAr5!LXlg#xR}=IXx5jfQUl$0Q4HdR7iO)(^S;1O zY8~=B{4)M+7#c-1HZ%oCr!mIUqUk2~xR`Z|20b$w@!pO7lrsn`V@sqWA(c-*LEZ*Y z#1?XKp!M|3>tLyQB(Q4hRROgteJwV2`)q&utTG8%dA?Eh;IM?9;A4Ru?K)1G$xTQA z%ND%{UVIny{=L_(c3B!uKbo20`}G36D-oXw0t^)G}yCjvF&kMdu7?w?mu%dFjS$NkQF(@UH%yr6zwvL zpc~CGRIyWx_(_i!Zu{EQt(qHh1o04TK40t9S^p8s9-`W=TGNbAfC|&Bw(z|uXA~CB zQqADI;UwC(350;Nuw%Gkx9js2J2ous`qvam`=LFsSHXA%Et=C1<+scd^)dHtK222L zGcozU!2*r+DmTN77o3@@lcZ6Y*FW;KD{CJzw0bXnG(BOKal1H!W6m4S-#^&>=)iP4 zFq#yyCeI5ee)0R#dvNr(yw7h2U7HhH+NB*hA3|{EnWYhligpwpFyYw)u|U z#F=yzB6xfbTlO8*SUJ}&<{4PJ?9d+1bH}EK3H~zyQkbT1SN2-@J?3DIw$`cAmC5t~ zG!tMmPvd@0w!ZNhcRLyQY}n|{AEMa}sPJen@6FI5QDOoBU%wCH`@~`FQ|7eqV*2#b zN-qCsh5bdj#i;u+6i;_z+7YcDPUF|etG_a9mn?Bc3tQBdcEJmwzfsL7k_a& zv`1BsZgWUtU|CB*lS(OX4NV&ouH{O$WUEh)sUFN6CQ%R1WC z#+~7xhs^)O!UvRVJ4dsEWHik`o<_n#kq6M_1ADRom!WIQl=z_Vjg(bv9q$-K1)5>T zVr5~oL=y+hJJDkaNF^RDLJ@oHWXw@u&HtSzToi|J;+a>HVPWPxA#RHc>8tgQQ71C#!)k;8+>2FucfP%~^V5+i8?;q<`8W;T`89{xM!ZG= zJZ9COi9MyX_naTlE%kP2iUlKO?OAOo_QMYAc8wL&erhN%@fp&`Ckx9wlf{{^$QImq zhMP?F!V;-aW)J=AI6g%%I*7cvb`r6m__umLSywAS0I68ecu3j$Q1F(m8 z{T9|fb#FWy`b2Q}Nx%ft1hOY-R@*B3$+pt z&Nx1@c*22S+(AStBL%O1M+7#4fAw&c4B_jF`FA+vJ@mNfV1y-En^h{=N|s_pOJtc@ z&nmj4QrpQdlfmcH5Qd2R#Q?fnR(PL7*(L$(5W6_rE`Z4J4Ym_LVwCgma&&)JjXK1` zGLCrQglIIbOZ5BZmZIN`+=TF}A?%fNZdabiVrUS|HV=3BIkjyGEDnN=R3M}#h^H)w z;eaTb~3uGQW z=_fqg$9Ph?dA3cEkro-XOK>`1*>b_-pc8{8(6+is6YVAdsHsXKgR2UZI`QWbIL0(& z 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 z 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 HzkmM^9E|&e literal 0 HcmV?d00001 diff --git a/test/reports/2016-08-29-fuzz/high_freq.png b/test/reports/2016-08-29-fuzz/high_freq.png new file mode 100644 index 0000000000000000000000000000000000000000..6e595f107339f06fd3df26b456b1a5a7c1283802 GIT binary patch literal 26251 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 literal 0 HcmV?d00001 diff --git a/test/reports/2016-08-29-fuzz/index.html b/test/reports/2016-08-29-fuzz/index.html new file mode 100644 index 000000000..d0c402b74 --- /dev/null +++ b/test/reports/2016-08-29-fuzz/index.html @@ -0,0 +1,10 @@ + + + + +
Banner:fuzz
Directory:fuzz-testing/out
Generated on:Mo 29 Aug 2016 22:14:22 CEST
+

+

+

+ + diff --git a/test/reports/2016-08-29-fuzz/low_freq.png b/test/reports/2016-08-29-fuzz/low_freq.png new file mode 100644 index 0000000000000000000000000000000000000000..6b312141f199f410a22416322d2cbde6c8411170 GIT binary patch literal 11752 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%0Dm~z^C$YRJ`?H+K){E+HW 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. diff --git a/test/src/unit-allocator.cpp b/test/src/unit-allocator.cpp index 9ad162c1e..9176b6dab 100644 --- a/test/src/unit-allocator.cpp +++ b/test/src/unit-allocator.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.3 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-capacity.cpp b/test/src/unit-capacity.cpp index f671ae99c..5560ed1fb 100644 --- a/test/src/unit-capacity.cpp +++ b/test/src/unit-capacity.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.3 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-class_const_iterator.cpp b/test/src/unit-class_const_iterator.cpp index b64e7e3c2..eceaa0029 100644 --- a/test/src/unit-class_const_iterator.cpp +++ b/test/src/unit-class_const_iterator.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.3 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-class_iterator.cpp b/test/src/unit-class_iterator.cpp index 1d4b29015..1fb7deb19 100644 --- a/test/src/unit-class_iterator.cpp +++ b/test/src/unit-class_iterator.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.3 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-class_lexer.cpp b/test/src/unit-class_lexer.cpp index 7cece21c6..27a67fe79 100644 --- a/test/src/unit-class_lexer.cpp +++ b/test/src/unit-class_lexer.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.3 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index 6fcf947de..1c23df574 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.3 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-comparison.cpp b/test/src/unit-comparison.cpp index a8ce4c8ad..fa5a9ac86 100644 --- a/test/src/unit-comparison.cpp +++ b/test/src/unit-comparison.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.3 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-concepts.cpp b/test/src/unit-concepts.cpp index 9cbdd8e90..acd787521 100644 --- a/test/src/unit-concepts.cpp +++ b/test/src/unit-concepts.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.3 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-constructor1.cpp b/test/src/unit-constructor1.cpp index 8a26738bd..6815db651 100644 --- a/test/src/unit-constructor1.cpp +++ b/test/src/unit-constructor1.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.3 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-constructor2.cpp b/test/src/unit-constructor2.cpp index b5f1a5e39..17c538984 100644 --- a/test/src/unit-constructor2.cpp +++ b/test/src/unit-constructor2.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.3 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-convenience.cpp b/test/src/unit-convenience.cpp index 4d3c9b7cf..f5c6f07d4 100644 --- a/test/src/unit-convenience.cpp +++ b/test/src/unit-convenience.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.3 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-conversions.cpp b/test/src/unit-conversions.cpp index 982efe420..592dcf0ab 100644 --- a/test/src/unit-conversions.cpp +++ b/test/src/unit-conversions.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.3 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-deserialization.cpp b/test/src/unit-deserialization.cpp index d94632133..9de25f97c 100644 --- a/test/src/unit-deserialization.cpp +++ b/test/src/unit-deserialization.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.3 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . @@ -150,7 +150,7 @@ TEST_CASE("deserialization") SECTION("from chars") { - uint8_t *v = new uint8_t[5]; + uint8_t* v = new uint8_t[5]; v[0] = 't'; v[1] = 'r'; v[2] = 'u'; diff --git a/test/src/unit-element_access1.cpp b/test/src/unit-element_access1.cpp index 0e515a8e4..d32b3f808 100644 --- a/test/src/unit-element_access1.cpp +++ b/test/src/unit-element_access1.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.3 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-element_access2.cpp b/test/src/unit-element_access2.cpp index adadb726c..fe53ddd7a 100644 --- a/test/src/unit-element_access2.cpp +++ b/test/src/unit-element_access2.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.3 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-inspection.cpp b/test/src/unit-inspection.cpp index 25fcb5353..d99241b69 100644 --- a/test/src/unit-inspection.cpp +++ b/test/src/unit-inspection.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.3 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-iterator_wrapper.cpp b/test/src/unit-iterator_wrapper.cpp index 5d6764144..df358282f 100644 --- a/test/src/unit-iterator_wrapper.cpp +++ b/test/src/unit-iterator_wrapper.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.3 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-iterators1.cpp b/test/src/unit-iterators1.cpp index f8b4e6bd8..c33791d25 100644 --- a/test/src/unit-iterators1.cpp +++ b/test/src/unit-iterators1.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.3 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-iterators2.cpp b/test/src/unit-iterators2.cpp index cc5d98185..1354ad0fc 100644 --- a/test/src/unit-iterators2.cpp +++ b/test/src/unit-iterators2.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.3 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-json_patch.cpp b/test/src/unit-json_patch.cpp index af4d45476..5f7ea36d6 100644 --- a/test/src/unit-json_patch.cpp +++ b/test/src/unit-json_patch.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.3 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-json_pointer.cpp b/test/src/unit-json_pointer.cpp index b14a45931..d73d801ad 100644 --- a/test/src/unit-json_pointer.cpp +++ b/test/src/unit-json_pointer.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.3 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-modifiers.cpp b/test/src/unit-modifiers.cpp index 9cb336846..7b2443271 100644 --- a/test/src/unit-modifiers.cpp +++ b/test/src/unit-modifiers.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.3 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-pointer_access.cpp b/test/src/unit-pointer_access.cpp index 9353b5b0d..2b33e64da 100644 --- a/test/src/unit-pointer_access.cpp +++ b/test/src/unit-pointer_access.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.3 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-readme.cpp b/test/src/unit-readme.cpp index 7e7ef6b2c..8fc7886ff 100644 --- a/test/src/unit-readme.cpp +++ b/test/src/unit-readme.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.3 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-reference_access.cpp b/test/src/unit-reference_access.cpp index 922c4825d..30a0dea64 100644 --- a/test/src/unit-reference_access.cpp +++ b/test/src/unit-reference_access.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.3 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp index da4c3d23d..f61595a90 100644 --- a/test/src/unit-regression.cpp +++ b/test/src/unit-regression.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.3 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-serialization.cpp b/test/src/unit-serialization.cpp index 13333ff2a..6b9eed22d 100644 --- a/test/src/unit-serialization.cpp +++ b/test/src/unit-serialization.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.3 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-testsuites.cpp b/test/src/unit-testsuites.cpp index 54f094792..1ef2b97d6 100644 --- a/test/src/unit-testsuites.cpp +++ b/test/src/unit-testsuites.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.3 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit-unicode.cpp b/test/src/unit-unicode.cpp index 8efb6154a..a8fbaf960 100644 --- a/test/src/unit-unicode.cpp +++ b/test/src/unit-unicode.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.3 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . diff --git a/test/src/unit.cpp b/test/src/unit.cpp index ec957b7a8..57ebc3ef3 100644 --- a/test/src/unit.cpp +++ b/test/src/unit.cpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 2.0.2 +| | |__ | | | | | | version 2.0.3 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License .