1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
|
//===-- flang/unittests/Runtime/Time.cpp ----------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef __clang__ // 16.0.3 lacks <charconv>
#include "gtest/gtest.h"
#include "flang/Runtime/time-intrinsic.h"
#include <algorithm>
#include <cctype>
#include <charconv>
#include <string>
using namespace Fortran::runtime;
TEST(TimeIntrinsics, CpuTime) {
// We can't really test that we get the "right" result for CPU_TIME, but we
// can have a smoke test to see that we get something reasonable on the
// platforms where we expect to support it.
double start{RTNAME(CpuTime)()};
ASSERT_GE(start, 0.0);
// Loop until we get a different value from CpuTime. If we don't get one
// before we time out, then we should probably look into an implementation
// for CpuTime with a better timer resolution.
for (double end = start; end == start; end = RTNAME(CpuTime)()) {
ASSERT_GE(end, 0.0);
ASSERT_GE(end, start);
}
}
using count_t = std::int64_t;
TEST(TimeIntrinsics, SystemClock) {
// We can't really test that we get the "right" result for SYSTEM_CLOCK, but
// we can have a smoke test to see that we get something reasonable on the
// platforms where we expect to support it.
// The value of the count rate and max will vary by platform, but they should
// always be strictly positive if we have a working implementation of
// SYSTEM_CLOCK.
EXPECT_GT(RTNAME(SystemClockCountRate)(), 0);
count_t max1{RTNAME(SystemClockCountMax)(1)};
EXPECT_GT(max1, 0);
EXPECT_LE(max1, static_cast<count_t>(0x7f));
count_t start1{RTNAME(SystemClockCount)(1)};
EXPECT_GE(start1, 0);
EXPECT_LE(start1, max1);
count_t max2{RTNAME(SystemClockCountMax)(2)};
EXPECT_GT(max2, 0);
EXPECT_LE(max2, static_cast<count_t>(0x7fff));
count_t start2{RTNAME(SystemClockCount)(2)};
EXPECT_GE(start2, 0);
EXPECT_LE(start2, max2);
count_t max4{RTNAME(SystemClockCountMax)(4)};
EXPECT_GT(max4, 0);
EXPECT_LE(max4, static_cast<count_t>(0x7fffffff));
count_t start4{RTNAME(SystemClockCount)(4)};
EXPECT_GE(start4, 0);
EXPECT_LE(start4, max4);
count_t max8{RTNAME(SystemClockCountMax)(8)};
EXPECT_GT(max8, 0);
count_t start8{RTNAME(SystemClockCount)(8)};
EXPECT_GE(start8, 0);
EXPECT_LT(start8, max8);
count_t max16{RTNAME(SystemClockCountMax)(16)};
EXPECT_GT(max16, 0);
count_t start16{RTNAME(SystemClockCount)(16)};
EXPECT_GE(start16, 0);
EXPECT_LT(start16, max16);
// Loop until we get a different value from SystemClockCount. If we don't get
// one before we time out, then we should probably look into an implementation
// for SystemClokcCount with a better timer resolution on this platform.
for (count_t end{start8}; end == start8; end = RTNAME(SystemClockCount)(8)) {
EXPECT_GE(end, 0);
EXPECT_LE(end, max8);
EXPECT_GE(end, start8);
}
}
TEST(TimeIntrinsics, DateAndTime) {
constexpr std::size_t bufferSize{16};
std::string date(bufferSize, 'Z'), time(bufferSize, 'Z'),
zone(bufferSize, 'Z');
RTNAME(DateAndTime)
(date.data(), date.size(), time.data(), time.size(), zone.data(), zone.size(),
/*source=*/nullptr, /*line=*/0, /*values=*/nullptr);
auto isBlank = [](const std::string &s) -> bool {
return std::all_of(
s.begin(), s.end(), [](char c) { return std::isblank(c); });
};
// Validate date is blank or YYYYMMDD.
if (isBlank(date)) {
EXPECT_TRUE(true);
} else {
count_t number{-1};
auto [_, ec]{
std::from_chars(date.data(), date.data() + date.size(), number)};
ASSERT_TRUE(ec != std::errc::invalid_argument &&
ec != std::errc::result_out_of_range);
EXPECT_GE(number, 0);
auto year = number / 10000;
auto month = (number - year * 10000) / 100;
auto day = number % 100;
// Do not assume anything about the year, the test could be
// run on system with fake/outdated dates.
EXPECT_LE(month, 12);
EXPECT_GT(month, 0);
EXPECT_LE(day, 31);
EXPECT_GT(day, 0);
}
// Validate time is hhmmss.sss or blank.
if (isBlank(time)) {
EXPECT_TRUE(true);
} else {
count_t number{-1};
auto [next, ec]{
std::from_chars(time.data(), time.data() + date.size(), number)};
ASSERT_TRUE(ec != std::errc::invalid_argument &&
ec != std::errc::result_out_of_range);
ASSERT_GE(number, 0);
auto hours = number / 10000;
auto minutes = (number - hours * 10000) / 100;
auto seconds = number % 100;
EXPECT_LE(hours, 23);
EXPECT_LE(minutes, 59);
// Accept 60 for leap seconds.
EXPECT_LE(seconds, 60);
ASSERT_TRUE(next != time.data() + time.size());
EXPECT_EQ(*next, '.');
count_t milliseconds{-1};
ASSERT_TRUE(next + 1 != time.data() + time.size());
auto [_, ec2]{
std::from_chars(next + 1, time.data() + date.size(), milliseconds)};
ASSERT_TRUE(ec2 != std::errc::invalid_argument &&
ec2 != std::errc::result_out_of_range);
EXPECT_GE(milliseconds, 0);
EXPECT_LE(milliseconds, 999);
}
// Validate zone is +hhmm or -hhmm or blank.
if (isBlank(zone)) {
EXPECT_TRUE(true);
} else {
ASSERT_TRUE(zone.size() > 1);
EXPECT_TRUE(zone[0] == '+' || zone[0] == '-');
count_t number{-1};
auto [next, ec]{
std::from_chars(zone.data() + 1, zone.data() + zone.size(), number)};
ASSERT_TRUE(ec != std::errc::invalid_argument &&
ec != std::errc::result_out_of_range);
ASSERT_GE(number, 0);
auto hours = number / 100;
auto minutes = number % 100;
EXPECT_LE(hours, 23);
EXPECT_LE(minutes, 59);
}
}
#endif // __clang__
|