Promoted from DIYStickyNotes/lib/ReleaseUpdater (byte-identical src) into a reusable Gitea library. Adds LICENSE, .gitignore, library.properties, keywords.txt and carries the ReleaseUpdaterCore native unit test for downstream reuse. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
130 lines
5.9 KiB
C++
130 lines
5.9 KiB
C++
/** @file Native unit tests for ReleaseUpdaterCore (version + release/asset parsing). */
|
|
#include <unity.h>
|
|
#include "ReleaseUpdaterCore.hpp"
|
|
#include <cstring>
|
|
|
|
static void test_version_parse_basic(void) {
|
|
RuVersion v;
|
|
TEST_ASSERT_TRUE(ruVersionParse("v1.2.3", v));
|
|
TEST_ASSERT_EQUAL_INT(1, v.major); TEST_ASSERT_EQUAL_INT(2, v.minor); TEST_ASSERT_EQUAL_INT(3, v.patch);
|
|
TEST_ASSERT_TRUE(ruVersionParse("1.2.3", v));
|
|
TEST_ASSERT_EQUAL_INT(3, v.patch);
|
|
TEST_ASSERT_TRUE(ruVersionParse("v10.0.1", v));
|
|
TEST_ASSERT_EQUAL_INT(10, v.major);
|
|
}
|
|
|
|
static void test_version_parse_short(void) {
|
|
RuVersion v;
|
|
TEST_ASSERT_TRUE(ruVersionParse("1.2", v));
|
|
TEST_ASSERT_EQUAL_INT(1, v.major); TEST_ASSERT_EQUAL_INT(2, v.minor); TEST_ASSERT_EQUAL_INT(0, v.patch);
|
|
TEST_ASSERT_TRUE(ruVersionParse("v1", v));
|
|
TEST_ASSERT_EQUAL_INT(1, v.major); TEST_ASSERT_EQUAL_INT(0, v.minor); TEST_ASSERT_EQUAL_INT(0, v.patch);
|
|
}
|
|
|
|
static void test_version_parse_reject(void) {
|
|
RuVersion v;
|
|
TEST_ASSERT_FALSE(ruVersionParse("", v));
|
|
TEST_ASSERT_FALSE(ruVersionParse("x", v));
|
|
TEST_ASSERT_FALSE(ruVersionParse("1.x.0", v));
|
|
TEST_ASSERT_FALSE(ruVersionParse("v.1.2", v));
|
|
}
|
|
|
|
static void test_version_parse_suffix_tolerated(void) {
|
|
RuVersion v;
|
|
// git-describe style build suffix after patch is ignored.
|
|
TEST_ASSERT_TRUE(ruVersionParse("v1.2.3-5-gabcdef", v));
|
|
TEST_ASSERT_EQUAL_INT(1, v.major); TEST_ASSERT_EQUAL_INT(2, v.minor); TEST_ASSERT_EQUAL_INT(3, v.patch);
|
|
// dotted trailing content after patch is also ignored (treated as build suffix).
|
|
TEST_ASSERT_TRUE(ruVersionParse("1.2.3.99", v));
|
|
TEST_ASSERT_EQUAL_INT(1, v.major); TEST_ASSERT_EQUAL_INT(2, v.minor); TEST_ASSERT_EQUAL_INT(3, v.patch);
|
|
}
|
|
|
|
static void test_version_compare(void) {
|
|
RuVersion a{1,2,3}, b{1,2,3};
|
|
TEST_ASSERT_EQUAL_INT(0, ruVersionCompare(a, b));
|
|
TEST_ASSERT_EQUAL_INT(1, ruVersionCompare(RuVersion{1,3,0}, RuVersion{1,2,9}));
|
|
TEST_ASSERT_EQUAL_INT(-1, ruVersionCompare(RuVersion{1,2,3}, RuVersion{2,0,0}));
|
|
TEST_ASSERT_EQUAL_INT(1, ruVersionCompare(RuVersion{1,2,4}, RuVersion{1,2,3}));
|
|
}
|
|
|
|
// Minimal GitHub/Gitea "releases/latest" shape (both share tag_name + assets[].{name,browser_download_url}).
|
|
static const char* SAMPLE_JSON =
|
|
"{\"tag_name\":\"v1.4.0\",\"assets\":["
|
|
"{\"name\":\"DIYStickyNotes-sc01plus-firmware-1.4.0.bin\",\"browser_download_url\":\"https://h/a/sc.bin\"},"
|
|
"{\"name\":\"DIYStickyNotes-JC-jc3248w535-firmware-1.4.0.bin\",\"browser_download_url\":\"https://h/a/jc.bin\"}"
|
|
"]}";
|
|
|
|
static void test_parse_selects_sc01(void) {
|
|
RuRelease r;
|
|
TEST_ASSERT_TRUE(ruParseLatestRelease(SAMPLE_JSON, "sc01plus", r));
|
|
TEST_ASSERT_EQUAL_STRING("v1.4.0", r.tag);
|
|
TEST_ASSERT_EQUAL_INT(1, r.version.major); TEST_ASSERT_EQUAL_INT(4, r.version.minor);
|
|
TEST_ASSERT_EQUAL_STRING("https://h/a/sc.bin", r.assetUrl);
|
|
}
|
|
|
|
static void test_parse_selects_jc_not_sc(void) {
|
|
RuRelease r;
|
|
// jc token must NOT cross-match the sc01 asset, and sc01 token must NOT match the jc asset.
|
|
TEST_ASSERT_TRUE(ruParseLatestRelease(SAMPLE_JSON, "jc3248w535", r));
|
|
TEST_ASSERT_EQUAL_STRING("https://h/a/jc.bin", r.assetUrl);
|
|
}
|
|
|
|
static void test_parse_no_matching_asset(void) {
|
|
RuRelease r;
|
|
TEST_ASSERT_TRUE(ruParseLatestRelease(SAMPLE_JSON, "nosuchboard", r)); // parse ok...
|
|
TEST_ASSERT_EQUAL_STRING("", r.assetUrl); // ...but no asset
|
|
}
|
|
|
|
// A same-token asset with a non-.bin extension listed BEFORE the real firmware must be skipped
|
|
// (locks the `&& ruEndsWithBin(name)` filter against a regression).
|
|
static const char* SAMPLE_JSON_NONBIN_FIRST =
|
|
"{\"tag_name\":\"v1.4.0\",\"assets\":["
|
|
"{\"name\":\"DIYStickyNotes-sc01plus-firmware-1.4.0.sha256\",\"browser_download_url\":\"https://h/a/sc.sha256\"},"
|
|
"{\"name\":\"DIYStickyNotes-sc01plus-firmware-1.4.0.bin\",\"browser_download_url\":\"https://h/a/sc.bin\"}"
|
|
"]}";
|
|
|
|
static void test_parse_skips_non_bin_asset(void) {
|
|
RuRelease r;
|
|
TEST_ASSERT_TRUE(ruParseLatestRelease(SAMPLE_JSON_NONBIN_FIRST, "sc01plus", r));
|
|
TEST_ASSERT_EQUAL_STRING("https://h/a/sc.bin", r.assetUrl); // .sha256 skipped, real .bin chosen
|
|
}
|
|
|
|
// A board-token ".bin" asset that is NOT the app image (missing the "-firmware-" segment, e.g. a
|
|
// LittleFS/factory/combined image) listed BEFORE the real firmware asset must be skipped (locks the
|
|
// "{token}-firmware-" needle against a regression that could flash a non-app binary to the app
|
|
// partition and brick the device).
|
|
static const char* SAMPLE_JSON_NONFIRMWARE_FIRST =
|
|
"{\"tag_name\":\"v1.4.0\",\"assets\":["
|
|
"{\"name\":\"DIYStickyNotes-sc01plus-littlefs-1.4.0.bin\",\"browser_download_url\":\"https://h/a/lfs.bin\"},"
|
|
"{\"name\":\"DIYStickyNotes-sc01plus-firmware-1.4.0.bin\",\"browser_download_url\":\"https://h/a/sc.bin\"}"
|
|
"]}";
|
|
|
|
static void test_parse_skips_non_firmware_bin_asset(void) {
|
|
RuRelease r;
|
|
TEST_ASSERT_TRUE(ruParseLatestRelease(SAMPLE_JSON_NONFIRMWARE_FIRST, "sc01plus", r));
|
|
TEST_ASSERT_EQUAL_STRING("https://h/a/sc.bin", r.assetUrl); // littlefs .bin skipped, real firmware chosen
|
|
}
|
|
|
|
static void test_parse_malformed(void) {
|
|
RuRelease r;
|
|
TEST_ASSERT_FALSE(ruParseLatestRelease("not json", "sc01plus", r));
|
|
TEST_ASSERT_FALSE(ruParseLatestRelease("{\"assets\":[]}", "sc01plus", r)); // missing tag_name
|
|
TEST_ASSERT_FALSE(ruParseLatestRelease("{\"tag_name\":\"\",\"assets\":[]}", "sc01plus", r)); // empty tag_name
|
|
}
|
|
|
|
int main(int, char**) {
|
|
UNITY_BEGIN();
|
|
RUN_TEST(test_version_parse_basic);
|
|
RUN_TEST(test_version_parse_short);
|
|
RUN_TEST(test_version_parse_reject);
|
|
RUN_TEST(test_version_parse_suffix_tolerated);
|
|
RUN_TEST(test_version_compare);
|
|
RUN_TEST(test_parse_selects_sc01);
|
|
RUN_TEST(test_parse_selects_jc_not_sc);
|
|
RUN_TEST(test_parse_no_matching_asset);
|
|
RUN_TEST(test_parse_skips_non_bin_asset);
|
|
RUN_TEST(test_parse_skips_non_firmware_bin_asset);
|
|
RUN_TEST(test_parse_malformed);
|
|
return UNITY_END();
|
|
}
|