1 //===-- flang/unittests/RuntimeGTest/Time.cpp -----------------------===//
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 <charconv>
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 = CppTypeFor<TypeCategory::Integer, 8>;
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 max{RTNAME(SystemClockCountMax)()};
47   EXPECT_GT(max, 0);
48 
49   count_t start{RTNAME(SystemClockCount)()};
50   EXPECT_GE(start, 0);
51   EXPECT_LE(start, max);
52 
53   // Loop until we get a different value from SystemClockCount. If we don't get
54   // one before we time out, then we should probably look into an implementation
55   // for SystemClokcCount with a better timer resolution on this platform.
56   for (count_t end = start; end == start; end = RTNAME(SystemClockCount)()) {
57     EXPECT_GE(end, 0);
58     EXPECT_LE(end, max);
59 
60     EXPECT_GE(end, start);
61   }
62 }
63 
64 TEST(TimeIntrinsics, DateAndTime) {
65   constexpr std::size_t bufferSize{16};
66   std::string date(bufferSize, 'Z'), time(bufferSize, 'Z'),
67       zone(bufferSize, 'Z');
68   RTNAME(DateAndTime)
69   (date.data(), date.size(), time.data(), time.size(), zone.data(), zone.size(),
70       /*source=*/nullptr, /*line=*/0, /*values=*/nullptr);
71   auto isBlank = [](const std::string &s) -> bool {
72     return std::all_of(
73         s.begin(), s.end(), [](char c) { return std::isblank(c); });
74   };
75   // Validate date is blank or YYYYMMDD.
76   if (isBlank(date)) {
77     EXPECT_TRUE(true);
78   } else {
79     count_t number{-1};
80     auto [_, ec]{
81         std::from_chars(date.data(), date.data() + date.size(), number)};
82     ASSERT_TRUE(ec != std::errc::invalid_argument &&
83         ec != std::errc::result_out_of_range);
84     EXPECT_GE(number, 0);
85     auto year = number / 10000;
86     auto month = (number - year * 10000) / 100;
87     auto day = number % 100;
88     // Do not assume anything about the year, the test could be
89     // run on system with fake/outdated dates.
90     EXPECT_LE(month, 12);
91     EXPECT_GT(month, 0);
92     EXPECT_LE(day, 31);
93     EXPECT_GT(day, 0);
94   }
95 
96   // Validate time is hhmmss.sss or blank.
97   if (isBlank(time)) {
98     EXPECT_TRUE(true);
99   } else {
100     count_t number{-1};
101     auto [next, ec]{
102         std::from_chars(time.data(), time.data() + date.size(), number)};
103     ASSERT_TRUE(ec != std::errc::invalid_argument &&
104         ec != std::errc::result_out_of_range);
105     ASSERT_GE(number, 0);
106     auto hours = number / 10000;
107     auto minutes = (number - hours * 10000) / 100;
108     auto seconds = number % 100;
109     EXPECT_LE(hours, 23);
110     EXPECT_LE(minutes, 59);
111     // Accept 60 for leap seconds.
112     EXPECT_LE(seconds, 60);
113     ASSERT_TRUE(next != time.data() + time.size());
114     EXPECT_EQ(*next, '.');
115 
116     count_t milliseconds{-1};
117     ASSERT_TRUE(next + 1 != time.data() + time.size());
118     auto [_, ec2]{
119         std::from_chars(next + 1, time.data() + date.size(), milliseconds)};
120     ASSERT_TRUE(ec2 != std::errc::invalid_argument &&
121         ec2 != std::errc::result_out_of_range);
122     EXPECT_GE(milliseconds, 0);
123     EXPECT_LE(milliseconds, 999);
124   }
125 
126   // Validate zone is +hhmm or -hhmm or blank.
127   if (isBlank(zone)) {
128     EXPECT_TRUE(true);
129   } else {
130     ASSERT_TRUE(zone.size() > 1);
131     EXPECT_TRUE(zone[0] == '+' || zone[0] == '-');
132     count_t number{-1};
133     auto [next, ec]{
134         std::from_chars(zone.data() + 1, zone.data() + zone.size(), number)};
135     ASSERT_TRUE(ec != std::errc::invalid_argument &&
136         ec != std::errc::result_out_of_range);
137     ASSERT_GE(number, 0);
138     auto hours = number / 100;
139     auto minutes = number % 100;
140     EXPECT_LE(hours, 23);
141     EXPECT_LE(minutes, 59);
142   }
143 }
144