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