#include "simdjson.h" #include "test_ondemand.h" using namespace simdjson; namespace object_error_tests { using namespace std; template bool assert_iterate_object(T &&object, const char **expected_key, V *expected, size_t N, simdjson::error_code *expected_error, size_t N2) { size_t count = 0; for (auto field : object) { V actual{}; auto actual_error = field.value().get(actual); if (count >= N) { ASSERT((count - N) < N2, "Extra error reported"); ASSERT_ERROR(actual_error, expected_error[count - N]); } else { ASSERT_SUCCESS(actual_error); ASSERT_EQUAL(field.key().value_unsafe(), expected_key[count]); ASSERT_EQUAL(actual, expected[count]); } count++; } ASSERT_EQUAL(count, N+N2); return true; } template bool assert_iterate_object(T &&object, const char *(&&expected_key)[N], V (&&expected)[N], simdjson::error_code (&&expected_error)[N2]) { return assert_iterate_object(std::forward(object), expected_key, expected, N, expected_error, N2); } template bool assert_iterate_object(T &&object, simdjson::error_code (&&expected_error)[N2]) { return assert_iterate_object(std::forward(object), nullptr, nullptr, 0, expected_error, N2); } template bool assert_iterate_object(T &&object, const char *(&&expected_key)[N], V (&&expected)[N]) { return assert_iterate_object(std::forward(object), expected_key, expected, N, nullptr, 0); } bool object_iterate_error() { TEST_START(); ONDEMAND_SUBTEST("missing colon", R"({ "a" 1, "b": 2 })", assert_iterate_object(doc.get_object(), { TAPE_ERROR })); ONDEMAND_SUBTEST("missing key ", R"({ : 1, "b": 2 })", assert_iterate_object(doc.get_object(), { TAPE_ERROR })); ONDEMAND_SUBTEST("missing value", R"({ "a": , "b": 2 })", assert_iterate_object(doc.get_object(), { INCORRECT_TYPE, TAPE_ERROR })); ONDEMAND_SUBTEST("missing comma", R"({ "a": 1 "b": 2 })", assert_iterate_object(doc.get_object(), { "a" }, { int64_t(1) }, { TAPE_ERROR })); TEST_SUCCEED(); } bool object_iterate_wrong_key_type_error() { TEST_START(); ONDEMAND_SUBTEST("wrong key type", R"({ 1: 1, "b": 2 })", assert_iterate_object(doc.get_object(), { TAPE_ERROR })); ONDEMAND_SUBTEST("wrong key type", R"({ true: 1, "b": 2 })", assert_iterate_object(doc.get_object(), { TAPE_ERROR })); ONDEMAND_SUBTEST("wrong key type", R"({ false: 1, "b": 2 })", assert_iterate_object(doc.get_object(), { TAPE_ERROR })); ONDEMAND_SUBTEST("wrong key type", R"({ null: 1, "b": 2 })", assert_iterate_object(doc.get_object(), { TAPE_ERROR })); ONDEMAND_SUBTEST("wrong key type", R"({ []: 1, "b": 2 })", assert_iterate_object(doc.get_object(), { TAPE_ERROR })); ONDEMAND_SUBTEST("wrong key type", R"({ {}: 1, "b": 2 })", assert_iterate_object(doc.get_object(), { TAPE_ERROR })); TEST_SUCCEED(); } bool object_iterate_unclosed_error() { TEST_START(); ONDEMAND_SUBTEST("unclosed", R"({ "a": 1, )", assert_iterate_object(doc.get_object(), { INCOMPLETE_ARRAY_OR_OBJECT })); ONDEMAND_SUBTEST("unclosed", R"({ "a": 1 )", assert_iterate_object(doc.get_object(), { INCOMPLETE_ARRAY_OR_OBJECT })); ONDEMAND_SUBTEST("unclosed", R"({ "a": )", assert_iterate_object(doc.get_object(), { INCOMPLETE_ARRAY_OR_OBJECT })); ONDEMAND_SUBTEST("unclosed", R"({ "a" )", assert_iterate_object(doc.get_object(), { INCOMPLETE_ARRAY_OR_OBJECT })); ONDEMAND_SUBTEST("unclosed", R"({ )", assert_iterate_object(doc.get_object(), { INCOMPLETE_ARRAY_OR_OBJECT })); TEST_SUCCEED(); } bool object_iterate_incomplete_error() { TEST_START(); ONDEMAND_SUBTEST("unclosed", R"({ "x": { "a": 1, })", assert_iterate_object(doc.get_object(), { "a" }, { int64_t(1) }, { TAPE_ERROR })); ONDEMAND_SUBTEST("unclosed", R"({ "x": { "a": 1 })", assert_iterate_object(doc.get_object(), { "a" }, { int64_t(1) }, { INCOMPLETE_ARRAY_OR_OBJECT })); ONDEMAND_SUBTEST("unclosed", R"({ "x": { "a": })", assert_iterate_object(doc.get_object(), { INCORRECT_TYPE, INCOMPLETE_ARRAY_OR_OBJECT })); ONDEMAND_SUBTEST("unclosed", R"({ "x": { "a" })", assert_iterate_object(doc.get_object(), { INCOMPLETE_ARRAY_OR_OBJECT })); ONDEMAND_SUBTEST("unclosed", R"({ "x": { })", assert_iterate_object(doc.get_object(), { INCOMPLETE_ARRAY_OR_OBJECT })); TEST_SUCCEED(); } bool object_lookup_error() { TEST_START(); ONDEMAND_SUBTEST("missing colon", R"({ "a" 1, "b": 2 })", assert_error(doc["a"], TAPE_ERROR)); ONDEMAND_SUBTEST("missing key ", R"({ : 1, "b": 2 })", assert_error(doc["a"], TAPE_ERROR)); ONDEMAND_SUBTEST("missing value", R"({ "a": , "b": 2 })", assert_success(doc["a"])); ONDEMAND_SUBTEST("missing comma", R"({ "a": 1 "b": 2 })", assert_success(doc["a"])); TEST_SUCCEED(); } bool object_lookup_unclosed_error() { TEST_START(); #if SIMDJSON_CHECK_EOF ONDEMAND_SUBTEST("unclosed", R"({ "a": )", assert_error(doc["a"], INCOMPLETE_ARRAY_OR_OBJECT)); #else ONDEMAND_SUBTEST("unclosed", R"({ "a": )", assert_error(doc["a"], INCOMPLETE_ARRAY_OR_OBJECT)); #endif ONDEMAND_SUBTEST("unclosed", R"({ "a" )", assert_error(doc["a"], INCOMPLETE_ARRAY_OR_OBJECT)); ONDEMAND_SUBTEST("unclosed", R"({ )", assert_error(doc["a"], INCOMPLETE_ARRAY_OR_OBJECT)); TEST_SUCCEED(); } bool object_lookup_miss_error() { TEST_START(); ONDEMAND_SUBTEST("missing colon", R"({ "a" 1, "b": 2 })", assert_error(doc["b"], TAPE_ERROR)); ONDEMAND_SUBTEST("missing key ", R"({ : 1, "b": 2 })", assert_error(doc["b"], TAPE_ERROR)); ONDEMAND_SUBTEST("missing value", R"({ "a": , "b": 2 })", assert_error(doc["b"], TAPE_ERROR)); ONDEMAND_SUBTEST("missing comma", R"({ "a": 1 "b": 2 })", assert_error(doc["b"], TAPE_ERROR)); TEST_SUCCEED(); } bool object_lookup_miss_wrong_key_type_error() { TEST_START(); ONDEMAND_SUBTEST("wrong key type", R"({ 1: 1, "b": 2 })", assert_error(doc["b"], TAPE_ERROR)); ONDEMAND_SUBTEST("wrong key type", R"({ true: 1, "b": 2 })", assert_error(doc["b"], TAPE_ERROR)); ONDEMAND_SUBTEST("wrong key type", R"({ false: 1, "b": 2 })", assert_error(doc["b"], TAPE_ERROR)); ONDEMAND_SUBTEST("wrong key type", R"({ null: 1, "b": 2 })", assert_error(doc["b"], TAPE_ERROR)); ONDEMAND_SUBTEST("wrong key type", R"({ []: 1, "b": 2 })", assert_error(doc["b"], TAPE_ERROR)); ONDEMAND_SUBTEST("wrong key type", R"({ {}: 1, "b": 2 })", assert_error(doc["b"], TAPE_ERROR)); TEST_SUCCEED(); } bool object_lookup_miss_unclosed_error() { TEST_START(); ONDEMAND_SUBTEST("unclosed", R"({ "a": 1, )", assert_error(doc["b"], INCOMPLETE_ARRAY_OR_OBJECT)); // TODO These next two pass the user a value that may run past the end of the buffer if they are not careful. // In particular, if the padding is decorated with the wrong values, we could cause overrun! ONDEMAND_SUBTEST("unclosed", R"({ "a": 1 )", assert_error(doc["b"], INCOMPLETE_ARRAY_OR_OBJECT)); ONDEMAND_SUBTEST("unclosed", R"({ "a": )", assert_error(doc["b"], INCOMPLETE_ARRAY_OR_OBJECT)); ONDEMAND_SUBTEST("unclosed", R"({ "a" )", assert_error(doc["b"], INCOMPLETE_ARRAY_OR_OBJECT)); ONDEMAND_SUBTEST("unclosed", R"({ )", assert_error(doc["b"], INCOMPLETE_ARRAY_OR_OBJECT)); TEST_SUCCEED(); } bool object_lookup_miss_next_error() { TEST_START(); ONDEMAND_SUBTEST("missing comma", R"({ "a": 1 "b": 2 })", ([&]() { auto obj = doc.get_object(); return assert_result(obj["a"], int64_t(1)) && assert_error(obj["b"], TAPE_ERROR); })()); TEST_SUCCEED(); } #ifdef SIMDJSON_DEVELOPMENT_CHECKS bool issue2084() { TEST_START(); auto json = R"( {"foo": "bar"} )"_padded; SUBTEST("document->value after access", test_ondemand_doc(json, [&](auto doc) { std::string_view foo; ASSERT_SUCCESS(doc["foo"].get(foo)); ondemand::value value; ASSERT_ERROR(doc.get_value().get(value), OUT_OF_ORDER_ITERATION); return true; })); SUBTEST("document->value after access and rewind", test_ondemand_doc(json, [&](auto doc) { std::string_view foo; ASSERT_SUCCESS(doc["foo"].get(foo)); doc.rewind(); ondemand::value value; ASSERT_SUCCESS(doc.get_value().get(value)); return true; })); TEST_SUCCEED(); } bool out_of_order_object_iteration_error() { TEST_START(); auto json = R"([ { "x": 1, "y": 2 } ])"_padded; SUBTEST("simdjson_result", test_ondemand_doc(json, [&](auto doc) { for (auto element : doc) { auto obj = element.get_object(); for (auto field : obj) { ASSERT_SUCCESS(field); } ASSERT_ITERATE_ERROR( obj, OUT_OF_ORDER_ITERATION ); } return true; })); SUBTEST("object", test_ondemand_doc(json, [&](auto doc) { for (auto element : doc) { ondemand::object obj; ASSERT_SUCCESS( element.get(obj) ); for (auto field : obj) { ASSERT_SUCCESS(field); } ASSERT_ITERATE_ERROR( obj, OUT_OF_ORDER_ITERATION ); } return true; })); TEST_SUCCEED(); } bool out_of_order_top_level_object_iteration_error() { TEST_START(); auto json = R"({ "x": 1, "y": 2 })"_padded; SUBTEST("simdjson_result", test_ondemand_doc(json, [&](auto doc) { auto obj = doc.get_object(); for (auto field : obj) { ASSERT_SUCCESS(field); } ASSERT_ITERATE_ERROR( obj, OUT_OF_ORDER_ITERATION ); return true; })); SUBTEST("object", test_ondemand_doc(json, [&](auto doc) { ondemand::object obj; ASSERT_SUCCESS( doc.get(obj) ); for (auto field : obj) { ASSERT_SUCCESS(field); } ASSERT_ITERATE_ERROR( obj, OUT_OF_ORDER_ITERATION ); return true; })); TEST_SUCCEED(); } bool out_of_order_object_index_child_error() { TEST_START(); auto json = R"([ { "x": 1, "y": 2 } ])"_padded; SUBTEST("simdjson_result", test_ondemand_doc(json, [&](auto doc) { simdjson_result obj; for (auto element : doc) { obj = element.get_object(); for (auto field : obj) { ASSERT_SUCCESS(field); } } ASSERT_ERROR( obj["x"], OUT_OF_ORDER_ITERATION ); return true; })); SUBTEST("object", test_ondemand_doc(json, [&](auto doc) { ondemand::object obj; for (auto element : doc) { ASSERT_SUCCESS( element.get(obj) ); for (auto field : obj) { ASSERT_SUCCESS(field); } } ASSERT_ERROR( obj["x"], OUT_OF_ORDER_ITERATION ); return true; })); SUBTEST("simdjson_result", test_ondemand_doc(json, [&](auto doc) { simdjson_result obj; for (auto element : doc) { obj = element; ASSERT_SUCCESS( obj["x"] ); } ASSERT_ERROR( obj["x"], OUT_OF_ORDER_ITERATION ); return true; })); SUBTEST("value", test_ondemand_doc(json, [&](auto doc) { ondemand::value obj; for (auto element : doc) { ASSERT_SUCCESS( element.get(obj) ); ASSERT_SUCCESS( obj["x"] ); } ASSERT_ERROR( obj["x"], OUT_OF_ORDER_ITERATION ); return true; })); TEST_SUCCEED(); } bool out_of_order_object_index_sibling_error() { TEST_START(); auto json = R"([ { "x": 0, "y": 2 }, { "x": 1, "y": 4 } ])"_padded; SUBTEST("simdjson_result", test_ondemand_doc(json, [&](auto doc) { simdjson_result last_obj; uint64_t i = 0; for (auto element : doc) { auto obj = element.get_object(); uint64_t x; ASSERT_SUCCESS(obj["x"].get(x)); ASSERT_EQUAL(x, i); if (i > 0) { ASSERT_ERROR(last_obj["x"].get(x), OUT_OF_ORDER_ITERATION); break; } last_obj = obj; i++; } return true; })); SUBTEST("object", test_ondemand_doc(json, [&](auto doc) { ondemand::object last_obj; uint64_t i = 0; for (auto element : doc) { ondemand::object obj; ASSERT_SUCCESS( element.get_object().get(obj) ); uint64_t x; ASSERT_SUCCESS( obj["x"].get(x) ); ASSERT_EQUAL(x, i); if (i > 0) { ASSERT_ERROR(last_obj["x"].get(x), OUT_OF_ORDER_ITERATION); break; } last_obj = obj; i++; } return true; })); SUBTEST("simdjson_result", test_ondemand_doc(json, [&](auto doc) { simdjson_result last_obj; uint64_t i = 0; for (auto element : doc) { auto obj = element; uint64_t x; ASSERT_SUCCESS(obj["x"].get(x)); ASSERT_EQUAL(x, i); if (i > 0) { ASSERT_ERROR(last_obj["x"].get(x), OUT_OF_ORDER_ITERATION); break; } last_obj = obj; i++; } return true; })); SUBTEST("value", test_ondemand_doc(json, [&](auto doc) { ondemand::value last_obj; uint64_t i = 0; for (auto element : doc) { ondemand::value obj; ASSERT_SUCCESS( element.get(obj) ); uint64_t x; ASSERT_SUCCESS( obj["x"].get(x) ); ASSERT_EQUAL(x, i); if (i > 0) { ASSERT_ERROR(last_obj["x"].get(x), OUT_OF_ORDER_ITERATION); break; } last_obj = obj; i++; } return true; })); TEST_SUCCEED(); } bool out_of_order_object_find_field_child_error() { TEST_START(); auto json = R"([ { "x": 1, "y": 2 } ])"_padded; SUBTEST("simdjson_result", test_ondemand_doc(json, [&](auto doc) { simdjson_result obj; for (auto element : doc) { obj = element.get_object(); for (auto field : obj) { ASSERT_SUCCESS(field); } } ASSERT_ERROR( obj.find_field("x"), OUT_OF_ORDER_ITERATION ); return true; })); SUBTEST("object", test_ondemand_doc(json, [&](auto doc) { ondemand::object obj; for (auto element : doc) { ASSERT_SUCCESS( element.get(obj) ); for (auto field : obj) { ASSERT_SUCCESS(field); } } ASSERT_ERROR( obj.find_field("x"), OUT_OF_ORDER_ITERATION ); return true; })); SUBTEST("simdjson_result", test_ondemand_doc(json, [&](auto doc) { simdjson_result obj; for (auto element : doc) { obj = element; ASSERT_SUCCESS( obj.find_field("x") ); } ASSERT_ERROR( obj.find_field("x"), OUT_OF_ORDER_ITERATION ); return true; })); SUBTEST("value", test_ondemand_doc(json, [&](auto doc) { ondemand::value obj; for (auto element : doc) { ASSERT_SUCCESS( element.get(obj) ); ASSERT_SUCCESS( obj.find_field("x") ); } ASSERT_ERROR( obj.find_field("x"), OUT_OF_ORDER_ITERATION ); return true; })); TEST_SUCCEED(); } bool out_of_order_object_find_field_sibling_error() { TEST_START(); auto json = R"([ { "x": 0, "y": 2 }, { "x": 1, "y": 4 } ])"_padded; SUBTEST("simdjson_result", test_ondemand_doc(json, [&](auto doc) { simdjson_result last_obj; uint64_t i = 0; for (auto element : doc) { auto obj = element.get_object(); uint64_t x; ASSERT_SUCCESS(obj.find_field("x").get(x)); ASSERT_EQUAL(x, i); if (i > 0) { ASSERT_ERROR(last_obj.find_field("x").get(x), OUT_OF_ORDER_ITERATION); break; } last_obj = obj; i++; } return true; })); SUBTEST("object", test_ondemand_doc(json, [&](auto doc) { ondemand::object last_obj; uint64_t i = 0; for (auto element : doc) { ondemand::object obj; ASSERT_SUCCESS( element.get_object().get(obj) ); uint64_t x; ASSERT_SUCCESS( obj.find_field("x").get(x) ); ASSERT_EQUAL(x, i); if (i > 0) { ASSERT_ERROR(last_obj.find_field("x").get(x), OUT_OF_ORDER_ITERATION); break; } last_obj = obj; i++; } return true; })); SUBTEST("simdjson_result", test_ondemand_doc(json, [&](auto doc) { simdjson_result last_obj; uint64_t i = 0; for (auto element : doc) { auto obj = element; uint64_t x; ASSERT_SUCCESS(obj.find_field("x").get(x)); ASSERT_EQUAL(x, i); if (i > 0) { ASSERT_ERROR(last_obj.find_field("x").get(x), OUT_OF_ORDER_ITERATION); break; } last_obj = obj; i++; } return true; })); SUBTEST("value", test_ondemand_doc(json, [&](auto doc) { ondemand::value last_obj; uint64_t i = 0; for (auto element : doc) { ondemand::value obj; ASSERT_SUCCESS( element.get(obj) ); uint64_t x; ASSERT_SUCCESS( obj.find_field("x").get(x) ); ASSERT_EQUAL(x, i); if (i > 0) { ASSERT_ERROR(last_obj.find_field("x").get(x), OUT_OF_ORDER_ITERATION); break; } last_obj = obj; i++; } return true; })); TEST_SUCCEED(); } bool out_of_order_object_find_field_unordered_child_error() { TEST_START(); auto json = R"([ { "x": 1, "y": 2 } ])"_padded; SUBTEST("simdjson_result", test_ondemand_doc(json, [&](auto doc) { simdjson_result obj; for (auto element : doc) { obj = element.get_object(); for (auto field : obj) { ASSERT_SUCCESS(field); } } ASSERT_ERROR( obj.find_field_unordered("x"), OUT_OF_ORDER_ITERATION ); return true; })); SUBTEST("object", test_ondemand_doc(json, [&](auto doc) { ondemand::object obj; for (auto element : doc) { ASSERT_SUCCESS( element.get(obj) ); for (auto field : obj) { ASSERT_SUCCESS(field); } } ASSERT_ERROR( obj.find_field_unordered("x"), OUT_OF_ORDER_ITERATION ); return true; })); SUBTEST("simdjson_result", test_ondemand_doc(json, [&](auto doc) { simdjson_result obj; for (auto element : doc) { obj = element; ASSERT_SUCCESS( obj.find_field_unordered("x") ); } ASSERT_ERROR( obj.find_field_unordered("x"), OUT_OF_ORDER_ITERATION ); return true; })); SUBTEST("value", test_ondemand_doc(json, [&](auto doc) { ondemand::value obj; for (auto element : doc) { ASSERT_SUCCESS( element.get(obj) ); ASSERT_SUCCESS( obj.find_field_unordered("x") ); } ASSERT_ERROR( obj.find_field_unordered("x"), OUT_OF_ORDER_ITERATION ); return true; })); TEST_SUCCEED(); } bool out_of_order_object_find_field_unordered_sibling_error() { TEST_START(); auto json = R"([ { "x": 0, "y": 2 }, { "x": 1, "y": 4 } ])"_padded; SUBTEST("simdjson_result", test_ondemand_doc(json, [&](auto doc) { simdjson_result last_obj; uint64_t i = 0; for (auto element : doc) { auto obj = element.get_object(); uint64_t x; ASSERT_SUCCESS(obj.find_field_unordered("x").get(x)); ASSERT_EQUAL(x, i); if (i > 0) { ASSERT_ERROR(last_obj.find_field_unordered("x").get(x), OUT_OF_ORDER_ITERATION); break; } last_obj = obj; i++; } return true; })); SUBTEST("object", test_ondemand_doc(json, [&](auto doc) { ondemand::object last_obj; uint64_t i = 0; for (auto element : doc) { ondemand::object obj; ASSERT_SUCCESS( element.get_object().get(obj) ); uint64_t x; ASSERT_SUCCESS( obj.find_field_unordered("x").get(x) ); ASSERT_EQUAL(x, i); if (i > 0) { ASSERT_ERROR(last_obj.find_field_unordered("x").get(x), OUT_OF_ORDER_ITERATION); break; } last_obj = obj; i++; } return true; })); SUBTEST("simdjson_result", test_ondemand_doc(json, [&](auto doc) { simdjson_result last_obj; uint64_t i = 0; for (auto element : doc) { auto obj = element; uint64_t x; ASSERT_SUCCESS(obj.find_field_unordered("x").get(x)); ASSERT_EQUAL(x, i); if (i > 0) { ASSERT_ERROR(last_obj.find_field_unordered("x").get(x), OUT_OF_ORDER_ITERATION); break; } last_obj = obj; i++; } return true; })); SUBTEST("value", test_ondemand_doc(json, [&](auto doc) { ondemand::value last_obj; uint64_t i = 0; for (auto element : doc) { ondemand::value obj; ASSERT_SUCCESS( element.get(obj) ); uint64_t x; ASSERT_SUCCESS( obj.find_field_unordered("x").get(x) ); ASSERT_EQUAL(x, i); if (i > 0) { ASSERT_ERROR(last_obj.find_field_unordered("x").get(x), OUT_OF_ORDER_ITERATION); break; } last_obj = obj; i++; } return true; })); TEST_SUCCEED(); } #endif bool run() { return object_iterate_error() && object_iterate_wrong_key_type_error() && object_iterate_unclosed_error() && object_lookup_error() && object_lookup_unclosed_error() && object_lookup_miss_error() && object_lookup_miss_unclosed_error() && object_lookup_miss_wrong_key_type_error() && object_lookup_miss_next_error() && #ifdef SIMDJSON_DEVELOPMENT_CHECKS issue2084() && out_of_order_object_iteration_error() && out_of_order_top_level_object_iteration_error() && out_of_order_object_index_child_error() && out_of_order_object_index_sibling_error() && out_of_order_object_find_field_child_error() && out_of_order_object_find_field_sibling_error() && out_of_order_object_find_field_unordered_child_error() && out_of_order_object_find_field_unordered_sibling_error() && #endif true; } } // namespace object_error_tests int main(int argc, char *argv[]) { return test_main(argc, argv, object_error_tests::run); }