未验证 提交 63c2c62f 编写于 作者: N Niels Lohmann

🔨 changed call from "not good()" to "fail()" #493

Also merged develop into this feature branch.
......@@ -78,13 +78,13 @@ matrix:
env:
- COMPILER=g++-4.9
- SPECIAL=no_exceptions
- TEST_PATTERN=-e \"*\"
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: [g++-4.9, cppcheck]
before_script:
- CPPFLAGS="-DJSON_NOEXCEPTION" make
after_success:
- make clean
- CPPFLAGS="-DJSON_NOEXCEPTION" make check TEST_PATTERN="-e \"*\""
# Coveralls (http://gronlier.fr/blog/2015/01/adding-code-coverage-to-your-c-project/)
......
......@@ -49,6 +49,7 @@ doctest:
# -Wno-documentation-unknown-command: code uses user-defined commands like @complexity
# -Wno-exit-time-destructors: warning in Catch code
# -Wno-keyword-macro: unit-tests use "#define private public"
# -Wno-weak-vtables: exception class is defined inline, but has virtual method
# -Wno-range-loop-analysis: iterator_wrapper tests tests "for(const auto i...)"
pedantic:
$(MAKE) json_unit CXXFLAGS="\
......@@ -58,6 +59,7 @@ pedantic:
-Wno-documentation-unknown-command \
-Wno-exit-time-destructors \
-Wno-keyword-macro \
-Wno-weak-vtables \
-Wno-range-loop-analysis"
......
......@@ -58,6 +58,9 @@ doxygen: create_output create_links
$(SED) -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer >@@g' html/*.html
$(SED) -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer >@@g' html/*.html
$(SED) -i 's@&lt;&#160;ObjectType,&#160;ArrayType,&#160;StringType,&#160;BooleanType,&#160;NumberIntegerType,&#160;NumberUnsignedType,&#160;NumberFloatType,&#160;AllocatorType&#160;JSONSerializer&#160;&gt;@@g' html/*.html
$(SED) -i 's@template&lt;template&lt; typename U, typename V, typename... Args &gt; class ObjectType = std::map, template&lt; typename U, typename... Args &gt; class ArrayType = std::vector, class StringType = std::string, class BooleanType = bool, class NumberIntegerType = std::int64_t, class NumberUnsignedType = std::uint64_t, class NumberFloatType = double, template&lt; typename U &gt; class AllocatorType = std::allocator, template&lt; typename T, typename SFINAE=void &gt; class JSONSerializer = adl_serializer&gt;@@g' html/*.html
$(SED) -i 's@&lt; ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer &gt;@@g' html/*.html
$(SED) -i 's@&lt;&#160;ObjectType,&#160;ArrayType,&#160;StringType,&#160;BooleanType,&#160;NumberIntegerType,&#160;NumberUnsignedType,&#160;NumberFloatType,&#160;AllocatorType,&#160;JSONSerializer&#160;&gt;@@g' html/*.html
upload: clean doxygen check_output
cd html ; ../scripts/git-update-ghpages nlohmann/json
......
......@@ -21,13 +21,27 @@ int main()
// output changed array
std::cout << object << '\n';
// try to write at a nonexisting key
// exception type_error.304
try
{
// use at() on a non-object type
json str = "I am a string";
str.at("the good") = "Another string";
}
catch (json::type_error& e)
{
std::cout << e.what() << '\n';
}
// exception out_of_range.401
try
{
// try to write at a nonexisting key
object.at("the fast") = "il rapido";
}
catch (std::out_of_range& e)
catch (json::out_of_range& e)
{
std::cout << "out of range: " << e.what() << '\n';
std::cout << e.what() << '\n';
}
}
<a target="_blank" href="http://melpon.org/wandbox/permlink/tb5CaFfsMWpAvi7m"><b>online</b></a>
\ No newline at end of file
<a target="_blank" href="http://melpon.org/wandbox/permlink/8ldtT0NOhidn0fOA"><b>online</b></a>
\ No newline at end of file
"il brutto"
{"the bad":"il cattivo","the good":"il buono","the ugly":"il brutto"}
out of range: key 'the fast' not found
[json.exception.type_error.304] cannot use at() with string
[json.exception.out_of_range.403] key 'the fast' not found
"il brutto"
{"the bad":"il cattivo","the good":"il buono","the ugly":"il brutto"}
[json.exception.out_of_range.403] key 'the fast' not found
......@@ -15,12 +15,26 @@ int main()
// output element with key "the ugly"
std::cout << object.at("the ugly") << '\n';
// try to read from a nonexisting key
// exception type_error.304
try
{
// use at() on a non-object type
const json str = "I am a string";
std::cout << str.at("the good") << '\n';
}
catch (json::type_error& e)
{
std::cout << e.what() << '\n';
}
// exception out_of_range.401
try
{
// try to read from a nonexisting key
std::cout << object.at("the fast") << '\n';
}
catch (std::out_of_range)
catch (json::out_of_range)
{
std::cout << "out of range" << '\n';
}
......
<a target="_blank" href="http://melpon.org/wandbox/permlink/NFG86H5khRUePc1s"><b>online</b></a>
\ No newline at end of file
<a target="_blank" href="http://melpon.org/wandbox/permlink/nfmFWMaJJHFJ7eVK"><b>online</b></a>
\ No newline at end of file
"il brutto"
[json.exception.type_error.304] cannot use at() with string
out of range
......@@ -16,13 +16,27 @@ int main()
// output changed array
std::cout << array << '\n';
// try to write beyond the array limit
// exception type_error.304
try
{
// use at() on a non-array type
json str = "I am a string";
str.at(0) = "Another string";
}
catch (json::type_error& e)
{
std::cout << e.what() << '\n';
}
// exception out_of_range.401
try
{
// try to write beyond the array limit
array.at(5) = "sixth";
}
catch (std::out_of_range& e)
catch (json::out_of_range& e)
{
std::cout << "out of range: " << e.what() << '\n';
std::cout << e.what() << '\n';
}
}
<a target="_blank" href="http://melpon.org/wandbox/permlink/R7z2SB2rMdFQ9XtR"><b>online</b></a>
\ No newline at end of file
<a target="_blank" href="http://melpon.org/wandbox/permlink/8UnQY256zGX2Lx6d"><b>online</b></a>
\ No newline at end of file
"third"
["first","second","third","fourth"]
out of range: array index 5 is out of range
[json.exception.type_error.304] cannot use at() with string
[json.exception.out_of_range.401] array index 5 is out of range
......@@ -5,18 +5,32 @@ using json = nlohmann::json;
int main()
{
// create JSON array
json array = {"first", "2nd", "third", "fourth"};
const json array = {"first", "2nd", "third", "fourth"};
// output element at index 2 (third element)
std::cout << array.at(2) << '\n';
// try to read beyond the array limit
// exception type_error.304
try
{
// use at() on a non-array type
const json str = "I am a string";
std::cout << str.at(0) << '\n';
}
catch (json::type_error& e)
{
std::cout << e.what() << '\n';
}
// exception out_of_range.401
try
{
// try to read beyond the array limit
std::cout << array.at(5) << '\n';
}
catch (std::out_of_range)
catch (json::out_of_range& e)
{
std::cout << "out of range" << '\n';
std::cout << e.what() << '\n';
}
}
<a target="_blank" href="http://melpon.org/wandbox/permlink/L1bMeiN6nYm7JrvA"><b>online</b></a>
\ No newline at end of file
<a target="_blank" href="http://melpon.org/wandbox/permlink/U1fv6LY7xZOAuSBs"><b>online</b></a>
\ No newline at end of file
"third"
out of range
[json.exception.type_error.304] cannot use at() with string
[json.exception.out_of_range.401] array index 5 is out of range
......@@ -32,4 +32,60 @@ int main()
j.at("/array/1"_json_pointer) = 21;
// output the changed array
std::cout << j["array"] << '\n';
// out_of_range.106
try
{
// try to use an array index with leading '0'
json::reference ref = j.at("/array/01"_json_pointer);
}
catch (json::parse_error& e)
{
std::cout << e.what() << '\n';
}
// out_of_range.109
try
{
// try to use an array index that is not a number
json::reference ref = j.at("/array/one"_json_pointer);
}
catch (json::parse_error& e)
{
std::cout << e.what() << '\n';
}
// out_of_range.401
try
{
// try to use a an invalid array index
json::reference ref = j.at("/array/4"_json_pointer);
}
catch (json::out_of_range& e)
{
std::cout << e.what() << '\n';
}
// out_of_range.402
try
{
// try to use the array index '-'
json::reference ref = j.at("/array/-"_json_pointer);
}
catch (json::out_of_range& e)
{
std::cout << e.what() << '\n';
}
// out_of_range.404
try
{
// try to use a JSON pointer that cannot be resolved
json::reference ref = j.at("/number/foo"_json_pointer);
}
catch (json::out_of_range& e)
{
std::cout << e.what() << '\n';
}
}
<a target="_blank" href="http://melpon.org/wandbox/permlink/NDSjglHZIjIZ0Uxg"><b>online</b></a>
\ No newline at end of file
<a target="_blank" href="http://melpon.org/wandbox/permlink/Fy2xBfZMols2DUQC"><b>online</b></a>
\ No newline at end of file
......@@ -4,3 +4,8 @@
2
"bar"
[1,21]
[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'
[json.exception.parse_error.109] parse error: array index 'one' is not a number
[json.exception.out_of_range.401] array index 4 is out of range
[json.exception.out_of_range.402] array index '-' (2) is out of range
[json.exception.out_of_range.404] unresolved reference token 'foo'
......@@ -5,7 +5,7 @@ using json = nlohmann::json;
int main()
{
// create a JSON value
json j =
const json j =
{
{"number", 1}, {"string", "foo"}, {"array", {1, 2}}
};
......@@ -20,4 +20,48 @@ int main()
std::cout << j.at("/array"_json_pointer) << '\n';
// output element with JSON pointer "/array/1"
std::cout << j.at("/array/1"_json_pointer) << '\n';
// out_of_range.109
try
{
// try to use an array index that is not a number
json::const_reference ref = j.at("/array/one"_json_pointer);
}
catch (json::parse_error& e)
{
std::cout << e.what() << '\n';
}
// out_of_range.401
try
{
// try to use a an invalid array index
json::const_reference ref = j.at("/array/4"_json_pointer);
}
catch (json::out_of_range& e)
{
std::cout << e.what() << '\n';
}
// out_of_range.402
try
{
// try to use the array index '-'
json::const_reference ref = j.at("/array/-"_json_pointer);
}
catch (json::out_of_range& e)
{
std::cout << e.what() << '\n';
}
// out_of_range.404
try
{
// try to use a JSON pointer that cannot be resolved
json::const_reference ref = j.at("/number/foo"_json_pointer);
}
catch (json::out_of_range& e)
{
std::cout << e.what() << '\n';
}
}
<a target="_blank" href="http://melpon.org/wandbox/permlink/OuLYiMJ3pgyOHupb"><b>online</b></a>
\ No newline at end of file
<a target="_blank" href="http://melpon.org/wandbox/permlink/WxhV3mL9YX8FJonk"><b>online</b></a>
\ No newline at end of file
......@@ -2,3 +2,7 @@
"foo"
[1,2]
2
[json.exception.parse_error.109] parse error: array index 'one' is not a number
[json.exception.out_of_range.401] array index 4 is out of range
[json.exception.out_of_range.402] array index '-' (2) is out of range
[json.exception.out_of_range.404] unresolved reference token 'foo'
......@@ -5,7 +5,6 @@ using json = nlohmann::json;
int main()
{
// create JSON values
json j_null;
json j_boolean = true;
json j_number_integer = 17;
json j_number_float = 23.42;
......@@ -16,7 +15,6 @@ int main()
json j_string = "Hello, world";
// call back()
//std::cout << j_null.back() << '\n'; // would throw
std::cout << j_boolean.back() << '\n';
std::cout << j_number_integer.back() << '\n';
std::cout << j_number_float.back() << '\n';
......@@ -25,4 +23,15 @@ int main()
std::cout << j_array.back() << '\n';
//std::cout << j_array_empty.back() << '\n'; // undefined behavior
std::cout << j_string.back() << '\n';
// back() called on a null value
try
{
json j_null;
j_null.back();
}
catch (json::invalid_iterator& e)
{
std::cout << e.what() << '\n';
}
}
<a target="_blank" href="http://melpon.org/wandbox/permlink/nPVnBcHf8nrNpGOJ"><b>online</b></a>
\ No newline at end of file
<a target="_blank" href="http://melpon.org/wandbox/permlink/CAv4NNr4D1FJhhUv"><b>online</b></a>
\ No newline at end of file
......@@ -4,3 +4,4 @@ true
2
16
"Hello, world"
[json.exception.invalid_iterator.214] cannot get value
......@@ -18,4 +18,14 @@ int main()
std::cout << j_array_range << '\n';
std::cout << j_number_range << '\n';
std::cout << j_object_range << '\n';
// example for an exception
try
{
json j_invalid(j_number.begin() + 1, j_number.end());
}
catch (json::invalid_iterator& e)
{
std::cout << e.what() << '\n';
}
}
<a target="_blank" href="http://melpon.org/wandbox/permlink/nKF1QcieoCHm6Lez"><b>online</b></a>
\ No newline at end of file
<a target="_blank" href="http://melpon.org/wandbox/permlink/oUU2X0pbZq7gawRB"><b>online</b></a>
\ No newline at end of file
["bravo","charly"]
42
{"one":"eins"}
[json.exception.invalid_iterator.204] iterators out of range
......@@ -19,7 +19,7 @@ int main()
{
auto r3 = value.get_ref<json::number_float_t&>();
}
catch (std::domain_error& ex)
catch (json::type_error& ex)
{
std::cout << ex.what() << '\n';
}
......
<a target="_blank" href="http://melpon.org/wandbox/permlink/WiO1oBWDvIs82OX1"><b>online</b></a>
\ No newline at end of file
<a target="_blank" href="http://melpon.org/wandbox/permlink/lsl8Ex3d3SOYnKHu"><b>online</b></a>
\ No newline at end of file
17 17
incompatible ReferenceType for get_ref, actual type is number
[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number
17 17
[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number
......@@ -19,9 +19,9 @@ int main()
{
json::json_pointer p9("foo");
}
catch (std::domain_error& e)
catch (json::parse_error& e)
{
std::cout << "domain_error: " << e.what() << '\n';
std::cout << e.what() << '\n';
}
// error: JSON pointer uses escape symbol ~ not followed by 0 or 1
......@@ -29,9 +29,9 @@ int main()
{
json::json_pointer p10("/foo/~");
}
catch (std::domain_error& e)
catch (json::parse_error& e)
{
std::cout << "domain_error: " << e.what() << '\n';
std::cout << e.what() << '\n';
}
// error: JSON pointer uses escape symbol ~ not followed by 0 or 1
......@@ -39,8 +39,8 @@ int main()
{
json::json_pointer p11("/foo/~3");
}
catch (std::domain_error& e)
catch (json::parse_error& e)
{
std::cout << "domain_error: " << e.what() << '\n';
std::cout << e.what() << '\n';
}
}
<a target="_blank" href="http://melpon.org/wandbox/permlink/WM2WWKnXdmdw17Wu"><b>online</b></a>
\ No newline at end of file
<a target="_blank" href="http://melpon.org/wandbox/permlink/Wlvqfd3JpEXTv2iH"><b>online</b></a>
\ No newline at end of file
domain_error: JSON pointer must be empty or begin with '/'
domain_error: escape error: '~' must be followed with '0' or '1'
domain_error: escape error: '~' must be followed with '0' or '1'
[json.exception.parse_error.107] parse error at 1: JSON pointer must be empty or begin with '/' - was: 'foo'
[json.exception.parse_error.108] parse error: escape character '~' must be followed with '0' or '1'
[json.exception.parse_error.108] parse error: escape character '~' must be followed with '0' or '1'
[json.exception.parse_error.107] parse error at 1: JSON pointer must be empty or begin with '/' - was: 'foo'
[json.exception.parse_error.108] parse error: escape character '~' must be followed with '0' or '1'
[json.exception.parse_error.108] parse error: escape character '~' must be followed with '0' or '1'
......@@ -8,10 +8,20 @@ int main()
json j_no_init_list = json::object();
json j_empty_init_list = json::object({});
json j_list_of_pairs = json::object({ {"one", 1}, {"two", 2} });
//json j_invalid_list = json::object({ "one", 1 }); // would throw
// serialize the JSON objects
std::cout << j_no_init_list << '\n';
std::cout << j_empty_init_list << '\n';
std::cout << j_list_of_pairs << '\n';
// example for an exception
try
{
// can only create an object from a list of pairs
json j_invalid_object = json::object({{ "one", 1, 2 }});
}
catch (json::type_error& e)
{
std::cout << e.what() << '\n';
}
}
<a target="_blank" href="http://melpon.org/wandbox/permlink/hhxRaUctq3FA54SW"><b>online</b></a>
\ No newline at end of file
<a target="_blank" href="http://melpon.org/wandbox/permlink/Ub9U5AMbng3oZiao"><b>online</b></a>
\ No newline at end of file
{}
{}
{"one":1,"two":2}
[json.exception.type_error.301] cannot create object from initializer list
......@@ -46,4 +46,14 @@ int main()
{
std::cout << i.first << ": " << i.second << '\n';
}
// example for an exception
try
{
bool v1 = json_types["string"];
}
catch (json::type_error& e)
{
std::cout << e.what() << '\n';
}
}
<a target="_blank" href="http://melpon.org/wandbox/permlink/rUGX6AaVuZfwiiYI"><b>online</b></a>
\ No newline at end of file
<a target="_blank" href="http://melpon.org/wandbox/permlink/drFSKFXJd8IMzMK3"><b>online</b></a>
\ No newline at end of file
......@@ -9,3 +9,4 @@ number: {"floating-point":17.23,"integer":42}
null: null
boolean: true
array: [1,2,3,4,5]
[json.exception.type_error.302] type must be boolean, but is string
此差异已折叠。
此差异已折叠。
......@@ -41,26 +41,22 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
// parse serialization
json j2 = json::from_cbor(vec2);
// deserializations must match
assert(j1 == j2);
// serializations must match
assert(json::to_cbor(j2) == vec2);
}
catch (const std::invalid_argument&)
catch (const json::parse_error&)
{
// parsing a CBOR serialization must not fail
assert(false);
}
}
catch (const std::invalid_argument&)
catch (const json::parse_error&)
{
// parse errors are ok, because input may be random bytes
}
catch (const std::out_of_range&)
catch (const json::type_error&)
{
// parse errors are ok, because input may be random bytes
}
catch (const std::domain_error&)
{
// parse errors are ok, because input may be random bytes
// type errors can occur during parsing, too
}
// return 0 - non-zero return values are reserved for future use
......
......@@ -49,13 +49,13 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
// serializations must match
assert(s1 == s2);
}
catch (const std::invalid_argument&)
catch (const json::parse_error&)
{
// parsing a JSON serialization must not fail
assert(false);
}
}
catch (const std::invalid_argument&)
catch (const json::parse_error&)
{
// parse errors are ok, because input may be random bytes
}
......
......@@ -44,23 +44,19 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
// deserializations must match
assert(j1 == j2);
}
catch (const std::invalid_argument&)
catch (const json::parse_error&)
{
// parsing a MessagePack serialization must not fail
assert(false);
}
}
catch (const std::invalid_argument&)
catch (const json::parse_error&)
{
// parse errors are ok, because input may be random bytes
}
catch (const std::out_of_range&)
catch (const json::type_error&)
{
// parse errors are ok, because input may be random bytes
}
catch (const std::domain_error&)
{
// parse errors are ok, because input may be random bytes
// type errors can occur during parsing, too
}
// return 0 - non-zero return values are reserved for future use
......
......@@ -240,8 +240,9 @@ TEST_CASE("algorithms")
SECTION("sorting an object")
{
json j({{"one", 1}, {"two", 2}});
CHECK_THROWS_AS(std::sort(j.begin(), j.end()), std::domain_error);
CHECK_THROWS_WITH(std::sort(j.begin(), j.end()), "cannot use offsets with object iterators");
CHECK_THROWS_AS(std::sort(j.begin(), j.end()), json::invalid_iterator);
CHECK_THROWS_WITH(std::sort(j.begin(), j.end()),
"[json.exception.invalid_iterator.209] cannot use offsets with object iterators");
}
}
......
......@@ -744,13 +744,17 @@ TEST_CASE("CBOR")
SECTION("infinity")
{
json j = json::from_cbor(std::vector<uint8_t>({0xf9, 0x7c, 0x00}));
CHECK(j == nullptr);
json::number_float_t d = j;
CHECK(not std::isfinite(d));
CHECK(j.dump() == "null");
}
SECTION("NaN")
{
json j = json::from_cbor(std::vector<uint8_t>({0xf9, 0x7c, 0x01}));
CHECK(j == nullptr);
json::number_float_t d = j;
CHECK(std::isnan(d));
CHECK(j.dump() == "null");
}
}
}
......@@ -1145,21 +1149,115 @@ TEST_CASE("CBOR")
{
SECTION("too short byte vector")
{
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x18})), std::out_of_range);
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x19})), std::out_of_range);
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x19, 0x00})), std::out_of_range);
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x1a})), std::out_of_range);
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x1a, 0x00})), std::out_of_range);
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x1a, 0x00, 0x00})), std::out_of_range);
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x1a, 0x00, 0x00, 0x00})), std::out_of_range);
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x1b})), std::out_of_range);
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00})), std::out_of_range);
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00})), std::out_of_range);
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00})), std::out_of_range);
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00, 0x00})), std::out_of_range);
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00})), std::out_of_range);
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), std::out_of_range);
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), std::out_of_range);
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x18})), json::parse_error);
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x19})), json::parse_error);
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x19, 0x00})), json::parse_error);
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x1a})), json::parse_error);
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x1a, 0x00})), json::parse_error);
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x1a, 0x00, 0x00})), json::parse_error);
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x1a, 0x00, 0x00, 0x00})), json::parse_error);
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x1b})), json::parse_error);
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00})), json::parse_error);
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00})), json::parse_error);
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00})), json::parse_error);
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00, 0x00})), json::parse_error);
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00})), json::parse_error);
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), json::parse_error);
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), json::parse_error);
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x18})),
"[json.exception.parse_error.110] parse error at 2: cannot read 1 bytes from vector");
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x19})),
"[json.exception.parse_error.110] parse error at 2: cannot read 2 bytes from vector");
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x19, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 2 bytes from vector");
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x1a})),
"[json.exception.parse_error.110] parse error at 2: cannot read 4 bytes from vector");
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x1a, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 4 bytes from vector");
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x1a, 0x00, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 4 bytes from vector");
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x1a, 0x00, 0x00, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 4 bytes from vector");
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x1b})),
"[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector");
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector");
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector");
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector");
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector");
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector");
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector");
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector");
}
SECTION("unsupported bytes")
{
SECTION("concrete examples")
{
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x1c})), json::parse_error);
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x1c})),
"[json.exception.parse_error.112] parse error at 1: error reading CBOR; last byte: 0x1c");
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0xf8})), json::parse_error);
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0xf8})),
"[json.exception.parse_error.112] parse error at 1: error reading CBOR; last byte: 0xf8");
}
SECTION("all unsupported bytes")
{
for (auto byte :
{
// ?
0x1c, 0x1d, 0x1e, 0x1f,
// ?
0x3c, 0x3d, 0x3e, 0x3f,
// byte strings
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
// byte strings
0x58, 0x59, 0x5a, 0x5b,
// ?
0x5c, 0x5d, 0x5e,
// byte string
0x5f,
// ?
0x7c, 0x7d, 0x7e,
// ?
0x9c, 0x9d, 0x9e,
// ?
0xbc, 0xbd, 0xbe,
// date/time
0xc0, 0xc1,
// bignum
0xc2, 0xc3,
// fraction
0xc4,
// bigfloat
0xc5,
// tagged item
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4,
// expected conversion
0xd5, 0xd6, 0xd7,
// more tagged items
0xd8, 0xd9, 0xda, 0xdb,
// ?
0xdc, 0xdd, 0xde, 0xdf,
// (simple value)
0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3,
// undefined
0xf7,
// simple value
0xf8
})
{
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({static_cast<uint8_t>(byte)})), json::parse_error);
}
}
}
}
}
......@@ -1247,21 +1345,13 @@ TEST_CASE("CBOR regressions", "[!throws]")
// deserializations must match
CHECK(j1 == j2);
}
catch (const std::invalid_argument&)
catch (const json::parse_error&)
{
// parsing a CBOR serialization must not fail
CHECK(false);
}
}
catch (const std::invalid_argument&)
{
// parse errors are ok, because input may be random bytes
}
catch (const std::out_of_range&)
{
// parse errors are ok, because input may be random bytes
}
catch (const std::domain_error&)
catch (const json::parse_error&)
{
// parse errors are ok, because input may be random bytes
}
......@@ -1271,7 +1361,7 @@ TEST_CASE("CBOR regressions", "[!throws]")
SECTION("improve code coverage")
{
// exotic edge case
CHECK_THROWS_AS(json::check_length(0xffffffffffffffffull, 0xfffffffffffffff0ull, 0xff), std::out_of_range);
CHECK_THROWS_AS(json::check_length(0xffffffffffffffffull, 0xfffffffffffffff0ull, 0xff), json::parse_error);
}
}
......@@ -1344,7 +1434,7 @@ TEST_CASE("CBOR roundtrips", "[hide]")
"test/data/nst_json_testsuite/test_parsing/y_number_after_space.json",
"test/data/nst_json_testsuite/test_parsing/y_number_double_close_to_zero.json",
"test/data/nst_json_testsuite/test_parsing/y_number_double_huge_neg_exp.json",
"test/data/nst_json_testsuite/test_parsing/y_number_huge_exp.json",
//"test/data/nst_json_testsuite/test_parsing/y_number_huge_exp.json",
"test/data/nst_json_testsuite/test_parsing/y_number_int_with_exp.json",
"test/data/nst_json_testsuite/test_parsing/y_number_minus_zero.json",
"test/data/nst_json_testsuite/test_parsing/y_number_negative_int.json",
......@@ -1356,9 +1446,9 @@ TEST_CASE("CBOR roundtrips", "[hide]")
"test/data/nst_json_testsuite/test_parsing/y_number_real_exponent.json",
"test/data/nst_json_testsuite/test_parsing/y_number_real_fraction_exponent.json",
"test/data/nst_json_testsuite/test_parsing/y_number_real_neg_exp.json",
"test/data/nst_json_testsuite/test_parsing/y_number_real_neg_overflow.json",
//"test/data/nst_json_testsuite/test_parsing/y_number_real_neg_overflow.json",
"test/data/nst_json_testsuite/test_parsing/y_number_real_pos_exponent.json",
"test/data/nst_json_testsuite/test_parsing/y_number_real_pos_overflow.json",
//"test/data/nst_json_testsuite/test_parsing/y_number_real_pos_overflow.json",
"test/data/nst_json_testsuite/test_parsing/y_number_real_underflow.json",
"test/data/nst_json_testsuite/test_parsing/y_number_simple_int.json",
"test/data/nst_json_testsuite/test_parsing/y_number_simple_real.json",
......
......@@ -147,8 +147,8 @@ TEST_CASE("const_iterator class")
{
json j(json::value_t::null);
json::const_iterator it = j.cbegin();
CHECK_THROWS_AS(*it, std::out_of_range);
CHECK_THROWS_WITH(*it, "cannot get value");
CHECK_THROWS_AS(*it, json::invalid_iterator);
CHECK_THROWS_WITH(*it, "[json.exception.invalid_iterator.214] cannot get value");
}
SECTION("number")
......@@ -157,8 +157,8 @@ TEST_CASE("const_iterator class")
json::const_iterator it = j.cbegin();
CHECK(*it == json(17));
it = j.cend();
CHECK_THROWS_AS(*it, std::out_of_range);
CHECK_THROWS_WITH(*it, "cannot get value");
CHECK_THROWS_AS(*it, json::invalid_iterator);
CHECK_THROWS_WITH(*it, "[json.exception.invalid_iterator.214] cannot get value");
}
SECTION("object")
......@@ -182,8 +182,8 @@ TEST_CASE("const_iterator class")
{
json j(json::value_t::null);
json::const_iterator it = j.cbegin();
CHECK_THROWS_AS(it->type_name(), std::out_of_range);
CHECK_THROWS_WITH(it->type_name(), "cannot get value");
CHECK_THROWS_AS(it->type_name(), json::invalid_iterator);
CHECK_THROWS_WITH(it->type_name(), "[json.exception.invalid_iterator.214] cannot get value");
}
SECTION("number")
......@@ -192,8 +192,8 @@ TEST_CASE("const_iterator class")
json::const_iterator it = j.cbegin();
CHECK(it->type_name() == "number");
it = j.cend();
CHECK_THROWS_AS(it->type_name(), std::out_of_range);
CHECK_THROWS_WITH(it->type_name(), "cannot get value");
CHECK_THROWS_AS(it->type_name(), json::invalid_iterator);
CHECK_THROWS_WITH(it->type_name(), "[json.exception.invalid_iterator.214] cannot get value");
}
SECTION("object")
......
......@@ -131,8 +131,8 @@ TEST_CASE("iterator class")
{
json j(json::value_t::null);
json::iterator it = j.begin();
CHECK_THROWS_AS(*it, std::out_of_range);
CHECK_THROWS_WITH(*it, "cannot get value");
CHECK_THROWS_AS(*it, json::invalid_iterator);
CHECK_THROWS_WITH(*it, "[json.exception.invalid_iterator.214] cannot get value");
}
SECTION("number")
......@@ -141,8 +141,8 @@ TEST_CASE("iterator class")
json::iterator it = j.begin();
CHECK(*it == json(17));
it = j.end();
CHECK_THROWS_AS(*it, std::out_of_range);
CHECK_THROWS_WITH(*it, "cannot get value");
CHECK_THROWS_AS(*it, json::invalid_iterator);
CHECK_THROWS_WITH(*it, "[json.exception.invalid_iterator.214] cannot get value");
}
SECTION("object")
......@@ -166,8 +166,8 @@ TEST_CASE("iterator class")
{
json j(json::value_t::null);
json::iterator it = j.begin();
CHECK_THROWS_AS(it->type_name(), std::out_of_range);
CHECK_THROWS_WITH(it->type_name(), "cannot get value");
CHECK_THROWS_AS(it->type_name(), json::invalid_iterator);
CHECK_THROWS_WITH(it->type_name(), "[json.exception.invalid_iterator.214] cannot get value");
}
SECTION("number")
......@@ -176,8 +176,8 @@ TEST_CASE("iterator class")
json::iterator it = j.begin();
CHECK(it->type_name() == "number");
it = j.end();
CHECK_THROWS_AS(it->type_name(), std::out_of_range);
CHECK_THROWS_WITH(it->type_name(), "cannot get value");
CHECK_THROWS_AS(it->type_name(), json::invalid_iterator);
CHECK_THROWS_WITH(it->type_name(), "[json.exception.invalid_iterator.214] cannot get value");
}
SECTION("object")
......
......@@ -190,8 +190,10 @@ TEST_CASE("lexer class")
SECTION("to_unicode")
{
CHECK(json::lexer::to_unicode(0x1F4A9) == "💩");
CHECK_THROWS_AS(json::lexer::to_unicode(0x200000), std::out_of_range);
CHECK_THROWS_WITH(json::lexer::to_unicode(0x200000), "code points above 0x10FFFF are invalid");
// lexer to call to_unicode on
json::lexer dummy_lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(""), 0);
CHECK(dummy_lexer.to_unicode(0x1F4A9) == "💩");
CHECK_THROWS_AS(dummy_lexer.to_unicode(0x200000), json::parse_error);
CHECK_THROWS_WITH(dummy_lexer.to_unicode(0x200000), "[json.exception.parse_error.103] parse error: code points above 0x10FFFF are invalid");
}
}
此差异已折叠。
......@@ -702,11 +702,17 @@ TEST_CASE("constructors")
SECTION("infinity")
{
// infinity is stored as null
// should change in the future: https://github.com/nlohmann/json/issues/388
// infinity is stored properly, but serialized to null
json::number_float_t n(std::numeric_limits<json::number_float_t>::infinity());
json j(n);
CHECK(j.type() == json::value_t::null);
CHECK(j.type() == json::value_t::number_float);
// check round trip of infinity
json::number_float_t d = j;
CHECK(d == n);
// check that inf is serialized to null
CHECK(j.dump() == "null");
}
}
......@@ -943,9 +949,9 @@ TEST_CASE("constructors")
SECTION("object with error")
{
CHECK_THROWS_AS(json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false}, 13 }),
std::logic_error);
json::type_error);
CHECK_THROWS_WITH(json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false}, 13 }),
"cannot create object from initializer list");
"[json.exception.type_error.301] cannot create object from initializer list");
}
SECTION("empty array")
......@@ -1017,18 +1023,18 @@ TEST_CASE("constructors")
{
json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}};
json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}};
CHECK_THROWS_AS(json(jobject.begin(), jobject2.end()), std::domain_error);
CHECK_THROWS_AS(json(jobject2.begin(), jobject.end()), std::domain_error);
CHECK_THROWS_WITH(json(jobject.begin(), jobject2.end()), "iterators are not compatible");
CHECK_THROWS_WITH(json(jobject2.begin(), jobject.end()), "iterators are not compatible");
CHECK_THROWS_AS(json(jobject.begin(), jobject2.end()), json::invalid_iterator);
CHECK_THROWS_AS(json(jobject2.begin(), jobject.end()), json::invalid_iterator);
CHECK_THROWS_WITH(json(jobject.begin(), jobject2.end()), "[json.exception.invalid_iterator.201] iterators are not compatible");
CHECK_THROWS_WITH(json(jobject2.begin(), jobject.end()), "[json.exception.invalid_iterator.201] iterators are not compatible");
}
{
json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}};
json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}};
CHECK_THROWS_AS(json(jobject.cbegin(), jobject2.cend()), std::domain_error);
CHECK_THROWS_AS(json(jobject2.cbegin(), jobject.cend()), std::domain_error);
CHECK_THROWS_WITH(json(jobject.cbegin(), jobject2.cend()), "iterators are not compatible");
CHECK_THROWS_WITH(json(jobject2.cbegin(), jobject.cend()), "iterators are not compatible");
CHECK_THROWS_AS(json(jobject.cbegin(), jobject2.cend()), json::invalid_iterator);
CHECK_THROWS_AS(json(jobject2.cbegin(), jobject.cend()), json::invalid_iterator);
CHECK_THROWS_WITH(json(jobject.cbegin(), jobject2.cend()), "[json.exception.invalid_iterator.201] iterators are not compatible");
CHECK_THROWS_WITH(json(jobject2.cbegin(), jobject.cend()), "[json.exception.invalid_iterator.201] iterators are not compatible");
}
}
}
......@@ -1082,18 +1088,18 @@ TEST_CASE("constructors")
{
json jarray = {1, 2, 3, 4};
json jarray2 = {2, 3, 4, 5};
CHECK_THROWS_AS(json(jarray.begin(), jarray2.end()), std::domain_error);
CHECK_THROWS_AS(json(jarray2.begin(), jarray.end()), std::domain_error);
CHECK_THROWS_WITH(json(jarray.begin(), jarray2.end()), "iterators are not compatible");
CHECK_THROWS_WITH(json(jarray2.begin(), jarray.end()), "iterators are not compatible");
CHECK_THROWS_AS(json(jarray.begin(), jarray2.end()), json::invalid_iterator);
CHECK_THROWS_AS(json(jarray2.begin(), jarray.end()), json::invalid_iterator);
CHECK_THROWS_WITH(json(jarray.begin(), jarray2.end()), "[json.exception.invalid_iterator.201] iterators are not compatible");
CHECK_THROWS_WITH(json(jarray2.begin(), jarray.end()), "[json.exception.invalid_iterator.201] iterators are not compatible");
}
{
json jarray = {1, 2, 3, 4};
json jarray2 = {2, 3, 4, 5};
CHECK_THROWS_AS(json(jarray.cbegin(), jarray2.cend()), std::domain_error);
CHECK_THROWS_AS(json(jarray2.cbegin(), jarray.cend()), std::domain_error);
CHECK_THROWS_WITH(json(jarray.cbegin(), jarray2.cend()), "iterators are not compatible");
CHECK_THROWS_WITH(json(jarray2.cbegin(), jarray.cend()), "iterators are not compatible");
CHECK_THROWS_AS(json(jarray.cbegin(), jarray2.cend()), json::invalid_iterator);
CHECK_THROWS_AS(json(jarray2.cbegin(), jarray.cend()), json::invalid_iterator);
CHECK_THROWS_WITH(json(jarray.cbegin(), jarray2.cend()), "[json.exception.invalid_iterator.201] iterators are not compatible");
CHECK_THROWS_WITH(json(jarray2.cbegin(), jarray.cend()), "[json.exception.invalid_iterator.201] iterators are not compatible");
}
}
}
......@@ -1106,13 +1112,15 @@ TEST_CASE("constructors")
{
{
json j;
CHECK_THROWS_AS(json(j.begin(), j.end()), std::domain_error);
CHECK_THROWS_WITH(json(j.begin(), j.end()), "cannot use construct with iterators from null");
CHECK_THROWS_AS(json(j.begin(), j.end()), json::invalid_iterator);
CHECK_THROWS_WITH(json(j.begin(), j.end()),
"[json.exception.invalid_iterator.206] cannot construct with iterators from null");
}
{
json j;
CHECK_THROWS_AS(json(j.cbegin(), j.cend()), std::domain_error);
CHECK_THROWS_WITH(json(j.cbegin(), j.cend()), "cannot use construct with iterators from null");
CHECK_THROWS_AS(json(j.cbegin(), j.cend()), json::invalid_iterator);
CHECK_THROWS_WITH(json(j.cbegin(), j.cend()),
"[json.exception.invalid_iterator.206] cannot construct with iterators from null");
}
}
......@@ -1193,17 +1201,17 @@ TEST_CASE("constructors")
{
{
json j = "foo";
CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range);
CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range);
CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range");
CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range");
CHECK_THROWS_AS(json(j.end(), j.end()), json::invalid_iterator);
CHECK_THROWS_AS(json(j.begin(), j.begin()), json::invalid_iterator);
CHECK_THROWS_WITH(json(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range");
CHECK_THROWS_WITH(json(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range");
}
{
json j = "bar";
CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range);
CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range);
CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range");
CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range");
CHECK_THROWS_AS(json(j.cend(), j.cend()), json::invalid_iterator);
CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), json::invalid_iterator);
CHECK_THROWS_WITH(json(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range");
CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range");
}
}
......@@ -1211,17 +1219,17 @@ TEST_CASE("constructors")
{
{
json j = false;
CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range);
CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range);
CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range");
CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range");
CHECK_THROWS_AS(json(j.end(), j.end()), json::invalid_iterator);
CHECK_THROWS_AS(json(j.begin(), j.begin()), json::invalid_iterator);
CHECK_THROWS_WITH(json(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range");
CHECK_THROWS_WITH(json(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range");
}
{
json j = true;
CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range);
CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range);
CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range");
CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range");
CHECK_THROWS_AS(json(j.cend(), j.cend()), json::invalid_iterator);
CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), json::invalid_iterator);
CHECK_THROWS_WITH(json(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range");
CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range");
}
}
......@@ -1229,17 +1237,17 @@ TEST_CASE("constructors")
{
{
json j = 17;
CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range);
CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range);
CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range");
CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range");
CHECK_THROWS_AS(json(j.end(), j.end()), json::invalid_iterator);
CHECK_THROWS_AS(json(j.begin(), j.begin()), json::invalid_iterator);
CHECK_THROWS_WITH(json(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range");
CHECK_THROWS_WITH(json(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range");
}
{
json j = 17;
CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range);
CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range);
CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range");
CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range");
CHECK_THROWS_AS(json(j.cend(), j.cend()), json::invalid_iterator);
CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), json::invalid_iterator);
CHECK_THROWS_WITH(json(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range");
CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range");
}
}
......@@ -1247,17 +1255,17 @@ TEST_CASE("constructors")
{
{
json j = 17u;
CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range);
CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range);
CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range");
CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range");
CHECK_THROWS_AS(json(j.end(), j.end()), json::invalid_iterator);
CHECK_THROWS_AS(json(j.begin(), j.begin()), json::invalid_iterator);
CHECK_THROWS_WITH(json(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range");
CHECK_THROWS_WITH(json(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range");
}
{
json j = 17u;
CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range);
CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range);
CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range");
CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range");
CHECK_THROWS_AS(json(j.cend(), j.cend()), json::invalid_iterator);
CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), json::invalid_iterator);
CHECK_THROWS_WITH(json(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range");
CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range");
}
}
......@@ -1265,17 +1273,17 @@ TEST_CASE("constructors")
{
{
json j = 23.42;
CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range);
CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range);
CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range");
CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range");
CHECK_THROWS_AS(json(j.end(), j.end()), json::invalid_iterator);
CHECK_THROWS_AS(json(j.begin(), j.begin()), json::invalid_iterator);
CHECK_THROWS_WITH(json(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range");
CHECK_THROWS_WITH(json(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range");
}
{
json j = 23.42;
CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range);
CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range);
CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range");
CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range");
CHECK_THROWS_AS(json(j.cend(), j.cend()), json::invalid_iterator);
CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), json::invalid_iterator);
CHECK_THROWS_WITH(json(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range");
CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range");
}
}
}
......
此差异已折叠。
......@@ -90,15 +90,17 @@ TEST_CASE("deserialization")
std::stringstream ss1, ss2;
ss1 << "[\"foo\",1,2,3,false,{\"one\":1}";
ss2 << "[\"foo\",1,2,3,false,{\"one\":1}";
CHECK_THROWS_AS(json::parse(ss1), std::invalid_argument);
CHECK_THROWS_WITH(json::parse(ss2), "parse error - unexpected end of input; expected ']'");
CHECK_THROWS_AS(json::parse(ss1), json::parse_error);
CHECK_THROWS_WITH(json::parse(ss2),
"[json.exception.parse_error.101] parse error at 30: parse error - unexpected end of input; expected ']'");
}
SECTION("string")
{
json::string_t s = "[\"foo\",1,2,3,false,{\"one\":1}";
CHECK_THROWS_AS(json::parse(s), std::invalid_argument);
CHECK_THROWS_WITH(json::parse(s), "parse error - unexpected end of input; expected ']'");
CHECK_THROWS_AS(json::parse(s), json::parse_error);
CHECK_THROWS_WITH(json::parse(s),
"[json.exception.parse_error.101] parse error at 29: parse error - unexpected end of input; expected ']'");
}
SECTION("operator<<")
......@@ -107,8 +109,9 @@ TEST_CASE("deserialization")
ss1 << "[\"foo\",1,2,3,false,{\"one\":1}";
ss2 << "[\"foo\",1,2,3,false,{\"one\":1}";
json j;
CHECK_THROWS_AS(j << ss1, std::invalid_argument);
CHECK_THROWS_WITH(j << ss2, "parse error - unexpected end of input; expected ']'");
CHECK_THROWS_AS(j << ss1, json::parse_error);
CHECK_THROWS_WITH(j << ss2,
"[json.exception.parse_error.101] parse error at 30: parse error - unexpected end of input; expected ']'");
}
SECTION("operator>>")
......@@ -117,15 +120,16 @@ TEST_CASE("deserialization")
ss1 << "[\"foo\",1,2,3,false,{\"one\":1}";
ss2 << "[\"foo\",1,2,3,false,{\"one\":1}";
json j;
CHECK_THROWS_AS(ss1 >> j, std::invalid_argument);
CHECK_THROWS_WITH(ss2 >> j, "parse error - unexpected end of input; expected ']'");
CHECK_THROWS_AS(ss1 >> j, json::parse_error);
CHECK_THROWS_WITH(ss2 >> j,
"[json.exception.parse_error.101] parse error at 30: parse error - unexpected end of input; expected ']'");
}
SECTION("user-defined string literal")
{
CHECK_THROWS_AS("[\"foo\",1,2,3,false,{\"one\":1}"_json, std::invalid_argument);
CHECK_THROWS_AS("[\"foo\",1,2,3,false,{\"one\":1}"_json, json::parse_error);
CHECK_THROWS_WITH("[\"foo\",1,2,3,false,{\"one\":1}"_json,
"parse error - unexpected end of input; expected ']'");
"[json.exception.parse_error.101] parse error at 29: parse error - unexpected end of input; expected ']'");
}
}
......@@ -178,7 +182,7 @@ TEST_CASE("deserialization")
SECTION("empty container")
{
std::vector<uint8_t> v;
CHECK_THROWS_AS(json::parse(v), std::invalid_argument);
CHECK_THROWS_AS(json::parse(v), json::parse_error);
}
}
......@@ -223,7 +227,7 @@ TEST_CASE("deserialization")
SECTION("with empty range")
{
std::vector<uint8_t> v;
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument);
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error);
}
}
......@@ -233,91 +237,91 @@ TEST_CASE("deserialization")
SECTION("case 1")
{
uint8_t v[] = {'\"', 'a', 'a', 'a', 'a', 'a', 'a', '\\', 'u'};
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument);
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error);
}
SECTION("case 2")
{
uint8_t v[] = {'\"', 'a', 'a', 'a', 'a', 'a', 'a', '\\', 'u', '1'};
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument);
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error);
}
SECTION("case 3")
{
uint8_t v[] = {'\"', 'a', 'a', 'a', 'a', 'a', 'a', '\\', 'u', '1', '1', '1', '1', '1', '1', '1', '1'};
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument);
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error);
}
SECTION("case 4")
{
uint8_t v[] = {'\"', 'a', 'a', 'a', 'a', 'a', 'a', 'u', '1', '1', '1', '1', '1', '1', '1', '1', '\\'};
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument);
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error);
}
SECTION("case 5")
{
uint8_t v[] = {'\"', 0x7F, 0xC1};
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument);
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error);
}
SECTION("case 6")
{
uint8_t v[] = {'\"', 0x7F, 0xDF, 0x7F};
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument);
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error);
}
SECTION("case 7")
{
uint8_t v[] = {'\"', 0x7F, 0xDF, 0xC0};
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument);
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error);
}
SECTION("case 8")
{
uint8_t v[] = {'\"', 0x7F, 0xE0, 0x9F};
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument);
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error);
}
SECTION("case 9")
{
uint8_t v[] = {'\"', 0x7F, 0xEF, 0xC0};
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument);
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error);
}
SECTION("case 10")
{
uint8_t v[] = {'\"', 0x7F, 0xED, 0x7F};
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument);
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error);
}
SECTION("case 11")
{
uint8_t v[] = {'\"', 0x7F, 0xF0, 0x8F};
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument);
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error);
}
SECTION("case 12")
{
uint8_t v[] = {'\"', 0x7F, 0xF0, 0xC0};
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument);
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error);
}
SECTION("case 13")
{
uint8_t v[] = {'\"', 0x7F, 0xF3, 0x7F};
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument);
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error);
}
SECTION("case 14")
{
uint8_t v[] = {'\"', 0x7F, 0xF3, 0xC0};
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument);
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error);
}
SECTION("case 15")
{
uint8_t v[] = {'\"', 0x7F, 0xF4, 0x7F};
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument);
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error);
}
}
}
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
......@@ -1016,6 +1016,90 @@ TEST_CASE("MessagePack")
json j = json::from_msgpack(given);
CHECK(j.get<double>() == Approx(25.0000019073486));
}
SECTION("errors")
{
SECTION("too short byte vector")
{
CHECK_THROWS_AS(json::from_msgpack(std::vector<uint8_t>({0xcc})), json::parse_error);
CHECK_THROWS_AS(json::from_msgpack(std::vector<uint8_t>({0xcd})), json::parse_error);
CHECK_THROWS_AS(json::from_msgpack(std::vector<uint8_t>({0xcd, 0x00})), json::parse_error);
CHECK_THROWS_AS(json::from_msgpack(std::vector<uint8_t>({0xce})), json::parse_error);
CHECK_THROWS_AS(json::from_msgpack(std::vector<uint8_t>({0xce, 0x00})), json::parse_error);
CHECK_THROWS_AS(json::from_msgpack(std::vector<uint8_t>({0xce, 0x00, 0x00})), json::parse_error);
CHECK_THROWS_AS(json::from_msgpack(std::vector<uint8_t>({0xce, 0x00, 0x00, 0x00})), json::parse_error);
CHECK_THROWS_AS(json::from_msgpack(std::vector<uint8_t>({0xcf})), json::parse_error);
CHECK_THROWS_AS(json::from_msgpack(std::vector<uint8_t>({0xcf, 0x00})), json::parse_error);
CHECK_THROWS_AS(json::from_msgpack(std::vector<uint8_t>({0xcf, 0x00, 0x00})), json::parse_error);
CHECK_THROWS_AS(json::from_msgpack(std::vector<uint8_t>({0xcf, 0x00, 0x00, 0x00})), json::parse_error);
CHECK_THROWS_AS(json::from_msgpack(std::vector<uint8_t>({0xcf, 0x00, 0x00, 0x00, 0x00})), json::parse_error);
CHECK_THROWS_AS(json::from_msgpack(std::vector<uint8_t>({0xcf, 0x00, 0x00, 0x00, 0x00, 0x00})), json::parse_error);
CHECK_THROWS_AS(json::from_msgpack(std::vector<uint8_t>({0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), json::parse_error);
CHECK_THROWS_AS(json::from_msgpack(std::vector<uint8_t>({0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), json::parse_error);
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xcc})),
"[json.exception.parse_error.110] parse error at 2: cannot read 1 bytes from vector");
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xcd})),
"[json.exception.parse_error.110] parse error at 2: cannot read 2 bytes from vector");
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xcd, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 2 bytes from vector");
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xce})),
"[json.exception.parse_error.110] parse error at 2: cannot read 4 bytes from vector");
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xce, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 4 bytes from vector");
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xce, 0x00, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 4 bytes from vector");
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xce, 0x00, 0x00, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 4 bytes from vector");
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xcf})),
"[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector");
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xcf, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector");
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xcf, 0x00, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector");
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xcf, 0x00, 0x00, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector");
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xcf, 0x00, 0x00, 0x00, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector");
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xcf, 0x00, 0x00, 0x00, 0x00, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector");
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector");
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})),
"[json.exception.parse_error.110] parse error at 2: cannot read 8 bytes from vector");
}
SECTION("unsupported bytes")
{
SECTION("concrete examples")
{
CHECK_THROWS_AS(json::from_msgpack(std::vector<uint8_t>({0xc1})), json::parse_error);
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xc1})),
"[json.exception.parse_error.112] parse error at 1: error reading MessagePack; last byte: 0xc1");
CHECK_THROWS_AS(json::from_msgpack(std::vector<uint8_t>({0xc6})), json::parse_error);
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xc6})),
"[json.exception.parse_error.112] parse error at 1: error reading MessagePack; last byte: 0xc6");
}
SECTION("all unsupported bytes")
{
for (auto byte :
{
// never used
0xc1,
// bin
0xc4, 0xc5, 0xc6,
// ext
0xc7, 0xc8, 0xc9,
// fixext
0xd4, 0xd5, 0xd6, 0xd7, 0xd8
})
{
CHECK_THROWS_AS(json::from_msgpack(std::vector<uint8_t>({static_cast<uint8_t>(byte)})), json::parse_error);
}
}
}
}
}
......@@ -1116,7 +1200,7 @@ TEST_CASE("MessagePack roundtrips", "[hide]")
"test/data/nst_json_testsuite/test_parsing/y_number_after_space.json",
"test/data/nst_json_testsuite/test_parsing/y_number_double_close_to_zero.json",
"test/data/nst_json_testsuite/test_parsing/y_number_double_huge_neg_exp.json",
"test/data/nst_json_testsuite/test_parsing/y_number_huge_exp.json",
//"test/data/nst_json_testsuite/test_parsing/y_number_huge_exp.json",
"test/data/nst_json_testsuite/test_parsing/y_number_int_with_exp.json",
"test/data/nst_json_testsuite/test_parsing/y_number_minus_zero.json",
"test/data/nst_json_testsuite/test_parsing/y_number_negative_int.json",
......@@ -1128,9 +1212,9 @@ TEST_CASE("MessagePack roundtrips", "[hide]")
"test/data/nst_json_testsuite/test_parsing/y_number_real_exponent.json",
"test/data/nst_json_testsuite/test_parsing/y_number_real_fraction_exponent.json",
"test/data/nst_json_testsuite/test_parsing/y_number_real_neg_exp.json",
"test/data/nst_json_testsuite/test_parsing/y_number_real_neg_overflow.json",
//"test/data/nst_json_testsuite/test_parsing/y_number_real_neg_overflow.json",
"test/data/nst_json_testsuite/test_parsing/y_number_real_pos_exponent.json",
"test/data/nst_json_testsuite/test_parsing/y_number_real_pos_overflow.json",
//"test/data/nst_json_testsuite/test_parsing/y_number_real_pos_overflow.json",
"test/data/nst_json_testsuite/test_parsing/y_number_real_underflow.json",
"test/data/nst_json_testsuite/test_parsing/y_number_simple_int.json",
"test/data/nst_json_testsuite/test_parsing/y_number_simple_real.json",
......
此差异已折叠。
此差异已折叠。
......@@ -38,6 +38,9 @@ TEST_CASE("Unicode", "[hide]")
{
SECTION("full enumeration of Unicode code points")
{
// lexer to call to_unicode on
json::lexer dummy_lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(""), 0);
// create an escaped string from a code point
const auto codepoint_to_unicode = [](std::size_t cp)
{
......@@ -85,7 +88,7 @@ TEST_CASE("Unicode", "[hide]")
// they are checked with codepoint_to_unicode.
if (cp > 0x1f and cp != 0x22 and cp != 0x5c)
{
unescaped_string = json::lexer::to_unicode(cp);
unescaped_string = dummy_lexer.to_unicode(cp);
}
}
else
......@@ -97,7 +100,7 @@ TEST_CASE("Unicode", "[hide]")
const auto codepoint2 = 0xdc00u + ((cp - 0x10000u) & 0x3ffu);
escaped_string = codepoint_to_unicode(codepoint1);
escaped_string += codepoint_to_unicode(codepoint2);
unescaped_string += json::lexer::to_unicode(codepoint1, codepoint2);
unescaped_string += dummy_lexer.to_unicode(codepoint1, codepoint2);
}
// all other code points are valid and must not yield parse errors
......@@ -170,7 +173,7 @@ TEST_CASE("Unicode", "[hide]")
SECTION("error for incomplete/wrong BOM")
{
CHECK_THROWS_AS(json::parse("\xef\xbb"), std::invalid_argument);
CHECK_THROWS_AS(json::parse("\xef\xbb\xbb"), std::invalid_argument);
CHECK_THROWS_AS(json::parse("\xef\xbb"), json::parse_error);
CHECK_THROWS_AS(json::parse("\xef\xbb\xbb"), json::parse_error);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册