From 98c036f9e3cb987f67157996913c28f2423e73eb Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Tue, 28 Nov 2023 01:30:36 +0100 Subject: [PATCH] Add unit tests --- lib/btclock/data_handler.cpp | 154 ++++++++++++++++++++++++++++ lib/btclock/data_handler.hpp | 10 ++ {src/lib => lib/btclock}/utils.cpp | 11 -- {src/lib => lib/btclock}/utils.hpp | 5 +- platformio.ini | 2 + src/lib/config.cpp | 11 ++ src/lib/config.hpp | 1 + src/lib/epd.cpp | 10 ++ src/lib/epd.hpp | 3 + src/lib/screen_handler.cpp | 118 ++------------------- src/lib/screen_handler.hpp | 1 + src/lib/shared.hpp | 1 + src/lib/webserver.cpp | 2 +- test/test_datahandler/test_main.cpp | 60 +++++++++++ 14 files changed, 263 insertions(+), 126 deletions(-) create mode 100644 lib/btclock/data_handler.cpp create mode 100644 lib/btclock/data_handler.hpp rename {src/lib => lib/btclock}/utils.cpp (80%) rename {src/lib => lib/btclock}/utils.hpp (66%) create mode 100644 test/test_datahandler/test_main.cpp diff --git a/lib/btclock/data_handler.cpp b/lib/btclock/data_handler.cpp new file mode 100644 index 0000000..250dbc9 --- /dev/null +++ b/lib/btclock/data_handler.cpp @@ -0,0 +1,154 @@ +#include "data_handler.hpp" + +std::array parsePriceData(uint price, char currencySymbol) +{ + std::array ret; + std::string priceString = currencySymbol + std::to_string(price); + uint firstIndex = 0; + if (priceString.length() < (NUM_SCREENS)) + { + priceString.insert(priceString.begin(), NUM_SCREENS - priceString.length(), ' '); + if (currencySymbol == '[') + { + ret[0] = "BTC/EUR"; + } + else + { + ret[0] = "BTC/USD"; + } + firstIndex = 1; + } + + for (uint i = firstIndex; i < NUM_SCREENS; i++) + { + ret[i] = priceString[i]; + } + + return ret; +} + +std::array parseSatsPerCurrency(uint price, char currencySymbol) +{ + std::array ret; + std::string priceString = std::to_string(int(round(1 / float(price) * 10e7))); + uint firstIndex = 0; + + if (priceString.length() < (NUM_SCREENS)) + { + priceString.insert(priceString.begin(), NUM_SCREENS - priceString.length(), ' '); + if (currencySymbol == '[') + { + ret[0] = "SATS/EUR"; + } + else + { + ret[0] = "MSCW/TIME"; + } + firstIndex = 1; + + for (uint i = firstIndex; i < NUM_SCREENS; i++) + { + ret[i] = priceString[i]; + } + } + return ret; +} + +std::array parseBlockHeight(uint blockHeight) +{ + std::array ret; + std::string blockNrString = std::to_string(blockHeight); + uint firstIndex = 0; + + if (blockNrString.length() < NUM_SCREENS) + { + blockNrString.insert(blockNrString.begin(), NUM_SCREENS - blockNrString.length(), ' '); + ret[0] = "BLOCK/HEIGHT"; + firstIndex = 1; + } + + for (uint i = firstIndex; i < NUM_SCREENS; i++) + { + ret[i] = blockNrString[i]; + } + + return ret; +} + +std::array parseHalvingCountdown(uint blockHeight) +{ + std::array ret; + + const uint nextHalvingBlock = 210000 - (blockHeight % 210000); + const uint minutesToHalving = nextHalvingBlock * 10; + + const int years = floor(minutesToHalving / 525600); + const int days = floor((minutesToHalving - (years * 525600)) / (24 * 60)); + const int hours = floor((minutesToHalving - (years * 525600) - (days * (24 * 60))) / 60); + const int mins = floor(minutesToHalving - (years * 525600) - (days * (24 * 60)) - (hours * 60)); + ret[0] = "BIT/COIN"; + ret[1] = "HALV/ING"; + ret[(NUM_SCREENS - 5)] = std::to_string(years) + "/YRS"; + ret[(NUM_SCREENS - 4)] = std::to_string(days) + "/DAYS"; + ret[(NUM_SCREENS - 3)] = std::to_string(hours) + "/HRS"; + ret[(NUM_SCREENS - 2)] = std::to_string(mins) + "/MINS"; + ret[(NUM_SCREENS - 1)] = "TO/GO"; + + return ret; +} + +std::array parseMarketCap(uint blockHeight, uint price, char currencySymbol, bool bigChars) +{ + std::array ret; + uint firstIndex = 0; + double supply = getSupplyAtBlock(blockHeight); + int64_t marketCap = static_cast(supply * double(price)); + if (currencySymbol == '[') + { + ret[0] = "EUR/MCAP"; + } + else + { + ret[0] = "USD/MCAP"; + } + + if (bigChars) + { + firstIndex = 1; + + std::string priceString = currencySymbol + formatNumberWithSuffix(marketCap); + priceString.insert(priceString.begin(), NUM_SCREENS - priceString.length(), ' '); + + for (uint i = firstIndex; i < NUM_SCREENS; i++) + { + ret[i] = priceString[i]; + } + } + else + { + std::string stringValue = std::to_string(marketCap); + size_t mcLength = stringValue.length(); + size_t leadingSpaces = (3 - mcLength % 3) % 3; + stringValue = std::string(leadingSpaces, ' ') + stringValue; + + uint groups = (mcLength + leadingSpaces) / 3; + + if (groups < NUM_SCREENS) + { + firstIndex = 1; + } + + for (int i = firstIndex; i < NUM_SCREENS - groups - 1; i++) + { + ret[i] = ""; + } + + ret[NUM_SCREENS - groups - 1] = " $ "; + for (uint i = 0; i < groups; i++) + { + ret[(NUM_SCREENS - groups + i)] = stringValue.substr(i * 3, 3).c_str(); + } + } + + return ret; +} \ No newline at end of file diff --git a/lib/btclock/data_handler.hpp b/lib/btclock/data_handler.hpp new file mode 100644 index 0000000..373d78a --- /dev/null +++ b/lib/btclock/data_handler.hpp @@ -0,0 +1,10 @@ +#include +#include +#include +#include "utils.hpp" + +std::array parsePriceData(uint price, char currencySymbol); +std::array parseSatsPerCurrency(uint price, char currencySymbol); +std::array parseBlockHeight(uint blockHeight); +std::array parseHalvingCountdown(uint blockHeight); +std::array parseMarketCap(uint blockHeight, uint price, char currencySymbol, bool bigChars); \ No newline at end of file diff --git a/src/lib/utils.cpp b/lib/btclock/utils.cpp similarity index 80% rename from src/lib/utils.cpp rename to lib/btclock/utils.cpp index 445b1b0..b68b327 100644 --- a/src/lib/utils.cpp +++ b/lib/btclock/utils.cpp @@ -5,17 +5,6 @@ int modulo(int x, int N) return (x % N + N) % N; } -String getMyHostname() { - uint8_t mac[6]; - //WiFi.macAddress(mac); - esp_efuse_mac_get_default(mac); - char hostname[15]; - String hostnamePrefix = preferences.getString("hostnamePrefix", "btclock"); - snprintf(hostname, sizeof(hostname), "%s-%02x%02x%02x", - hostnamePrefix, mac[3], mac[4], mac[5]); - return hostname; -} - double getSupplyAtBlock(uint blockNr) { if (blockNr >= 33 * 210000) { return 20999999.9769; diff --git a/src/lib/utils.hpp b/lib/btclock/utils.hpp similarity index 66% rename from src/lib/utils.hpp rename to lib/btclock/utils.hpp index 81730e2..0a14328 100644 --- a/src/lib/utils.hpp +++ b/lib/btclock/utils.hpp @@ -1,11 +1,10 @@ #pragma once -#include -#include "shared.hpp" +#include +#include int modulo(int x,int N); double getSupplyAtBlock(uint blockNr); -String getMyHostname(); std::string formatNumberWithSuffix(int64_t num); \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index a9b497c..e73d0b6 100644 --- a/platformio.ini +++ b/platformio.ini @@ -56,6 +56,7 @@ build_unflags = [env:lolin_s3_mini_qr] extends = env:lolin_s3_mini +test_framework = unity build_flags = ${env:lolin_s3_mini.build_flags} -D USE_QR @@ -64,6 +65,7 @@ build_flags = extends = btclock_base board = btclock board_build.partitions = partition_16mb.csv +test_framework = unity build_flags = ${btclock_base.build_flags} -D MCP_INT_PIN=4 diff --git a/src/lib/config.cpp b/src/lib/config.cpp index 417686c..97344a0 100644 --- a/src/lib/config.cpp +++ b/src/lib/config.cpp @@ -605,4 +605,15 @@ void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) default: break; } +} + +String getMyHostname() { + uint8_t mac[6]; + //WiFi.macAddress(mac); + esp_efuse_mac_get_default(mac); + char hostname[15]; + String hostnamePrefix = preferences.getString("hostnamePrefix", "btclock"); + snprintf(hostname, sizeof(hostname), "%s-%02x%02x%02x", + hostnamePrefix, mac[3], mac[4], mac[5]); + return hostname; } \ No newline at end of file diff --git a/src/lib/config.hpp b/src/lib/config.hpp index 56716f3..1e9e769 100644 --- a/src/lib/config.hpp +++ b/src/lib/config.hpp @@ -42,6 +42,7 @@ void tryImprovSetup(); void setupTimers(); void finishSetup(); void setupMcp(); +String getMyHostname(); std::vector getScreenNameMap(); std::vector getLocalUrl(); diff --git a/src/lib/epd.cpp b/src/lib/epd.cpp index d982556..7402b16 100644 --- a/src/lib/epd.cpp +++ b/src/lib/epd.cpp @@ -173,6 +173,16 @@ void setEpdContent(std::array newEpdContent) setEpdContent(newEpdContent, false); } +void setEpdContent(std::array newEpdContent) { + std::array conv; + + for (size_t i = 0; i < newEpdContent.size(); ++i) { + conv[i] = String(newEpdContent[i].c_str()); + } + + return setEpdContent(conv); +} + void setEpdContent(std::array newEpdContent, bool forceUpdate) { std::lock_guard lock(epdUpdateMutex); diff --git a/src/lib/epd.hpp b/src/lib/epd.hpp index 2c81e3f..20b5ea9 100644 --- a/src/lib/epd.hpp +++ b/src/lib/epd.hpp @@ -44,5 +44,8 @@ void renderQr(const uint dispNum, const String& text, bool partial); void setEpdContent(std::array newEpdContent, bool forceUpdate); void setEpdContent(std::array newEpdContent); + +void setEpdContent(std::array newEpdContent); + std::array getCurrentEpdContent(); void waitUntilNoneBusy(); \ No newline at end of file diff --git a/src/lib/screen_handler.cpp b/src/lib/screen_handler.cpp index f838fa0..5c2c9a1 100644 --- a/src/lib/screen_handler.cpp +++ b/src/lib/screen_handler.cpp @@ -8,7 +8,7 @@ TaskHandle_t workerTaskHandle; esp_timer_handle_t screenRotateTimer; esp_timer_handle_t minuteTimer; -std::array taskEpdContent = {"", "", "", "", "", "", ""}; +std::array taskEpdContent = {"", "", "", "", "", "", ""}; std::string priceString; // typedef enum @@ -45,7 +45,6 @@ void workerTask(void *pvParameters) { case TASK_PRICE_UPDATE: { - firstIndex = 0; uint price = getPrice(); char priceSymbol = '$'; if (preferences.getBool("fetchEurPrice", false)) @@ -54,92 +53,15 @@ void workerTask(void *pvParameters) } if (getCurrentScreen() == SCREEN_BTC_TICKER) { - priceString = (priceSymbol + String(price)).c_str(); - - if (priceString.length() < (NUM_SCREENS)) - { - priceString.insert(priceString.begin(), NUM_SCREENS - priceString.length(), ' '); - if (preferences.getBool("fetchEurPrice", false)) - { - taskEpdContent[0] = "BTC/EUR"; - } - else - { - taskEpdContent[0] = "BTC/USD"; - } - firstIndex = 1; - } + taskEpdContent = parsePriceData(price, priceSymbol); } else if (getCurrentScreen() == SCREEN_MSCW_TIME) { - priceString = String(int(round(1 / float(price) * 10e7))).c_str(); - - if (priceString.length() < (NUM_SCREENS)) - { - priceString.insert(priceString.begin(), NUM_SCREENS - priceString.length(), ' '); - if (preferences.getBool("fetchEurPrice", false)) - { - taskEpdContent[0] = "SATS/EUR"; - } else { - taskEpdContent[0] = "MSCW/TIME"; - } - firstIndex = 1; - } + taskEpdContent = parseSatsPerCurrency(price, priceSymbol); } else { - double supply = getSupplyAtBlock(getBlockHeight()); - int64_t marketCap = static_cast(supply * double(price)); - - if (preferences.getBool("fetchEurPrice", false)) - { - taskEpdContent[0] = "EUR/MCAP"; - } - else - { - taskEpdContent[0] = "USD/MCAP"; - } - - if (preferences.getBool("mcapBigChar", true)) - { - firstIndex = 1; - - priceString = priceSymbol + formatNumberWithSuffix(marketCap); - priceString.insert(priceString.begin(), NUM_SCREENS - priceString.length(), ' '); - } - else - { - std::string stringValue = std::to_string(marketCap); - size_t mcLength = stringValue.length(); - size_t leadingSpaces = (3 - mcLength % 3) % 3; - stringValue = std::string(leadingSpaces, ' ') + stringValue; - - uint groups = (mcLength + leadingSpaces) / 3; - - if (groups < NUM_SCREENS) - { - firstIndex = 1; - } - - for (int i = firstIndex; i < NUM_SCREENS - groups - 1; i++) - { - taskEpdContent[i] = ""; - } - - taskEpdContent[NUM_SCREENS - groups - 1] = " $ "; - for (uint i = 0; i < groups; i++) - { - taskEpdContent[(NUM_SCREENS - groups + i)] = stringValue.substr(i * 3, 3).c_str(); - } - } - } - - if (!(getCurrentScreen() == SCREEN_MARKET_CAP && !preferences.getBool("mcapBigChar", true))) - { - for (uint i = firstIndex; i < NUM_SCREENS; i++) - { - taskEpdContent[i] = priceString[i]; - } + taskEpdContent = parseMarketCap(getBlockHeight(), price, priceSymbol, preferences.getBool("mcapBigChar", true)); } setEpdContent(taskEpdContent); @@ -147,39 +69,13 @@ void workerTask(void *pvParameters) } case TASK_BLOCK_UPDATE: { - std::string blockNrString = String(getBlockHeight()).c_str(); - firstIndex = 0; - if (getCurrentScreen() != SCREEN_HALVING_COUNTDOWN) { - if (blockNrString.length() < NUM_SCREENS) - { - blockNrString.insert(blockNrString.begin(), NUM_SCREENS - blockNrString.length(), ' '); - taskEpdContent[0] = "BLOCK/HEIGHT"; - firstIndex = 1; - } - - for (uint i = firstIndex; i < NUM_SCREENS; i++) - { - taskEpdContent[i] = blockNrString[i]; - } + taskEpdContent = parseBlockHeight(getBlockHeight()); } else { - const uint nextHalvingBlock = 210000 - (getBlockHeight() % 210000); - const uint minutesToHalving = nextHalvingBlock * 10; - - const int years = floor(minutesToHalving / 525600); - const int days = floor((minutesToHalving - (years * 525600)) / (24 * 60)); - const int hours = floor((minutesToHalving - (years * 525600) - (days * (24 * 60))) / 60); - const int mins = floor(minutesToHalving - (years * 525600) - (days * (24 * 60)) - (hours * 60)); - taskEpdContent[0] = "BIT/COIN"; - taskEpdContent[1] = "HALV/ING"; - taskEpdContent[(NUM_SCREENS - 5)] = String(years) + "/YRS"; - taskEpdContent[(NUM_SCREENS - 4)] = String(days) + "/DAYS"; - taskEpdContent[(NUM_SCREENS - 3)] = String(hours) + "/HRS"; - taskEpdContent[(NUM_SCREENS - 2)] = String(mins) + "/MINS"; - taskEpdContent[(NUM_SCREENS - 1)] = "TO/GO"; + taskEpdContent = parseHalvingCountdown(getBlockHeight()); } if (getCurrentScreen() == SCREEN_HALVING_COUNTDOWN || getCurrentScreen() == SCREEN_BLOCK_HEIGHT) @@ -206,7 +102,7 @@ void workerTask(void *pvParameters) timeString = std::to_string(timeinfo.tm_hour) + ":" + minute.c_str(); timeString.insert(timeString.begin(), NUM_SCREENS - timeString.length(), ' '); - taskEpdContent[0] = String(timeinfo.tm_mday) + "/" + String(timeinfo.tm_mon + 1); + taskEpdContent[0] = std::to_string(timeinfo.tm_mday) + "/" + std::to_string(timeinfo.tm_mon + 1); for (uint i = 1; i < NUM_SCREENS; i++) { diff --git a/src/lib/screen_handler.hpp b/src/lib/screen_handler.hpp index 20f8f3b..f3faa4e 100644 --- a/src/lib/screen_handler.hpp +++ b/src/lib/screen_handler.hpp @@ -4,6 +4,7 @@ #include #include +#include #include "price_fetch.hpp" #include "shared.hpp" diff --git a/src/lib/shared.hpp b/src/lib/shared.hpp index 71bc0de..f9543d9 100644 --- a/src/lib/shared.hpp +++ b/src/lib/shared.hpp @@ -45,3 +45,4 @@ struct SpiRamAllocator { }; using SpiRamJsonDocument = BasicJsonDocument; + diff --git a/src/lib/webserver.cpp b/src/lib/webserver.cpp index f4ffa51..40f3f66 100644 --- a/src/lib/webserver.cpp +++ b/src/lib/webserver.cpp @@ -313,7 +313,7 @@ void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json) } } - String uintSettings[] = {"minSecPriceUpd", "fullRefreshMin", "ledBrightness", "mcapBigChar"}; + String uintSettings[] = {"minSecPriceUpd", "fullRefreshMin", "ledBrightness"}; for (String setting : uintSettings) { diff --git a/test/test_datahandler/test_main.cpp b/test/test_datahandler/test_main.cpp new file mode 100644 index 0000000..922a911 --- /dev/null +++ b/test/test_datahandler/test_main.cpp @@ -0,0 +1,60 @@ +#include +#include + +void setUp(void) { + // set stuff up here +} + +void tearDown(void) { + // clean stuff up here +} + +void test_sats_per_dollar(void) { + std::array output = parseSatsPerCurrency(37253, '$'); + TEST_ASSERT_EQUAL_STRING("MSCW/TIME", output[0].c_str()); + TEST_ASSERT_EQUAL_STRING("2", output[NUM_SCREENS-4].c_str()); + TEST_ASSERT_EQUAL_STRING("6", output[NUM_SCREENS-3].c_str()); + TEST_ASSERT_EQUAL_STRING("8", output[NUM_SCREENS-2].c_str()); + TEST_ASSERT_EQUAL_STRING("4", output[NUM_SCREENS-1].c_str()); +} + + +void test_block_height_6screens(void) { + std::array output = parseBlockHeight(999999); + TEST_ASSERT_EQUAL_STRING("BLOCK/HEIGHT", output[0].c_str()); + TEST_ASSERT_EQUAL_STRING("9", output[1].c_str()); +} + +void test_block_height_7screens(void) { + std::array output = parseBlockHeight(1000000); + TEST_ASSERT_EQUAL_STRING("1", output[0].c_str()); + TEST_ASSERT_EQUAL_STRING("0", output[1].c_str()); +} + +void test_ticker_6screens(void) { + std::array output = parsePriceData(100000, '$'); + TEST_ASSERT_EQUAL_STRING("$", output[0].c_str()); + TEST_ASSERT_EQUAL_STRING("1", output[1].c_str()); +} + +void test_ticker_7screens(void) { + std::array output = parsePriceData(1000000, '$'); + TEST_ASSERT_EQUAL_STRING("1", output[0].c_str()); + TEST_ASSERT_EQUAL_STRING("0", output[1].c_str()); +} + +// not needed when using generate_test_runner.rb +int runUnityTests(void) { + UNITY_BEGIN(); + RUN_TEST(test_sats_per_dollar); + RUN_TEST(test_block_height_6screens); + RUN_TEST(test_block_height_7screens); + RUN_TEST(test_ticker_6screens); + RUN_TEST(test_ticker_7screens); + + return UNITY_END(); +} + +extern "C" void app_main() { + runUnityTests(); +}