From b509e0469db791be22ed469bb6024eb84bd24a78 Mon Sep 17 00:00:00 2001 From: Sander Speetjens Date: Tue, 5 Sep 2023 11:18:09 +0200 Subject: [PATCH] TmeLib tests It is now possible to use the internal vscode test tool with the cmake plugin to test timelib --- CMakeLists.txt | 32 +++++++++ tests/CMakeLists.txt | 30 ++++++++ tests/testTime.cpp | 159 +++++++++++++++++++++++++++++++++++++++++ tests/time.cpp | 165 +++++++++++++++++++++++++++++++++++++++++++ tests/time.hpp | 31 ++++++++ 5 files changed, 417 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 tests/CMakeLists.txt create mode 100644 tests/testTime.cpp create mode 100644 tests/time.cpp create mode 100644 tests/time.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..5ddb2e9 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +project(Network_Clock LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set (VERSION_MAJOR 1) +set (VERSION_MINOR 0) + +include(GNUInstallDirs) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY + ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY + ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY + ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}) +set(PROJECT_DIR ${CMAKE_CURRENT_LIST_DIR}) + +# defines targets and sources +# add_subdirectory(src) + +# contains an "external" library we will link to +# add_subdirectory(external) + +# defines the executable +# add_subdirectory(tools) + +# enable testing and define tests +enable_testing() +add_subdirectory(tests) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..1570f17 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,30 @@ +# Third Party +include_directories(${GTEST_INCLUDE_DIR}) + +link_directories(${GTEST_LIB_DIR}) + +find_package(GTest REQUIRED) + +# tests +file(GLOB_RECURSE TEST_SOURCES "*.cpp") +add_executable(tests ${TEST_SOURCES}) +target_link_libraries(tests ${LIBS}) + +target_compile_options(tests PRIVATE $<$: + -Wall -Wextra -pedantic-errors -Wconversion -Wsign-conversion + >) +target_link_libraries(tests + PRIVATE + gtest + GTest::gtest_main +) + +target_include_directories(tests + PUBLIC + ${CMAKE_CURRENT_LIST_DIR} + ${PROJECT_BINARY_DIR} +) + +include(GoogleTest) +include (CTest) +gtest_discover_tests(tests) \ No newline at end of file diff --git a/tests/testTime.cpp b/tests/testTime.cpp new file mode 100644 index 0000000..3fc3b89 --- /dev/null +++ b/tests/testTime.cpp @@ -0,0 +1,159 @@ +#include +#include "time.hpp" + +// TEST(TestCaseName, IndividualTestName) +TEST(timeLib, isDST1) +{ + ts t; + + // 2022-10-5 00:00:00 UTC DST on + t.Wday = Wed; + t.Day = 5; + t.Month = 10; + t.Year = 2022 - 1970; + t.Hour = 0; + + EXPECT_EQ(1, IsDST(&t)); +} +TEST(timeLib, isDST2) +{ + ts t; + + // 2022-10-29 59:59:59 UTC DST still on + t.Wday = Sun; + t.Day = 29; + t.Month = 10; + t.Hour = 23; + t.Minute = 59; + t.Second = 59; + t.Year = 2022 - 1970; + + EXPECT_EQ(1 ,IsDST(&t)); +} + +TEST(timeLib, isDST3) +{ + ts t; + + // 2022-10-30 00:00:00 UTC DST ends + t.Wday = Sun; + t.Day = 30; + t.Month = 10; + t.Hour = 0; + t.Year = 2022 - 1970; + + EXPECT_EQ(0 ,IsDST(&t)); +} + +TEST(timeLib, isDST4) +{ + ts t; + + // 2022-11-23 00:00:00 UTC DST off + t.Wday = Wed; + t.Day = 23; + t.Month = 11; + t.Hour = 0; + t.Year = 2022 - 1970; + + EXPECT_EQ(0 ,IsDST(&t)); +} + +TEST(timeLib, isDST5) +{ + ts t; + + // 2022-03-25 23:59:59 UTC DST off + t.Wday = Sun; + t.Day = 25; + t.Month = 3; + t.Hour = 23; + t.Minute = 59; + t.Second = 59; + t.Year = 2023 - 1970; + + EXPECT_EQ(0 ,IsDST(&t)); +} + +TEST(timeLib, isDST6) +{ + ts t; + + // 2022-03-26 00:00:00 UTC DST starts + t.Wday = Sun; + t.Day = 26; + t.Month = 3; + t.Hour = 0; + t.Year = 2023 - 1970; + + EXPECT_EQ(1 ,IsDST(&t)); +} + +TEST(timeLib, isDST7) +{ + ts t; + + // 2022-03-27 00:00:00 UTC DST on + t.Wday = Mon; + t.Day = 27; + t.Month = 3; + t.Hour = 0; + t.Year = 2023 - 1970; + + EXPECT_EQ(1 ,IsDST(&t)); +} + +// TEST(TestCaseName, IndividualTestName) +TEST(timeLib, breakTime) +{ + ts t; + + // 1669233881 = Wed 2022-11-23 20:04:41 UTC + breakTime(1669233881, &t); + + EXPECT_EQ(Wed, t.Wday); + EXPECT_EQ(23, t.Day); + EXPECT_EQ(11, t.Month); + EXPECT_EQ(20, t.Hour); + EXPECT_EQ(4, t.Minute); + EXPECT_EQ(41, t.Second); + EXPECT_EQ(2022, t.Year + 1970); + EXPECT_EQ(0, t.IsDST); + EXPECT_EQ(1669233881, t.unixtime); +} + +TEST(timeLib, makeTime) +{ + ts t; + t.Wday = Wed; + t.Day = 23; + t.Month = 11; + t.Hour = 20; + t.Minute = 4; + t.Second = 41; + t.Year = 2022 - 1970; + t.IsDST = 0; + t.unixtime = 1669233881; + EXPECT_EQ(1669233881, makeTime(&t)); + EXPECT_EQ(1669233881, t.unixtime); +} + +TEST(timeLib, toTimeZone) +{ + ts t; + t.Day = 23; + t.Month = 11; + t.Year = 2022 - 1970; + t.Hour = 20; + t.Minute = 4; + t.Second = 41; + + ts local; + toTimeZone(&t, &local, 1); + EXPECT_EQ(23, local.Day); + EXPECT_EQ(11, local.Month); + EXPECT_EQ(2022, local.Year + 1970); + EXPECT_EQ(21, local.Hour); + EXPECT_EQ(4, local.Minute); + EXPECT_EQ(41, local.Second); +} \ No newline at end of file diff --git a/tests/time.cpp b/tests/time.cpp new file mode 100644 index 0000000..6c22415 --- /dev/null +++ b/tests/time.cpp @@ -0,0 +1,165 @@ +#include "time.hpp" + +static const uint8_t monthDays[] = + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // API starts months from 1, this array starts from 0 + +/** + * @fn uint8_t IsDST(ts) + * @brief + * + * @param tm + * @return + */ +uint8_t IsDST(ts *tm) +{ + uint8_t nextSunday; + uint16_t y, m, d; + //number of days of each month + int days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + + // January, february, and december are out. + if (tm->Month < 3 || tm->Month > 10) + { + tm->IsDST = 0; + return tm->IsDST; + } + // April to september are in + if (tm->Month > 3 && tm->Month < 10) + { + tm->IsDST = 1; + return tm->IsDST; + } + + m = tm->Month; + y = tm->Year + 1970; + days[1] -= (y % 4) || (!(y % 100) && (y % 400)); + d = days[m - 1]; + + /* dow is in normal format*/ + nextSunday = days[m-1] - ((d += m < 3 ? y-- : y - 2, 23*m/9 + d + 4 + y/4- y/100 + y/400)%7); + // Start: Last Sunday in March + if (tm->Month == 3) + { + tm->IsDST = tm->Day >= nextSunday ? (tm->Day == nextSunday ? (tm->Hour >= 0) : 1) : 0; + return tm->IsDST; + } + + // End: Last Sunday in October + tm->IsDST = tm->Day >= nextSunday ? (tm->Day == nextSunday ? (tm->Hour < 0) : 0) : 1; + return tm->IsDST; +} + +/*============================================================================*/ +/* functions to convert to and from system time */ +/* These are for interfacing with time services and are not normally needed in a sketch */ + +void breakTime(uint32_t timeInput, ts *tm) +{ + // break the given time_t into time components + // this is a more compact version of the C library localtime function + // note that year is offset from 1970 !!! + + uint8_t year; + uint8_t month, monthLength; + unsigned long days; + + tm->unixtime = timeInput; + tm->Second = timeInput % 60; + timeInput /= 60; // now it is minutes + tm->Minute = timeInput % 60; + timeInput /= 60; // now it is hours + tm->Hour = timeInput % 24; + timeInput /= 24; // now it is days + tm->Wday = ((timeInput + 4) % 7) + 1; // Sunday is day 1 + + year = 0; + days = 0; + while ((unsigned)(days += (LEAP_YEAR(year) ? 366 : 365)) <= timeInput) + { + year++; + } + tm->Year = year; // year is offset from 1970 + + days -= LEAP_YEAR(year) ? 366 : 365; + timeInput -= days; // now it is days in this year, starting at 0 + + days = 0; + month = 0; + monthLength = 0; + for (month = 0; month < 12; month++) + { + if (month == 1) + { // february + if (LEAP_YEAR(year)) + { + monthLength = 29; + } + else + { + monthLength = 28; + } + } + else + { + monthLength = monthDays[month]; + } + + if (timeInput >= monthLength) + { + timeInput -= monthLength; + } + else + { + break; + } + } + tm->Month = month + 1; // jan is month 1 + tm->Day = timeInput + 1; // day of month + + IsDST(tm); +} + +uint32_t makeTime(ts *tm) +{ +// assemble time elements into time_t +// note year argument is offset from 1970 (see macros in time.h to convert to other formats) +// previous version used full four digit year (or digits since 2000),i.e. 2009 was 2009 or 9 + + int i; + uint32_t seconds; + + // seconds from 1970 till 1 jan 00:00:00 of the given year + seconds = tm->Year*(NUMBEROFSECONDSPERDAY * 365); + for (i = 0; i < tm->Year; i++) { + if (LEAP_YEAR(i)) { + seconds += NUMBEROFSECONDSPERDAY; // add extra days for leap years + } + } + + // add days for this year, months start from 1 + for (i = 1; i < tm->Month; i++) { + if ( (i == 2) && LEAP_YEAR(tm->Year)) { + seconds += NUMBEROFSECONDSPERDAY * 29; + } else { + seconds += NUMBEROFSECONDSPERDAY * monthDays[i-1]; //monthDay array starts from 0 + } + } + seconds+= (tm->Day-1) * NUMBEROFSECONDSPERDAY; + seconds+= tm->Hour * NUMBEROFSECONDSPERHOUR; + seconds+= tm->Minute * NUMBEROFSECONDSPERMINUTE; + seconds+= tm->Second; + + tm->unixtime = seconds; + return seconds; +} + +void toTimeZone(ts *utc, ts* local, int8_t timeZone) +{ + // convert UTC time to local time + // timeZone is in hours + // local is a pointer to a tm structure + // this function is not reentrant + + uint32_t localTime = makeTime(utc) + timeZone * NUMBEROFSECONDSPERHOUR; + breakTime(localTime, local); +} \ No newline at end of file diff --git a/tests/time.hpp b/tests/time.hpp new file mode 100644 index 0000000..c4a2cc6 --- /dev/null +++ b/tests/time.hpp @@ -0,0 +1,31 @@ +#include +#include + +typedef struct { + uint8_t Second; /* seconds */ + uint8_t Minute; /* minutes */ + uint8_t Hour; /* hours */ + uint8_t Day; /* day of the month */ + uint8_t Month; /* month */ + int16_t Year; /* year ofset from 1970*/ + uint8_t Wday; /* day of the week */ + uint8_t Yday; /* day in the year */ + uint8_t IsDST; /* daylight saving time */ + + uint32_t unixtime; /* seconds since 01.01.1970 00:00:00 UTC*/ +} ts; + +enum Wday_e {Sun = 1, Mon, Tue, Wed, Thu, Fri, Sat}; + +// leap year calculator expects year argument as years offset from 1970 +#define LEAP_YEAR(Y) ( ((1970+(Y))>0) && !((1970+(Y))%4) && ( ((1970+(Y))%100) || !((1970+(Y))%400) ) ) + +// to transform a number of seconds into a current time you need to do some maths +#define NUMBEROFSECONDSPERDAY 86400UL +#define NUMBEROFSECONDSPERHOUR 3600UL +#define NUMBEROFSECONDSPERMINUTE 60UL + +uint8_t IsDST(ts* tm); +void breakTime(uint32_t timeInput, ts *tm); +uint32_t makeTime(ts *tm); +void toTimeZone(ts *utc, ts* local, int8_t timeZone); \ No newline at end of file