Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions test/ip/ipv6_classify_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -241,3 +241,13 @@ TEST(invalid_empty) {
TEST(invalid_ipv4) {
EXPECT_FALSE(sourcemeta::core::ipv6_classify("127.0.0.1").has_value());
}

TEST(anycast_block_with_nonzero_interface_is_reserved) {
const auto result{sourcemeta::core::ipv6_classify("2001:1:0:0:0:0:100:1")};
EXPECT_EQ(result.value(), sourcemeta::core::IPAddressClass::Reserved);
}

TEST(leading_zero_but_not_mapped_form) {
const auto result{sourcemeta::core::ipv6_classify("::100:0:0")};
EXPECT_EQ(result.value(), sourcemeta::core::IPAddressClass::Reserved);
}
12 changes: 12 additions & 0 deletions test/ip/ipv6_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -355,3 +355,15 @@ TEST(valid_two_digit_hex_groups_with_leading_zeros) {
TEST(valid_three_digit_hex_groups_with_leading_zeros) {
EXPECT_TRUE(sourcemeta::core::is_ipv6("001:002:003:004:005:006:007:008"));
}

TEST(rejects_trailing_bracket) {
EXPECT_FALSE(sourcemeta::core::is_ipv6("1::]"));
}

TEST(rejects_leading_single_colon_with_double) {
EXPECT_FALSE(sourcemeta::core::is_ipv6(":1::2"));
}

TEST(rejects_trailing_single_colon_with_double) {
EXPECT_FALSE(sourcemeta::core::is_ipv6("1::2:"));
}
97 changes: 97 additions & 0 deletions test/json/json_auto_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1189,3 +1189,100 @@ TEST(to_json_string_view_subview) {
EXPECT_TRUE(result.is_string());
EXPECT_EQ(result.to_string(), "hello");
}

TEST(from_json_optional_null_returns_nullopt_inner) {
const auto result{sourcemeta::core::from_json<std::optional<int>>(
sourcemeta::core::parse_json("null"))};
EXPECT_TRUE(result.has_value());
EXPECT_FALSE(result.value().has_value());
}

TEST(from_json_vector_element_mismatch_fails) {
const auto result{sourcemeta::core::from_json<std::vector<bool>>(
sourcemeta::core::parse_json("[1]"))};
EXPECT_FALSE(result.has_value());
}

TEST(from_json_vector_non_array_fails) {
const auto result{sourcemeta::core::from_json<std::vector<int>>(
sourcemeta::core::parse_json("5"))};
EXPECT_FALSE(result.has_value());
}

TEST(from_json_map_value_mismatch_fails) {
const auto result{sourcemeta::core::from_json<std::map<std::string, bool>>(
sourcemeta::core::parse_json(R"({ "a": 1 })"))};
EXPECT_FALSE(result.has_value());
}

TEST(from_json_map_non_object_fails) {
const auto result{sourcemeta::core::from_json<std::map<std::string, int>>(
sourcemeta::core::parse_json("5"))};
EXPECT_FALSE(result.has_value());
}

TEST(from_json_pair_non_array_fails) {
const auto result{sourcemeta::core::from_json<std::pair<int, int>>(
sourcemeta::core::parse_json("5"))};
EXPECT_FALSE(result.has_value());
}

TEST(from_json_pair_wrong_size_fails) {
const auto result{sourcemeta::core::from_json<std::pair<int, int>>(
sourcemeta::core::parse_json("[1]"))};
EXPECT_FALSE(result.has_value());
}

TEST(from_json_pair_first_mismatch_fails) {
const auto result{sourcemeta::core::from_json<std::pair<bool, int>>(
sourcemeta::core::parse_json("[1, 2]"))};
EXPECT_FALSE(result.has_value());
}

TEST(from_json_single_tuple_non_array_fails) {
const auto result{sourcemeta::core::from_json<std::tuple<int>>(
sourcemeta::core::parse_json("5"))};
EXPECT_FALSE(result.has_value());
}

TEST(from_json_single_tuple_wrong_size_fails) {
const auto result{sourcemeta::core::from_json<std::tuple<int>>(
sourcemeta::core::parse_json("[1, 2]"))};
EXPECT_FALSE(result.has_value());
}

TEST(from_json_tuple_non_array_fails) {
const auto result{sourcemeta::core::from_json<std::tuple<int, int>>(
sourcemeta::core::parse_json("5"))};
EXPECT_FALSE(result.has_value());
}

TEST(from_json_tuple_wrong_size_fails) {
const auto result{sourcemeta::core::from_json<std::tuple<int, int>>(
sourcemeta::core::parse_json("[1]"))};
EXPECT_FALSE(result.has_value());
}

TEST(from_json_variant_non_array_fails) {
const auto result{sourcemeta::core::from_json<std::variant<int, bool>>(
sourcemeta::core::parse_json("5"))};
EXPECT_FALSE(result.has_value());
}

TEST(from_json_variant_wrong_size_fails) {
const auto result{sourcemeta::core::from_json<std::variant<int, bool>>(
sourcemeta::core::parse_json("[0]"))};
EXPECT_FALSE(result.has_value());
}

TEST(from_json_variant_non_integer_index_fails) {
const auto result{sourcemeta::core::from_json<std::variant<int, bool>>(
sourcemeta::core::parse_json(R"([ "x", 1 ])"))};
EXPECT_FALSE(result.has_value());
}

TEST(from_json_variant_index_out_of_range_fails) {
const auto result{sourcemeta::core::from_json<std::variant<int, bool>>(
sourcemeta::core::parse_json("[ 5, 0 ]"))};
EXPECT_FALSE(result.has_value());
}
82 changes: 82 additions & 0 deletions test/json/json_value_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include <cstddef> // std::size_t
#include <cstdint> // std::int64_t
#include <functional> // std::reference_wrapper
#include <limits> // std::numeric_limits
#include <stdexcept> // std::out_of_range
#include <string> // std::string
#include <type_traits> // std::is_default_constructible, etc
#include <unordered_map> // std::unordered_map
Expand Down Expand Up @@ -841,3 +843,83 @@ TEST(deep_copy_of_a_nested_object) {
EXPECT_EQ(copy, document);
EXPECT_EQ(copy.at(0).at("a").at("b").at(1).to_integer(), 2);
}

TEST(add_integer_overflow_promotes_to_decimal) {
const sourcemeta::core::JSON left{std::numeric_limits<std::int64_t>::min()};
const sourcemeta::core::JSON right{-1};
// The integer sum overflows int64, so the operator promotes to Decimal. The
// exact value would be -9223372036854775809, but the Decimal add currently
// rounds to the working precision (tracked in the decimal precision bug
// report). Pin the exact current output so a change in either direction is
// caught
const auto result{left + right};
EXPECT_TRUE(result.is_decimal());
EXPECT_EQ(result.to_decimal().to_string(), "-9.223372036854776e+18");
}

TEST(subtract_integer_overflow_promotes_to_decimal) {
const sourcemeta::core::JSON left{std::numeric_limits<std::int64_t>::max()};
const sourcemeta::core::JSON right{-1};
// See the note on the addition overflow test above. Exact value would be
// 9223372036854775808; pin the current rounded output
const auto result{left - right};
EXPECT_TRUE(result.is_decimal());
EXPECT_EQ(result.to_decimal().to_string(), "9.223372036854776e+18");
}

TEST(copy_self_assignment) {
sourcemeta::core::JSON value{
sourcemeta::core::parse_json(R"JSON({ "a": [ 1, 2 ] })JSON")};
const sourcemeta::core::JSON &alias{value};
value = alias;
EXPECT_EQ(value,
sourcemeta::core::parse_json(R"JSON({ "a": [ 1, 2 ] })JSON"));
}

TEST(move_self_assignment) {
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
sourcemeta::core::JSON value{
sourcemeta::core::parse_json(R"JSON({ "a": [ 1, 2 ] })JSON")};
// Route through a reference so the compiler cannot statically flag the
// self-move, while the runtime self-assignment branch is still exercised
sourcemeta::core::JSON &alias{value};
value = std::move(alias);
EXPECT_EQ(value,
sourcemeta::core::parse_json(R"JSON({ "a": [ 1, 2 ] })JSON"));
}

TEST(null_less_than_null_is_false) {
EXPECT_FALSE(sourcemeta::core::JSON{nullptr} <
sourcemeta::core::JSON{nullptr});
}

TEST(real_below_int64_range_throws_on_as_integer) {
const sourcemeta::core::JSON value{-1e300};
try {
static_cast<void>(value.as_integer());
FAIL();
} catch (const std::out_of_range &) {
}
}

TEST(empty_on_string_returns_false) {
const sourcemeta::core::JSON value{"abc"};
EXPECT_FALSE(value.empty());
}

TEST(fast_hash_of_real_below_int64_range) {
const sourcemeta::core::JSON value{-1e300};
// A real outside the int64 range falls back to the real type hash
EXPECT_EQ(value.fast_hash(), 5);
}

TEST(fast_hash_of_real_above_int64_range) {
const sourcemeta::core::JSON value{1e300};
EXPECT_EQ(value.fast_hash(), 5);
}

TEST(merge_object_key_over_non_object_source) {
auto target{sourcemeta::core::parse_json(R"JSON({ "k": { "x": 1 } })JSON")};
const auto source{sourcemeta::core::parse_json(R"JSON({ "k": 2 })JSON")};
target.merge(source.as_object());
EXPECT_EQ(target.at("k").to_integer(), 2);
}
98 changes: 98 additions & 0 deletions test/jsonld/jsonld_expand_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -562,3 +562,101 @@ TEST(self_referential_compact_term_via_prefix) {
])")};
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
EXPECT_EQ(result, expected);
}

TEST(type_array_with_non_string_item_is_rejected) {
const auto input = sourcemeta::core::parse_json(R"({ "@type": [ 123 ] })");
try {
const auto result{sourcemeta::core::jsonld_expand(input)};
FAIL();
} catch (const sourcemeta::core::JSONLDError &error) {
EXPECT_STREQ(error.what(), "Invalid type value");
}
}

TEST(top_level_non_string_direction_is_rejected) {
const auto input = sourcemeta::core::parse_json(R"({ "@direction": 123 })");
try {
const auto result{sourcemeta::core::jsonld_expand(input)};
FAIL();
} catch (const sourcemeta::core::JSONLDError &error) {
EXPECT_STREQ(error.what(), "Invalid base direction");
}
}

TEST(nest_array_item_not_an_object_is_rejected) {
const auto input = sourcemeta::core::parse_json(R"({
"@context": { "@vocab": "http://example.com/" },
"@nest": [ "x" ]
})");
try {
const auto result{sourcemeta::core::jsonld_expand(input)};
FAIL();
} catch (const sourcemeta::core::JSONLDError &error) {
EXPECT_STREQ(error.what(), "Invalid @nest value");
}
}

TEST(nest_array_item_value_object_is_rejected) {
const auto input = sourcemeta::core::parse_json(R"({
"@context": { "@vocab": "http://example.com/" },
"@nest": [ { "@value": "x" } ]
})");
try {
const auto result{sourcemeta::core::jsonld_expand(input)};
FAIL();
} catch (const sourcemeta::core::JSONLDError &error) {
EXPECT_STREQ(error.what(), "Invalid @nest value");
}
}

TEST(term_container_array_with_non_string_is_rejected) {
const auto input = sourcemeta::core::parse_json(R"({
"@context": { "t": { "@id": "http://ex/t", "@container": [ 123 ] } },
"t": "x"
})");
try {
const auto result{sourcemeta::core::jsonld_expand(input)};
FAIL();
} catch (const sourcemeta::core::JSONLDError &error) {
EXPECT_STREQ(error.what(), "Invalid container mapping");
}
}

TEST(term_container_array_with_invalid_container_is_rejected) {
const auto input = sourcemeta::core::parse_json(R"({
"@context": { "t": { "@id": "http://ex/t", "@container": [ "@bogus" ] } },
"t": "x"
})");
try {
const auto result{sourcemeta::core::jsonld_expand(input)};
FAIL();
} catch (const sourcemeta::core::JSONLDError &error) {
EXPECT_STREQ(error.what(), "Invalid container mapping");
}
}

TEST(term_direction_non_string_is_rejected) {
const auto input = sourcemeta::core::parse_json(R"({
"@context": { "t": { "@id": "http://ex/t", "@direction": 123 } },
"t": "x"
})");
try {
const auto result{sourcemeta::core::jsonld_expand(input)};
FAIL();
} catch (const sourcemeta::core::JSONLDError &error) {
EXPECT_STREQ(error.what(), "Invalid base direction");
}
}

TEST(term_nest_non_string_is_rejected) {
const auto input = sourcemeta::core::parse_json(R"({
"@context": { "t": { "@id": "http://ex/t", "@nest": 123 } },
"t": "x"
})");
try {
const auto result{sourcemeta::core::jsonld_expand(input)};
FAIL();
} catch (const sourcemeta::core::JSONLDError &error) {
EXPECT_STREQ(error.what(), "Invalid @nest value");
}
}
Loading
Loading