aboutsummaryrefslogtreecommitdiff
path: root/libc/test
diff options
context:
space:
mode:
authorRaman Tenneti <rtenneti@google.com>2020-11-30 21:06:47 -0800
committerRaman Tenneti <rtenneti@google.com>2020-11-30 21:07:16 -0800
commit6f0f844e9af98dc935f80d8149f6e4fcebddf8f1 (patch)
treea71936287cbe416a103acaca96635b0abc000b62 /libc/test
parent40659cd2c6f4347650d477a16e0cf60ce4401fa6 (diff)
downloadllvm-project-6f0f844e9af98dc935f80d8149f6e4fcebddf8f1.tar.gz
Initial commit of mktime.
This introduces mktime to LLVM libc, based on C99/C2X/Single Unix Spec. Co-authored-by: Jeff Bailey <jeffbailey@google.com> This change doesn't handle TIMEZONE, tm_isdst and leap seconds. It returns -1 for invalid dates. I have verified the return results for all the possible dates with glibc's mktime. TODO: + Handle leap seconds. + Handle out of range time and date values that don't overflow or underflow. + Implement the following suggestion Siva - As we start accumulating the seconds, we should be able to check if the next amount of seconds to be added can lead to an overflow. If it does, return the overflow value. If not keep accumulating. The benefit is that, we don't have to validate every input, and also do not need the special cases for sizeof(time_t) == 4. + Handle timezone and update of tm_isdst Reviewed By: sivachandra Differential Revision: https://reviews.llvm.org/D91551
Diffstat (limited to 'libc/test')
-rw-r--r--libc/test/src/CMakeLists.txt1
-rw-r--r--libc/test/src/time/CMakeLists.txt11
-rw-r--r--libc/test/src/time/mktime_test.cpp157
3 files changed, 169 insertions, 0 deletions
diff --git a/libc/test/src/CMakeLists.txt b/libc/test/src/CMakeLists.txt
index aa606ae630bc..026e7eab8aef 100644
--- a/libc/test/src/CMakeLists.txt
+++ b/libc/test/src/CMakeLists.txt
@@ -8,6 +8,7 @@ add_subdirectory(stdlib)
add_subdirectory(string)
add_subdirectory(sys)
add_subdirectory(threads)
+add_subdirectory(time)
add_subdirectory(unistd)
set(public_test ${CMAKE_CURRENT_BINARY_DIR}/public_integration_test.cpp)
diff --git a/libc/test/src/time/CMakeLists.txt b/libc/test/src/time/CMakeLists.txt
new file mode 100644
index 000000000000..c466af4214e2
--- /dev/null
+++ b/libc/test/src/time/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_libc_testsuite(libc_time_unittests)
+
+add_libc_unittest(
+ mktime
+ SUITE
+ libc_time_unittests
+ SRCS
+ mktime_test.cpp
+ DEPENDS
+ libc.src.time.mktime
+)
diff --git a/libc/test/src/time/mktime_test.cpp b/libc/test/src/time/mktime_test.cpp
new file mode 100644
index 000000000000..1e4b60a3bb17
--- /dev/null
+++ b/libc/test/src/time/mktime_test.cpp
@@ -0,0 +1,157 @@
+//===-- Unittests for mktime ----------------------------------------------===//
+//
+// 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 "src/time/mktime.h"
+#include "test/ErrnoSetterMatcher.h"
+#include "utils/UnitTest/Test.h"
+
+#include <errno.h>
+#include <string.h>
+
+using __llvm_libc::testing::ErrnoSetterMatcher::Fails;
+
+static constexpr time_t OutOfRangeReturnValue = -1;
+
+// A helper function to initialize tm data structure.
+static inline void initialize_tm_data(struct tm *tm_data, int year, int month,
+ int mday, int hour, int min, int sec) {
+ struct tm temp = {.tm_sec = sec,
+ .tm_min = min,
+ .tm_hour = hour,
+ .tm_mday = mday,
+ .tm_mon = month,
+ .tm_year = year - 1900};
+ *tm_data = temp;
+}
+
+static inline time_t call_mktime(struct tm *tm_data, int year, int month,
+ int mday, int hour, int min, int sec) {
+ initialize_tm_data(tm_data, year, month, mday, hour, min, sec);
+ return __llvm_libc::mktime(tm_data);
+}
+
+TEST(MkTime, FailureSetsErrno) {
+ struct tm tm_data;
+ initialize_tm_data(&tm_data, 0, 0, 0, 0, 0, -1);
+ EXPECT_THAT(__llvm_libc::mktime(&tm_data), Fails(EOVERFLOW));
+}
+
+TEST(MkTime, MktimeTestsInvalidSeconds) {
+ struct tm tm_data;
+ EXPECT_EQ(call_mktime(&tm_data, 0, 0, 0, 0, 0, -1), OutOfRangeReturnValue);
+ EXPECT_EQ(call_mktime(&tm_data, 0, 0, 0, 0, 0, 60), OutOfRangeReturnValue);
+}
+
+TEST(MkTime, MktimeTestsInvalidMinutes) {
+ struct tm tm_data;
+ EXPECT_EQ(call_mktime(&tm_data, 0, 0, 0, 0, -1, 0), OutOfRangeReturnValue);
+ EXPECT_EQ(call_mktime(&tm_data, 0, 0, 1, 0, 60, 0), OutOfRangeReturnValue);
+}
+
+TEST(MkTime, MktimeTestsInvalidHours) {
+ struct tm tm_data;
+ EXPECT_EQ(call_mktime(&tm_data, 0, 0, 0, -1, 0, 0), OutOfRangeReturnValue);
+ EXPECT_EQ(call_mktime(&tm_data, 0, 0, 0, 24, 0, 0), OutOfRangeReturnValue);
+}
+
+TEST(MkTime, MktimeTestsInvalidYear) {
+ struct tm tm_data;
+ EXPECT_EQ(call_mktime(&tm_data, 1969, 0, 0, 0, 0, 0), OutOfRangeReturnValue);
+}
+
+TEST(MkTime, MktimeTestsInvalidEndOf32BitEpochYear) {
+ if (sizeof(time_t) != 4)
+ return;
+ struct tm tm_data;
+ // 2038-01-19 03:14:08 tests overflow of the second in 2038.
+ EXPECT_EQ(call_mktime(&tm_data, 2038, 0, 19, 3, 14, 8),
+ OutOfRangeReturnValue);
+ // 2038-01-19 03:15:07 tests overflow of the minute in 2038.
+ EXPECT_EQ(call_mktime(&tm_data, 2038, 0, 19, 3, 15, 7),
+ OutOfRangeReturnValue);
+ // 2038-01-19 04:14:07 tests overflow of the hour in 2038.
+ EXPECT_EQ(call_mktime(&tm_data, 2038, 0, 19, 4, 14, 7),
+ OutOfRangeReturnValue);
+ // 2038-01-20 03:14:07 tests overflow of the day in 2038.
+ EXPECT_EQ(call_mktime(&tm_data, 2038, 0, 20, 3, 14, 7),
+ OutOfRangeReturnValue);
+ // 2038-02-19 03:14:07 tests overflow of the month in 2038.
+ EXPECT_EQ(call_mktime(&tm_data, 2038, 1, 19, 3, 14, 7),
+ OutOfRangeReturnValue);
+ // 2039-01-19 03:14:07 tests overflow of the year.
+ EXPECT_EQ(call_mktime(&tm_data, 2039, 0, 19, 3, 14, 7),
+ OutOfRangeReturnValue);
+}
+
+TEST(MkTime, MktimeTestsInvalidMonths) {
+ struct tm tm_data;
+ // Before Jan of 1970
+ EXPECT_EQ(call_mktime(&tm_data, 1970, -1, 15, 0, 0, 0),
+ OutOfRangeReturnValue);
+ // After Dec of 1970
+ EXPECT_EQ(call_mktime(&tm_data, 1970, 12, 15, 0, 0, 0),
+ OutOfRangeReturnValue);
+}
+
+TEST(MkTime, MktimeTestsInvalidDays) {
+ struct tm tm_data;
+ // -1 day of Jan, 1970
+ EXPECT_EQ(call_mktime(&tm_data, 1970, 0, -1, 0, 0, 0), OutOfRangeReturnValue);
+ // 32 day of Jan, 1970
+ EXPECT_EQ(call_mktime(&tm_data, 1970, 0, 32, 0, 0, 0), OutOfRangeReturnValue);
+ // 29 day of Feb, 1970
+ EXPECT_EQ(call_mktime(&tm_data, 1970, 1, 29, 0, 0, 0), OutOfRangeReturnValue);
+ // 30 day of Feb, 1972
+ EXPECT_EQ(call_mktime(&tm_data, 1972, 1, 30, 0, 0, 0), OutOfRangeReturnValue);
+ // 31 day of Apr, 1970
+ EXPECT_EQ(call_mktime(&tm_data, 1970, 3, 31, 0, 0, 0), OutOfRangeReturnValue);
+}
+
+TEST(MkTime, MktimeTestsStartEpochYear) {
+ // Thu Jan 1 00:00:00 1970
+ struct tm tm_data;
+ EXPECT_EQ(call_mktime(&tm_data, 1970, 0, 1, 0, 0, 0), static_cast<time_t>(0));
+ EXPECT_EQ(4, tm_data.tm_wday);
+ EXPECT_EQ(0, tm_data.tm_yday);
+}
+
+TEST(MkTime, MktimeTestsEpochYearRandomTime) {
+ // Thu Jan 1 12:50:50 1970
+ struct tm tm_data;
+ EXPECT_EQ(call_mktime(&tm_data, 1970, 0, 1, 12, 50, 50),
+ static_cast<time_t>(46250));
+ EXPECT_EQ(4, tm_data.tm_wday);
+ EXPECT_EQ(0, tm_data.tm_yday);
+}
+
+TEST(MkTime, MktimeTestsEndOf32BitEpochYear) {
+ struct tm tm_data;
+ // Test for maximum value of a signed 32-bit integer.
+ // Test implementation can encode time for Tue 19 January 2038 03:14:07 UTC.
+ EXPECT_EQ(call_mktime(&tm_data, 2038, 0, 19, 3, 14, 7),
+ static_cast<time_t>(0x7FFFFFFF));
+ EXPECT_EQ(2, tm_data.tm_wday);
+ EXPECT_EQ(18, tm_data.tm_yday);
+}
+
+TEST(MkTime, MktimeTests64BitYear) {
+ if (sizeof(time_t) == 4)
+ return;
+ // Mon Jan 1 12:50:50 2170
+ struct tm tm_data;
+ EXPECT_EQ(call_mktime(&tm_data, 2170, 0, 1, 12, 50, 50),
+ static_cast<time_t>(6311479850));
+ EXPECT_EQ(1, tm_data.tm_wday);
+ EXPECT_EQ(0, tm_data.tm_yday);
+
+ // Test for Tue Jan 1 12:50:50 in 2,147,483,647th year.
+ EXPECT_EQ(call_mktime(&tm_data, 2147483647, 0, 1, 12, 50, 50),
+ static_cast<time_t>(67767976202043050));
+ EXPECT_EQ(2, tm_data.tm_wday);
+ EXPECT_EQ(0, tm_data.tm_yday);
+}