From e53e25c8bb2c0bd8c65a35db0620fe2c8196f7aa Mon Sep 17 00:00:00 2001 From: Maxim Prokhorov Date: Mon, 12 Dec 2022 14:25:01 +0300 Subject: [PATCH] test: utils --- code/espurna/system.cpp | 19 ++++++++ code/espurna/system.h | 15 ++----- code/espurna/types.h | 15 +++++++ code/espurna/utils.cpp | 23 +--------- code/espurna/utils.h | 5 +-- code/test/unit/CMakeLists.txt | 25 ++++++++--- code/test/unit/src/utils/utils.cpp | 71 ++++++++++++++++++++++++++++++ 7 files changed, 129 insertions(+), 44 deletions(-) create mode 100644 code/test/unit/src/utils/utils.cpp diff --git a/code/espurna/system.cpp b/code/espurna/system.cpp index 42d81af9..b7b88782 100644 --- a/code/espurna/system.cpp +++ b/code/espurna/system.cpp @@ -15,6 +15,7 @@ Copyright (C) 2019 by Xose Pérez #include #include #include +#include #include extern "C" { @@ -1215,6 +1216,24 @@ void setup() { // ----------------------------------------------------------------------------- +// using 'random device' as-is, while most common implementations +// would've used it as a seed for some generator func +// TODO notice that stdlib std::mt19937 struct needs ~2KiB for it's internal +// `result_type state[std::mt19937::state_size]` (ref. sizeof()) +uint32_t randomNumber(uint32_t minimum, uint32_t maximum) { + using Device = espurna::system::RandomDevice; + using Type = Device::result_type; + + static Device random; + auto distribution = std::uniform_int_distribution(minimum, maximum); + + return distribution(random); +} + +uint32_t randomNumber() { + return (espurna::system::RandomDevice{})(); +} + unsigned long systemFreeStack() { return espurna::memory::freeStack(); } diff --git a/code/espurna/system.h b/code/espurna/system.h index 2baac3b0..dde800e3 100644 --- a/code/espurna/system.h +++ b/code/espurna/system.h @@ -9,6 +9,7 @@ Copyright (C) 2019 by Xose Pérez #pragma once #include "settings.h" +#include "types.h" #include #include @@ -62,17 +63,6 @@ namespace duration { // (also notice the discrepancy when OTA'ing between different values, as CPU *may* keep the old value) using ClockCycles = std::chrono::duration>; -// Only micros are 64bit, millis stored as 32bit to match what is actually returned & used by Core functions -using Microseconds = std::chrono::duration; -using Milliseconds = std::chrono::duration; - -// Our own helper types, a lot of things are based off of the `millis()` -// (and it can be seamlessly used with any Core functions accepting u32 millisecond inputs) -using Seconds = std::chrono::duration>; -using Minutes = std::chrono::duration>; -using Hours = std::chrono::duration>; -using Days = std::chrono::duration>; - namespace critical { using Microseconds = std::chrono::duration; @@ -366,6 +356,9 @@ String serialize(duration::ClockCycles); } // namespace settings } // namespace espurna +uint32_t randomNumber(uint32_t minimum, uint32_t maximum); +uint32_t randomNumber(); + unsigned long systemFreeStack(); HeapStats systemHeapStats(); diff --git a/code/espurna/types.h b/code/espurna/types.h index 274a928f..fd144dcf 100644 --- a/code/espurna/types.h +++ b/code/espurna/types.h @@ -10,6 +10,7 @@ Copyright (C) 2019-2021 by Maxim Prokhorov #include +#include #include #include "compat.h" @@ -18,6 +19,20 @@ Copyright (C) 2019-2021 by Maxim Prokhorov ; +using Milliseconds = std::chrono::duration; + +// Our own helper types, a lot of things are based off of the `millis()` +// (and it can be seamlessly used with any Core functions accepting u32 millisecond inputs) +using Seconds = std::chrono::duration >; +using Minutes = std::chrono::duration >; +using Hours = std::chrono::duration >; +using Days = std::chrono::duration >; + +} // namespace duration // base class for loop / oneshot / generic callbacks that do not need arguments // *not expected* to be used instead of std function at all times. diff --git a/code/espurna/utils.cpp b/code/espurna/utils.cpp index 490c0ce8..017dfb11 100644 --- a/code/espurna/utils.cpp +++ b/code/espurna/utils.cpp @@ -6,12 +6,9 @@ Copyright (C) 2017-2019 by Xose Pérez */ -#include "espurna.h" - -#include "ntp.h" +#include "utils.h" #include -#include // We can only return small values (max 'z' aka 122) static constexpr uint8_t InvalidByte { 255u }; @@ -192,24 +189,6 @@ bool sslFingerPrintChar(const char * fingerprint, char * destination) { // Helper functions // ----------------------------------------------------------------------------- -// using 'random device' as-is, while most common implementations -// would've used it as a seed for some generator func -// TODO notice that stdlib std::mt19937 struct needs ~2KiB for it's internal -// `result_type state[std::mt19937::state_size]` (ref. sizeof()) -uint32_t randomNumber(uint32_t minimum, uint32_t maximum) { - using Device = espurna::system::RandomDevice; - using Type = Device::result_type; - - static Device random; - auto distribution = std::uniform_int_distribution(minimum, maximum); - - return distribution(random); -} - -uint32_t randomNumber() { - return (espurna::system::RandomDevice{})(); -} - double roundTo(double num, unsigned char positions) { double multiplier = 1; while (positions-- > 0) multiplier *= 10; diff --git a/code/espurna/utils.h b/code/espurna/utils.h index 763fcb6e..7029a0c6 100644 --- a/code/espurna/utils.h +++ b/code/espurna/utils.h @@ -10,7 +10,7 @@ Copyright (C) 2017-2019 by Xose Pérez #include -#include "system.h" +#include "types.h" String prettyDuration(espurna::duration::Seconds); @@ -22,9 +22,6 @@ char* strnstr(const char* buffer, const char* token, size_t n); bool isNumber(const char* begin, const char* end); bool isNumber(const String&); -uint32_t randomNumber(); -uint32_t randomNumber(uint32_t minimum, uint32_t maximum); - double roundTo(double num, unsigned char positions); bool almostEqual(double lhs, double rhs, int ulp); bool almostEqual(double lhs, double rhs); diff --git a/code/test/unit/CMakeLists.txt b/code/test/unit/CMakeLists.txt index 769ad8a1..3b1e1914 100644 --- a/code/test/unit/CMakeLists.txt +++ b/code/test/unit/CMakeLists.txt @@ -26,6 +26,7 @@ set(COMMON_FLAGS -DCORE_MOCK -DHOST_MOCK=1 -DLWIP_IPV6=0 + -Dstrnlen_P=strnlen -Dmemcmp_P=memcmp -Dstrncasecmp_P=strncasecmp ) @@ -148,19 +149,21 @@ target_compile_options(esp8266 PUBLIC target_link_libraries(esp8266 PUBLIC common) # our library source (maybe some day this will be a simple glob) -add_library(terminal STATIC +add_library(espurna STATIC ${ESPURNA_PATH}/code/espurna/terminal_commands.cpp ${ESPURNA_PATH}/code/espurna/terminal_parsing.cpp ${ESPURNA_PATH}/code/espurna/types.cpp + ${ESPURNA_PATH}/code/espurna/utils.cpp ) -target_link_libraries(terminal PUBLIC esp8266) -target_include_directories(terminal PUBLIC +target_link_libraries(espurna PUBLIC esp8266) +target_include_directories(espurna PUBLIC ${ESPURNA_PATH}/code/ + ${CMAKE_SOURCE_DIR}/cache/arduinojson-${arduinojson_version}-src/src ) -target_compile_options(terminal PUBLIC +target_compile_options(espurna PUBLIC ${COMMON_FLAGS} ) -target_compile_options(terminal PRIVATE +target_compile_options(espurna PRIVATE -Wall -Wextra ) @@ -172,7 +175,7 @@ list(APPEND CMAKE_CTEST_ARGUMENTS "--output-on-failure") function(build_tests) foreach(ARG IN LISTS ARGN) add_executable(test-${ARG} src/${ARG}/${ARG}.cpp) - target_link_libraries(test-${ARG} terminal unity) + target_link_libraries(test-${ARG} espurna unity) target_compile_options(test-${ARG} PRIVATE ${COMMON_FLAGS} -Wall @@ -183,4 +186,12 @@ function(build_tests) endforeach() endfunction() -build_tests(basic settings terminal tuya types url) +build_tests( + basic + settings + terminal + tuya + types + url + utils +) diff --git a/code/test/unit/src/utils/utils.cpp b/code/test/unit/src/utils/utils.cpp new file mode 100644 index 00000000..783fe424 --- /dev/null +++ b/code/test/unit/src/utils/utils.cpp @@ -0,0 +1,71 @@ +#include +#include +#include +#include + +#include + +namespace espurna { +namespace test { +namespace { + +void test_parse_unsigned_result() { + const auto result = parseUnsigned(""); + using Value = std::is_same; + TEST_ASSERT(static_cast(!result.ok)); + TEST_ASSERT(Value::value); +} + +void test_parse_unsigned_value() { +#define TEST_RESULT(VALUE) ([] {\ + const auto result = parseUnsigned(#VALUE);\ + TEST_ASSERT(result.ok);\ + TEST_ASSERT_EQUAL(VALUE, result.value);})() + + TEST_RESULT(12345); + TEST_RESULT(54321); + TEST_RESULT(0b111); + TEST_RESULT(0xfeaf); +} + +void test_parse_unsigned_overflow() { + const auto a = parseUnsigned("0b1111111111111111111111111111111111111111111111111111111111111111111111111111111111111"); + TEST_ASSERT(!a.ok); + + const auto b = parseUnsigned("0o12345123451234512345123451234512345"); + TEST_ASSERT(!b.ok); + + const auto c = parseUnsigned("12345678901234567890"); + TEST_ASSERT(!c.ok); + + const auto d = parseUnsigned("0xfefefefefe"); + TEST_ASSERT(!d.ok); +} + +void test_parse_unsigned_prefix() { + const auto a = parseUnsigned("0b101010101", 2); + TEST_ASSERT(!a.ok); + + const auto b = parseUnsigned("101010101", 2); + TEST_ASSERT_EQUAL(0b101010101, b.value); + TEST_ASSERT(b.ok); + + const auto c = parseUnsigned("0o123134"); + TEST_ASSERT_EQUAL(42588, c.value); + TEST_ASSERT(c.ok); +} + +} // namespace +} // namespace test +} // namespace espurna + +int main(int, char**) { + UNITY_BEGIN(); + using namespace espurna::test; + RUN_TEST(test_parse_unsigned_result); + RUN_TEST(test_parse_unsigned_value); + RUN_TEST(test_parse_unsigned_overflow); + RUN_TEST(test_parse_unsigned_prefix); + return UNITY_END(); +} +