diff options
author | Thiago Macieira <thiago.macieira@intel.com> | 2023-04-20 17:35:22 -0700 |
---|---|---|
committer | Thiago Macieira <thiago.macieira@intel.com> | 2023-05-06 17:54:23 +0000 |
commit | a2551c45d496c23045eb8451e080e75b2f8b42c1 (patch) | |
tree | 66ce1e4a0cb5341a34150d410484b4ff6e8009f2 | |
parent | 59f8da17e6a2989b072254970d23281301114503 (diff) | |
download | qtbase-a2551c45d496c23045eb8451e080e75b2f8b42c1.tar.gz |
Move the formatting of <chrono> durations to QDebug & QtTest
[ChangeLog][QtCore][QDebug] Added pretty formatting of C++ <chrono>
durations.
[ChangeLog][QtTest] Added pretty formatting of C++ <chrono> durations
for QCOMPARE expressions.
Change-Id: I3b169860d8bd41e9be6bfffd1757cc087ba957fa
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
-rw-r--r-- | src/corelib/io/qdebug.cpp | 98 | ||||
-rw-r--r-- | src/corelib/io/qdebug.h | 9 | ||||
-rw-r--r-- | src/testlib/qtest.h | 13 | ||||
-rw-r--r-- | src/testlib/qtestcase.h | 3 | ||||
-rw-r--r-- | tests/auto/corelib/io/qdebug/tst_qdebug.cpp | 107 | ||||
-rw-r--r-- | tests/auto/corelib/kernel/qdeadlinetimer/tst_qdeadlinetimer.cpp | 46 | ||||
-rw-r--r-- | tests/auto/testlib/CMakeLists.txt | 1 | ||||
-rw-r--r-- | tests/auto/testlib/tostring/CMakeLists.txt | 7 | ||||
-rw-r--r-- | tests/auto/testlib/tostring/tst_tostring.cpp | 150 |
9 files changed, 388 insertions, 46 deletions
diff --git a/src/corelib/io/qdebug.cpp b/src/corelib/io/qdebug.cpp index 0a72696a37..56deeb7cf7 100644 --- a/src/corelib/io/qdebug.cpp +++ b/src/corelib/io/qdebug.cpp @@ -15,6 +15,8 @@ #include <private/qtextstream_p.h> #include <private/qtools_p.h> +#include <q20chrono.h> + QT_BEGIN_NAMESPACE using namespace QtMiscUtils; @@ -346,6 +348,90 @@ void QDebug::putByteArray(const char *begin, size_t length, Latin1Content conten } /*! + \since 6.6 + \internal + Helper to the std::chrono::duration debug streaming output. + */ +QByteArray QDebug::timeUnit(qint64 num, qint64 den) +{ + using namespace std::chrono; + using namespace q20::chrono; + + if (num == 1 && den > 1) { + // sub-multiple of seconds + char prefix = '\0'; + auto tryprefix = [&](auto d, char c) { + static_assert(decltype(d)::num == 1, "not an SI prefix"); + if (den == decltype(d)::den) + prefix = c; + }; + + // "u" should be "µ", but debugging output is not always UTF-8-safe + tryprefix(std::milli{}, 'm'); + tryprefix(std::micro{}, 'u'); + tryprefix(std::nano{}, 'n'); + tryprefix(std::pico{}, 'p'); + tryprefix(std::femto{}, 'f'); + tryprefix(std::atto{}, 'a'); + // uncommon ones later + tryprefix(std::centi{}, 'c'); + tryprefix(std::deci{}, 'd'); + if (prefix) { + char unit[3] = { prefix, 's' }; + return QByteArray(unit, sizeof(unit) - 1); + } + } + + const char *unit = nullptr; + if (num > 1 && den == 1) { + // multiple of seconds - but we don't use SI prefixes + auto tryunit = [&](auto d, const char *name) { + static_assert(decltype(d)::period::den == 1, "not a multiple of a second"); + if (unit || num % decltype(d)::period::num) + return; + unit = name; + num /= decltype(d)::period::num; + }; + tryunit(years{}, "yr"); + tryunit(weeks{}, "wk"); + tryunit(days{}, "d"); + tryunit(hours{}, "h"); + tryunit(minutes{}, "min"); + } + if (!unit) + unit = "s"; + + if (num == 1 && den == 1) + return unit; + if (Q_UNLIKELY(num < 1 || den < 1)) + return QString::asprintf("<invalid time unit %lld/%lld>", num, den).toLatin1(); + + // uncommon units: will return something like "[2/3]s" + // strlen("[/]min") = 6 + char buf[2 * (std::numeric_limits<qint64>::digits10 + 2) + 10]; + size_t len = 0; + auto appendChar = [&](char c) { + Q_ASSERT(len < sizeof(buf)); + buf[len++] = c; + }; + auto appendNumber = [&](qint64 value) { + if (value >= 10'000 && (value % 1000) == 0) + len += qsnprintf(buf + len, sizeof(buf) - len, "%.6g", double(value)); // "1e+06" + else + len += qsnprintf(buf + len, sizeof(buf) - len, "%lld", value); + }; + appendChar('['); + appendNumber(num); + if (den != 1) { + appendChar('/'); + appendNumber(den); + } + appendChar(']'); + memcpy(buf + len, unit, strlen(unit)); + return QByteArray(buf, len + strlen(unit)); +} + +/*! \fn QDebug::swap(QDebug &other) \since 5.0 @@ -777,6 +863,18 @@ QDebug &QDebug::resetFormat() */ /*! + \since 6.6 + \fn template <typename Rep, typename Period> QDebug &QDebug::operator<<(std::chrono::duration<Rep, Period> duration) + + Prints the time duration \a duration to the stream and returns a reference + to the stream. The printed string is the numeric representation of the + period followed by the time unit, similar to what the C++ Standard Library + would produce with \c{std::ostream}. + + The unit is not localized. +*/ + +/*! \fn template <class T> QString QDebug::toString(T &&object) \since 6.0 diff --git a/src/corelib/io/qdebug.h b/src/corelib/io/qdebug.h index e39f9afd31..3fa9c42052 100644 --- a/src/corelib/io/qdebug.h +++ b/src/corelib/io/qdebug.h @@ -16,6 +16,7 @@ #include <QtCore/qsharedpointer.h> // all these have already been included by various headers above, but don't rely on indirect includes: +#include <chrono> #include <list> #include <map> #include <string> @@ -67,6 +68,7 @@ class QT6_ONLY(Q_CORE_EXPORT) QDebug : public QIODeviceBase QT7_ONLY(Q_CORE_EXPORT) void putUcs4(uint ucs4); QT7_ONLY(Q_CORE_EXPORT) void putString(const QChar *begin, size_t length); QT7_ONLY(Q_CORE_EXPORT) void putByteArray(const char *begin, size_t length, Latin1Content content); + QT7_ONLY(Q_CORE_EXPORT) static QByteArray timeUnit(qint64 num, qint64 den); public: explicit QDebug(QIODevice *device) : stream(new Stream(device)) {} explicit QDebug(QString *string) : stream(new Stream(string)) {} @@ -189,6 +191,13 @@ public: { return *this << QString::fromUcs4(s.data(), s.size()); } #endif // !Q_QDOC + template <typename Rep, typename Period> + QDebug &operator<<(std::chrono::duration<Rep, Period> duration) + { + stream->ts << duration.count() << timeUnit(Period::num, Period::den); + return maybeSpace(); + } + template <typename T> static QString toString(T &&object) { diff --git a/src/testlib/qtest.h b/src/testlib/qtest.h index 175affebcf..73278791ca 100644 --- a/src/testlib/qtest.h +++ b/src/testlib/qtest.h @@ -356,6 +356,19 @@ template<> inline char *toString(const QCborMap &m) return Internal::QCborValueFormatter::format(m); } +template <typename Rep, typename Period> char *toString(std::chrono::duration<Rep, Period> dur) +{ + QString r; + QDebug d(&r); + d.nospace() << qSetRealNumberPrecision(9) << dur; + if constexpr (Period::num != 1 || Period::den != 1) { + // include the equivalent value in seconds, in parentheses + using namespace std::chrono; + d << " (" << duration_cast<duration<qreal>>(dur).count() << "s)"; + } + return qstrdup(std::move(r).toUtf8().constData()); +} + template <typename T1, typename T2> inline char *toString(const std::pair<T1, T2> &pair) { diff --git a/src/testlib/qtestcase.h b/src/testlib/qtestcase.h index 3e0b2b3fb3..bd65c0ecab 100644 --- a/src/testlib/qtestcase.h +++ b/src/testlib/qtestcase.h @@ -360,6 +360,9 @@ namespace QTest template <class... Types> inline char *toString(const std::tuple<Types...> &tuple); + template <typename Rep, typename Period> + inline char *toString(std::chrono::duration<Rep, Period> duration); + Q_TESTLIB_EXPORT char *toHexRepresentation(const char *ba, qsizetype length); Q_TESTLIB_EXPORT char *toPrettyCString(const char *unicode, qsizetype length); Q_TESTLIB_EXPORT char *toPrettyUnicode(QStringView string); diff --git a/tests/auto/corelib/io/qdebug/tst_qdebug.cpp b/tests/auto/corelib/io/qdebug/tst_qdebug.cpp index a54018c3fa..37bb6aa65a 100644 --- a/tests/auto/corelib/io/qdebug/tst_qdebug.cpp +++ b/tests/auto/corelib/io/qdebug/tst_qdebug.cpp @@ -16,6 +16,8 @@ #include <QMimeDatabase> #include <QMetaType> +#include <q20chrono.h> + #ifdef __cpp_lib_memory_resource # include <memory_resource> namespace pmr = std::pmr; @@ -23,6 +25,8 @@ namespace pmr = std::pmr; namespace pmr = std; #endif +using namespace std::chrono; +using namespace q20::chrono; using namespace Qt::StringLiterals; static_assert(QTypeTraits::has_ostream_operator_v<QDebug, int>); @@ -83,6 +87,8 @@ private slots: void qDebugQByteArray() const; void qDebugQByteArrayView() const; void qDebugQFlags() const; + void qDebugStdChrono_data() const; + void qDebugStdChrono() const; void textStreamModifiers() const; void resetFormat() const; void defaultMessagehandler() const; @@ -1102,6 +1108,107 @@ void tst_QDebug::qDebugQFlags() const QCOMPARE(s_msg, QString::fromLatin1("QFlags<tst_QDebug::FlagType>(EnumFlag1)")); } +using ToStringFunction = std::function<QString()>; +void tst_QDebug::qDebugStdChrono_data() const +{ + using attoseconds = duration<int64_t, std::atto>; + using femtoseconds = duration<int64_t, std::femto>; + using picoseconds = duration<int64_t, std::pico>; + using centiseconds = duration<int64_t, std::centi>; + using deciseconds = duration<int64_t, std::deci>; + + using quadriennia = duration<int, std::ratio_multiply<std::ratio<4>, years::period>>; + using decades = duration<int, std::ratio_multiply<years::period, std::deca>>; // decayears + using centuries = duration<int16_t, std::ratio_multiply<years::period, std::hecto>>; // hectoyears + using millennia = duration<int16_t, std::ratio_multiply<years::period, std::kilo>>; // kiloyears + using gigayears = duration<int8_t, std::ratio_multiply<years::period, std::giga>>; + using fortnights = duration<int, std::ratio_multiply<days::period, std::ratio<14>>>; + using microfortnights = duration<int64_t, std::ratio_multiply<fortnights::period, std::micro>>; + using telecom = duration<int64_t, std::ratio<1, 8000>>; // 8 kHz + + using kiloseconds = duration<int64_t, std::kilo>; + using exaseconds = duration<int8_t, std::exa>; + using meter_per_light = duration<int64_t, std::ratio<1, 299'792'458>>; + using kilometer_per_light = duration<int64_t, std::ratio<1000, 299'792'458>>; + + QTest::addColumn<ToStringFunction>("fn"); + QTest::addColumn<QString>("expected"); + + auto addRow = [](const char *name, auto duration, const char *expected) { + auto toString = [duration]() { return QDebug::toString(duration); }; + QTest::newRow(name) << ToStringFunction(toString) << expected; + }; + + addRow("1as", attoseconds{1}, "1as"); + addRow("1fs", femtoseconds{1}, "1fs"); + addRow("1ps", picoseconds{1}, "1ps"); + addRow("0ns", 0ns, "0ns"); + addRow("1000ns", 1000ns, "1000ns"); + addRow("0us", 0us, "0us"); + addRow("0ms", 0ms, "0ms"); + addRow("1cs", centiseconds{1}, "1cs"); + addRow("2ds", deciseconds{2}, "2ds"); + addRow("-1s", -1s, "-1s"); + addRow("0s", 0s, "0s"); + addRow("1s", 1s, "1s"); + addRow("60s", 60s, "60s"); + addRow("1min", 1min, "1min"); + addRow("1h", 1h, "1h"); + addRow("1days", days{1}, "1d"); + addRow("365days", days{365}, "365d"); + addRow("1weeks", weeks{1}, "1wk"); + addRow("1years", years{1}, "1yr"); // 365.2425 days + addRow("42years", years{42}, "42yr"); + + addRow("1ks", kiloseconds{1}, "1[1000]s"); + addRow("2fortnights", fortnights{2}, "2[2]wk"); + addRow("1quadriennia", quadriennia{1}, "1[4]yr"); + addRow("1decades", decades{1}, "1[10]yr"); + addRow("1centuries", centuries{1}, "1[100]yr"); + addRow("1millennia", millennia{1}, "1[1000]yr"); +#if defined(Q_OS_LINUX) || defined(Q_OS_DARWIN) + // some OSes print the exponent differently + addRow("1Es", exaseconds{1}, "1[1e+18]s"); + addRow("13gigayears", gigayears{13}, "13[1e+09]yr"); +#endif + + // months are one twelfth of a Gregorian year, not 30 days + addRow("1months", months{1}, "1[2629746]s"); + + // weird units + addRow("2microfortnights", microfortnights{2}, "2[756/625]s"); + addRow("1telecom", telecom{1}, "1[1/8000]s"); + addRow("10m/c", meter_per_light{10}, "10[1/299792458]s"); + addRow("10km/c", kilometer_per_light{10}, "10[500/149896229]s"); + + // real floting point + using fpsec = duration<double>; + using fpmsec = duration<double, std::milli>; + using fpnsec = duration<double, std::nano>; + addRow("1.0s", fpsec{1}, "1s"); + addRow("1.5s", fpsec{1.5}, "1.5s"); + addRow("1.0ms", fpmsec{1}, "1ms"); + addRow("1.5ms", fpmsec{1.5}, "1.5ms"); + addRow("1.0ns", fpnsec{1}, "1ns"); + addRow("1.5ns", fpnsec{1.5}, "1.5ns"); + + // and some precision setting too + QTest::newRow("1.00000ns") + << ToStringFunction([]() { + QString buffer; + QDebug d(&buffer); + d.nospace() << qSetRealNumberPrecision(5) << Qt::fixed << fpnsec{1}; + return buffer; + }) << "1.00000ns"; +} + +void tst_QDebug::qDebugStdChrono() const +{ + QFETCH(ToStringFunction, fn); + QFETCH(QString, expected); + QCOMPARE(fn(), expected); +} + void tst_QDebug::textStreamModifiers() const { QString file, function; diff --git a/tests/auto/corelib/kernel/qdeadlinetimer/tst_qdeadlinetimer.cpp b/tests/auto/corelib/kernel/qdeadlinetimer/tst_qdeadlinetimer.cpp index bdf2e7a573..a584cda9fc 100644 --- a/tests/auto/corelib/kernel/qdeadlinetimer/tst_qdeadlinetimer.cpp +++ b/tests/auto/corelib/kernel/qdeadlinetimer/tst_qdeadlinetimer.cpp @@ -27,52 +27,6 @@ template<> char *toString(const QDeadlineTimer &dt) dt.hasExpired() ? " (expired)" : ""); return buf; } - -template <typename Rep, typename Period> char *toString(std::chrono::duration<Rep, Period> dur) -{ - using namespace std::chrono; - static_assert(sizeof(double) == sizeof(qlonglong)); - - if constexpr (Period::num == 1 && sizeof(Rep) <= sizeof(qlonglong)) { - // typical case: second or sub-multiple of second, in a representation - // we can directly use - char *buf = new char[128]; - if constexpr (std::is_integral_v<Rep>) { - char unit[] = "ss"; - if constexpr (std::is_same_v<Period, std::atto>) { // from Norwegian "atten", 18 - unit[0] = 'a'; - } else if constexpr (std::is_same_v<Period, std::femto>) { // Norwegian "femten", 15 - unit[0] = 'f'; - } else if constexpr (std::is_same_v<Period, std::pico>) { - unit[0] = 'p'; - } else if constexpr (std::is_same_v<Period, std::nano>) { - unit[0] = 'n'; - } else if constexpr (std::is_same_v<Period, std::micro>) { - unit[0] = 'u'; // µ, really, but the output may not be UTF-8-safe - } else if constexpr (std::is_same_v<Period, std::milli>) { - unit[0] = 'm'; - } else { - // deci, centi, cycles of something (60 Hz, 8000 Hz, etc.) - static_assert(Period::den == 1, - "Unsupported std::chrono::duration of a sub-multiple of second"); - unit[1] = '\0'; - } - - // cast to qlonglong in case Rep is not int64_t - qsnprintf(buf, 128, "%lld %s", qlonglong(dur.count()), unit); - } else { - auto secs = duration_cast<duration<double>>(dur); - qsnprintf(buf, 128, "%g s", secs.count()); - } - return buf; - } else if constexpr (std::is_integral_v<Rep> && Period::den == 1) { - // multiple of second, so just print it in seconds - return toString(std::chrono::seconds(dur)); - } else { - // something else, use floating-point seconds - return toString(std::chrono::duration_cast<double>(dur)); - } -} } QT_END_NAMESPACE diff --git a/tests/auto/testlib/CMakeLists.txt b/tests/auto/testlib/CMakeLists.txt index 1f8438fa64..35a26a8e34 100644 --- a/tests/auto/testlib/CMakeLists.txt +++ b/tests/auto/testlib/CMakeLists.txt @@ -2,6 +2,7 @@ # SPDX-License-Identifier: BSD-3-Clause add_subdirectory(qsignalspy) +add_subdirectory(tostring) # QTBUG-88507 if(QT_FEATURE_process AND NOT ANDROID) diff --git a/tests/auto/testlib/tostring/CMakeLists.txt b/tests/auto/testlib/tostring/CMakeLists.txt new file mode 100644 index 0000000000..57fc65d352 --- /dev/null +++ b/tests/auto/testlib/tostring/CMakeLists.txt @@ -0,0 +1,7 @@ +# Copyright (C) 2023 Intel Corporation. +# SPDX-License-Identifier: BSD-3-Clause + +qt_internal_add_test(tst_tostring + SOURCES + tst_tostring.cpp +) diff --git a/tests/auto/testlib/tostring/tst_tostring.cpp b/tests/auto/testlib/tostring/tst_tostring.cpp new file mode 100644 index 0000000000..3f12e0c798 --- /dev/null +++ b/tests/auto/testlib/tostring/tst_tostring.cpp @@ -0,0 +1,150 @@ +// Copyright (C) 2023 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include <QTest> +#include <memory> + +#include <q20chrono.h> + +using ToStringFunction = std::function<char *()>; +class tst_toString : public QObject +{ + Q_OBJECT +private: + void addColumns(); + void testRows(); +private slots: + void chrono_duration_data(); + void chrono_duration() { testRows(); } +}; + +void tst_toString::addColumns() +{ + QTest::addColumn<ToStringFunction>("fn"); + QTest::addColumn<QByteArrayView>("expected"); + QTest::addColumn<QByteArrayView>("expr"); + QTest::addColumn<QByteArrayView>("file"); + QTest::addColumn<int>("line"); +} +void tst_toString::testRows() +{ + QFETCH(ToStringFunction, fn); + QFETCH(QByteArrayView, expected); + QFETCH(QByteArrayView, expr); + QFETCH(QByteArrayView, file); + QFETCH(int, line); + + std::unique_ptr<char []> ptr{fn()}; + QTest::qCompare(ptr.get(), expected, expr.data(), expected.data(), file.data(), line); +} + +template <typename T> void addRow(QByteArrayView name, T &&value, QByteArrayView expression, + QByteArrayView expected, QByteArrayView file, int line) +{ + ToStringFunction fn = [v = std::move(value)]() { return QTest::toString(v); }; + QTest::newRow(name.data()) << fn << expected << expression << file << line; +} + +#define ADD_ROW(name, expr, expected) \ + ::addRow(name, expr, #expr, expected, __FILE__, __LINE__) + +void tst_toString::chrono_duration_data() +{ + addColumns(); + + using namespace std::chrono; + using namespace q20::chrono; + + using attoseconds = duration<int64_t, std::atto>; + using femtoseconds = duration<int64_t, std::femto>; + using picoseconds = duration<int64_t, std::pico>; + using centiseconds = duration<int64_t, std::centi>; + using deciseconds = duration<int64_t, std::deci>; + using kiloseconds = duration<int64_t, std::kilo>; + using decades = duration<int, std::ratio_multiply<years::period, std::deca>>; // decayears + using centuries = duration<int16_t, std::ratio_multiply<years::period, std::hecto>>; // hectoyears + using millennia = duration<int16_t, std::ratio_multiply<years::period, std::kilo>>; // kiloyears + using gigayears = duration<int8_t, std::ratio_multiply<years::period, std::giga>>; + using fortnights = duration<int, std::ratio_multiply<days::period, std::ratio<14>>>; + using microfortnights = duration<int64_t, std::ratio_multiply<fortnights::period, std::micro>>; + using meter_per_light = duration<int64_t, std::ratio<1, 299'792'458>>; + using kilometer_per_light = duration<int64_t, std::ratio<1000, 299'792'458>>; + using AU_per_light = duration<int64_t, std::ratio<149'597'871'800, 299'792'458>>; + using pstn_rate = duration<int64_t, std::ratio<1, 8000>>; // PSTN sampling rate (8 kHz) + using hyperfine = duration<int64_t, std::ratio<1, 9'192'631'770>>; // definition of second + + ADD_ROW("1as", attoseconds{1}, "1as (1e-18s)"); // from Norwegian "atten" (18) + ADD_ROW("1fs", femtoseconds{1}, "1fs (1e-15s)"); // from Norwegian "femten" (15) + ADD_ROW("1ps", picoseconds{1}, "1ps (1e-12s)"); // from Italian piccolo? + ADD_ROW("0ns", 0ns, "0ns (0s)"); + ADD_ROW("1000ns", 1000ns, "1000ns (1e-06s)"); + ADD_ROW("1us", 1us, "1us (1e-06s)"); + ADD_ROW("125us", 125us, "125us (0.000125s)"); + ADD_ROW("0ms", 0ms, "0ms (0s)"); + ADD_ROW("-1s", -1s, "-1s"); + ADD_ROW("0s", 0s, "0s"); + ADD_ROW("1cs", centiseconds{1}, "1cs (0.01s)"); + ADD_ROW("2ds", deciseconds{2}, "2ds (0.2s)"); + ADD_ROW("1s", 1s, "1s"); + ADD_ROW("60s", 60s, "60s"); + ADD_ROW("1min", 1min, "1min (60s)"); + ADD_ROW("1h", 1h, "1h (3600s)"); + ADD_ROW("1days", days{1}, "1d (86400s)"); + ADD_ROW("7days", days{7}, "7d (604800s)"); + ADD_ROW("1weeks", weeks{1}, "1wk (604800s)"); + ADD_ROW("365days", days{365}, "365d (31536000s)"); + ADD_ROW("1years", years{1}, "1yr (31556952s)"); // 365.2425 days + + ADD_ROW("2ks", kiloseconds{2}, "2[1000]s (2000s)"); + ADD_ROW("1fortnights", fortnights{1}, "1[2]wk (1209600s)"); + ADD_ROW("1decades", decades{1}, "1[10]yr (315569520s)"); + ADD_ROW("1centuries", centuries{1}, "1[100]yr (3.1556952e+09s)"); + ADD_ROW("1millennia", millennia{1}, "1[1000]yr (3.1556952e+10s)"); +#if defined(Q_OS_LINUX) || defined(Q_OS_DARWIN) + // some OSes print the exponent differently + ADD_ROW("13gigayears", gigayears{13}, "13[1e+09]yr (4.10240376e+17s)"); +#endif + + // months are one twelfth of a Gregorian year, not 30 days + ADD_ROW("1months", months{1}, "1[2629746]s (2629746s)"); + ADD_ROW("12months", months{12}, "12[2629746]s (31556952s)"); + + // weird units + ADD_ROW("2microfortnights", microfortnights{2}, "2[756/625]s (2.4192s)"); + ADD_ROW("1pstn_rate", pstn_rate{1}, "1[1/8000]s (0.000125s)"); // 125µs + ADD_ROW("10m/c", meter_per_light{10}, "10[1/299792458]s (3.33564095e-08s)"); + ADD_ROW("10km/c", kilometer_per_light{10}, "10[500/149896229]s (3.33564095e-05s)"); + ADD_ROW("1AU/c", AU_per_light{1}, "1[74798935900/149896229]s (499.004788s)"); + ADD_ROW("Cs133-hyperfine", hyperfine{1}, "1[1/9192631770]s (1.08782776e-10s)"); + ADD_ROW("1sec-definition", hyperfine{9'192'631'770}, "9192631770[1/9192631770]s (1s)"); + ADD_ROW("8000pstn_rate", pstn_rate{8000}, "8000[1/8000]s (1s)"); + + // real floting point + // current (2023) best estimate is 13.813 ± 0.038 billion years (Plank Collaboration) + using universe = duration<double, std::ratio_multiply<std::ratio<13'813'000'000>, years::period>>; + using fpksec = duration<double, std::kilo>; + using fpsec = duration<double>; + using fpmsec = duration<double, std::milli>; + using fpnsec = duration<double, std::nano>; + using fpGyr = duration<double, std::ratio_multiply<years::period, std::giga>>; + + ADD_ROW("1.0s", fpsec{1}, "1s"); + ADD_ROW("1.5s", fpsec{1.5}, "1.5s"); + ADD_ROW("-1.0ms", fpmsec{-1}, "-1ms (-0.001s)"); + ADD_ROW("1.5ms", fpmsec{1.5}, "1.5ms (0.0015s)"); + ADD_ROW("1.0ns", fpnsec{1}, "1ns (1e-09s)"); + ADD_ROW("-1.5ns", fpnsec{-1.5}, "-1.5ns (-1.5e-09s)"); + ADD_ROW("1.0ks", fpksec{1}, "1[1000]s (1000s)"); + ADD_ROW("-1.5ks", fpksec{-1.5}, "-1.5[1000]s (-1500s)"); + ADD_ROW("1.0zs", fpsec{1e-21}, "1e-21s"); // zeptosecond + ADD_ROW("1.0ys", fpsec{1e-24}, "1e-24s"); // yoctosecond + ADD_ROW("planck-time", fpsec(5.39124760e-44), "5.3912476e-44s"); +#if defined(Q_OS_LINUX) || defined(Q_OS_DARWIN) + // some OSes print the exponent differently + ADD_ROW("13.813Gyr", fpGyr(13.813), "13.813[1e+09]yr (4.35896178e+17s)"); + ADD_ROW("1universe", universe{1}, "1[1.3813e+10]yr (4.35896178e+17s)"); +#endif +} + +QTEST_APPLESS_MAIN(tst_toString) +#include "tst_tostring.moc" |