/** * Copyright (C) 2018-present MongoDB, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the Server Side Public License, version 1, * as published by MongoDB, Inc. * * This program 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 * Server Side Public License for more details. * * You should have received a copy of the Server Side Public License * along with this program. If not, see * . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the Server Side Public License in all respects for * all of the code used other than as permitted herein. If you modify file(s) * with this exception, you may extend this exception to your version of the * file(s), but you are not obligated to do so. If you do not wish to do so, * delete this exception statement from your version. If you delete this * exception statement from all source files in the program, then also delete * it in the license file. */ #include "mongo/platform/basic.h" #include "mongo/util/duration.h" #include #include "mongo/stdx/chrono.h" #include "mongo/unittest/unittest.h" namespace mongo { namespace { using namespace fmt::literals; // The DurationTestSameType Compare* tests server to check the implementation of the comparison // operators as well as the compare() method, and so sometimes must explicitly check ASSERT_FALSE(v1 // OP v2). The DurationTestDifferentTypes Compare* tests rely on the fact that the operators all // just call compare(), so if the operators worked in the SameType tests, they can be trusted in the // DifferentType tests. As such, the DifferentType tests only use ASSERT_{GT,LT,LTE,GTE,EQ,NE} and // never do an ASSERT_FALSE. TEST(DurationComparisonSameType, CompareEqual) { ASSERT_EQ(Microseconds::zero(), Microseconds::zero()); ASSERT_EQ(Microseconds::max(), Microseconds::max()); ASSERT_EQ(Microseconds::min(), Microseconds::min()); ASSERT_FALSE(Microseconds::zero() == Microseconds{-1}); } TEST(DurationComparisonSameType, CompareNotEqual) { ASSERT_NE(Microseconds{1}, Microseconds::zero()); ASSERT_NE(Microseconds{-1}, Microseconds{1}); ASSERT_FALSE(Microseconds::zero() != Microseconds{0}); } TEST(DurationComparisonSameType, SameTypeCompareGreaterThan) { ASSERT_GT(Microseconds::zero(), Microseconds::min()); ASSERT_GT(Microseconds{Microseconds::min().count() + 1}, Microseconds::min()); ASSERT_FALSE(Microseconds{-10} > Microseconds{103}); ASSERT_FALSE(Microseconds{1} > Microseconds{1}); } TEST(DurationComparisonSameType, CompareLessThan) { ASSERT_LT(Microseconds::zero(), Microseconds::max()); ASSERT_LT(Microseconds{Microseconds::max().count() - 1}, Microseconds::max()); ASSERT_LT(Microseconds{1}, Microseconds{10}); ASSERT_FALSE(Microseconds{1} < Microseconds{1}); ASSERT_FALSE(Microseconds{-3} < Microseconds{-1200}); } TEST(DurationComparisonSameType, CompareGreaterThanOrEqual) { ASSERT_GTE(Microseconds::zero(), Microseconds::min()); ASSERT_GTE(Microseconds{Microseconds::min().count() + 1}, Microseconds::min()); ASSERT_GTE(Microseconds::max(), Microseconds::max()); ASSERT_GTE(Microseconds::min(), Microseconds::min()); ASSERT_GTE(Microseconds{5}, Microseconds{5}); ASSERT_FALSE(Microseconds{-10} > Microseconds{103}); } TEST(DurationComparisonSameType, CompareLessThanOrEqual) { ASSERT_LTE(Microseconds::zero(), Microseconds::max()); ASSERT_LTE(Microseconds{Microseconds::max().count() - 1}, Microseconds::max()); ASSERT_LTE(Microseconds{1}, Microseconds{10}); ASSERT_LTE(Microseconds{1}, Microseconds{1}); ASSERT_FALSE(Microseconds{-3} < Microseconds{-1200}); } // Since comparison operators are implemented in terms of Duration::compare, we do not need to // re-test all of the operators when the duration types are different. It suffices to know that // compare works, which can be accomplished with EQ, NE and LT alone. TEST(DurationComparisonDifferentTypes, CompareEqual) { ASSERT_EQ(Seconds::zero(), Milliseconds::zero()); ASSERT_EQ(Seconds{16}, Milliseconds{16000}); ASSERT_EQ(Minutes{60}, Hours{1}); } TEST(DurationComparisonDifferentTypes, CompareNotEqual) { ASSERT_NE(Milliseconds::max(), Seconds::max()); ASSERT_NE(Milliseconds::min(), Seconds::min()); ASSERT_NE(Seconds::max(), Milliseconds::max()); ASSERT_NE(Seconds::min(), Milliseconds::min()); ASSERT_NE(Seconds{1}, Milliseconds{1}); } TEST(DurationComparisonDifferentTypes, CompareLessThan) { ASSERT_LT(Milliseconds{1}, Seconds{1}); ASSERT_LT(Milliseconds{999}, Seconds{1}); ASSERT_LT(Seconds{1}, Milliseconds{1001}); ASSERT_LT(Milliseconds{-1001}, Seconds{-1}); ASSERT_LT(Seconds{-1}, Milliseconds{-1}); ASSERT_LT(Seconds{-1}, Milliseconds{-999}); } TEST(DurationComparisonDifferentTypes, CompareAtLimits) { ASSERT_LT(Milliseconds::max(), Seconds::max()); ASSERT_LT(Seconds::min(), Milliseconds::min()); ASSERT_LT(Milliseconds::min(), duration_cast(duration_cast(Milliseconds::min()))); ASSERT_GT(Milliseconds::max(), duration_cast(duration_cast(Milliseconds::max()))); } TEST(DurationCast, NonTruncatingDurationCasts) { ASSERT_EQ(1, duration_cast(Milliseconds{1000}).count()); ASSERT_EQ(1000, duration_cast(Seconds{1}).count()); ASSERT_EQ(1000, Milliseconds{Seconds{1}}.count()); ASSERT_EQ(1053, duration_cast(Milliseconds{1053}).count()); } TEST(DurationCast, TruncatingDurationCasts) { ASSERT_EQ(1, duration_cast(Milliseconds{1600}).count()); ASSERT_EQ(0, duration_cast(Milliseconds{999}).count()); ASSERT_EQ(-1, duration_cast(Milliseconds{-1600}).count()); ASSERT_EQ(0, duration_cast(Milliseconds{-999}).count()); } TEST(DurationCast, OverflowingCastsThrow) { ASSERT_THROWS_CODE(duration_cast(Seconds::max()), AssertionException, ErrorCodes::DurationOverflow); ASSERT_THROWS_CODE(duration_cast(Seconds::min()), AssertionException, ErrorCodes::DurationOverflow); } TEST(DurationCast, DurationCastConstexpr) { // Converting from one Duration period to another is constexpr. { constexpr auto ms = duration_cast(Seconds(2)); ASSERT_EQ(2000, ms.count()); constexpr auto secs = duration_cast(ms); ASSERT_EQ(2, secs.count()); } // Converting from stdx::chrono::duration to Duration is constexpr. { constexpr auto ms = duration_cast(stdx::chrono::seconds(2)); ASSERT_EQ(2000, ms.count()); } // Test implicit conversion constructor. { constexpr Milliseconds ms = Seconds(2); ASSERT_EQ(2000, ms.count()); } } TEST(DurationCast, ImplicitConversionToStdxDuration) { auto standardMillis = Milliseconds{10}.toSystemDuration(); ASSERT_EQUALS(Milliseconds{10}, duration_cast(standardMillis)); } TEST(DurationAssignment, DurationAssignment) { Milliseconds ms = Milliseconds{15}; Milliseconds ms2 = ms; Milliseconds ms3 = Milliseconds{30}; ms3 = ms; ASSERT_EQ(ms, ms3); ASSERT_EQ(ms2, ms3); } TEST(DurationArithmetic, AddNoOverflowSucceeds) { ASSERT_EQ(Milliseconds{1001}, Milliseconds{1} + Seconds{1}); ASSERT_EQ(Milliseconds{1001}, Seconds{1} + Milliseconds{1}); ASSERT_EQ(Milliseconds{1001}, Milliseconds{1} + Milliseconds{1000}); } TEST(DurationArithmetic, AddOverflowThrows) { // Max + 1 should throw ASSERT_THROWS_CODE( Milliseconds::max() + Milliseconds{1}, AssertionException, ErrorCodes::DurationOverflow); // Min + -1 should throw ASSERT_THROWS_CODE( Milliseconds::min() + Milliseconds{-1}, AssertionException, ErrorCodes::DurationOverflow); // Conversion of Seconds::min() to Milliseconds should throw ASSERT_THROWS_CODE( Seconds::min() + Milliseconds{1}, AssertionException, ErrorCodes::DurationOverflow); ASSERT_THROWS_CODE( Milliseconds{1} + Seconds::min(), AssertionException, ErrorCodes::DurationOverflow); } TEST(DurationArithmetic, SubtractNoOverflowSucceeds) { ASSERT_EQ(Milliseconds{-999}, Milliseconds{1} - Seconds{1}); ASSERT_EQ(Milliseconds{999}, Seconds{1} - Milliseconds{1}); ASSERT_EQ(Milliseconds{-999}, Milliseconds{1} - Milliseconds{1000}); ASSERT_EQ(Milliseconds::zero() - Milliseconds{1}, -Milliseconds{1}); } TEST(DurationArithmetic, SubtractOverflowThrows) { // Min - 1 should throw ASSERT_THROWS_CODE( Milliseconds::min() - Milliseconds{1}, AssertionException, ErrorCodes::DurationOverflow); // Max + -1 should throw ASSERT_THROWS_CODE( Milliseconds::max() - Milliseconds{-1}, AssertionException, ErrorCodes::DurationOverflow); // Conversion of Seconds::min() to Milliseconds should throw ASSERT_THROWS_CODE( Seconds::min() - Milliseconds{1}, AssertionException, ErrorCodes::DurationOverflow); ASSERT_THROWS_CODE( Milliseconds{1} - Seconds::min(), AssertionException, ErrorCodes::DurationOverflow); } TEST(DurationArithmetic, MultiplyNoOverflowSucceds) { ASSERT_EQ(Milliseconds{150}, 15 * Milliseconds{10}); ASSERT_EQ(Milliseconds{150}, Milliseconds{15} * 10); } TEST(DurationArithmetic, MultiplyOverflowThrows) { ASSERT_THROWS_CODE(Milliseconds::max() * 2, AssertionException, ErrorCodes::DurationOverflow); ASSERT_THROWS_CODE(2 * Milliseconds::max(), AssertionException, ErrorCodes::DurationOverflow); ASSERT_THROWS_CODE(Milliseconds::max() * -2, AssertionException, ErrorCodes::DurationOverflow); ASSERT_THROWS_CODE(-2 * Milliseconds::max(), AssertionException, ErrorCodes::DurationOverflow); } TEST(DurationArithmetic, DivideNoOverflowSucceeds) { ASSERT_EQ(Milliseconds{-1}, Milliseconds{2} / -2); } TEST(DurationArithmetic, DivideOverflowThrows) { ASSERT_THROWS_CODE(Milliseconds::min() / -1, AssertionException, ErrorCodes::DurationOverflow); } /** Calls `deduceChronoDuration(in)`, asserts that it returns `equivalent`. */ template auto validateDeduce(In in, Equivalent equivalent) { static constexpr bool useDefaultPeriod = std::is_same_v; using OutPeriod = std::conditional_t, Period>; auto deduced = [&] { if constexpr (useDefaultPeriod) { return deduceChronoDuration(in); } else { return deduceChronoDuration(in); } }(); ASSERT((std::is_same_v)) << "expected:{}got:{}"_format( demangleName(typeid(Equivalent)), demangleName(typeid(deduced))); ASSERT_EQ(deduced.count(), equivalent.count()) << "in:{}, equivalent:{}, deduced:{}"_format(in, equivalent.count(), deduced.count()); } TEST(DeduceChronoDuration, DefaultPeriod) { validateDeduce(3600, stdx::chrono::duration(3600)); validateDeduce(3600, stdx::chrono::duration(3600)); validateDeduce((short)3600, stdx::chrono::duration(3600)); validateDeduce(3600u, stdx::chrono::duration(3600)); validateDeduce(3600., stdx::chrono::duration(3600)); validateDeduce(3600.5, stdx::chrono::duration(3600.5)); validateDeduce(-3600.5, stdx::chrono::duration(-3600.5)); } TEST(DeduceChronoDuration, ExplicitPeriod) { validateDeduce(123, stdx::chrono::duration(123)); validateDeduce(123, stdx::chrono::duration(123)); validateDeduce>(50, stdx::chrono::duration>(50)); } } // namespace } // namespace mongo