Add non-top-level array iteration test

This commit is contained in:
John Keiser 2020-10-04 12:47:01 -07:00
parent 5533f8d87b
commit 676a3d068c
7 changed files with 374 additions and 179 deletions

View File

@ -14,7 +14,8 @@ simdjson_really_inline simdjson_result<array_iterator<T>> array_iterator<T>::sta
}
template<typename T>
simdjson_really_inline simdjson_result<value> array_iterator<T>::operator*() noexcept {
if (iter->get_iterator().error()) { iter->iteration_finished(); return iter->get_iterator().error(); }
error_code error = iter->get_iterator().error();
if (error) { iter->iteration_finished(); return error; }
return value::start(iter->borrow_iterator());
}
template<typename T>

View File

@ -10,11 +10,11 @@ simdjson_really_inline field::field(raw_json_string key, ondemand::value &&value
{
}
simdjson_really_inline simdjson_result<field> field::start(json_iterator_ref &&iter) noexcept {
simdjson_really_inline simdjson_result<field> field::start(json_iterator_ref &parent_iter) noexcept {
raw_json_string key;
SIMDJSON_TRY( iter->field_key().get(key) );
SIMDJSON_TRY( iter->field_value() );
return field::start(std::forward<json_iterator_ref>(iter), key);
SIMDJSON_TRY( parent_iter->field_key().get(key) );
SIMDJSON_TRY( parent_iter->field_value() );
return field::start(parent_iter.borrow(), key);
}
simdjson_really_inline simdjson_result<field> field::start(json_iterator_ref &&iter, raw_json_string key) noexcept {

View File

@ -40,7 +40,7 @@ public:
protected:
simdjson_really_inline field(raw_json_string key, ondemand::value &&value) noexcept;
static simdjson_really_inline simdjson_result<field> start(json_iterator_ref &&iter) noexcept;
static simdjson_really_inline simdjson_result<field> start(json_iterator_ref &iter) noexcept;
static simdjson_really_inline simdjson_result<field> start(json_iterator_ref &&iter, raw_json_string key) noexcept;
friend struct simdjson_result<field>;
friend class object_iterator;

View File

@ -140,7 +140,7 @@ SIMDJSON_WARN_UNUSED simdjson_result<std::string_view> json_iterator::consume_st
return parse_string(advance());
}
SIMDJSON_WARN_UNUSED simdjson_result<raw_json_string> json_iterator::parse_raw_json_string(const uint8_t *json) noexcept {
logger::log_value(*this, "string", "", 0);
logger::log_value(*this, "string", "");
if (*json != '"') { logger::log_error(*this, "Not a string"); return INCORRECT_TYPE; }
return raw_json_string(json+1);
}
@ -148,28 +148,28 @@ SIMDJSON_WARN_UNUSED simdjson_result<raw_json_string> json_iterator::consume_raw
return parse_raw_json_string(advance());
}
SIMDJSON_WARN_UNUSED simdjson_result<uint64_t> json_iterator::parse_uint64(const uint8_t *json) noexcept {
logger::log_value(*this, "uint64", "", 0);
logger::log_value(*this, "uint64", "");
return numberparsing::parse_unsigned(json);
}
SIMDJSON_WARN_UNUSED simdjson_result<uint64_t> json_iterator::consume_uint64() noexcept {
return parse_uint64(advance());
}
SIMDJSON_WARN_UNUSED simdjson_result<int64_t> json_iterator::parse_int64(const uint8_t *json) noexcept {
logger::log_value(*this, "int64", "", 0);
logger::log_value(*this, "int64", "");
return numberparsing::parse_integer(json);
}
SIMDJSON_WARN_UNUSED simdjson_result<int64_t> json_iterator::consume_int64() noexcept {
return parse_int64(advance());
}
SIMDJSON_WARN_UNUSED simdjson_result<double> json_iterator::parse_double(const uint8_t *json) noexcept {
logger::log_value(*this, "double", "", 0);
logger::log_value(*this, "double", "");
return numberparsing::parse_double(json);
}
SIMDJSON_WARN_UNUSED simdjson_result<double> json_iterator::consume_double() noexcept {
return parse_double(advance());
}
SIMDJSON_WARN_UNUSED simdjson_result<bool> json_iterator::parse_bool(const uint8_t *json) noexcept {
logger::log_value(*this, "bool", "", 0);
logger::log_value(*this, "bool", "");
auto not_true = atomparsing::str4ncmp(json, "true");
auto not_false = atomparsing::str4ncmp(json, "fals") | (json[4] ^ 'e');
bool error = (not_true && not_false) || jsoncharutils::is_not_structural_or_whitespace(json[not_true ? 5 : 4]);
@ -181,7 +181,7 @@ SIMDJSON_WARN_UNUSED simdjson_result<bool> json_iterator::consume_bool() noexcep
}
simdjson_really_inline bool json_iterator::is_null(const uint8_t *json) noexcept {
if (!atomparsing::str4ncmp(json, "null")) {
logger::log_value(*this, "null", "", 0);
logger::log_value(*this, "null", "");
return true;
}
return false;
@ -214,7 +214,7 @@ constexpr const uint32_t MAX_INT_LENGTH = 1024;
SIMDJSON_WARN_UNUSED simdjson_result<uint64_t> json_iterator::parse_root_uint64(const uint8_t *json) noexcept {
uint8_t tmpbuf[20+1]; // <20 digits> is the longest possible unsigned integer
if (!copy_to_buffer(json, tmpbuf)) { logger::log_error(*this, "Root number more than 20 characters"); return NUMBER_ERROR; }
logger::log_value(*this, "uint64", "", 0);
logger::log_value(*this, "uint64", "");
auto result = numberparsing::parse_unsigned(tmpbuf);
if (result.error()) { logger::log_error(*this, "Error parsing unsigned integer"); return result.error(); }
return result;
@ -225,7 +225,7 @@ SIMDJSON_WARN_UNUSED simdjson_result<uint64_t> json_iterator::consume_root_uint6
SIMDJSON_WARN_UNUSED simdjson_result<int64_t> json_iterator::parse_root_int64(const uint8_t *json) noexcept {
uint8_t tmpbuf[20+1]; // -<19 digits> is the longest possible integer
if (!copy_to_buffer(json, tmpbuf)) { logger::log_error(*this, "Root number more than 20 characters"); return NUMBER_ERROR; }
logger::log_value(*this, "int64", "", 0);
logger::log_value(*this, "int64", "");
auto result = numberparsing::parse_integer(tmpbuf);
if (result.error()) { report_error(result.error(), "Error parsing integer"); }
return result;
@ -237,7 +237,7 @@ SIMDJSON_WARN_UNUSED simdjson_result<double> json_iterator::parse_root_double(co
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/, 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest number: -0.<fraction>e-308.
uint8_t tmpbuf[1074+8+1];
if (!copy_to_buffer(json, tmpbuf)) { logger::log_error(*this, "Root number more than 1082 characters"); return NUMBER_ERROR; }
logger::log_value(*this, "double", "", 0);
logger::log_value(*this, "double", "");
auto result = numberparsing::parse_double(tmpbuf);
if (result.error()) { report_error(result.error(), "Error parsing double"); }
return result;
@ -427,7 +427,7 @@ simdjson_really_inline void json_iterator_ref::assert_is_active() const noexcept
#endif
}
simdjson_really_inline void json_iterator_ref::assert_is_not_active() const noexcept {
// We don't call const functions because VC++ doesn't
// We don't call const functions because VC++ is worried they might have side effects in __assume
#ifdef SIMDJSON_ONDEMAND_SAFETY_RAILS
SIMDJSON_ASSUME(!(iter != nullptr && lease_depth == iter->active_lease_depth));
#else

View File

@ -11,7 +11,7 @@ simdjson_really_inline object_iterator::object_iterator(json_iterator_ref &_iter
simdjson_really_inline simdjson_result<field> object_iterator::operator*() noexcept {
error_code error = (*iter)->error();
if (error) { iter->release(); return error; }
auto result = field::start(iter->borrow());
auto result = field::start(*iter);
// TODO this is a safety rail ... users should exit loops as soon as they receive an error.
// Nonetheless, let's see if performance is OK with this if statement--the compiler may give it to us for free.
if (result.error()) { iter->release(); }
@ -29,7 +29,7 @@ simdjson_really_inline object_iterator &object_iterator::operator++() noexcept {
if (!iter->is_alive()) { return *this; } // Iterator will be released if there is an error
bool has_value;
error_code error = (*iter)->has_next_field().get(has_value);
if (!(error || has_value)) { std::cout << std::endl; iter->release(); }
if (!(error || has_value)) { iter->release(); }
return *this;
}

View File

@ -48,8 +48,8 @@ namespace number_tests {
bool small_integers() {
std::cout << __func__ << std::endl;
for (int m = 10; m < 20; m++) {
for (int i = -1024; i < 1024; i++) {
for (int64_t m = 10; m < 20; m++) {
for (int64_t i = -1024; i < 1024; i++) {
return test_ondemand<int64_t>(std::to_string(i), [&](int64_t actual) {
ASSERT_EQUAL(actual, i);
return true;
@ -244,7 +244,7 @@ namespace dom_api_tests {
int i = 0;
for (auto [ field, error ] : object) {
ASSERT_SUCCESS(error);
ASSERT( field.key() == expected_key[i] , "Keys not equal" );
ASSERT_EQUAL( field.key(), expected_key[i]);
ASSERT_EQUAL( field.value().get_uint64().first, expected_value[i] );
i++;
}
@ -256,7 +256,7 @@ namespace dom_api_tests {
int i = 0;
for (auto [ field, error ] : object_result) {
ASSERT_SUCCESS(error);
ASSERT( field.key() == expected_key[i] , "Keys not equal" );
ASSERT_EQUAL( field.key(), expected_key[i] );
ASSERT_EQUAL( field.value().get_uint64().first, expected_value[i] );
i++;
}
@ -500,7 +500,7 @@ namespace dom_api_tests {
ASSERT_TRUE(test_ondemand_doc(json, [&](auto doc_result) {
int i = 0;
for (ondemand::field field : doc_result.get_object()) {
ASSERT( field.key() == expected_key[i] , "Keys not equal" );
ASSERT_EQUAL( field.key(), expected_key[i] );
ASSERT_EQUAL( uint64_t(field.value()), expected_value[i] );
i++;
}
@ -811,128 +811,179 @@ namespace error_tests {
TEST_SUCCEED();
}
namespace wrong_type {
#define TEST_CAST_ERROR(JSON, TYPE, ERROR) \
std::cout << "- Subtest: get_" << (#TYPE) << "() - JSON: " << (JSON) << std::endl; \
if (!test_ondemand_doc((JSON##_padded), [&](auto doc_result) { \
ASSERT_ERROR( doc_result.get_##TYPE(), (ERROR) ); \
return true; \
})) { \
return false; \
} \
{ \
padded_string a_json(std::string(R"({ "a": )") + JSON + " })"); \
std::cout << R"(- Subtest: get_)" << (#TYPE) << "() - JSON: " << a_json << std::endl; \
if (!test_ondemand_doc(a_json, [&](auto doc_result) { \
ASSERT_ERROR( doc_result["a"].get_##TYPE(), (ERROR) ); \
std::cout << "- Subtest: get_" << (#TYPE) << "() - JSON: " << (JSON) << std::endl; \
if (!test_ondemand_doc((JSON##_padded), [&](auto doc_result) { \
ASSERT_ERROR( doc_result.get_##TYPE(), (ERROR) ); \
return true; \
})) { \
return false; \
}; \
}
} \
{ \
padded_string a_json(std::string(R"({ "a": )") + JSON + " })"); \
std::cout << R"(- Subtest: get_)" << (#TYPE) << "() - JSON: " << a_json << std::endl; \
if (!test_ondemand_doc(a_json, [&](auto doc_result) { \
ASSERT_ERROR( doc_result["a"].get_##TYPE(), (ERROR) ); \
return true; \
})) { \
return false; \
}; \
}
bool wrong_type() {
TEST_START();
bool wrong_type_array() {
TEST_START();
TEST_CAST_ERROR("[]", object, INCORRECT_TYPE);
TEST_CAST_ERROR("[]", bool, INCORRECT_TYPE);
TEST_CAST_ERROR("[]", int64, NUMBER_ERROR);
TEST_CAST_ERROR("[]", uint64, NUMBER_ERROR);
TEST_CAST_ERROR("[]", double, NUMBER_ERROR);
TEST_CAST_ERROR("[]", string, INCORRECT_TYPE);
TEST_CAST_ERROR("[]", raw_json_string, INCORRECT_TYPE);
TEST_SUCCEED();
}
TEST_CAST_ERROR("[]", object, INCORRECT_TYPE);
TEST_CAST_ERROR("[]", bool, INCORRECT_TYPE);
TEST_CAST_ERROR("[]", int64, NUMBER_ERROR);
TEST_CAST_ERROR("[]", uint64, NUMBER_ERROR);
TEST_CAST_ERROR("[]", double, NUMBER_ERROR);
TEST_CAST_ERROR("[]", string, INCORRECT_TYPE);
TEST_CAST_ERROR("[]", raw_json_string, INCORRECT_TYPE);
bool wrong_type_object() {
TEST_START();
TEST_CAST_ERROR("{}", array, INCORRECT_TYPE);
TEST_CAST_ERROR("{}", bool, INCORRECT_TYPE);
TEST_CAST_ERROR("{}", int64, NUMBER_ERROR);
TEST_CAST_ERROR("{}", uint64, NUMBER_ERROR);
TEST_CAST_ERROR("{}", double, NUMBER_ERROR);
TEST_CAST_ERROR("{}", string, INCORRECT_TYPE);
TEST_CAST_ERROR("{}", raw_json_string, INCORRECT_TYPE);
TEST_SUCCEED();
}
TEST_CAST_ERROR("{}", array, INCORRECT_TYPE);
TEST_CAST_ERROR("{}", bool, INCORRECT_TYPE);
TEST_CAST_ERROR("{}", int64, NUMBER_ERROR);
TEST_CAST_ERROR("{}", uint64, NUMBER_ERROR);
TEST_CAST_ERROR("{}", double, NUMBER_ERROR);
TEST_CAST_ERROR("{}", string, INCORRECT_TYPE);
TEST_CAST_ERROR("{}", raw_json_string, INCORRECT_TYPE);
bool wrong_type_true() {
TEST_START();
TEST_CAST_ERROR("true", array, INCORRECT_TYPE);
TEST_CAST_ERROR("true", object, INCORRECT_TYPE);
TEST_CAST_ERROR("true", int64, NUMBER_ERROR);
TEST_CAST_ERROR("true", uint64, NUMBER_ERROR);
TEST_CAST_ERROR("true", double, NUMBER_ERROR);
TEST_CAST_ERROR("true", string, INCORRECT_TYPE);
TEST_CAST_ERROR("true", raw_json_string, INCORRECT_TYPE);
TEST_SUCCEED();
}
TEST_CAST_ERROR("true", array, INCORRECT_TYPE);
TEST_CAST_ERROR("true", object, INCORRECT_TYPE);
TEST_CAST_ERROR("true", int64, NUMBER_ERROR);
TEST_CAST_ERROR("true", uint64, NUMBER_ERROR);
TEST_CAST_ERROR("true", double, NUMBER_ERROR);
TEST_CAST_ERROR("true", string, INCORRECT_TYPE);
TEST_CAST_ERROR("true", raw_json_string, INCORRECT_TYPE);
bool wrong_type_false() {
TEST_START();
TEST_CAST_ERROR("false", array, INCORRECT_TYPE);
TEST_CAST_ERROR("false", object, INCORRECT_TYPE);
TEST_CAST_ERROR("false", int64, NUMBER_ERROR);
TEST_CAST_ERROR("false", uint64, NUMBER_ERROR);
TEST_CAST_ERROR("false", double, NUMBER_ERROR);
TEST_CAST_ERROR("false", string, INCORRECT_TYPE);
TEST_CAST_ERROR("false", raw_json_string, INCORRECT_TYPE);
TEST_SUCCEED();
}
TEST_CAST_ERROR("false", array, INCORRECT_TYPE);
TEST_CAST_ERROR("false", object, INCORRECT_TYPE);
TEST_CAST_ERROR("false", int64, NUMBER_ERROR);
TEST_CAST_ERROR("false", uint64, NUMBER_ERROR);
TEST_CAST_ERROR("false", double, NUMBER_ERROR);
TEST_CAST_ERROR("false", string, INCORRECT_TYPE);
TEST_CAST_ERROR("false", raw_json_string, INCORRECT_TYPE);
bool wrong_type_null() {
TEST_START();
TEST_CAST_ERROR("null", array, INCORRECT_TYPE);
TEST_CAST_ERROR("null", object, INCORRECT_TYPE);
TEST_CAST_ERROR("null", int64, NUMBER_ERROR);
TEST_CAST_ERROR("null", uint64, NUMBER_ERROR);
TEST_CAST_ERROR("null", double, NUMBER_ERROR);
TEST_CAST_ERROR("null", string, INCORRECT_TYPE);
TEST_CAST_ERROR("null", raw_json_string, INCORRECT_TYPE);
TEST_SUCCEED();
}
TEST_CAST_ERROR("null", array, INCORRECT_TYPE);
TEST_CAST_ERROR("null", object, INCORRECT_TYPE);
TEST_CAST_ERROR("null", int64, NUMBER_ERROR);
TEST_CAST_ERROR("null", uint64, NUMBER_ERROR);
TEST_CAST_ERROR("null", double, NUMBER_ERROR);
TEST_CAST_ERROR("null", string, INCORRECT_TYPE);
TEST_CAST_ERROR("null", raw_json_string, INCORRECT_TYPE);
bool wrong_type_1() {
TEST_START();
TEST_CAST_ERROR("1", array, INCORRECT_TYPE);
TEST_CAST_ERROR("1", object, INCORRECT_TYPE);
TEST_CAST_ERROR("1", bool, INCORRECT_TYPE);
TEST_CAST_ERROR("1", string, INCORRECT_TYPE);
TEST_CAST_ERROR("1", raw_json_string, INCORRECT_TYPE);
TEST_SUCCEED();
}
TEST_CAST_ERROR("1", array, INCORRECT_TYPE);
TEST_CAST_ERROR("1", object, INCORRECT_TYPE);
TEST_CAST_ERROR("1", bool, INCORRECT_TYPE);
TEST_CAST_ERROR("1", string, INCORRECT_TYPE);
TEST_CAST_ERROR("1", raw_json_string, INCORRECT_TYPE);
bool wrong_type_negative_1() {
TEST_START();
TEST_CAST_ERROR("-1", array, INCORRECT_TYPE);
TEST_CAST_ERROR("-1", object, INCORRECT_TYPE);
TEST_CAST_ERROR("-1", bool, INCORRECT_TYPE);
TEST_CAST_ERROR("-1", uint64, NUMBER_ERROR);
TEST_CAST_ERROR("-1", string, INCORRECT_TYPE);
TEST_CAST_ERROR("-1", raw_json_string, INCORRECT_TYPE);
TEST_SUCCEED();
}
TEST_CAST_ERROR("-1", array, INCORRECT_TYPE);
TEST_CAST_ERROR("-1", object, INCORRECT_TYPE);
TEST_CAST_ERROR("-1", bool, INCORRECT_TYPE);
TEST_CAST_ERROR("-1", uint64, NUMBER_ERROR);
TEST_CAST_ERROR("-1", string, INCORRECT_TYPE);
TEST_CAST_ERROR("-1", raw_json_string, INCORRECT_TYPE);
bool wrong_type_float() {
TEST_START();
TEST_CAST_ERROR("1.1", array, INCORRECT_TYPE);
TEST_CAST_ERROR("1.1", object, INCORRECT_TYPE);
TEST_CAST_ERROR("1.1", bool, INCORRECT_TYPE);
TEST_CAST_ERROR("1.1", int64, NUMBER_ERROR);
TEST_CAST_ERROR("1.1", uint64, NUMBER_ERROR);
TEST_CAST_ERROR("1.1", string, INCORRECT_TYPE);
TEST_CAST_ERROR("1.1", raw_json_string, INCORRECT_TYPE);
TEST_SUCCEED();
}
TEST_CAST_ERROR("1.1", array, INCORRECT_TYPE);
TEST_CAST_ERROR("1.1", object, INCORRECT_TYPE);
TEST_CAST_ERROR("1.1", bool, INCORRECT_TYPE);
TEST_CAST_ERROR("1.1", int64, NUMBER_ERROR);
TEST_CAST_ERROR("1.1", uint64, NUMBER_ERROR);
TEST_CAST_ERROR("1.1", string, INCORRECT_TYPE);
TEST_CAST_ERROR("1.1", raw_json_string, INCORRECT_TYPE);
bool wrong_type_negative_int64_overflow() {
TEST_START();
TEST_CAST_ERROR("-9223372036854775809", array, INCORRECT_TYPE);
TEST_CAST_ERROR("-9223372036854775809", object, INCORRECT_TYPE);
TEST_CAST_ERROR("-9223372036854775809", bool, INCORRECT_TYPE);
TEST_CAST_ERROR("-9223372036854775809", int64, NUMBER_ERROR);
TEST_CAST_ERROR("-9223372036854775809", uint64, NUMBER_ERROR);
TEST_CAST_ERROR("-9223372036854775809", string, INCORRECT_TYPE);
TEST_CAST_ERROR("-9223372036854775809", raw_json_string, INCORRECT_TYPE);
TEST_SUCCEED();
}
TEST_CAST_ERROR("-9223372036854775809", array, INCORRECT_TYPE);
TEST_CAST_ERROR("-9223372036854775809", object, INCORRECT_TYPE);
TEST_CAST_ERROR("-9223372036854775809", bool, INCORRECT_TYPE);
TEST_CAST_ERROR("-9223372036854775809", int64, NUMBER_ERROR);
TEST_CAST_ERROR("-9223372036854775809", uint64, NUMBER_ERROR);
TEST_CAST_ERROR("-9223372036854775809", string, INCORRECT_TYPE);
TEST_CAST_ERROR("-9223372036854775809", raw_json_string, INCORRECT_TYPE);
bool wrong_type_int64_overflow() {
TEST_START();
TEST_CAST_ERROR("9223372036854775808", array, INCORRECT_TYPE);
TEST_CAST_ERROR("9223372036854775808", object, INCORRECT_TYPE);
TEST_CAST_ERROR("9223372036854775808", bool, INCORRECT_TYPE);
// TODO BUG: this should be an error but is presently not
// TEST_CAST_ERROR("9223372036854775808", int64, NUMBER_ERROR);
TEST_CAST_ERROR("9223372036854775808", string, INCORRECT_TYPE);
TEST_CAST_ERROR("9223372036854775808", raw_json_string, INCORRECT_TYPE);
TEST_SUCCEED();
}
TEST_CAST_ERROR("9223372036854775808", array, INCORRECT_TYPE);
TEST_CAST_ERROR("9223372036854775808", object, INCORRECT_TYPE);
TEST_CAST_ERROR("9223372036854775808", bool, INCORRECT_TYPE);
// TODO BUG: this should be an error but is presently not
// TEST_CAST_ERROR("9223372036854775808", int64, NUMBER_ERROR);
TEST_CAST_ERROR("9223372036854775808", string, INCORRECT_TYPE);
TEST_CAST_ERROR("9223372036854775808", raw_json_string, INCORRECT_TYPE);
bool wrong_type_uint64_overflow() {
TEST_START();
TEST_CAST_ERROR("18446744073709551616", array, INCORRECT_TYPE);
TEST_CAST_ERROR("18446744073709551616", object, INCORRECT_TYPE);
TEST_CAST_ERROR("18446744073709551616", bool, INCORRECT_TYPE);
TEST_CAST_ERROR("18446744073709551616", int64, NUMBER_ERROR);
// TODO BUG: this should be an error but is presently not
// TEST_CAST_ERROR("18446744073709551616", uint64, NUMBER_ERROR);
TEST_CAST_ERROR("18446744073709551616", string, INCORRECT_TYPE);
TEST_CAST_ERROR("18446744073709551616", raw_json_string, INCORRECT_TYPE);
TEST_SUCCEED();
}
TEST_CAST_ERROR("18446744073709551616", array, INCORRECT_TYPE);
TEST_CAST_ERROR("18446744073709551616", object, INCORRECT_TYPE);
TEST_CAST_ERROR("18446744073709551616", bool, INCORRECT_TYPE);
TEST_CAST_ERROR("18446744073709551616", int64, NUMBER_ERROR);
// TODO BUG: this should be an error but is presently not
// TEST_CAST_ERROR("18446744073709551616", uint64, NUMBER_ERROR);
TEST_CAST_ERROR("18446744073709551616", string, INCORRECT_TYPE);
TEST_CAST_ERROR("18446744073709551616", raw_json_string, INCORRECT_TYPE);
bool run() {
return
wrong_type_1() &&
wrong_type_array() &&
wrong_type_false() &&
wrong_type_float() &&
wrong_type_int64_overflow() &&
wrong_type_negative_1() &&
wrong_type_negative_int64_overflow() &&
wrong_type_null() &&
wrong_type_object() &&
wrong_type_true() &&
wrong_type_uint64_overflow() &&
true;
}
TEST_CAST_ERROR(R"("[]")", array, INCORRECT_TYPE);
TEST_CAST_ERROR(R"("{}")", object, INCORRECT_TYPE);
TEST_CAST_ERROR(R"("true")", bool, INCORRECT_TYPE);
TEST_CAST_ERROR(R"("1")", int64, NUMBER_ERROR);
TEST_CAST_ERROR(R"("1")", uint64, NUMBER_ERROR);
TEST_CAST_ERROR(R"("1")", double, NUMBER_ERROR);
TEST_SUCCEED();
}
} // namespace wrong_type
template<typename V, typename T>
bool assert_iterate(T &array, V *expected, size_t N, simdjson::error_code *expected_error, size_t N2) {
bool assert_iterate(T array, V *expected, size_t N, simdjson::error_code *expected_error, size_t N2) {
size_t count = 0;
for (auto elem : array) {
for (auto elem : std::forward<T>(array)) {
V actual;
auto actual_error = elem.get(actual);
if (count >= N) {
@ -950,34 +1001,77 @@ namespace error_tests {
template<typename V, size_t N, size_t N2, typename T>
bool assert_iterate(T &array, V (&&expected)[N], simdjson::error_code (&&expected_error)[N2]) {
return assert_iterate<V, T>(array, expected, N, expected_error, N2);
return assert_iterate<V, T&>(array, expected, N, expected_error, N2);
}
template<size_t N2, typename T>
bool assert_iterate(T &array, simdjson::error_code (&&expected_error)[N2]) {
return assert_iterate<int64_t, T>(array, nullptr, 0, expected_error, N2);
return assert_iterate<int64_t, T&>(array, nullptr, 0, expected_error, N2);
}
template<typename V, size_t N, typename T>
bool assert_iterate(T &array, V (&&expected)[N]) {
return assert_iterate<V, T>(array, expected, N, nullptr, 0);
return assert_iterate<V, T&&>(array, expected, N, nullptr, 0);
}
template<typename V, size_t N, size_t N2, typename T>
bool assert_iterate(T &&array, V (&&expected)[N], simdjson::error_code (&&expected_error)[N2]) {
return assert_iterate<V, T&&>(std::forward<T>(array), expected, N, expected_error, N2);
}
bool array_iterate_error() {
template<size_t N2, typename T>
bool assert_iterate(T &&array, simdjson::error_code (&&expected_error)[N2]) {
return assert_iterate<int64_t, T&&>(std::forward<T>(array), nullptr, 0, expected_error, N2);
}
template<typename V, size_t N, typename T>
bool assert_iterate(T &&array, V (&&expected)[N]) {
return assert_iterate<V, T&&>(std::forward<T>(array), expected, N, nullptr, 0);
}
bool top_level_array_iterate_error() {
TEST_START();
ONDEMAND_SUBTEST("missing comma", "[1 1]", assert_iterate(doc, { int64_t(1) }, { TAPE_ERROR }));
ONDEMAND_SUBTEST("extra comma ", "[1,,1]", assert_iterate(doc, { int64_t(1) }, { NUMBER_ERROR, TAPE_ERROR }));
ONDEMAND_SUBTEST("extra comma ", "[,", assert_iterate(doc, { NUMBER_ERROR, TAPE_ERROR }));
ONDEMAND_SUBTEST("extra comma ", "[,]", assert_iterate(doc, { NUMBER_ERROR }));
ONDEMAND_SUBTEST("extra comma ", "[,,]", assert_iterate(doc, { NUMBER_ERROR, NUMBER_ERROR, TAPE_ERROR }));
TEST_SUCCEED();
}
bool top_level_array_iterate_unclosed_error() {
TEST_START();
ONDEMAND_SUBTEST("unclosed extra comma", "[,", assert_iterate(doc, { NUMBER_ERROR, TAPE_ERROR }));
ONDEMAND_SUBTEST("unclosed ", "[1 ", assert_iterate(doc, { int64_t(1) }, { TAPE_ERROR }));
// TODO These pass the user values that may run past the end of the buffer if they aren't careful
// In particular, if the padding is decorated with the wrong values, we could cause overrun!
ONDEMAND_SUBTEST("unclosed extra comma", "[,,", assert_iterate(doc, { NUMBER_ERROR, NUMBER_ERROR, TAPE_ERROR }));
ONDEMAND_SUBTEST("unclosed ", "[1,", assert_iterate(doc, { int64_t(1) }, { NUMBER_ERROR, TAPE_ERROR }));
ONDEMAND_SUBTEST("unclosed ", "[1", assert_iterate(doc, { NUMBER_ERROR, TAPE_ERROR }));
ONDEMAND_SUBTEST("unclosed ", "[", assert_iterate(doc, { NUMBER_ERROR, TAPE_ERROR }));
TEST_SUCCEED();
}
bool array_iterate_error() {
TEST_START();
ONDEMAND_SUBTEST("missing comma", R"({ "a": [1 1] })", assert_iterate(doc["a"], { int64_t(1) }, { TAPE_ERROR }));
ONDEMAND_SUBTEST("extra comma ", R"({ "a": [1,,1] })", assert_iterate(doc["a"], { int64_t(1) }, { NUMBER_ERROR, TAPE_ERROR }));
ONDEMAND_SUBTEST("extra comma ", R"({ "a": [1,,] })", assert_iterate(doc["a"], { int64_t(1) }, { NUMBER_ERROR }));
ONDEMAND_SUBTEST("extra comma ", R"({ "a": [,] })", assert_iterate(doc["a"], { NUMBER_ERROR }));
ONDEMAND_SUBTEST("extra comma ", R"({ "a": [,,] })", assert_iterate(doc["a"], { NUMBER_ERROR, NUMBER_ERROR, TAPE_ERROR }));
TEST_SUCCEED();
}
bool array_iterate_unclosed_error() {
TEST_START();
ONDEMAND_SUBTEST("unclosed extra comma", R"({ "a": [,)", assert_iterate(doc["a"], { NUMBER_ERROR, TAPE_ERROR }));
ONDEMAND_SUBTEST("unclosed extra comma", R"({ "a": [,,)", assert_iterate(doc["a"], { NUMBER_ERROR, NUMBER_ERROR, TAPE_ERROR }));
ONDEMAND_SUBTEST("unclosed ", R"({ "a": [1 )", assert_iterate(doc["a"], { int64_t(1) }, { TAPE_ERROR }));
// TODO These pass the user values that may run past the end of the buffer if they aren't careful
// In particular, if the padding is decorated with the wrong values, we could cause overrun!
ONDEMAND_SUBTEST("unclosed ", R"({ "a": [1,)", assert_iterate(doc["a"], { int64_t(1) }, { NUMBER_ERROR, TAPE_ERROR }));
ONDEMAND_SUBTEST("unclosed ", R"({ "a": [1)", assert_iterate(doc["a"], { NUMBER_ERROR, TAPE_ERROR }));
ONDEMAND_SUBTEST("unclosed ", R"({ "a": [)", assert_iterate(doc["a"], { NUMBER_ERROR, TAPE_ERROR }));
TEST_SUCCEED();
}
template<typename V, typename T>
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;
@ -1015,39 +1109,107 @@ namespace error_tests {
bool object_iterate_error() {
TEST_START();
ONDEMAND_SUBTEST("missing semicolon", 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(), { NUMBER_ERROR, TAPE_ERROR }));
ONDEMAND_SUBTEST("missing comma ", R"({ "a": 1 "b": 2 })", assert_iterate_object(doc.get_object(), { "a" }, { int64_t(1) }, { TAPE_ERROR }));
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 }));
ONDEMAND_SUBTEST("unclosed ", R"({ "a": 1, )", assert_iterate_object(doc.get_object(), { "a" }, { int64_t(1) }, { TAPE_ERROR }));
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(), { NUMBER_ERROR, 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(), { "a" }, { int64_t(1) }, { TAPE_ERROR }));
// TODO These next two pass the user a value that may run past the end of the buffer if they aren't careful.
// In particular, if the padding is decorated with the wrong values, we could cause overrun!
ONDEMAND_SUBTEST("unclosed ", R"({ "a": 1 )", assert_iterate_object(doc.get_object(), { "a" }, { int64_t(1) }, { TAPE_ERROR }));
ONDEMAND_SUBTEST("unclosed ", R"({ "a": )", assert_iterate_object(doc.get_object(), { NUMBER_ERROR, TAPE_ERROR }));
ONDEMAND_SUBTEST("unclosed ", R"({ "a" )", assert_iterate_object(doc.get_object(), { TAPE_ERROR }));
ONDEMAND_SUBTEST("unclosed ", R"({ )", assert_iterate_object(doc.get_object(), { TAPE_ERROR }));
ONDEMAND_SUBTEST("unclosed", R"({ "a": 1 )", assert_iterate_object(doc.get_object(), { "a" }, { int64_t(1) }, { TAPE_ERROR }));
ONDEMAND_SUBTEST("unclosed", R"({ "a": )", assert_iterate_object(doc.get_object(), { NUMBER_ERROR, TAPE_ERROR }));
ONDEMAND_SUBTEST("unclosed", R"({ "a" )", assert_iterate_object(doc.get_object(), { TAPE_ERROR }));
ONDEMAND_SUBTEST("unclosed", R"({ )", assert_iterate_object(doc.get_object(), { TAPE_ERROR }));
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();
// TODO This one passes the user a value that may run past the end of the buffer if they aren't careful.
// In particular, if the padding is decorated with the wrong values, we could cause overrun!
ONDEMAND_SUBTEST("unclosed", R"({ "a": )", assert_success(doc["a"]));
ONDEMAND_SUBTEST("unclosed", R"({ "a" )", assert_error(doc["a"], TAPE_ERROR));
ONDEMAND_SUBTEST("unclosed", R"({ )", assert_error(doc["a"], TAPE_ERROR));
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"], TAPE_ERROR));
// TODO These next two pass the user a value that may run past the end of the buffer if they aren't 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"], TAPE_ERROR));
ONDEMAND_SUBTEST("unclosed", R"({ "a": )", assert_error(doc["b"], TAPE_ERROR));
ONDEMAND_SUBTEST("unclosed", R"({ "a" )", assert_error(doc["b"], TAPE_ERROR));
ONDEMAND_SUBTEST("unclosed", R"({ )", assert_error(doc["b"], TAPE_ERROR));
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<int64_t>(obj["a"], 1) && assert_error(obj["b"], TAPE_ERROR);
})());
TEST_SUCCEED();
}
bool run() {
return
empty_document_error() &&
wrong_type() &&
top_level_array_iterate_error() &&
top_level_array_iterate_unclosed_error() &&
array_iterate_error() &&
array_iterate_unclosed_error() &&
wrong_type::run() &&
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() &&
true;
}
}
@ -1084,10 +1246,10 @@ int main(int argc, char *argv[]) {
std::cout << "Running basic tests." << std::endl;
if (
parse_api_tests::run() &&
dom_api_tests::run() &&
twitter_tests::run() &&
number_tests::run() &&
// parse_api_tests::run() &&
// dom_api_tests::run() &&
// twitter_tests::run() &&
// number_tests::run() &&
error_tests::run() &&
true
) {

View File

@ -32,7 +32,7 @@ simdjson_really_inline bool equals_expected<const char *, const char *>(const ch
return !strcmp(actual, expected);
}
template<>
simdjson_really_inline bool equals_expected<simdjson::builtin::ondemand::raw_json_string, const char *>(simdjson::builtin::ondemand::raw_json_string actual, const char *expected) {
simdjson_really_inline bool equals_expected<simdjson::builtin::ondemand::raw_json_string, const char *>(simdjson::builtin::ondemand::raw_json_string actual, const char * expected) {
return actual == expected;
}
@ -44,29 +44,61 @@ simdjson_really_inline simdjson::error_code to_error_code(const simdjson::simdjs
return result.error();
}
#define TEST_START() { std::cout << "Running " << __func__ << " ..." << std::endl; }
#define SUBTEST(NAME, TEST) \
{ \
std::cout << "- Subtest " << (NAME) << " ..." << std::endl; \
bool succeeded = (TEST); \
ASSERT(succeeded, "Subtest " NAME " failed"); \
template<typename T>
simdjson_really_inline bool assert_success(const T &actual, const char *operation = "result") {
simdjson::error_code error = to_error_code(actual);
if (error) {
std::cerr << "FAIL: " << operation << " returned error: " << error << std::endl;
return false;
}
return true;
}
#define ASSERT_EQUAL(ACTUAL, EXPECTED) \
do { \
auto _actual = (ACTUAL); \
auto _expected = (EXPECTED); \
if (!equals_expected(_actual, _expected)) { \
std::cerr << "Expected " << (#ACTUAL) << " to be " << _expected << ", got " << _actual << " instead!" << std::endl; \
return false; \
} \
} while(0);
#define ASSERT_ERROR(ACTUAL, EXPECTED) do { auto _actual = to_error_code(ACTUAL); auto _expected = to_error_code(EXPECTED); if (_actual != _expected) { std::cerr << "FAIL: Unexpected error \"" << _actual << "\" (expected \"" << _expected << "\")" << std::endl; return false; } } while (0);
#define ASSERT_TRUE(RESULT) if (!(RESULT)) { std::cerr << "False invariant: " << #RESULT << std::endl; return false; }
#define ASSERT(RESULT, MESSAGE) if (!(RESULT)) { std::cerr << MESSAGE << std::endl; return false; }
#define RUN_TEST(RESULT) if (!RESULT) { return false; }
#define ASSERT_SUCCESS(ERROR) do { auto _error = to_error_code(ERROR); if (_error) { std::cerr << "Expected success, got error: " << _error << std::endl; return false; } } while(0);
#define TEST_FAIL(MESSAGE) { std::cerr << "FAIL: " << (MESSAGE) << std::endl; return false; }
#define TEST_SUCCEED() { return true; }
template<typename A, typename E=A>
simdjson_really_inline bool assert_equal(const A &actual, const E &expected, const char *operation = "result") {
if (!equals_expected(actual, expected)) {
std::cerr << "FAIL: " << operation << " returned " << actual << " (expected " << expected << ")" << std::endl;
return false;
}
return true;
}
template<typename T>
simdjson_really_inline bool assert_error(const T &actual_result, simdjson::error_code expected, const char *operation = "result") {
simdjson::error_code actual = to_error_code(actual_result);
if (actual != expected) {
if (actual) {
std::cerr << "FAIL: " << operation << " failed with error \"" << actual << "\"";
} else {
std::cerr << "FAIL: " << operation << " succeeded";
}
std::cerr << " (expected error \"" << expected << "\")" << std::endl;
return false;
}
return true;
}
template<typename E, typename A>
simdjson_really_inline bool assert_result(simdjson::simdjson_result<A> &&actual_result, const E &expected, const char *operation = "result") {
E actual;
return assert_success(actual_result.get(actual), operation) && assert_equal(actual, expected, operation);
}
simdjson_really_inline bool assert_true(bool value, const char *operation = "result") {
if (!value) {
std::cerr << "FAIL: " << operation << " was false!" << std::endl;
return false;
}
return true;
}
#define TEST_START() do { std::cout << "Running " << __func__ << " ..." << std::endl; } while(0);
#define SUBTEST(NAME, TEST) do { std::cout << "- Subtest " << (NAME) << " ..." << std::endl; if (!(TEST)) { return false; } } while (0);
#define ASSERT_EQUAL(ACTUAL, EXPECTED) do { if (!::assert_equal ((ACTUAL), (EXPECTED), #ACTUAL)) { return false; } } while (0);
#define ASSERT_RESULT(ACTUAL, EXPECTED) do { if (!::assert_equal ((ACTUAL), (EXPECTED), #ACTUAL)) { return false; } } while (0);
#define ASSERT_SUCCESS(ACTUAL) do { if (!::assert_success((ACTUAL), #ACTUAL)) { return false; } } while (0);
#define ASSERT_ERROR(ACTUAL, EXPECTED) do { if (!::assert_error ((ACTUAL), (EXPECTED), #ACTUAL)) { return false; } } while (0);
#define ASSERT_TRUE(ACTUAL) do { if (!::assert_true ((ACTUAL), #ACTUAL)) { return false; } } while (0);
#define ASSERT(ACTUAL, MESSAGE) do { if (!::assert_true ((ACTUAL), (MESSAGE))) { return false; } } while (0);
#define RUN_TEST(ACTUAL) do { if (!(ACTUAL)) { return false; } } while (0);
#define TEST_FAIL(MESSAGE) do { std::cerr << "FAIL: " << (MESSAGE) << std::endl; return false; } while (0);
#define TEST_SUCCEED() do { return true; } while (0);
#endif // TEST_MACROS_H