1 //===----------------------------------------------------------------------===//
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 // UNSUPPORTED: c++03, c++11
10 // UNSUPPORTED: gcc-5
11 
12 // <filesystem>
13 
14 // typedef TrivialClock file_time_type;
15 
16 // ADDITIONAL_COMPILE_FLAGS: -I %S/../../../../src/filesystem
17 
18 #include <filesystem>
19 #include <chrono>
20 #include <type_traits>
21 #include <limits>
22 #include <cstddef>
23 #include <cassert>
24 
25 #include "filesystem_common.h"
26 
27 #ifndef __SIZEOF_INT128__
28 #define TEST_HAS_NO_INT128_T
29 #endif
30 
31 using namespace std::chrono;
32 namespace fs = std::__fs::filesystem;
33 using fs::file_time_type;
34 using fs::detail::time_util;
35 
36 #ifdef TEST_HAS_NO_INT128_T
37 static_assert(sizeof(fs::file_time_type::rep) <= 8, "");
38 #endif
39 
40 enum TestKind { TK_128Bit, TK_64Bit, TK_32Bit, TK_FloatingPoint };
41 
42 template <class TimeT>
getTimeTTestKind()43 constexpr TestKind getTimeTTestKind() {
44   if (sizeof(TimeT) == 8 && !std::is_floating_point<TimeT>::value)
45     return TK_64Bit;
46   else if (sizeof(TimeT) == 4 && !std::is_floating_point<TimeT>::value)
47     return TK_32Bit;
48   else if (std::is_floating_point<TimeT>::value)
49     return TK_FloatingPoint;
50   else
51     assert(false && "test kind not supported");
52 }
53 template <class FileTimeT>
getFileTimeTestKind()54 constexpr TestKind getFileTimeTestKind() {
55   using Rep = typename FileTimeT::rep;
56   if (std::is_floating_point<Rep>::value)
57     return TK_FloatingPoint;
58   else if (sizeof(Rep) == 16)
59     return TK_128Bit;
60   else if (sizeof(Rep) == 8)
61     return TK_64Bit;
62   else
63     assert(false && "test kind not supported");
64 }
65 
66 template <class FileTimeT, class TimeT, class TimeSpecT,
67           class Base = time_util<FileTimeT, TimeT, TimeSpecT>,
68           TestKind = getTimeTTestKind<TimeT>(),
69           TestKind = getFileTimeTestKind<FileTimeT>()>
70 struct test_case;
71 
72 template <class FileTimeT, class TimeT, class TimeSpecT, class Base>
73 struct test_case<FileTimeT, TimeT, TimeSpecT, Base, TK_64Bit, TK_128Bit>
74     : public Base {
75 
76   using Base::convert_from_timespec;
77   using Base::convert_to_timespec;
78   using Base::is_representable;
79   using Base::max_nsec;
80   using Base::max_seconds;
81   using Base::min_nsec_timespec;
82   using Base::min_seconds;
83 
84   static constexpr auto max_time_t = std::numeric_limits<TimeT>::max();
85   static constexpr auto min_time_t = std::numeric_limits<TimeT>::min();
86 
test_timespectest_case87   static constexpr bool test_timespec() {
88     static_assert(is_representable(TimeSpecT{max_time_t, 0}), "");
89     static_assert(is_representable(TimeSpecT{max_time_t, 999999999}), "");
90     static_assert(is_representable(TimeSpecT{max_time_t, 1000000000}), "");
91     static_assert(is_representable(TimeSpecT{max_time_t, max_nsec}), "");
92 
93     static_assert(is_representable(TimeSpecT{min_time_t, 0}), "");
94     static_assert(is_representable(TimeSpecT{min_time_t, 999999999}), "");
95     static_assert(is_representable(TimeSpecT{min_time_t, 1000000000}), "");
96     static_assert(is_representable(TimeSpecT{min_time_t, min_nsec_timespec}),
97                   "");
98 
99     return true;
100   }
101 
test_file_time_typetest_case102   static constexpr bool test_file_time_type() {
103     // This kinda sucks. Oh well.
104     static_assert(!Base::is_representable(FileTimeT::max()), "");
105     static_assert(!Base::is_representable(FileTimeT::min()), "");
106     return true;
107   }
108 
check_round_triptest_case109   static constexpr bool check_round_trip(TimeSpecT orig) {
110     TimeSpecT new_ts = {};
111     FileTimeT out = convert_from_timespec(orig);
112     assert(convert_to_timespec(new_ts, out));
113     return new_ts.tv_sec == orig.tv_sec && new_ts.tv_nsec == orig.tv_nsec;
114   }
115 
test_convert_timespectest_case116   static constexpr bool test_convert_timespec() {
117     static_assert(check_round_trip({0, 0}), "");
118     static_assert(check_round_trip({0, 1}), "");
119     static_assert(check_round_trip({1, 1}), "");
120     static_assert(check_round_trip({-1, 1}), "");
121     static_assert(check_round_trip({max_time_t, max_nsec}), "");
122     static_assert(check_round_trip({max_time_t, 123}), "");
123     static_assert(check_round_trip({min_time_t, min_nsec_timespec}), "");
124     static_assert(check_round_trip({min_time_t, 123}), "");
125     return true;
126   }
127 
testtest_case128   static bool test() {
129     static_assert(test_timespec(), "");
130     static_assert(test_file_time_type(), "");
131     static_assert(test_convert_timespec(), "");
132     return true;
133   }
134 };
135 
136 template <class FileTimeT, class TimeT, class TimeSpecT, class Base>
137 struct test_case<FileTimeT, TimeT, TimeSpecT, Base, TK_32Bit, TK_128Bit>
138     : public test_case<FileTimeT, TimeT, TimeSpecT, Base, TK_64Bit, TK_128Bit> {
139 
140 };
141 
142 template <class FileTimeT, class TimeT, class TimeSpecT, class Base>
143 struct test_case<FileTimeT, TimeT, TimeSpecT, Base, TK_64Bit, TK_64Bit>
144     : public Base {
145 
146   using Base::convert_from_timespec;
147   using Base::is_representable;
148   using Base::max_nsec;
149   using Base::max_seconds;
150   using Base::min_nsec_timespec;
151   using Base::min_seconds;
152 
153   static constexpr auto max_time_t = std::numeric_limits<TimeT>::max();
154   static constexpr auto min_time_t = std::numeric_limits<TimeT>::min();
155 
test_timespectest_case156   static constexpr bool test_timespec() {
157     static_assert(is_representable(TimeSpecT{max_seconds, max_nsec}), "");
158     static_assert(!is_representable(TimeSpecT{max_seconds + 1, 0}), "");
159     static_assert(!is_representable(TimeSpecT{max_seconds, max_nsec + 1}), "");
160     static_assert(!is_representable(TimeSpecT{max_time_t, 0}), "");
161     static_assert(is_representable(TimeSpecT{min_seconds, 0}), "");
162     static_assert(
163         is_representable(TimeSpecT{min_seconds - 1, min_nsec_timespec}), "");
164     static_assert(
165         is_representable(TimeSpecT{min_seconds - 1, min_nsec_timespec + 1}),
166         "");
167     static_assert(
168         !is_representable(TimeSpecT{min_seconds - 1, min_nsec_timespec - 1}),
169         "");
170     static_assert(!is_representable(TimeSpecT{min_time_t, 999999999}), "");
171     return true;
172   }
173 
test_file_time_typetest_case174   static constexpr bool test_file_time_type() {
175     static_assert(Base::is_representable(FileTimeT::max()), "");
176     static_assert(Base::is_representable(FileTimeT::min()), "");
177     return true;
178   }
179 
test_convert_timespectest_case180   static constexpr bool test_convert_timespec() {
181     static_assert(convert_from_timespec(TimeSpecT{max_seconds, max_nsec}) ==
182                       FileTimeT::max(),
183                   "");
184     static_assert(convert_from_timespec(TimeSpecT{max_seconds, max_nsec - 1}) <
185                       FileTimeT::max(),
186                   "");
187     static_assert(convert_from_timespec(TimeSpecT{max_seconds - 1, 999999999}) <
188                       FileTimeT::max(),
189                   "");
190     static_assert(convert_from_timespec(TimeSpecT{
191                       min_seconds - 1, min_nsec_timespec}) == FileTimeT::min(),
192                   "");
193     static_assert(convert_from_timespec(
194                       TimeSpecT{min_seconds - 1, min_nsec_timespec + 1}) >
195                       FileTimeT::min(),
196                   "");
197     static_assert(convert_from_timespec(TimeSpecT{min_seconds, 0}) >
198                       FileTimeT::min(),
199                   "");
200     return true;
201   }
202 
testtest_case203   static bool test() {
204     static_assert(test_timespec(), "");
205     static_assert(test_file_time_type(), "");
206     static_assert(test_convert_timespec(), "");
207     return true;
208   }
209 };
210 
211 template <class FileTimeT, class TimeT, class TimeSpecT, class Base>
212 struct test_case<FileTimeT, TimeT, TimeSpecT, Base, TK_32Bit, TK_64Bit>
213     : public Base {
214   static constexpr auto max_time_t = std::numeric_limits<TimeT>::max();
215   static constexpr auto min_time_t = std::numeric_limits<TimeT>::min();
216 
217   using Base::convert_from_timespec;
218   using Base::is_representable;
219   using Base::max_nsec;
220   using Base::max_seconds;
221   using Base::min_nsec_timespec;
222   using Base::min_seconds;
223 
test_timespectest_case224   static constexpr bool test_timespec() {
225     static_assert(is_representable(TimeSpecT{max_time_t, 999999999}), "");
226     static_assert(is_representable(TimeSpecT{max_time_t, 1000000000}), "");
227     static_assert(is_representable(TimeSpecT{min_time_t, 0}), "");
228     return true;
229   }
230 
test_file_time_typetest_case231   static constexpr bool test_file_time_type() {
232     static_assert(!is_representable(FileTimeT::max()), "");
233     static_assert(!is_representable(FileTimeT::min()), "");
234     static_assert(is_representable(FileTimeT(seconds(max_time_t))), "");
235     static_assert(is_representable(FileTimeT(seconds(min_time_t))), "");
236     return true;
237   }
238 
test_convert_timespectest_case239   static constexpr bool test_convert_timespec() {
240     // FIXME add tests for 32 bit builds
241     return true;
242   }
243 
testtest_case244   static bool test() {
245     static_assert(test_timespec(), "");
246     static_assert(test_file_time_type(), "");
247     static_assert(test_convert_timespec(), "");
248     return true;
249   }
250 };
251 
252 template <class FileTimeT, class TimeT, class TimeSpec, class Base,
253           TestKind FileTimeTKind>
254 struct test_case<FileTimeT, TimeT, TimeSpec, Base, TK_FloatingPoint,
255                  FileTimeTKind> : public Base {
256 
testtest_case257   static bool test() { return true; }
258 };
259 
260 template <class TimeT, class NSecT = long>
261 struct TestTimeSpec {
262   TimeT tv_sec;
263   NSecT tv_nsec;
264 };
265 
266 template <class Dur>
267 struct TestClock {
268   typedef Dur duration;
269   typedef typename duration::rep rep;
270   typedef typename duration::period period;
271   typedef std::chrono::time_point<TestClock> time_point;
272   static constexpr const bool is_steady = false;
273 
nowTestClock274   static time_point now() noexcept { return {}; }
275 };
276 
277 template <class IntType, class Period = std::micro>
278 using TestFileTimeT = time_point<TestClock<duration<IntType, Period> > >;
279 
main(int,char **)280 int main(int, char**) {
281   { assert((test_case<file_time_type, time_t, struct timespec>::test())); }
282   {
283     assert((test_case<TestFileTimeT<int64_t>, int64_t,
284                       TestTimeSpec<int64_t, long> >::test()));
285   }
286   {
287     assert((test_case<TestFileTimeT<long long>, int32_t,
288                       TestTimeSpec<int32_t, int32_t> >::test()));
289   }
290   {
291     // Test that insane platforms like ppc64 linux, which use long double as time_t,
292     // at least compile.
293     assert((test_case<TestFileTimeT<long double>, double,
294                       TestTimeSpec<long double, long> >::test()));
295   }
296 #ifndef TEST_HAS_NO_INT128_T
297   {
298     assert((test_case<TestFileTimeT<__int128_t, std::nano>, int64_t,
299                       TestTimeSpec<int64_t, long> >::test()));
300   }
301   {
302     assert((test_case<TestFileTimeT<__int128_t, std::nano>, int32_t,
303                       TestTimeSpec<int32_t, int32_t> >::test()));
304   }
305 #endif
306 
307   return 0;
308 }
309