Merge pull request #1432 from simdjson/jkeiser/safe-lookup
Alert user when they use object indexing incorrectly
This commit is contained in:
commit
814726e5d4
|
@ -176,11 +176,10 @@ endif()
|
|||
#
|
||||
# Other optional flags
|
||||
#
|
||||
option(SIMDJSON_ONDEMAND_SAFETY_RAILS "Validate ondemand user code at runtime to ensure it is being used correctly. Defaults to ON for debug builds, OFF for release builds." $<IF:$<CONFIG:DEBUG>,ON,OFF>)
|
||||
if(SIMDJSON_ONDEMAND_SAFETY_RAILS)
|
||||
message(STATUS "Ondemand safety rails enabled. Ondemand user code will be checked at runtime. This will be slower than normal!")
|
||||
target_compile_definitions(simdjson-flags INTERFACE SIMDJSON_ONDEMAND_SAFETY_RAILS)
|
||||
endif(SIMDJSON_ONDEMAND_SAFETY_RAILS)
|
||||
option(SIMDJSON_DEVELOPMENT_CHECKS "Enable development-time aids, such as checks for incorrect API usage. Enabled by default in DEBUG." OFF)
|
||||
if(SIMDJSON_DEVELOPMENT_CHECKS)
|
||||
target_compile_definitions(simdjson-flags INTERFACE SIMDJSON_DEVELOPMENT_CHECKS)
|
||||
endif()
|
||||
|
||||
option(SIMDJSON_BASH "Allow usage of bash within CMake" ON)
|
||||
|
||||
|
|
|
@ -217,5 +217,10 @@ namespace std {
|
|||
/// If EXPR is an error, returns it.
|
||||
#define SIMDJSON_TRY(EXPR) { auto _err = (EXPR); if (_err) { return _err; } }
|
||||
|
||||
#ifndef SIMDJSON_DEVELOPMENT_CHECKS
|
||||
#ifndef NDEBUG
|
||||
#define SIMDJSON_DEVELOPMENT_CHECKS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif // SIMDJSON_COMMON_DEFS_H
|
||||
|
|
|
@ -56,7 +56,9 @@ simdjson_really_inline array array::started(value_iterator &iter) noexcept {
|
|||
}
|
||||
|
||||
simdjson_really_inline simdjson_result<array_iterator> array::begin() noexcept {
|
||||
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
|
||||
if (!iter.is_at_container_start()) { return OUT_OF_ORDER_ITERATION; }
|
||||
#endif
|
||||
return array_iterator(iter);
|
||||
}
|
||||
simdjson_really_inline simdjson_result<array_iterator> array::end() noexcept {
|
||||
|
|
|
@ -20,12 +20,11 @@ simdjson_really_inline json_iterator &json_iterator::operator=(json_iterator &&o
|
|||
}
|
||||
|
||||
simdjson_really_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parser *_parser) noexcept
|
||||
: token(buf, _parser->dom_parser->structural_indexes.get()),
|
||||
: token(buf, _parser->implementation->structural_indexes.get()),
|
||||
parser{_parser},
|
||||
_string_buf_loc{parser->string_buf.get()},
|
||||
_depth{1}
|
||||
{
|
||||
// Release the string buf so it can be reused by the next document
|
||||
logger::log_headers();
|
||||
}
|
||||
|
||||
|
@ -69,7 +68,7 @@ simdjson_warn_unused simdjson_really_inline error_code json_iterator::skip_child
|
|||
}
|
||||
|
||||
// Now that we've considered the first value, we only increment/decrement for arrays/objects
|
||||
auto end = &parser->dom_parser->structural_indexes[parser->dom_parser->n_structural_indexes];
|
||||
auto end = &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes];
|
||||
while (token.index <= end) {
|
||||
switch (*advance()) {
|
||||
case '[': case '{':
|
||||
|
@ -102,19 +101,19 @@ simdjson_really_inline bool json_iterator::at_root() const noexcept {
|
|||
}
|
||||
|
||||
simdjson_really_inline token_position json_iterator::root_checkpoint() const noexcept {
|
||||
return parser->dom_parser->structural_indexes.get();
|
||||
return parser->implementation->structural_indexes.get();
|
||||
}
|
||||
|
||||
simdjson_really_inline void json_iterator::assert_at_root() const noexcept {
|
||||
SIMDJSON_ASSUME( _depth == 1 );
|
||||
// Visual Studio Clang treats unique_ptr.get() as "side effecting."
|
||||
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
|
||||
SIMDJSON_ASSUME( token.index == parser->dom_parser->structural_indexes.get() );
|
||||
SIMDJSON_ASSUME( token.index == parser->implementation->structural_indexes.get() );
|
||||
#endif
|
||||
}
|
||||
|
||||
simdjson_really_inline bool json_iterator::at_eof() const noexcept {
|
||||
return token.index == &parser->dom_parser->structural_indexes[parser->dom_parser->n_structural_indexes];
|
||||
return token.index == &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes];
|
||||
}
|
||||
|
||||
simdjson_really_inline bool json_iterator::is_alive() const noexcept {
|
||||
|
@ -176,10 +175,28 @@ simdjson_really_inline error_code json_iterator::report_error(error_code _error,
|
|||
simdjson_really_inline token_position json_iterator::position() const noexcept {
|
||||
return token.position();
|
||||
}
|
||||
simdjson_really_inline void json_iterator::set_position(token_position target_checkpoint) noexcept {
|
||||
token.set_position(target_checkpoint);
|
||||
simdjson_really_inline void json_iterator::reenter_child(token_position position, depth_t child_depth) noexcept {
|
||||
SIMDJSON_ASSUME(child_depth >= 1 && child_depth < INT32_MAX);
|
||||
SIMDJSON_ASSUME(_depth == child_depth - 1);
|
||||
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
|
||||
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
|
||||
SIMDJSON_ASSUME(position >= parser->start_positions[child_depth]);
|
||||
#endif
|
||||
#endif
|
||||
token.set_position(position);
|
||||
_depth = child_depth;
|
||||
}
|
||||
|
||||
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
|
||||
simdjson_really_inline token_position json_iterator::start_position(depth_t depth) const noexcept {
|
||||
return parser->start_positions[depth];
|
||||
}
|
||||
simdjson_really_inline void json_iterator::set_start_position(depth_t depth, token_position position) noexcept {
|
||||
parser->start_positions[depth] = position;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
simdjson_really_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept {
|
||||
SIMDJSON_ASSUME(_error == INCORRECT_TYPE || _error == NO_SUCH_FIELD);
|
||||
|
|
|
@ -148,6 +148,7 @@ public:
|
|||
* @param child_depth the expected child depth.
|
||||
*/
|
||||
simdjson_really_inline void descend_to(depth_t parent_depth) noexcept;
|
||||
simdjson_really_inline void descend_to(depth_t parent_depth, int32_t delta) noexcept;
|
||||
|
||||
/**
|
||||
* Get current depth.
|
||||
|
@ -179,7 +180,11 @@ public:
|
|||
template<int N> simdjson_warn_unused simdjson_really_inline bool advance_to_buffer(uint8_t (&tmpbuf)[N]) noexcept;
|
||||
|
||||
simdjson_really_inline token_position position() const noexcept;
|
||||
simdjson_really_inline void set_position(token_position target_checkpoint) noexcept;
|
||||
simdjson_really_inline void reenter_child(token_position position, depth_t child_depth) noexcept;
|
||||
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
|
||||
simdjson_really_inline token_position start_position(depth_t depth) const noexcept;
|
||||
simdjson_really_inline void set_start_position(depth_t depth, token_position position) noexcept;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
simdjson_really_inline json_iterator(const uint8_t *buf, ondemand::parser *parser) noexcept;
|
||||
|
|
|
@ -52,7 +52,9 @@ simdjson_really_inline object::object(const value_iterator &_iter) noexcept
|
|||
}
|
||||
|
||||
simdjson_really_inline simdjson_result<object_iterator> object::begin() noexcept {
|
||||
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
|
||||
if (!iter.is_at_container_start()) { return OUT_OF_ORDER_ITERATION; }
|
||||
#endif
|
||||
return object_iterator(iter);
|
||||
}
|
||||
simdjson_really_inline simdjson_result<object_iterator> object::end() noexcept {
|
||||
|
|
|
@ -25,6 +25,12 @@ simdjson_really_inline bool object_iterator::operator==(const object_iterator &o
|
|||
simdjson_really_inline bool object_iterator::operator!=(const object_iterator &) const noexcept {
|
||||
return iter.is_open();
|
||||
}
|
||||
|
||||
// GCC 7 warns when the first line of this function is inlined away into oblivion due to the caller
|
||||
// relating depth and iterator depth, which is a desired effect. It does not happen if is_open is
|
||||
// marked non-inline.
|
||||
SIMDJSON_PUSH_DISABLE_WARNINGS
|
||||
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
|
||||
simdjson_really_inline object_iterator &object_iterator::operator++() noexcept {
|
||||
// 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.
|
||||
|
@ -37,6 +43,7 @@ simdjson_really_inline object_iterator &object_iterator::operator++() noexcept {
|
|||
if ((error = iter.has_next_field().get(has_value) )) { return *this; };
|
||||
return *this;
|
||||
}
|
||||
SIMDJSON_POP_DISABLE_WARNINGS
|
||||
|
||||
//
|
||||
// ### Live States
|
||||
|
|
|
@ -3,18 +3,20 @@ namespace SIMDJSON_IMPLEMENTATION {
|
|||
namespace ondemand {
|
||||
|
||||
simdjson_warn_unused simdjson_really_inline error_code parser::allocate(size_t new_capacity, size_t new_max_depth) noexcept {
|
||||
if (string_buf && new_capacity == _capacity && new_max_depth == _max_depth) { return SUCCESS; }
|
||||
if (string_buf && new_capacity == capacity() && new_max_depth == max_depth()) { return SUCCESS; }
|
||||
|
||||
// string_capacity copied from document::allocate
|
||||
_capacity = 0;
|
||||
_max_depth = 0;
|
||||
size_t string_capacity = SIMDJSON_ROUNDUP_N(5 * new_capacity / 3 + SIMDJSON_PADDING, 64);
|
||||
string_buf.reset(new (std::nothrow) uint8_t[string_capacity]);
|
||||
if (dom_parser) {
|
||||
SIMDJSON_TRY( dom_parser->set_capacity(new_capacity) );
|
||||
SIMDJSON_TRY( dom_parser->set_max_depth(new_max_depth) );
|
||||
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
|
||||
start_positions.reset(new (std::nothrow) token_position[new_max_depth]);
|
||||
#endif
|
||||
if (implementation) {
|
||||
SIMDJSON_TRY( implementation->set_capacity(new_capacity) );
|
||||
SIMDJSON_TRY( implementation->set_max_depth(new_max_depth) );
|
||||
} else {
|
||||
SIMDJSON_TRY( simdjson::active_implementation->create_dom_parser_implementation(new_capacity, new_max_depth, dom_parser) );
|
||||
SIMDJSON_TRY( simdjson::active_implementation->create_dom_parser_implementation(new_capacity, new_max_depth, implementation) );
|
||||
}
|
||||
_capacity = new_capacity;
|
||||
_max_depth = new_max_depth;
|
||||
|
@ -23,12 +25,12 @@ simdjson_warn_unused simdjson_really_inline error_code parser::allocate(size_t n
|
|||
|
||||
simdjson_warn_unused simdjson_really_inline simdjson_result<document> parser::iterate(const padded_string &buf) & noexcept {
|
||||
// Allocate if needed
|
||||
if (_capacity < buf.size() || !string_buf) {
|
||||
SIMDJSON_TRY( allocate(buf.size(), _max_depth) );
|
||||
if (capacity() < buf.size() || !string_buf) {
|
||||
SIMDJSON_TRY( allocate(buf.size(), max_depth()) );
|
||||
}
|
||||
|
||||
// Run stage 1.
|
||||
SIMDJSON_TRY( dom_parser->stage1(reinterpret_cast<const uint8_t *>(buf.data()), buf.size(), false) );
|
||||
SIMDJSON_TRY( implementation->stage1(reinterpret_cast<const uint8_t *>(buf.data()), buf.size(), false) );
|
||||
return document::start({ reinterpret_cast<const uint8_t *>(buf.data()), this });
|
||||
}
|
||||
|
||||
|
@ -41,15 +43,23 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<document> parser::it
|
|||
|
||||
simdjson_warn_unused simdjson_really_inline simdjson_result<json_iterator> parser::iterate_raw(const padded_string &buf) & noexcept {
|
||||
// Allocate if needed
|
||||
if (_capacity < buf.size()) {
|
||||
SIMDJSON_TRY( allocate(buf.size(), _max_depth) );
|
||||
if (capacity() < buf.size()) {
|
||||
SIMDJSON_TRY( allocate(buf.size(), max_depth()) );
|
||||
}
|
||||
|
||||
// Run stage 1.
|
||||
SIMDJSON_TRY( dom_parser->stage1(reinterpret_cast<const uint8_t *>(buf.data()), buf.size(), false) );
|
||||
SIMDJSON_TRY( implementation->stage1(reinterpret_cast<const uint8_t *>(buf.data()), buf.size(), false) );
|
||||
return json_iterator(reinterpret_cast<const uint8_t *>(buf.data()), this);
|
||||
}
|
||||
|
||||
simdjson_really_inline size_t parser::capacity() const noexcept {
|
||||
return _capacity;
|
||||
}
|
||||
simdjson_really_inline size_t parser::max_depth() const noexcept {
|
||||
return _max_depth;
|
||||
}
|
||||
|
||||
|
||||
} // namespace ondemand
|
||||
} // namespace SIMDJSON_IMPLEMENTATION
|
||||
} // namespace simdjson
|
||||
|
|
|
@ -103,12 +103,18 @@ public:
|
|||
*/
|
||||
simdjson_warn_unused simdjson_result<json_iterator> iterate_raw(const padded_string &json) & noexcept;
|
||||
|
||||
simdjson_really_inline size_t capacity() const noexcept;
|
||||
simdjson_really_inline size_t max_depth() const noexcept;
|
||||
|
||||
private:
|
||||
/** @private [for benchmarking access] The implementation to use */
|
||||
std::unique_ptr<internal::dom_parser_implementation> dom_parser{};
|
||||
std::unique_ptr<internal::dom_parser_implementation> implementation{};
|
||||
size_t _capacity{0};
|
||||
size_t _max_depth{0};
|
||||
size_t _max_depth{DEFAULT_MAX_DEPTH};
|
||||
std::unique_ptr<uint8_t[]> string_buf{};
|
||||
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
|
||||
std::unique_ptr<token_position[]> start_positions{};
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length
|
||||
|
|
|
@ -24,8 +24,10 @@ simdjson_warn_unused simdjson_really_inline bool value_iterator::started_object(
|
|||
_json_iter->ascend_to(depth()-1);
|
||||
return false;
|
||||
}
|
||||
_json_iter->descend_to(depth()+1);
|
||||
logger::log_start_value(*_json_iter, "object");
|
||||
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
|
||||
_json_iter->set_start_position(_depth, _start_position);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -38,7 +40,6 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
|
|||
_json_iter->ascend_to(depth()-1);
|
||||
return false;
|
||||
case ',':
|
||||
_json_iter->descend_to(depth()+1);
|
||||
return true;
|
||||
default:
|
||||
return _json_iter->report_error(TAPE_ERROR, "Missing comma between object fields");
|
||||
|
@ -73,10 +74,12 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
|
|||
// ```
|
||||
//
|
||||
} else if (!is_open()) {
|
||||
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
|
||||
// If we're past the end of the object, we're being iterated out of order.
|
||||
// Note: this isn't perfect detection. It's possible the user is inside some other object; if so,
|
||||
// this object iterator will blithely scan that object for fields.
|
||||
if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; }
|
||||
#endif
|
||||
has_value = false;
|
||||
|
||||
// 3. When a previous search found a field or an iterator yielded a value:
|
||||
|
@ -96,6 +99,9 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
|
|||
} else {
|
||||
if ((error = skip_child() )) { abandon(); return error; }
|
||||
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
|
||||
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
|
||||
if (_json_iter->start_position(_depth) != _start_position) { return OUT_OF_ORDER_ITERATION; }
|
||||
#endif
|
||||
}
|
||||
while (has_value) {
|
||||
// Get the key and colon, stopping at the value.
|
||||
|
@ -147,10 +153,12 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
|
|||
// ```
|
||||
//
|
||||
} else if (!is_open()) {
|
||||
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
|
||||
// If we're past the end of the object, we're being iterated out of order.
|
||||
// Note: this isn't perfect detection. It's possible the user is inside some other object; if so,
|
||||
// this object iterator will blithely scan that object for fields.
|
||||
if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; }
|
||||
#endif
|
||||
has_value = false;
|
||||
|
||||
// 3. When a previous search found a field or an iterator yielded a value:
|
||||
|
@ -171,6 +179,9 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
|
|||
// Finish the previous value and see if , or } is next
|
||||
if ((error = skip_child() )) { abandon(); return error; }
|
||||
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
|
||||
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
|
||||
if (_json_iter->start_position(_depth) != _start_position) { return OUT_OF_ORDER_ITERATION; }
|
||||
#endif
|
||||
}
|
||||
|
||||
// After initial processing, we will be in one of two states:
|
||||
|
@ -193,7 +204,7 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
|
|||
|
||||
// Next, we find a match starting from the current position.
|
||||
while (has_value) {
|
||||
SIMDJSON_ASSUME( _json_iter->_depth == _depth + 1 ); // We must be at the start of a field
|
||||
SIMDJSON_ASSUME( _json_iter->_depth == _depth ); // We must be at the start of a field
|
||||
|
||||
// Get the key and colon, stopping at the value.
|
||||
raw_json_string actual_key;
|
||||
|
@ -216,13 +227,12 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
|
|||
// beginning of the object.
|
||||
// (We have already run through the object before, so we've already validated its structure. We
|
||||
// don't check errors in this bit.)
|
||||
_json_iter->set_position(_start_position + 1);
|
||||
_json_iter->descend_to(_depth);
|
||||
_json_iter->reenter_child(_start_position + 1, _depth);
|
||||
|
||||
has_value = started_object();
|
||||
while (_json_iter->position() < search_start) {
|
||||
SIMDJSON_ASSUME(has_value); // we should reach search_start before ever reaching the end of the object
|
||||
SIMDJSON_ASSUME( _json_iter->_depth == _depth + 1 ); // We must be at the start of a field
|
||||
SIMDJSON_ASSUME( _json_iter->_depth == _depth ); // We must be at the start of a field
|
||||
|
||||
// Get the key and colon, stopping at the value.
|
||||
raw_json_string actual_key;
|
||||
|
@ -246,7 +256,7 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator
|
|||
}
|
||||
|
||||
simdjson_warn_unused simdjson_really_inline simdjson_result<raw_json_string> value_iterator::field_key() noexcept {
|
||||
assert_at_child();
|
||||
assert_at_next();
|
||||
|
||||
const uint8_t *key = _json_iter->advance();
|
||||
if (*(key++) != '"') { return _json_iter->report_error(TAPE_ERROR, "Object key is not a string"); }
|
||||
|
@ -254,9 +264,10 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<raw_json_string> val
|
|||
}
|
||||
|
||||
simdjson_warn_unused simdjson_really_inline error_code value_iterator::field_value() noexcept {
|
||||
assert_at_child();
|
||||
assert_at_next();
|
||||
|
||||
if (*_json_iter->advance() != ':') { return _json_iter->report_error(TAPE_ERROR, "Missing colon in object field"); }
|
||||
_json_iter->descend_to(depth()+1);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -277,6 +288,9 @@ simdjson_warn_unused simdjson_really_inline bool value_iterator::started_array()
|
|||
}
|
||||
logger::log_start_value(*_json_iter, "array");
|
||||
_json_iter->descend_to(depth()+1);
|
||||
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
|
||||
_json_iter->set_start_position(_depth, _start_position);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -448,7 +462,9 @@ simdjson_really_inline error_code value_iterator::advance_container_start(const
|
|||
|
||||
// If we're not at the position anymore, we don't want to advance the cursor.
|
||||
if (!is_at_start()) {
|
||||
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
|
||||
if (!is_at_container_start()) { return OUT_OF_ORDER_ITERATION; }
|
||||
#endif
|
||||
json = peek_start();
|
||||
return SUCCESS;
|
||||
}
|
||||
|
|
|
@ -3,18 +3,21 @@ link_libraries(simdjson)
|
|||
include_directories(..)
|
||||
add_subdirectory(compilation_failure_tests)
|
||||
|
||||
add_cpp_test(ondemand_active_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_array_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_compilation_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_error_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_key_string_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_number_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_object_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_ordering_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_parse_api_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_readme_examples LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_scalar_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_twitter_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_active_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_array_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_array_error_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_compilation_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_error_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_key_string_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_number_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_object_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_object_error_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_ordering_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_parse_api_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_readme_examples LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_scalar_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_twitter_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_wrong_type_error_tests LABELS ondemand acceptance per_implementation)
|
||||
|
||||
if(HAVE_POSIX_FORK AND HAVE_POSIX_WAIT) # assert tests use fork and wait, which aren't on MSVC
|
||||
add_cpp_test(ondemand_assert_out_of_order_values LABELS assert per_implementation explicitonly ondemand)
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
#include "simdjson.h"
|
||||
#include "test_ondemand.h"
|
||||
|
||||
using namespace simdjson;
|
||||
|
||||
namespace array_error_tests {
|
||||
using namespace std;
|
||||
|
||||
template<typename V, typename T>
|
||||
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 : std::forward<T>(array)) {
|
||||
V actual;
|
||||
auto actual_error = elem.get(actual);
|
||||
if (count >= N) {
|
||||
if (count >= (N+N2)) {
|
||||
std::cerr << "FAIL: Extra error reported: " << actual_error << std::endl;
|
||||
return false;
|
||||
}
|
||||
ASSERT_ERROR(actual_error, expected_error[count - N]);
|
||||
} else {
|
||||
ASSERT_SUCCESS(actual_error);
|
||||
ASSERT_EQUAL(actual, expected[count]);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
ASSERT_EQUAL(count, N+N2);
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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) }, { INCORRECT_TYPE, TAPE_ERROR }));
|
||||
ONDEMAND_SUBTEST("extra comma ", "[,]", assert_iterate(doc, { INCORRECT_TYPE }));
|
||||
ONDEMAND_SUBTEST("extra comma ", "[,,]", assert_iterate(doc, { INCORRECT_TYPE, INCORRECT_TYPE, TAPE_ERROR }));
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
bool top_level_array_iterate_unclosed_error() {
|
||||
TEST_START();
|
||||
ONDEMAND_SUBTEST("unclosed extra comma", "[,", assert_iterate(doc, { INCORRECT_TYPE, 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, { INCORRECT_TYPE, INCORRECT_TYPE, TAPE_ERROR }));
|
||||
ONDEMAND_SUBTEST("unclosed ", "[1,", assert_iterate(doc, { int64_t(1) }, { INCORRECT_TYPE, TAPE_ERROR }));
|
||||
ONDEMAND_SUBTEST("unclosed ", "[1", assert_iterate(doc, { NUMBER_ERROR, TAPE_ERROR }));
|
||||
ONDEMAND_SUBTEST("unclosed ", "[", assert_iterate(doc, { INCORRECT_TYPE, 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) }, { INCORRECT_TYPE, TAPE_ERROR }));
|
||||
ONDEMAND_SUBTEST("extra comma ", R"({ "a": [1,,] })", assert_iterate(doc["a"], { int64_t(1) }, { INCORRECT_TYPE }));
|
||||
ONDEMAND_SUBTEST("extra comma ", R"({ "a": [,] })", assert_iterate(doc["a"], { INCORRECT_TYPE }));
|
||||
ONDEMAND_SUBTEST("extra comma ", R"({ "a": [,,] })", assert_iterate(doc["a"], { INCORRECT_TYPE, INCORRECT_TYPE, TAPE_ERROR }));
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
bool array_iterate_unclosed_error() {
|
||||
TEST_START();
|
||||
ONDEMAND_SUBTEST("unclosed extra comma", R"({ "a": [,)", assert_iterate(doc["a"], { INCORRECT_TYPE, TAPE_ERROR }));
|
||||
ONDEMAND_SUBTEST("unclosed extra comma", R"({ "a": [,,)", assert_iterate(doc["a"], { INCORRECT_TYPE, INCORRECT_TYPE, 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) }, { INCORRECT_TYPE, 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"], { INCORRECT_TYPE, TAPE_ERROR }));
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
|
||||
bool out_of_order_array_iteration_error() {
|
||||
TEST_START();
|
||||
auto json = R"([ [ 1, 2 ] ])"_padded;
|
||||
SUBTEST("simdjson_result<value>", test_ondemand_doc(json, [&](auto doc) {
|
||||
for (auto element : doc) {
|
||||
for (auto subelement : element) { ASSERT_SUCCESS(subelement); }
|
||||
ASSERT_ERROR( element.begin(), OUT_OF_ORDER_ITERATION );
|
||||
}
|
||||
return true;
|
||||
}));
|
||||
SUBTEST("value", test_ondemand_doc(json, [&](auto doc) {
|
||||
for (auto element : doc) {
|
||||
ondemand::value val;
|
||||
ASSERT_SUCCESS( element.get(val) );
|
||||
for (auto subelement : val) { ASSERT_SUCCESS(subelement); }
|
||||
ASSERT_ERROR( val.begin(), OUT_OF_ORDER_ITERATION );
|
||||
}
|
||||
return true;
|
||||
}));
|
||||
SUBTEST("simdjson_result<array>", test_ondemand_doc(json, [&](auto doc) {
|
||||
for (auto element : doc) {
|
||||
auto arr = element.get_array();
|
||||
for (auto subelement : arr) { ASSERT_SUCCESS(subelement); }
|
||||
ASSERT_ERROR( arr.begin(), OUT_OF_ORDER_ITERATION );
|
||||
}
|
||||
return true;
|
||||
}));
|
||||
SUBTEST("array", test_ondemand_doc(json, [&](auto doc) {
|
||||
for (auto element : doc) {
|
||||
ondemand::array arr;
|
||||
ASSERT_SUCCESS( element.get(arr) );
|
||||
for (auto subelement : arr) { ASSERT_SUCCESS(subelement); }
|
||||
ASSERT_ERROR( arr.begin(), OUT_OF_ORDER_ITERATION );
|
||||
}
|
||||
return true;
|
||||
}));
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
#endif // SIMDJSON_DEVELOPMENT_CHECKS
|
||||
|
||||
bool run() {
|
||||
return
|
||||
top_level_array_iterate_error() &&
|
||||
top_level_array_iterate_unclosed_error() &&
|
||||
array_iterate_error() &&
|
||||
array_iterate_unclosed_error() &&
|
||||
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
|
||||
out_of_order_array_iteration_error() &&
|
||||
#endif
|
||||
true;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
return test_main(argc, argv, array_error_tests::run);
|
||||
}
|
|
@ -14,390 +14,6 @@ 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) ); \
|
||||
return true; \
|
||||
})) { \
|
||||
return false; \
|
||||
}; \
|
||||
}
|
||||
|
||||
bool wrong_type_array() {
|
||||
TEST_START();
|
||||
TEST_CAST_ERROR("[]", object, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("[]", bool, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("[]", int64, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("[]", uint64, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("[]", double, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("[]", string, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("[]", raw_json_string, INCORRECT_TYPE);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool wrong_type_object() {
|
||||
TEST_START();
|
||||
TEST_CAST_ERROR("{}", array, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("{}", bool, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("{}", int64, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("{}", uint64, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("{}", double, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("{}", string, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("{}", raw_json_string, INCORRECT_TYPE);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
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, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("true", uint64, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("true", double, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("true", string, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("true", raw_json_string, INCORRECT_TYPE);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
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, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("false", uint64, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("false", double, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("false", string, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("false", raw_json_string, INCORRECT_TYPE);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
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, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("null", uint64, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("null", double, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("null", string, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("null", raw_json_string, INCORRECT_TYPE);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("-1", string, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("-1", raw_json_string, INCORRECT_TYPE);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
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, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("1.1", uint64, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("1.1", string, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("1.1", raw_json_string, INCORRECT_TYPE);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
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, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("-9223372036854775809", uint64, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("-9223372036854775809", string, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("-9223372036854775809", raw_json_string, INCORRECT_TYPE);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
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, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("9223372036854775808", string, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("9223372036854775808", raw_json_string, INCORRECT_TYPE);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
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, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("18446744073709551616", uint64, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("18446744073709551616", string, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("18446744073709551616", raw_json_string, INCORRECT_TYPE);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
} // 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) {
|
||||
size_t count = 0;
|
||||
for (auto elem : std::forward<T>(array)) {
|
||||
V actual;
|
||||
auto actual_error = elem.get(actual);
|
||||
if (count >= N) {
|
||||
if (count >= (N+N2)) {
|
||||
std::cerr << "FAIL: Extra error reported: " << actual_error << std::endl;
|
||||
return false;
|
||||
}
|
||||
ASSERT_ERROR(actual_error, expected_error[count - N]);
|
||||
} else {
|
||||
ASSERT_SUCCESS(actual_error);
|
||||
ASSERT_EQUAL(actual, expected[count]);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
ASSERT_EQUAL(count, N+N2);
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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) }, { INCORRECT_TYPE, TAPE_ERROR }));
|
||||
ONDEMAND_SUBTEST("extra comma ", "[,]", assert_iterate(doc, { INCORRECT_TYPE }));
|
||||
ONDEMAND_SUBTEST("extra comma ", "[,,]", assert_iterate(doc, { INCORRECT_TYPE, INCORRECT_TYPE, TAPE_ERROR }));
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
bool top_level_array_iterate_unclosed_error() {
|
||||
TEST_START();
|
||||
ONDEMAND_SUBTEST("unclosed extra comma", "[,", assert_iterate(doc, { INCORRECT_TYPE, 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, { INCORRECT_TYPE, INCORRECT_TYPE, TAPE_ERROR }));
|
||||
ONDEMAND_SUBTEST("unclosed ", "[1,", assert_iterate(doc, { int64_t(1) }, { INCORRECT_TYPE, TAPE_ERROR }));
|
||||
ONDEMAND_SUBTEST("unclosed ", "[1", assert_iterate(doc, { NUMBER_ERROR, TAPE_ERROR }));
|
||||
ONDEMAND_SUBTEST("unclosed ", "[", assert_iterate(doc, { INCORRECT_TYPE, 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) }, { INCORRECT_TYPE, TAPE_ERROR }));
|
||||
ONDEMAND_SUBTEST("extra comma ", R"({ "a": [1,,] })", assert_iterate(doc["a"], { int64_t(1) }, { INCORRECT_TYPE }));
|
||||
ONDEMAND_SUBTEST("extra comma ", R"({ "a": [,] })", assert_iterate(doc["a"], { INCORRECT_TYPE }));
|
||||
ONDEMAND_SUBTEST("extra comma ", R"({ "a": [,,] })", assert_iterate(doc["a"], { INCORRECT_TYPE, INCORRECT_TYPE, TAPE_ERROR }));
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
bool array_iterate_unclosed_error() {
|
||||
TEST_START();
|
||||
ONDEMAND_SUBTEST("unclosed extra comma", R"({ "a": [,)", assert_iterate(doc["a"], { INCORRECT_TYPE, TAPE_ERROR }));
|
||||
ONDEMAND_SUBTEST("unclosed extra comma", R"({ "a": [,,)", assert_iterate(doc["a"], { INCORRECT_TYPE, INCORRECT_TYPE, 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) }, { INCORRECT_TYPE, 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"], { INCORRECT_TYPE, 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;
|
||||
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().first, expected_key[count]);
|
||||
ASSERT_EQUAL(actual, expected[count]);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
ASSERT_EQUAL(count, N+N2);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename V, size_t N, size_t N2, typename T>
|
||||
bool assert_iterate_object(T &&object, const char *(&&expected_key)[N], V (&&expected)[N], simdjson::error_code (&&expected_error)[N2]) {
|
||||
return assert_iterate_object<V, T>(std::forward<T>(object), expected_key, expected, N, expected_error, N2);
|
||||
}
|
||||
|
||||
template<size_t N2, typename T>
|
||||
bool assert_iterate_object(T &&object, simdjson::error_code (&&expected_error)[N2]) {
|
||||
return assert_iterate_object<int64_t, T>(std::forward<T>(object), nullptr, nullptr, 0, expected_error, N2);
|
||||
}
|
||||
|
||||
template<typename V, size_t N, typename T>
|
||||
bool assert_iterate_object(T &&object, const char *(&&expected_key)[N], V (&&expected)[N]) {
|
||||
return assert_iterate_object<V, T>(std::forward<T>(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(), { "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(), { INCORRECT_TYPE, 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 get_fail_then_succeed_bool() {
|
||||
TEST_START();
|
||||
auto json = R"({ "val" : true })"_padded;
|
||||
|
@ -565,164 +181,11 @@ namespace error_tests {
|
|||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool out_of_order_array_iteration_error() {
|
||||
TEST_START();
|
||||
auto json = R"([ [ 1, 2 ] ])"_padded;
|
||||
SUBTEST("simdjson_result<value>", test_ondemand_doc(json, [&](auto doc) {
|
||||
for (auto element : doc) {
|
||||
for (auto subelement : element) { ASSERT_SUCCESS(subelement); }
|
||||
ASSERT_ERROR( element.begin(), OUT_OF_ORDER_ITERATION );
|
||||
}
|
||||
return true;
|
||||
}));
|
||||
SUBTEST("value", test_ondemand_doc(json, [&](auto doc) {
|
||||
for (auto element : doc) {
|
||||
ondemand::value val;
|
||||
ASSERT_SUCCESS( element.get(val) );
|
||||
for (auto subelement : val) { ASSERT_SUCCESS(subelement); }
|
||||
ASSERT_ERROR( val.begin(), OUT_OF_ORDER_ITERATION );
|
||||
}
|
||||
return true;
|
||||
}));
|
||||
SUBTEST("simdjson_result<array>", test_ondemand_doc(json, [&](auto doc) {
|
||||
for (auto element : doc) {
|
||||
auto arr = element.get_array();
|
||||
for (auto subelement : arr) { ASSERT_SUCCESS(subelement); }
|
||||
ASSERT_ERROR( arr.begin(), OUT_OF_ORDER_ITERATION );
|
||||
}
|
||||
return true;
|
||||
}));
|
||||
SUBTEST("array", test_ondemand_doc(json, [&](auto doc) {
|
||||
for (auto element : doc) {
|
||||
ondemand::array arr;
|
||||
ASSERT_SUCCESS( element.get(arr) );
|
||||
for (auto subelement : arr) { ASSERT_SUCCESS(subelement); }
|
||||
ASSERT_ERROR( arr.begin(), OUT_OF_ORDER_ITERATION );
|
||||
}
|
||||
return true;
|
||||
}));
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool out_of_order_object_iteration_error() {
|
||||
TEST_START();
|
||||
auto json = R"([ { "x": 1, "y": 2 } ])"_padded;
|
||||
SUBTEST("simdjson_result<object>", test_ondemand_doc(json, [&](auto doc) {
|
||||
for (auto element : doc) {
|
||||
auto obj = element.get_object();
|
||||
for (auto field : obj) { ASSERT_SUCCESS(field); }
|
||||
ASSERT_ERROR( obj.begin(), 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_ERROR( obj.begin(), OUT_OF_ORDER_ITERATION );
|
||||
}
|
||||
return true;
|
||||
}));
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool out_of_order_object_index_error() {
|
||||
TEST_START();
|
||||
auto json = R"([ { "x": 1, "y": 2 } ])"_padded;
|
||||
SUBTEST("simdjson_result<object>", test_ondemand_doc(json, [&](auto doc) {
|
||||
simdjson_result<ondemand::object> 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;
|
||||
}));
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool out_of_order_object_find_field_error() {
|
||||
TEST_START();
|
||||
auto json = R"([ { "x": 1, "y": 2 } ])"_padded;
|
||||
SUBTEST("simdjson_result<object>", test_ondemand_doc(json, [&](auto doc) {
|
||||
simdjson_result<ondemand::object> 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;
|
||||
}));
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool out_of_order_object_find_field_unordered_error() {
|
||||
TEST_START();
|
||||
auto json = R"([ { "x": 1, "y": 2 } ])"_padded;
|
||||
SUBTEST("simdjson_result<object>", test_ondemand_doc(json, [&](auto doc) {
|
||||
simdjson_result<ondemand::object> 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;
|
||||
}));
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool run() {
|
||||
return
|
||||
empty_document_error() &&
|
||||
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() &&
|
||||
get_fail_then_succeed_bool() &&
|
||||
get_fail_then_succeed_null() &&
|
||||
out_of_order_array_iteration_error() &&
|
||||
out_of_order_object_iteration_error() &&
|
||||
out_of_order_object_index_error() &&
|
||||
out_of_order_object_find_field_error() &&
|
||||
out_of_order_object_find_field_unordered_error() &&
|
||||
true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,544 @@
|
|||
#include "simdjson.h"
|
||||
#include "test_ondemand.h"
|
||||
|
||||
using namespace simdjson;
|
||||
|
||||
namespace object_error_tests {
|
||||
using namespace std;
|
||||
|
||||
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;
|
||||
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().first, expected_key[count]);
|
||||
ASSERT_EQUAL(actual, expected[count]);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
ASSERT_EQUAL(count, N+N2);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename V, size_t N, size_t N2, typename T>
|
||||
bool assert_iterate_object(T &&object, const char *(&&expected_key)[N], V (&&expected)[N], simdjson::error_code (&&expected_error)[N2]) {
|
||||
return assert_iterate_object<V, T>(std::forward<T>(object), expected_key, expected, N, expected_error, N2);
|
||||
}
|
||||
|
||||
template<size_t N2, typename T>
|
||||
bool assert_iterate_object(T &&object, simdjson::error_code (&&expected_error)[N2]) {
|
||||
return assert_iterate_object<int64_t, T>(std::forward<T>(object), nullptr, nullptr, 0, expected_error, N2);
|
||||
}
|
||||
|
||||
template<typename V, size_t N, typename T>
|
||||
bool assert_iterate_object(T &&object, const char *(&&expected_key)[N], V (&&expected)[N]) {
|
||||
return assert_iterate_object<V, T>(std::forward<T>(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(), { "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(), { INCORRECT_TYPE, 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();
|
||||
}
|
||||
|
||||
#ifdef SIMDJSON_DEVELOPMENT_CHECKS
|
||||
bool out_of_order_object_iteration_error() {
|
||||
TEST_START();
|
||||
auto json = R"([ { "x": 1, "y": 2 } ])"_padded;
|
||||
SUBTEST("simdjson_result<object>", test_ondemand_doc(json, [&](auto doc) {
|
||||
for (auto element : doc) {
|
||||
auto obj = element.get_object();
|
||||
for (auto field : obj) { ASSERT_SUCCESS(field); }
|
||||
ASSERT_ERROR( obj.begin(), 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_ERROR( obj.begin(), 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<object>", test_ondemand_doc(json, [&](auto doc) {
|
||||
simdjson_result<ondemand::object> 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<value>", test_ondemand_doc(json, [&](auto doc) {
|
||||
simdjson_result<ondemand::value> 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<object>", test_ondemand_doc(json, [&](auto doc) {
|
||||
simdjson_result<ondemand::object> 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<value>", test_ondemand_doc(json, [&](auto doc) {
|
||||
simdjson_result<ondemand::value> 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<object>", test_ondemand_doc(json, [&](auto doc) {
|
||||
simdjson_result<ondemand::object> 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<value>", test_ondemand_doc(json, [&](auto doc) {
|
||||
simdjson_result<ondemand::value> 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<object>", test_ondemand_doc(json, [&](auto doc) {
|
||||
simdjson_result<ondemand::object> 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<value>", test_ondemand_doc(json, [&](auto doc) {
|
||||
simdjson_result<ondemand::value> 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<object>", test_ondemand_doc(json, [&](auto doc) {
|
||||
simdjson_result<ondemand::object> 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<value>", test_ondemand_doc(json, [&](auto doc) {
|
||||
simdjson_result<ondemand::value> 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<object>", test_ondemand_doc(json, [&](auto doc) {
|
||||
simdjson_result<ondemand::object> 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<value>", test_ondemand_doc(json, [&](auto doc) {
|
||||
simdjson_result<ondemand::value> 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
|
||||
out_of_order_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);
|
||||
}
|
|
@ -483,6 +483,11 @@ namespace object_tests {
|
|||
ASSERT_ERROR( object["d"], NO_SUCH_FIELD );
|
||||
return true;
|
||||
}));
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
bool document_object_index() {
|
||||
TEST_START();
|
||||
auto json = R"({ "a": 1, "b": 2, "c/d": 3})"_padded;
|
||||
SUBTEST("ondemand::document", test_ondemand_doc(json, [&](auto doc_result) {
|
||||
ondemand::document doc;
|
||||
ASSERT_SUCCESS( std::move(doc_result).get(doc) );
|
||||
|
@ -503,8 +508,11 @@ namespace object_tests {
|
|||
ASSERT_ERROR( doc_result["d"], NO_SUCH_FIELD );
|
||||
return true;
|
||||
}));
|
||||
|
||||
json = R"({ "outer": { "a": 1, "b": 2, "c/d": 3 } })"_padded;
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
bool value_object_index() {
|
||||
TEST_START();
|
||||
auto json = R"({ "outer": { "a": 1, "b": 2, "c/d": 3 } })"_padded;
|
||||
SUBTEST("ondemand::value", test_ondemand_doc(json, [&](auto doc_result) {
|
||||
ondemand::value object;
|
||||
ASSERT_SUCCESS( doc_result["outer"].get(object) );
|
||||
|
@ -556,6 +564,12 @@ namespace object_tests {
|
|||
ASSERT_ERROR( object.find_field_unordered("d"), NO_SUCH_FIELD );
|
||||
return true;
|
||||
}));
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool document_object_find_field_unordered() {
|
||||
TEST_START();
|
||||
auto json = R"({ "a": 1, "b": 2, "c/d": 3})"_padded;
|
||||
SUBTEST("ondemand::document", test_ondemand_doc(json, [&](auto doc_result) {
|
||||
ondemand::document doc;
|
||||
ASSERT_SUCCESS( std::move(doc_result).get(doc) );
|
||||
|
@ -576,8 +590,12 @@ namespace object_tests {
|
|||
ASSERT_ERROR( doc_result.find_field_unordered("d"), NO_SUCH_FIELD );
|
||||
return true;
|
||||
}));
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
json = R"({ "outer": { "a": 1, "b": 2, "c/d": 3 } })"_padded;
|
||||
bool value_object_find_field_unordered() {
|
||||
TEST_START();
|
||||
auto json = R"({ "outer": { "a": 1, "b": 2, "c/d": 3 } })"_padded;
|
||||
SUBTEST("ondemand::value", test_ondemand_doc(json, [&](auto doc_result) {
|
||||
ondemand::value object;
|
||||
ASSERT_SUCCESS( doc_result.find_field_unordered("outer").get(object) );
|
||||
|
@ -629,6 +647,12 @@ namespace object_tests {
|
|||
ASSERT_ERROR( object.find_field("d"), NO_SUCH_FIELD );
|
||||
return true;
|
||||
}));
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool document_object_find_field() {
|
||||
TEST_START();
|
||||
auto json = R"({ "a": 1, "b": 2, "c/d": 3})"_padded;
|
||||
SUBTEST("ondemand::document", test_ondemand_doc(json, [&](auto doc_result) {
|
||||
ondemand::document doc;
|
||||
ASSERT_SUCCESS( std::move(doc_result).get(doc) );
|
||||
|
@ -649,8 +673,12 @@ namespace object_tests {
|
|||
ASSERT_ERROR( doc_result.find_field("d"), NO_SUCH_FIELD );
|
||||
return true;
|
||||
}));
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
json = R"({ "outer": { "a": 1, "b": 2, "c/d": 3 } })"_padded;
|
||||
bool value_object_find_field() {
|
||||
TEST_START();
|
||||
auto json = R"({ "outer": { "a": 1, "b": 2, "c/d": 3 } })"_padded;
|
||||
SUBTEST("ondemand::value", test_ondemand_doc(json, [&](auto doc_result) {
|
||||
ondemand::value object;
|
||||
ASSERT_SUCCESS( doc_result.find_field("outer").get(object) );
|
||||
|
@ -675,7 +703,7 @@ namespace object_tests {
|
|||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool nested_object_index() {
|
||||
bool document_nested_object_index() {
|
||||
TEST_START();
|
||||
auto json = R"({ "x": { "y": { "z": 2 } } }})"_padded;
|
||||
SUBTEST("simdjson_result<ondemand::document>", test_ondemand_doc(json, [&](auto doc_result) {
|
||||
|
@ -688,6 +716,12 @@ namespace object_tests {
|
|||
ASSERT_EQUAL( doc["x"]["y"]["z"].get_uint64().value_unsafe(), 2 );
|
||||
return true;
|
||||
}));
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool nested_object_index() {
|
||||
TEST_START();
|
||||
auto json = R"({ "x": { "y": { "z": 2 } } }})"_padded;
|
||||
SUBTEST("simdjson_result<ondemand::object>", test_ondemand_doc(json, [&](auto doc_result) {
|
||||
simdjson_result<ondemand::object> object = doc_result.get_object();
|
||||
ASSERT_EQUAL( object["x"]["y"]["z"].get_uint64().value_unsafe(), 2 );
|
||||
|
@ -699,6 +733,12 @@ namespace object_tests {
|
|||
ASSERT_EQUAL( object["x"]["y"]["z"].get_uint64().value_unsafe(), 2 );
|
||||
return true;
|
||||
}));
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool value_nested_object_index() {
|
||||
TEST_START();
|
||||
auto json = R"({ "x": { "y": { "z": 2 } } }})"_padded;
|
||||
SUBTEST("simdjson_result<ondemand::value>", test_ondemand_doc(json, [&](auto doc_result) {
|
||||
simdjson_result<ondemand::value> x = doc_result["x"];
|
||||
ASSERT_EQUAL( x["y"]["z"].get_uint64().value_unsafe(), 2 );
|
||||
|
@ -778,9 +818,17 @@ namespace object_tests {
|
|||
iterate_object() &&
|
||||
iterate_empty_object() &&
|
||||
object_index() &&
|
||||
document_object_index() &&
|
||||
value_object_index() &&
|
||||
object_find_field_unordered() &&
|
||||
document_object_find_field_unordered() &&
|
||||
value_object_find_field_unordered() &&
|
||||
object_find_field() &&
|
||||
document_object_find_field() &&
|
||||
value_object_find_field() &&
|
||||
nested_object_index() &&
|
||||
document_nested_object_index() &&
|
||||
value_nested_object_index() &&
|
||||
iterate_object_partial_children() &&
|
||||
object_index_partial_children() &&
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
|
|
|
@ -0,0 +1,177 @@
|
|||
#include "simdjson.h"
|
||||
#include "test_ondemand.h"
|
||||
|
||||
using namespace simdjson;
|
||||
|
||||
namespace wrong_type_error_tests {
|
||||
using namespace std;
|
||||
|
||||
#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) ); \
|
||||
return true; \
|
||||
})) { \
|
||||
return false; \
|
||||
}; \
|
||||
}
|
||||
|
||||
bool wrong_type_array() {
|
||||
TEST_START();
|
||||
TEST_CAST_ERROR("[]", object, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("[]", bool, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("[]", int64, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("[]", uint64, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("[]", double, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("[]", string, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("[]", raw_json_string, INCORRECT_TYPE);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool wrong_type_object() {
|
||||
TEST_START();
|
||||
TEST_CAST_ERROR("{}", array, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("{}", bool, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("{}", int64, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("{}", uint64, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("{}", double, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("{}", string, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("{}", raw_json_string, INCORRECT_TYPE);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
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, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("true", uint64, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("true", double, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("true", string, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("true", raw_json_string, INCORRECT_TYPE);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
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, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("false", uint64, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("false", double, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("false", string, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("false", raw_json_string, INCORRECT_TYPE);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
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, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("null", uint64, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("null", double, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("null", string, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("null", raw_json_string, INCORRECT_TYPE);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("-1", string, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("-1", raw_json_string, INCORRECT_TYPE);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
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, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("1.1", uint64, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("1.1", string, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("1.1", raw_json_string, INCORRECT_TYPE);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
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, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("-9223372036854775809", uint64, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("-9223372036854775809", string, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("-9223372036854775809", raw_json_string, INCORRECT_TYPE);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
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, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("9223372036854775808", string, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("9223372036854775808", raw_json_string, INCORRECT_TYPE);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
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, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("18446744073709551616", uint64, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("18446744073709551616", string, INCORRECT_TYPE);
|
||||
TEST_CAST_ERROR("18446744073709551616", raw_json_string, INCORRECT_TYPE);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
} // namespace wrong_type_error_tests
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
return test_main(argc, argv, wrong_type_error_tests::run);
|
||||
}
|
Loading…
Reference in New Issue