forked from OSchip/llvm-project
169 lines
5.6 KiB
C++
169 lines
5.6 KiB
C++
//===-- flang/unittests/RuntimeGTest/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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#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);
|
|
}
|
|
}
|