mirror of
https://github.com/xoseperez/espurna.git
synced 2026-03-14 20:27:09 +01:00
test: utils
This commit is contained in:
@@ -15,6 +15,7 @@ Copyright (C) 2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <forward_list>
|
||||
#include <random>
|
||||
#include <vector>
|
||||
|
||||
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<Type>(minimum, maximum);
|
||||
|
||||
return distribution(random);
|
||||
}
|
||||
|
||||
uint32_t randomNumber() {
|
||||
return (espurna::system::RandomDevice{})();
|
||||
}
|
||||
|
||||
unsigned long systemFreeStack() {
|
||||
return espurna::memory::freeStack();
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ Copyright (C) 2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
#pragma once
|
||||
|
||||
#include "settings.h"
|
||||
#include "types.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
@@ -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<uint32_t, std::ratio<1, F_CPU>>;
|
||||
|
||||
// Only micros are 64bit, millis stored as 32bit to match what is actually returned & used by Core functions
|
||||
using Microseconds = std::chrono::duration<uint64_t, std::micro>;
|
||||
using Milliseconds = std::chrono::duration<uint32_t, std::milli>;
|
||||
|
||||
// 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<uint32_t, std::ratio<1>>;
|
||||
using Minutes = std::chrono::duration<uint32_t, std::ratio<60>>;
|
||||
using Hours = std::chrono::duration<uint32_t, std::ratio<Minutes::period::num * 60>>;
|
||||
using Days = std::chrono::duration<uint32_t, std::ratio<Hours::period::num * 24>>;
|
||||
|
||||
namespace critical {
|
||||
|
||||
using Microseconds = std::chrono::duration<uint16_t, std::micro>;
|
||||
@@ -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();
|
||||
|
||||
@@ -10,6 +10,7 @@ Copyright (C) 2019-2021 by Maxim Prokhorov <prokhorov dot max at outlook dot com
|
||||
#include <Arduino.h>
|
||||
#include <sys/pgmspace.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
|
||||
#include "compat.h"
|
||||
@@ -18,6 +19,20 @@ Copyright (C) 2019-2021 by Maxim Prokhorov <prokhorov dot max at outlook dot com
|
||||
extern "C" int memcmp_P(const void*, const void*, size_t);
|
||||
|
||||
namespace espurna {
|
||||
namespace duration {
|
||||
|
||||
// Only micros are 64bit, millis stored as 32bit to match what is actually returned & used by Core functions
|
||||
using Microseconds = std::chrono::duration<uint64_t, std::micro>;
|
||||
using Milliseconds = std::chrono::duration<uint32_t, std::milli>;
|
||||
|
||||
// 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<uint32_t, std::ratio<1> >;
|
||||
using Minutes = std::chrono::duration<uint32_t, std::ratio<60> >;
|
||||
using Hours = std::chrono::duration<uint32_t, std::ratio<Minutes::period::num * 60> >;
|
||||
using Days = std::chrono::duration<uint32_t, std::ratio<Hours::period::num * 24> >;
|
||||
|
||||
} // 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.
|
||||
|
||||
@@ -6,12 +6,9 @@ Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
*/
|
||||
|
||||
#include "espurna.h"
|
||||
|
||||
#include "ntp.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <limits>
|
||||
#include <random>
|
||||
|
||||
// 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<Type>(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;
|
||||
|
||||
@@ -10,7 +10,7 @@ Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#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);
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
71
code/test/unit/src/utils/utils.cpp
Normal file
71
code/test/unit/src/utils/utils.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
#include <unity.h>
|
||||
#include <Arduino.h>
|
||||
#include <StreamString.h>
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include <espurna/utils.h>
|
||||
|
||||
namespace espurna {
|
||||
namespace test {
|
||||
namespace {
|
||||
|
||||
void test_parse_unsigned_result() {
|
||||
const auto result = parseUnsigned("");
|
||||
using Value = std::is_same<decltype(result.value), uint32_t>;
|
||||
TEST_ASSERT(static_cast<bool>(!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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user