Skip to content

Commit ac0899c

Browse files
committed
Add error tests, doc_ref_result[] chaining
1 parent 40c6213 commit ac0899c

File tree

7 files changed

+208
-14
lines changed

7 files changed

+208
-14
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ objs
6969
/build-plain-*/
7070
/corpus.zip
7171
/distinctuseridcompetition
72+
/errortests
7273
/fuzz/fuzz_dump
7374
/fuzz/fuzz_dump_raw_tape
7475
/fuzz/fuzz_parser
@@ -107,6 +108,7 @@ objs
107108
/singleheader/amalgamation_demo
108109
/singleheader/demo
109110
/tests/basictests
111+
/tests/errortests
110112
/tests/jsoncheck
111113
/tests/pointercheck
112114
/tests/integer_tests

Makefile

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ ARCHFLAGS ?= -msse4.2 -mpclmul # lowest supported feature set?
2323
endif
2424

2525
CXXFLAGS = $(ARCHFLAGS) -std=c++17 -pthread -Wall -Wextra -Wshadow -Ibenchmark/linux
26-
CFLAGS = $(ARCHFLAGS) -Idependencies/ujson4c/3rdparty -Idependencies/ujson4c/src $(EXTRAFLAGS)
26+
CFLAGS = $(ARCHFLAGS) -Idependencies/ujson4c/3rdparty -Idependencies/ujson4c/src $(EXTRAFLAGS)
2727

2828
# This is a convenience flag
2929
ifdef SANITIZEGOLD
@@ -39,16 +39,16 @@ endif
3939

4040
# SANITIZE *implies* DEBUG
4141
ifeq ($(MEMSANITIZE),1)
42-
CXXFLAGS += -g3 -O0 -fsanitize=memory -fno-omit-frame-pointer -fsanitize=undefined
43-
CFLAGS += -g3 -O0 -fsanitize=memory -fno-omit-frame-pointer -fsanitize=undefined
42+
CXXFLAGS += -g3 -O0 -fsanitize=memory -fno-omit-frame-pointer -fsanitize=undefined
43+
CFLAGS += -g3 -O0 -fsanitize=memory -fno-omit-frame-pointer -fsanitize=undefined
4444
else
4545
ifeq ($(SANITIZE),1)
4646
CXXFLAGS += -g3 -O0 -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined
4747
CFLAGS += -g3 -O0 -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined
4848
else
4949
ifeq ($(DEBUG),1)
50-
CXXFLAGS += -g3 -O0
51-
CFLAGS += -g3 -O0
50+
CXXFLAGS += -g3 -O0
51+
CFLAGS += -g3 -O0
5252
else
5353
# we opt for -O3 for regular builds
5454
CXXFLAGS += -O3
@@ -95,7 +95,7 @@ JSON_INCLUDE:=dependencies/json/single_include/nlohmann/json.hpp
9595
EXTRAOBJECTS=ujdecode.o
9696

9797
MAINEXECUTABLES=parse minify json2json jsonstats statisticalmodel jsonpointer get_corpus_benchmark
98-
TESTEXECUTABLES=jsoncheck jsoncheck_noavx integer_tests numberparsingcheck stringparsingcheck pointercheck parse_many_test basictests readme_examples
98+
TESTEXECUTABLES=jsoncheck jsoncheck_noavx integer_tests numberparsingcheck stringparsingcheck pointercheck parse_many_test basictests errortests readme_examples
9999
COMPARISONEXECUTABLES=minifiercompetition parsingcompetition parseandstatcompetition distinctuseridcompetition allparserscheckfile allparsingcompetition
100100
SUPPLEMENTARYEXECUTABLES=parse_noutf8validation parse_nonumberparsing parse_nostringparsing
101101

@@ -112,6 +112,9 @@ benchmark:
112112
run_basictests: basictests
113113
./basictests
114114

115+
run_errortests: errortests
116+
./errortests
117+
115118
run_readme_examples: readme_examples
116119
./readme_examples
117120

@@ -217,6 +220,9 @@ jsoncheck_noavx:tests/jsoncheck.cpp $(HEADERS) $(LIBFILES)
217220
basictests:tests/basictests.cpp $(HEADERS) $(LIBFILES)
218221
$(CXX) $(CXXFLAGS) -o basictests tests/basictests.cpp -I. $(LIBFILES) $(LIBFLAGS)
219222

223+
errortests:tests/errortests.cpp $(HEADERS) $(LIBFILES)
224+
$(CXX) $(CXXFLAGS) -o errortests tests/errortests.cpp -I. $(LIBFILES) $(LIBFLAGS)
225+
220226
readme_examples: tests/readme_examples.cpp $(HEADERS) $(LIBFILES)
221227
$(CXX) $(CXXFLAGS) -o readme_examples tests/readme_examples.cpp -I. $(LIBFILES) $(LIBFLAGS)
222228

include/simdjson/document.h

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,33 @@ class document::doc_result {
265265
*/
266266
operator document() noexcept(false);
267267

268+
/**
269+
* Get the value associated with the given key.
270+
*
271+
* The key will be matched against **unescaped** JSON:
272+
*
273+
* document::parse(R"({ "a\n": 1 })")["a\n"].as_uint64_t().value == 1
274+
* document::parse(R"({ "a\n": 1 })")["a\\n"].as_uint64_t().error == NO_SUCH_FIELD
275+
*
276+
* @return The value associated with this field, or:
277+
* - NO_SUCH_FIELD if the field does not exist in the object
278+
* - UNEXPECTED_TYPE if the document is not an object
279+
*/
280+
inline element_result<element> operator[](const std::string_view &key) const noexcept;
281+
/**
282+
* Get the value associated with the given key.
283+
*
284+
* The key will be matched against **unescaped** JSON:
285+
*
286+
* document::parse(R"({ "a\n": 1 })")["a\n"].as_uint64_t().value == 1
287+
* document::parse(R"({ "a\n": 1 })")["a\\n"].as_uint64_t().error == NO_SUCH_FIELD
288+
*
289+
* @return The value associated with this field, or:
290+
* - NO_SUCH_FIELD if the field does not exist in the object
291+
* - UNEXPECTED_TYPE if the document is not an object
292+
*/
293+
inline element_result<element> operator[](const char *key) const noexcept;
294+
268295
~doc_result() noexcept=default;
269296

270297
private:
@@ -324,6 +351,34 @@ class document::doc_ref_result {
324351
*/
325352
operator document&() noexcept(false);
326353

354+
/**
355+
* Get the value associated with the given key.
356+
*
357+
* The key will be matched against **unescaped** JSON:
358+
*
359+
* document::parse(R"({ "a\n": 1 })")["a\n"].as_uint64_t().value == 1
360+
* document::parse(R"({ "a\n": 1 })")["a\\n"].as_uint64_t().error == NO_SUCH_FIELD
361+
*
362+
* @return The value associated with this field, or:
363+
* - NO_SUCH_FIELD if the field does not exist in the object
364+
* - UNEXPECTED_TYPE if the document is not an object
365+
*/
366+
inline element_result<element> operator[](const std::string_view &key) const noexcept;
367+
368+
/**
369+
* Get the value associated with the given key.
370+
*
371+
* The key will be matched against **unescaped** JSON:
372+
*
373+
* document::parse(R"({ "a\n": 1 })")["a\n"].as_uint64_t().value == 1
374+
* document::parse(R"({ "a\n": 1 })")["a\\n"].as_uint64_t().error == NO_SUCH_FIELD
375+
*
376+
* @return The value associated with this field, or:
377+
* - NO_SUCH_FIELD if the field does not exist in the object
378+
* - UNEXPECTED_TYPE if the document is not an object
379+
*/
380+
inline element_result<element> operator[](const char *key) const noexcept;
381+
327382
~doc_ref_result()=default;
328383

329384
private:
@@ -549,6 +604,7 @@ class document::element : protected document::tape_ref {
549604
* - UNEXPECTED_TYPE if the document is not an object
550605
*/
551606
inline element_result<element> operator[](const std::string_view &s) const noexcept;
607+
552608
/**
553609
* Get the value associated with the given key.
554610
*
@@ -685,6 +741,7 @@ class document::object : protected document::tape_ref {
685741
* - NO_SUCH_FIELD if the field does not exist in the object
686742
*/
687743
inline element_result<element> operator[](const std::string_view &s) const noexcept;
744+
688745
/**
689746
* Get the value associated with the given key.
690747
*

include/simdjson/inline/document.h

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -444,11 +444,17 @@ inline bool document::dump_raw_tape(std::ostream &os) const noexcept {
444444
//
445445
inline document::doc_ref_result::doc_ref_result(document &_doc, error_code _error) noexcept : doc(_doc), error(_error) { }
446446
inline document::doc_ref_result::operator document&() noexcept(false) {
447-
if (error) {
448-
throw simdjson_error(error);
449-
}
447+
if (error) { throw simdjson_error(error); }
450448
return doc;
451449
}
450+
inline document::element_result<document::element> document::doc_ref_result::operator[](const std::string_view &key) const noexcept {
451+
if (error) { return error; }
452+
return doc[key];
453+
}
454+
inline document::element_result<document::element> document::doc_ref_result::operator[](const char *key) const noexcept {
455+
if (error) { return error; }
456+
return doc[key];
457+
}
452458

453459
//
454460
// document::doc_result inline implementation
@@ -457,11 +463,17 @@ inline document::doc_result::doc_result(document &&_doc, error_code _error) noex
457463
inline document::doc_result::doc_result(document &&_doc) noexcept : doc(std::move(_doc)), error(SUCCESS) { }
458464
inline document::doc_result::doc_result(error_code _error) noexcept : doc(), error(_error) { }
459465
inline document::doc_result::operator document() noexcept(false) {
460-
if (error) {
461-
throw simdjson_error(error);
462-
}
466+
if (error) { throw simdjson_error(error); }
463467
return std::move(doc);
464468
}
469+
inline document::element_result<document::element> document::doc_result::operator[](const std::string_view &key) const noexcept {
470+
if (error) { return error; }
471+
return doc[key];
472+
}
473+
inline document::element_result<document::element> document::doc_result::operator[](const char *key) const noexcept {
474+
if (error) { return error; }
475+
return doc[key];
476+
}
465477

466478
//
467479
// document::parser inline implementation
@@ -871,7 +883,6 @@ inline document::element_result<int64_t> document::element::as_int64_t() const n
871883
case tape_type::INT64:
872884
return next_tape_value<int64_t>();
873885
default:
874-
std::cout << "Incorrect " << json_index << " = " << char(type()) << std::endl;
875886
return INCORRECT_TYPE;
876887
}
877888
}

include/simdjson/inline/document_stream.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ really_inline document::stream::stream(
104104
size_t batch_size,
105105
error_code _error
106106
) noexcept : parser{_parser}, _buf{buf}, _len{len}, _batch_size(batch_size), error{_error} {
107-
error = json_parse();
107+
if (!error) { error = json_parse(); }
108108
}
109109

110110
inline document::stream::~stream() noexcept {

tests/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ if(MSVC)
55
endif()
66

77
add_cpp_test(basictests)
8+
add_cpp_test(errortests)
89

910
## Next bit should not be needed!
1011
#if(CMAKE_INTERPROCEDURAL_OPTIMIZATION)
@@ -19,6 +20,7 @@ add_cpp_test(pointercheck)
1920
add_cpp_test(integer_tests)
2021

2122
target_compile_definitions(basictests PRIVATE JSON_TEST_PATH="${PROJECT_SOURCE_DIR}/jsonexamples/twitter.json")
23+
target_compile_definitions(errortests PRIVATE JSON_TEST_PATH="${PROJECT_SOURCE_DIR}/jsonexamples/twitter.json")
2224

2325
## This causes problems
2426
# add_executable(singleheader ./singleheadertest.cpp ${PROJECT_SOURCE_DIR}/singleheader/simdjson.cpp)

tests/errortests.cpp

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
#include <cassert>
2+
#include <cinttypes>
3+
#include <cstdio>
4+
#include <cstdlib>
5+
#include <cstring>
6+
#include <iostream>
7+
#include <string>
8+
#include <vector>
9+
#include <cmath>
10+
#include <set>
11+
#include <string_view>
12+
13+
#include "simdjson.h"
14+
15+
using namespace simdjson;
16+
using namespace std;
17+
18+
#ifndef JSON_TEST_PATH
19+
#define JSON_TEST_PATH "jsonexamples/twitter.json"
20+
#endif
21+
22+
#define TEST_START() { cout << "Running " << __func__ << " ..." << endl; }
23+
#define ASSERT_ERROR(ACTUAL, EXPECTED) if ((ACTUAL) != (EXPECTED)) { cerr << "FAIL: Unexpected error \"" << (ACTUAL) << "\" (expected \"" << (EXPECTED) << "\")" << endl; return false; }
24+
#define TEST_FAIL(MESSAGE) { cerr << "FAIL: " << (MESSAGE) << endl; return false; }
25+
#define TEST_SUCCEED() { return true; }
26+
namespace parser_load {
27+
const char * NONEXISTENT_FILE = "this_file_does_not_exit.json";
28+
bool parser_load_capacity() {
29+
TEST_START();
30+
document::parser parser(1); // 1 byte max capacity
31+
auto [doc, error] = parser.load(JSON_TEST_PATH);
32+
ASSERT_ERROR(error, CAPACITY);
33+
TEST_SUCCEED();
34+
}
35+
bool parser_load_many_capacity() {
36+
TEST_START();
37+
document::parser parser(1); // 1 byte max capacity
38+
for (auto [doc, error] : parser.load_many(JSON_TEST_PATH)) {
39+
ASSERT_ERROR(error, CAPACITY);
40+
TEST_SUCCEED();
41+
}
42+
TEST_FAIL("No documents returned");
43+
}
44+
45+
bool document_load_nonexistent() {
46+
TEST_START();
47+
auto [doc, error] = document::load(NONEXISTENT_FILE);
48+
ASSERT_ERROR(error, IO_ERROR);
49+
TEST_SUCCEED();
50+
}
51+
bool parser_load_nonexistent() {
52+
TEST_START();
53+
document::parser parser;
54+
auto [doc, error] = parser.load(NONEXISTENT_FILE);
55+
ASSERT_ERROR(error, IO_ERROR);
56+
TEST_SUCCEED();
57+
}
58+
bool parser_load_many_nonexistent() {
59+
TEST_START();
60+
document::parser parser;
61+
for (auto [doc, error] : parser.load_many(NONEXISTENT_FILE)) {
62+
ASSERT_ERROR(error, IO_ERROR);
63+
TEST_SUCCEED();
64+
}
65+
TEST_FAIL("No documents returned");
66+
}
67+
bool padded_string_load_nonexistent() {
68+
TEST_START();
69+
auto [str, error] = padded_string::load(NONEXISTENT_FILE);
70+
ASSERT_ERROR(error, IO_ERROR);
71+
TEST_SUCCEED();
72+
}
73+
74+
bool document_load_chain() {
75+
TEST_START();
76+
auto [val, error] = document::load(NONEXISTENT_FILE)["foo"].as_uint64_t();
77+
ASSERT_ERROR(error, IO_ERROR);
78+
TEST_SUCCEED();
79+
}
80+
bool parser_load_chain() {
81+
TEST_START();
82+
document::parser parser;
83+
auto [val, error] = parser.load(NONEXISTENT_FILE)["foo"].as_uint64_t();
84+
ASSERT_ERROR(error, IO_ERROR);
85+
TEST_SUCCEED();
86+
}
87+
bool parser_load_many_chain() {
88+
TEST_START();
89+
document::parser parser;
90+
for (auto doc_result : parser.load_many(NONEXISTENT_FILE)) {
91+
auto [val, error] = doc_result["foo"].as_uint64_t();
92+
ASSERT_ERROR(error, IO_ERROR);
93+
TEST_SUCCEED();
94+
}
95+
TEST_FAIL("No documents returned");
96+
}
97+
bool run() {
98+
return parser_load_capacity() && parser_load_many_capacity()
99+
&& parser_load_nonexistent() && parser_load_many_nonexistent() && document_load_nonexistent() && padded_string_load_nonexistent()
100+
&& document_load_chain() && parser_load_chain() && parser_load_many_chain();
101+
}
102+
}
103+
104+
int main() {
105+
// this is put here deliberately to check that the documentation is correct (README),
106+
// should this fail to compile, you should update the documentation:
107+
if (simdjson::active_implementation->name() == "unsupported") {
108+
printf("unsupported CPU\n");
109+
}
110+
std::cout << "Running error tests." << std::endl;
111+
if (!parser_load::run()) {
112+
return EXIT_FAILURE;
113+
}
114+
std::cout << "Error tests are ok." << std::endl;
115+
return EXIT_SUCCESS;
116+
}

0 commit comments

Comments
 (0)