summaryrefslogtreecommitdiffstats
path: root/flang/unittests/Runtime/Time.cpp
blob: 5c93282bca6115dd97e03c056575a684a3721f4f (plain)
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
//===-- 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 <cerrno>
#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};
    // Use stol to allow GCC 7.5 to build tests
    number = std::stol(date);
    ASSERT_TRUE(errno != ERANGE);
    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.
  std::string acceptedPattern("hhmmss.sss");
  if (isBlank(time)) {
    EXPECT_TRUE(true);
  } else {
    count_t number{-1};
    // Use stol to allow GCC 7.5 to build tests
    auto dotPosition = acceptedPattern.find('.');
    number = std::stol(time.substr(0, dotPosition));
    ASSERT_TRUE(errno != ERANGE);
    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);
    EXPECT_EQ(time.substr(dotPosition, 1), ".");

    count_t milliseconds{-1};
    milliseconds = std::stol(time.substr(dotPosition + 1, 3));
    ASSERT_TRUE(errno != ERANGE);
    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};
    // Use stol to allow GCC 7.5 to build tests
    number = std::stol(zone.substr(1, 4));
    ASSERT_TRUE(errno != ERANGE);
    ASSERT_GE(number, 0);
    auto hours = number / 100;
    auto minutes = number % 100;
    EXPECT_LE(hours, 23);
    EXPECT_LE(minutes, 59);
  }
}
#endif // __clang__