Files
scylladb/tests/duration_test.cc
Jesse Haber-Kucharsky 8fa47b74e8 cql: Add definition of underlying type for durations
Cassandra 3.10 added the `duration` type [1], intended to manipulate date-time
values with offsets (for example, `now() - 2y3h`).

The full implementation of the `duration` type in Scylla requires support
for version 5 of the binary protocol, which is not yet available.

In the meantime, this patch patch adds the implementation of the underlying type
for the eventual `duration` type. Included is also the ported test suite from
the reference implementation and additional tests.

Related to #2240.

[1] https://issues.apache.org/jira/browse/CASSANDRA-11873

Signed-off-by: Jesse Haber-Kucharsky <jhaberku@scylladb.com>
Message-Id: <b1e481da103efee82106bf31f261c5a1f4f8d9ca.1499885803.git.jhaberku@scylladb.com>
2017-07-13 17:26:00 +03:00

179 lines
8.0 KiB
C++

/*
* Copyright (C) 2017 ScyllaDB
*/
/*
* This file is part of Scylla.
*
* Scylla is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Scylla is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
*/
#define BOOST_TEST_MODULE core
#include "duration.hh"
#include "disk-error-handler.hh"
#include <boost/test/unit_test.hpp>
#include <experimental/string_view>
namespace stdx = std::experimental;
thread_local disk_error_signal_type commit_error;
thread_local disk_error_signal_type general_disk_error;
namespace {
//
// To avoid confusing literals.
//
constexpr auto ns_per_us = 1000L;
constexpr auto ns_per_ms = ns_per_us * 1000L;
constexpr auto ns_per_s = ns_per_ms * 1000L;
constexpr auto ns_per_m = ns_per_s * 60L;
constexpr auto ns_per_h = ns_per_m * 60L;
// Normally we want to be explict, but brevity is nice for tests.
constexpr duration make_duration(months_counter::value_type m,
days_counter::value_type d,
nanoseconds_counter::value_type ns) noexcept {
return {months_counter(m), days_counter(d), nanoseconds_counter(ns)};
}
}
BOOST_AUTO_TEST_CASE(parse_standard) {
BOOST_REQUIRE_EQUAL(make_duration(14, 0, 0), duration("1y2mo"));
BOOST_REQUIRE_EQUAL(make_duration(-14, 0, 0), duration("-1y2mo"));
BOOST_REQUIRE_EQUAL(make_duration(14, 0, 0), duration("1Y2MO"));
BOOST_REQUIRE_EQUAL(make_duration(0, 14, 0), duration("2w"));
BOOST_REQUIRE_EQUAL(make_duration(0, 2, 10 * ns_per_h), duration("2d10h"));
BOOST_REQUIRE_EQUAL(make_duration(0, 2, 0), duration("2d"));
BOOST_REQUIRE_EQUAL(make_duration(0, 0, 30 * ns_per_h), duration("30h"));
BOOST_REQUIRE_EQUAL(make_duration(0, 0, (30 * ns_per_h) + (20 * ns_per_m)), duration("30h20m"));
BOOST_REQUIRE_EQUAL(make_duration(0, 0, 20 * ns_per_m), duration("20m"));
BOOST_REQUIRE_EQUAL(make_duration(0, 0, 56 * ns_per_s), duration("56s"));
BOOST_REQUIRE_EQUAL(make_duration(0, 0, 567 * ns_per_ms), duration("567ms"));
BOOST_REQUIRE_EQUAL(make_duration(0, 0, 1950 * ns_per_us), duration("1950us"));
BOOST_REQUIRE_EQUAL(make_duration(0, 0, 1950 * ns_per_us), duration("1950µs"));
BOOST_REQUIRE_EQUAL(make_duration(0, 0, 1950000), duration("1950000ns"));
BOOST_REQUIRE_EQUAL(make_duration(0, 0, 1950000), duration("1950000NS"));
BOOST_REQUIRE_EQUAL(make_duration(0, 0, -1950000), duration("-1950000ns"));
BOOST_REQUIRE_EQUAL(make_duration(15, 0, 130 * ns_per_m), duration("1y3mo2h10m"));
}
BOOST_AUTO_TEST_CASE(parse_standard_syntax_error) {
// Read the entire input.
BOOST_REQUIRE_EXCEPTION(duration("1y500"), duration_error, [](auto&& exn) {
return stdx::string_view(exn.what()) == "Unable to convert '1y500' to a duration";
});
// Do not skip invalid characters in the middle.
BOOST_REQUIRE_EXCEPTION(duration("1y xxx 500d"), duration_error, [](auto&& exn) {
return stdx::string_view(exn.what()) == "Unable to convert '1y xxx 500d' to a duration";
});
// Do not skip invalid characters at the beginning.
BOOST_REQUIRE_EXCEPTION(duration("xxx1y500d"), duration_error, [](auto&& exn) {
return stdx::string_view(exn.what()) == "Unable to convert 'xxx1y500d' to a duration";
});
}
BOOST_AUTO_TEST_CASE(parse_standard_order_error) {
BOOST_REQUIRE_EXCEPTION(duration("20s1h3m"), duration_error, [](auto &&exn) {
return stdx::string_view(exn.what()) == "Invalid duration. The seconds should be after hours";
});
}
BOOST_AUTO_TEST_CASE(parse_standard_repeated_error) {
BOOST_REQUIRE_EXCEPTION(duration("1h2h3m"), duration_error, [](auto &&exn) {
return stdx::string_view(exn.what()) == "Invalid duration. The hours are specified multiple times";
});
}
BOOST_AUTO_TEST_CASE(parse_standard_overflow_error) {
BOOST_REQUIRE_EXCEPTION(duration("178956971y"), duration_error, [](auto &&exn) {
return stdx::string_view(exn.what())
== "Invalid duration. The number of years must be less than or equal to 178956970";
});
BOOST_REQUIRE_EXCEPTION(duration("178956970y14mo"), duration_error, [](auto &&exn) {
return stdx::string_view(exn.what())
== "Invalid duration. The number of months must be less than or equal to 7";
});
}
BOOST_AUTO_TEST_CASE(parse_iso8601) {
BOOST_REQUIRE_EQUAL(make_duration(12, 2, 0), duration("P1Y2D"));
BOOST_REQUIRE_EQUAL(make_duration(14, 0, 0), duration("P1Y2M"));
BOOST_REQUIRE_EQUAL(make_duration(0, 14, 0), duration("P2W"));
BOOST_REQUIRE_EQUAL(make_duration(12, 0, 2 * ns_per_h), duration("P1YT2H"));
BOOST_REQUIRE_EQUAL(make_duration(-14, 0, 0), duration("-P1Y2M"));
BOOST_REQUIRE_EQUAL(make_duration(0, 2, 0), duration("P2D"));
BOOST_REQUIRE_EQUAL(make_duration(0, 0, 30 * ns_per_h), duration("PT30H"));
BOOST_REQUIRE_EQUAL(make_duration(0, 0, (30 * ns_per_h) + (20 * ns_per_m)), duration("PT30H20M"));
BOOST_REQUIRE_EQUAL(make_duration(0, 0, 20 * ns_per_m), duration("PT20M"));
BOOST_REQUIRE_EQUAL(make_duration(0, 0, 56 * ns_per_s), duration("PT56S"));
BOOST_REQUIRE_EQUAL(make_duration(15, 0, 130 * ns_per_m), duration("P1Y3MT2H10M"));
}
BOOST_AUTO_TEST_CASE(parse_iso8601_syntax_error) {
BOOST_REQUIRE_EXCEPTION(duration("P2003T23s"), duration_error, [](auto&& exn) {
return stdx::string_view(exn.what()) == "Unable to convert 'P2003T23s' to a duration";
});
}
BOOST_AUTO_TEST_CASE(parse_iso8601_alternative) {
BOOST_REQUIRE_EQUAL(make_duration(12, 2, 0), duration("P0001-00-02T00:00:00"));
BOOST_REQUIRE_EQUAL(make_duration(14, 0, 0), duration("P0001-02-00T00:00:00"));
BOOST_REQUIRE_EQUAL(make_duration(12, 0, 2 * ns_per_h), duration("P0001-00-00T02:00:00"));
BOOST_REQUIRE_EQUAL(make_duration(-14, 0, 0), duration("-P0001-02-00T00:00:00"));
BOOST_REQUIRE_EQUAL(make_duration(0, 2, 0), duration("P0000-00-02T00:00:00"));
BOOST_REQUIRE_EQUAL(make_duration(0, 0, 30 * ns_per_h), duration("P0000-00-00T30:00:00"));
BOOST_REQUIRE_EQUAL(make_duration(0, 0, (30 * ns_per_h) + (20 * ns_per_m)), duration("P0000-00-00T30:20:00"));
BOOST_REQUIRE_EQUAL(make_duration(0, 0, 20 * ns_per_m), duration("P0000-00-00T00:20:00"));
BOOST_REQUIRE_EQUAL(make_duration(0, 0, 56 * ns_per_s), duration("P0000-00-00T00:00:56"));
BOOST_REQUIRE_EQUAL(make_duration(15, 0, 130 * ns_per_m), duration("P0001-03-00T02:10:00"));
}
BOOST_AUTO_TEST_CASE(parse_iso8601_alternative_syntax_error) {
BOOST_REQUIRE_EXCEPTION(duration("P0001-00-02T000000"), duration_error, [](auto&& exn) {
return stdx::string_view(exn.what()) == "Unable to convert 'P0001-00-02T000000' to a duration";
});
}
BOOST_AUTO_TEST_CASE(parse_component_overflow) {
BOOST_REQUIRE_EXCEPTION(duration("10000000000000000000000000000000000m"), duration_error, [](auto&& exn) {
return stdx::string_view(exn.what()) == "Invalid duration. The count for the minutes is out of range";
});
BOOST_REQUIRE_EXCEPTION(duration("P10000000000000000000000000000000000Y5D"), duration_error, [](auto&& exn) {
return stdx::string_view(exn.what()) == "Invalid duration. The count for the years is out of range";
});
}
BOOST_AUTO_TEST_CASE(pretty_print) {
BOOST_REQUIRE_EQUAL(to_string(duration("1y3d")), "1y3d");
BOOST_REQUIRE_EQUAL(to_string(duration("25mo")), "2y1mo");
BOOST_REQUIRE_EQUAL(to_string(duration("1y2mo3w4d5h6m7s8ms9us10ns")), "1y2mo25d5h6m7s8ms9us10ns");
BOOST_REQUIRE_EQUAL(to_string(duration("-1d5m")), "-1d5m");
}
BOOST_AUTO_TEST_CASE(equality) {
BOOST_REQUIRE_EQUAL(make_duration(1, 2, 3), make_duration(1, 2, 3));
BOOST_REQUIRE_NE(make_duration(1, 2, 3), make_duration(1, 2, 4));
}