Something went wrong. Try again.
A fork of https://github.com/crosspoint-reader/crosspoint-reader
Something went wrong. Try again.
22 kB · 652 lines
C++
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653#include <gtest/gtest.h>
#include <cstring>#include <string>
#include "lib/JsonParser/ReleaseJsonParser.h"
namespace {
const char* kRealisticPretty = R"({ "url": "https://api.github.com/repos/crosspoint-reader/crosspoint-reader/releases/12345", "assets_url": "https://api.github.com/repos/crosspoint-reader/crosspoint-reader/releases/12345/assets", "upload_url": "https://uploads.github.com/repos/crosspoint-reader/crosspoint-reader/releases/12345/assets{?name,label}", "html_url": "https://github.com/crosspoint-reader/crosspoint-reader/releases/tag/v2.4.1", "id": 12345, "author": { "login": "releasebot", "id": 99887766, "node_id": "MDQ6VXNlcjk5ODg3NzY2", "avatar_url": "https://avatars.githubusercontent.com/u/99887766?v=4", "url": "https://api.github.com/users/releasebot", "type": "User", "site_admin": false }, "node_id": "RE_kwDOAbCdEf4AADBN", "tag_name": "v2.4.1", "target_commitish": "main", "name": "CrossPoint Reader v2.4.1", "draft": false, "prerelease": false, "created_at": "2026-04-28T10:00:00Z", "published_at": "2026-04-28T10:30:00Z", "assets": [ { "url": "https://api.github.com/repos/crosspoint-reader/crosspoint-reader/releases/assets/100001", "id": 100001, "node_id": "RA_kwDOAbCdEf4AAGHR", "name": "crosspoint-reader-v2.4.1-source.zip", "label": null, "uploader": { "login": "releasebot", "id": 99887766, "node_id": "MDQ6VXNlcjk5ODg3NzY2", "type": "User" }, "content_type": "application/zip", "state": "uploaded", "size": 2048576, "download_count": 42, "created_at": "2026-04-28T10:15:00Z", "updated_at": "2026-04-28T10:15:30Z", "browser_download_url": "https://github.com/crosspoint-reader/crosspoint-reader/releases/download/v2.4.1/crosspoint-reader-v2.4.1-source.zip" }, { "url": "https://api.github.com/repos/crosspoint-reader/crosspoint-reader/releases/assets/100002", "id": 100002, "node_id": "RA_kwDOAbCdEf4AAGHS", "name": "firmware.bin", "label": "ESP32-C3 Firmware", "uploader": { "login": "releasebot", "id": 99887766, "node_id": "MDQ6VXNlcjk5ODg3NzY2", "type": "User" }, "content_type": "application/octet-stream", "state": "uploaded", "size": 1572864, "download_count": 187, "created_at": "2026-04-28T10:16:00Z", "updated_at": "2026-04-28T10:16:45Z", "browser_download_url": "https://github.com/crosspoint-reader/crosspoint-reader/releases/download/v2.4.1/firmware.bin" }, { "url": "https://api.github.com/repos/crosspoint-reader/crosspoint-reader/releases/assets/100003", "id": 100003, "node_id": "RA_kwDOAbCdEf4AAGHR", "name": "checksums.sha256", "label": null, "uploader": { "login": "releasebot", "id": 99887766, "node_id": "MDQ6VXNlcjk5ODg3NzY2", "type": "User" }, "content_type": "text/plain", "state": "uploaded", "size": 192, "download_count": 15, "created_at": "2026-04-28T10:17:00Z", "updated_at": "2026-04-28T10:17:10Z", "browser_download_url": "https://github.com/crosspoint-reader/crosspoint-reader/releases/download/v2.4.1/checksums.sha256" } ], "tarball_url": "https://api.github.com/repos/crosspoint-reader/crosspoint-reader/tarball/v2.4.1", "zipball_url": "https://api.github.com/repos/crosspoint-reader/crosspoint-reader/zipball/v2.4.1", "body": "## What's Changed\n\n* Fixed orientation crash (#123)\n* Improved EPUB rendering performance\n* Added Serbian translation\n\n**Full Changelog**: https://github.com/crosspoint-reader/crosspoint-reader/compare/v2.4.0...v2.4.1", "reactions": { "url": "https://api.github.com/repos/crosspoint-reader/crosspoint-reader/releases/12345/reactions", "total_count": 5, "+1": 3, "-1": 0, "laugh": 1, "hooray": 1, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0 }})";
const char* kRealisticMinified = R"({"url":"https://api.github.com/repos/crosspoint-reader/crosspoint-reader/releases/12345","assets_url":"https://api.github.com/repos/crosspoint-reader/crosspoint-reader/releases/12345/assets","id":12345,"author":{"login":"releasebot","id":99887766,"node_id":"MDQ6VXNlcjk5ODg3NzY2","type":"User","site_admin":false},"tag_name":"v2.4.1","target_commitish":"main","name":"CrossPoint Reader v2.4.1","draft":false,"prerelease":false,"assets":[{"url":"https://api.github.com/repos/crosspoint-reader/crosspoint-reader/releases/assets/100001","id":100001,"name":"crosspoint-reader-v2.4.1-source.zip","uploader":{"login":"releasebot","id":99887766},"content_type":"application/zip","state":"uploaded","size":2048576,"download_count":42,"browser_download_url":"https://github.com/crosspoint-reader/crosspoint-reader/releases/download/v2.4.1/crosspoint-reader-v2.4.1-source.zip"},{"url":"https://api.github.com/repos/crosspoint-reader/crosspoint-reader/releases/assets/100002","id":100002,"name":"firmware.bin","uploader":{"login":"releasebot","id":99887766},"content_type":"application/octet-stream","state":"uploaded","size":1572864,"download_count":187,"browser_download_url":"https://github.com/crosspoint-reader/crosspoint-reader/releases/download/v2.4.1/firmware.bin"},{"url":"https://api.github.com/repos/crosspoint-reader/crosspoint-reader/releases/assets/100003","id":100003,"name":"checksums.sha256","uploader":{"login":"releasebot","id":99887766},"content_type":"text/plain","state":"uploaded","size":192,"download_count":15,"browser_download_url":"https://github.com/crosspoint-reader/crosspoint-reader/releases/download/v2.4.1/checksums.sha256"}],"body":"## What's Changed\n\n* Fixed orientation crash","reactions":{"url":"https://api.github.com/repos/crosspoint-reader/crosspoint-reader/releases/12345/reactions","total_count":5,"+1":3}})";
void feedChunked(ReleaseJsonParser& p, const char* json, size_t chunkSize) { size_t len = strlen(json); for (size_t off = 0; off < len; off += chunkSize) { size_t n = len - off < chunkSize ? len - off : chunkSize; p.feed(json + off, n); }}
} // namespace
TEST(ReleaseJsonParser, RealisticPrettyPrinted) { ReleaseJsonParser p; p.feed(kRealisticPretty, strlen(kRealisticPretty));
EXPECT_TRUE(p.foundTag()); EXPECT_TRUE(p.foundFirmware()); EXPECT_STREQ(p.getTagName(), "v2.4.1"); EXPECT_STREQ(p.getFirmwareUrl(), "https://github.com/crosspoint-reader/crosspoint-reader/releases/download/v2.4.1/firmware.bin"); EXPECT_EQ(p.getFirmwareSize(), 1572864u);}
TEST(ReleaseJsonParser, RealisticMinified) { ReleaseJsonParser p; p.feed(kRealisticMinified, strlen(kRealisticMinified));
EXPECT_TRUE(p.foundTag()); EXPECT_TRUE(p.foundFirmware()); EXPECT_STREQ(p.getTagName(), "v2.4.1"); EXPECT_STREQ(p.getFirmwareUrl(), "https://github.com/crosspoint-reader/crosspoint-reader/releases/download/v2.4.1/firmware.bin"); EXPECT_EQ(p.getFirmwareSize(), 1572864u);}
TEST(ReleaseJsonParser, PrettyAndMinifiedAgree) { ReleaseJsonParser pretty; pretty.feed(kRealisticPretty, strlen(kRealisticPretty));
ReleaseJsonParser minified; minified.feed(kRealisticMinified, strlen(kRealisticMinified));
ASSERT_TRUE(pretty.foundTag()); ASSERT_TRUE(pretty.foundFirmware()); ASSERT_TRUE(minified.foundTag()); ASSERT_TRUE(minified.foundFirmware());
EXPECT_STREQ(pretty.getTagName(), minified.getTagName()); EXPECT_STREQ(pretty.getFirmwareUrl(), minified.getFirmwareUrl()); EXPECT_EQ(pretty.getFirmwareSize(), minified.getFirmwareSize());}
TEST(ReleaseJsonParser, FirmwareNotFirstAsset) { const char* json = R"({ "tag_name": "v1.0.0", "assets": [ {"name": "source.tar.gz", "browser_download_url": "https://example.com/src.tar.gz", "size": 500000}, {"name": "docs.pdf", "browser_download_url": "https://example.com/docs.pdf", "size": 120000}, {"name": "firmware.bin", "browser_download_url": "https://example.com/firmware.bin", "size": 987654}, {"name": "checksums.txt", "browser_download_url": "https://example.com/checksums.txt", "size": 256} ] })";
ReleaseJsonParser p; p.feed(json, strlen(json));
EXPECT_TRUE(p.foundTag()); EXPECT_TRUE(p.foundFirmware()); EXPECT_STREQ(p.getTagName(), "v1.0.0"); EXPECT_STREQ(p.getFirmwareUrl(), "https://example.com/firmware.bin"); EXPECT_EQ(p.getFirmwareSize(), 987654u);}
TEST(ReleaseJsonParser, FieldOrderUrlBeforeName) { const char* json = R"({ "tag_name": "v3.0", "assets": [{ "browser_download_url": "https://example.com/fw.bin", "name": "firmware.bin", "size": 2222 }] })";
ReleaseJsonParser p; p.feed(json, strlen(json));
EXPECT_TRUE(p.foundFirmware()); EXPECT_STREQ(p.getFirmwareUrl(), "https://example.com/fw.bin"); EXPECT_EQ(p.getFirmwareSize(), 2222u);}
TEST(ReleaseJsonParser, FieldOrderSizeBeforeUrl) { const char* json = R"({ "tag_name": "v3.1", "assets": [{ "size": 3333, "browser_download_url": "https://example.com/fw2.bin", "name": "firmware.bin" }] })";
ReleaseJsonParser p; p.feed(json, strlen(json));
EXPECT_TRUE(p.foundFirmware()); EXPECT_STREQ(p.getFirmwareUrl(), "https://example.com/fw2.bin"); EXPECT_EQ(p.getFirmwareSize(), 3333u);}
TEST(ReleaseJsonParser, FieldOrderNameFirst) { const char* json = R"({ "tag_name": "v3.2", "assets": [{ "name": "firmware.bin", "size": 4444, "browser_download_url": "https://example.com/fw3.bin" }] })";
ReleaseJsonParser p; p.feed(json, strlen(json));
EXPECT_TRUE(p.foundFirmware()); EXPECT_STREQ(p.getFirmwareUrl(), "https://example.com/fw3.bin"); EXPECT_EQ(p.getFirmwareSize(), 4444u);}
TEST(ReleaseJsonParser, AssetsBeforeTagName) { // tag_name appears after assets in the JSON const char* json = R"({ "name": "Release", "assets": [{ "name": "firmware.bin", "browser_download_url": "https://example.com/fw.bin", "size": 5555 }], "tag_name": "v4.0" })";
ReleaseJsonParser p; p.feed(json, strlen(json));
EXPECT_TRUE(p.foundTag()); EXPECT_TRUE(p.foundFirmware()); EXPECT_STREQ(p.getTagName(), "v4.0"); EXPECT_STREQ(p.getFirmwareUrl(), "https://example.com/fw.bin"); EXPECT_EQ(p.getFirmwareSize(), 5555u);}
TEST(ReleaseJsonParser, ChunkedFeedingSmallChunks) { ReleaseJsonParser p; feedChunked(p, kRealisticPretty, 64);
EXPECT_TRUE(p.foundTag()); EXPECT_TRUE(p.foundFirmware()); EXPECT_STREQ(p.getTagName(), "v2.4.1"); EXPECT_STREQ(p.getFirmwareUrl(), "https://github.com/crosspoint-reader/crosspoint-reader/releases/download/v2.4.1/firmware.bin"); EXPECT_EQ(p.getFirmwareSize(), 1572864u);}
TEST(ReleaseJsonParser, ChunkedFeedingByteByByte) { ReleaseJsonParser p; feedChunked(p, kRealisticMinified, 1);
EXPECT_TRUE(p.foundTag()); EXPECT_TRUE(p.foundFirmware()); EXPECT_STREQ(p.getTagName(), "v2.4.1"); EXPECT_EQ(p.getFirmwareSize(), 1572864u);}
TEST(ReleaseJsonParser, ChunkedFeedingVariousChunkSizes) { for (size_t chunkSize : {3u, 7u, 13u, 31u, 97u, 128u, 256u, 512u, 1024u}) { ReleaseJsonParser p; feedChunked(p, kRealisticPretty, chunkSize);
EXPECT_TRUE(p.foundTag()) << "chunkSize=" << chunkSize; EXPECT_TRUE(p.foundFirmware()) << "chunkSize=" << chunkSize; EXPECT_STREQ(p.getTagName(), "v2.4.1") << "chunkSize=" << chunkSize; EXPECT_STREQ(p.getFirmwareUrl(), "https://github.com/crosspoint-reader/crosspoint-reader/releases/download/v2.4.1/firmware.bin") << "chunkSize=" << chunkSize; EXPECT_EQ(p.getFirmwareSize(), 1572864u) << "chunkSize=" << chunkSize; }}
TEST(ReleaseJsonParser, MissingTagName) { const char* json = R"({ "name": "Some Release", "draft": false, "assets": [{ "name": "firmware.bin", "browser_download_url": "https://example.com/fw.bin", "size": 1000 }] })";
ReleaseJsonParser p; p.feed(json, strlen(json));
EXPECT_FALSE(p.foundTag()); EXPECT_TRUE(p.foundFirmware()); EXPECT_STREQ(p.getTagName(), "");}
TEST(ReleaseJsonParser, MissingFirmwareBinAsset) { const char* json = R"({ "tag_name": "v1.0.0", "assets": [ {"name": "source.zip", "browser_download_url": "https://example.com/src.zip", "size": 1000}, {"name": "docs.tar.gz", "browser_download_url": "https://example.com/docs.tar.gz", "size": 2000} ] })";
ReleaseJsonParser p; p.feed(json, strlen(json));
EXPECT_TRUE(p.foundTag()); EXPECT_FALSE(p.foundFirmware()); EXPECT_STREQ(p.getTagName(), "v1.0.0"); EXPECT_STREQ(p.getFirmwareUrl(), ""); EXPECT_EQ(p.getFirmwareSize(), 0u);}
TEST(ReleaseJsonParser, EmptyAssetsArray) { const char* json = R"({"tag_name": "v1.0.0", "assets": []})";
ReleaseJsonParser p; p.feed(json, strlen(json));
EXPECT_TRUE(p.foundTag()); EXPECT_FALSE(p.foundFirmware());}
TEST(ReleaseJsonParser, NoAssetsKey) { const char* json = R"({"tag_name": "v1.0.0", "name": "Release"})";
ReleaseJsonParser p; p.feed(json, strlen(json));
EXPECT_TRUE(p.foundTag()); EXPECT_FALSE(p.foundFirmware());}
TEST(ReleaseJsonParser, TruncatedBeforeTagValue) { const char* json = R"({"tag_name": )";
ReleaseJsonParser p; p.feed(json, strlen(json));
EXPECT_FALSE(p.foundTag()); EXPECT_FALSE(p.foundFirmware());}
TEST(ReleaseJsonParser, TruncatedInsideTagValue) { const char* json = R"({"tag_name": "v2.4)";
ReleaseJsonParser p; p.feed(json, strlen(json));
EXPECT_FALSE(p.foundTag());}
TEST(ReleaseJsonParser, TruncatedInsideAssetsArray) { const char* json = R"({"tag_name": "v2.4.1", "assets": [{"name": "firm)";
ReleaseJsonParser p; p.feed(json, strlen(json));
EXPECT_TRUE(p.foundTag()); EXPECT_STREQ(p.getTagName(), "v2.4.1"); EXPECT_FALSE(p.foundFirmware());}
TEST(ReleaseJsonParser, TruncatedAfterFirmwareName) { // Found the name but connection dropped before URL/size const char* json = R"({"tag_name":"v1.0","assets":[{"name":"firmware.bin","browser_dow)";
ReleaseJsonParser p; p.feed(json, strlen(json));
EXPECT_TRUE(p.foundTag()); EXPECT_FALSE(p.foundFirmware());}
TEST(ReleaseJsonParser, TruncatedRealisticJson) { std::string full(kRealisticPretty); for (size_t cutPoint : {10u, 50u, 100u, 200u, 500u, 1000u, 1500u, 2000u}) { if (cutPoint >= full.size()) continue;
ReleaseJsonParser p; p.feed(full.c_str(), cutPoint); (void)p.foundTag(); (void)p.foundFirmware(); } SUCCEED();}
TEST(ReleaseJsonParser, NestedObjectsInAsset) { // Asset with deeply nested "uploader" object -- should not confuse depth tracking const char* json = R"({ "tag_name": "v5.0", "assets": [{ "name": "firmware.bin", "uploader": { "login": "bot", "id": 42, "permissions": {"admin": false, "push": true, "pull": true} }, "browser_download_url": "https://example.com/fw5.bin", "size": 8888 }] })";
ReleaseJsonParser p; p.feed(json, strlen(json));
EXPECT_TRUE(p.foundFirmware()); EXPECT_STREQ(p.getFirmwareUrl(), "https://example.com/fw5.bin"); EXPECT_EQ(p.getFirmwareSize(), 8888u);}
TEST(ReleaseJsonParser, NestedObjectsAtTopLevel) { // Multiple nested objects at the top level before/after tag_name and assets const char* json = R"({ "author": {"login": "dev", "id": 1, "nested": {"deep": true}}, "tag_name": "v6.0", "reactions": {"url": "https://reactions", "total_count": 0, "+1": 0}, "assets": [{"name": "firmware.bin", "browser_download_url": "https://fw6", "size": 1111}], "mentions_count": 3 })";
ReleaseJsonParser p; p.feed(json, strlen(json));
EXPECT_TRUE(p.foundTag()); EXPECT_TRUE(p.foundFirmware()); EXPECT_STREQ(p.getTagName(), "v6.0"); EXPECT_EQ(p.getFirmwareSize(), 1111u);}
TEST(ReleaseJsonParser, ArraysAtTopLevel) { // A non-assets array at the top level should not interfere const char* json = R"({ "tag_name": "v7.0", "labels": ["release", "stable"], "assets": [{"name": "firmware.bin", "browser_download_url": "https://fw7", "size": 7070}] })";
ReleaseJsonParser p; p.feed(json, strlen(json));
EXPECT_TRUE(p.foundTag()); EXPECT_TRUE(p.foundFirmware()); EXPECT_STREQ(p.getTagName(), "v7.0"); EXPECT_EQ(p.getFirmwareSize(), 7070u);}
TEST(ReleaseJsonParser, ResetAndReuse) { ReleaseJsonParser p;
const char* json1 = R"({"tag_name":"v1.0","assets":[{"name":"firmware.bin","browser_download_url":"https://a","size":1}]})"; p.feed(json1, strlen(json1)); EXPECT_TRUE(p.foundTag()); EXPECT_STREQ(p.getTagName(), "v1.0"); EXPECT_STREQ(p.getFirmwareUrl(), "https://a"); EXPECT_EQ(p.getFirmwareSize(), 1u);
p.reset();
const char* json2 = R"({"tag_name":"v2.0","assets":[{"name":"firmware.bin","browser_download_url":"https://b","size":2}]})"; p.feed(json2, strlen(json2)); EXPECT_TRUE(p.foundTag()); EXPECT_STREQ(p.getTagName(), "v2.0"); EXPECT_STREQ(p.getFirmwareUrl(), "https://b"); EXPECT_EQ(p.getFirmwareSize(), 2u);}
TEST(ReleaseJsonParser, ResetClearsState) { ReleaseJsonParser p;
const char* json = R"({"tag_name":"v1.0","assets":[{"name":"firmware.bin","browser_download_url":"https://a","size":100}]})"; p.feed(json, strlen(json)); EXPECT_TRUE(p.foundTag()); EXPECT_TRUE(p.foundFirmware());
p.reset();
EXPECT_FALSE(p.foundTag()); EXPECT_FALSE(p.foundFirmware()); EXPECT_STREQ(p.getTagName(), ""); EXPECT_STREQ(p.getFirmwareUrl(), ""); EXPECT_EQ(p.getFirmwareSize(), 0u);}
TEST(ReleaseJsonParser, PartialAssetNameMatch) { // "firmware.bin.bak" should NOT match "firmware.bin" const char* json = R"({ "tag_name": "v1.0", "assets": [ {"name": "firmware.bin.bak", "browser_download_url": "https://bak", "size": 100}, {"name": "firmware.bin.old", "browser_download_url": "https://old", "size": 200} ] })";
ReleaseJsonParser p; p.feed(json, strlen(json));
EXPECT_TRUE(p.foundTag()); EXPECT_FALSE(p.foundFirmware());}
TEST(ReleaseJsonParser, FirmwareBinExactMatch) { // Only exact "firmware.bin" matches, not similar names const char* json = R"({ "tag_name": "v1.0", "assets": [ {"name": "FIRMWARE.BIN", "browser_download_url": "https://upper", "size": 100}, {"name": "firmware.bin", "browser_download_url": "https://exact", "size": 200}, {"name": "firmware.bin2", "browser_download_url": "https://suffix", "size": 300} ] })";
ReleaseJsonParser p; p.feed(json, strlen(json));
EXPECT_TRUE(p.foundFirmware()); EXPECT_STREQ(p.getFirmwareUrl(), "https://exact"); EXPECT_EQ(p.getFirmwareSize(), 200u);}
TEST(ReleaseJsonParser, ConfiguredFirmwareNameMatch) { const char* json = R"({ "tag_name": "1.6.1", "assets": [ {"name": "firmware-x4pro.bin", "browser_download_url": "https://legacy", "size": 100}, {"name": "crosspoint-1.6.1-sticky.bin", "browser_download_url": "https://sticky", "size": 200}, {"name": "crosspoint-1.6.1-x4pro.bin", "browser_download_url": "https://x4pro", "size": 300} ] })";
ReleaseJsonParser p; p.setFirmwareAssetName("crosspoint-1.6.1-x4pro.bin"); p.feed(json, strlen(json));
EXPECT_TRUE(p.foundFirmware()); EXPECT_STREQ(p.getFirmwareUrl(), "https://x4pro"); EXPECT_EQ(p.getFirmwareSize(), 300u);}
TEST(ReleaseJsonParser, ConfiguredFirmwareNameIsNotOverwrittenBySameSuffix) { const char* json = R"({ "tag_name": "1.6.1", "assets": [ {"name": "crosspoint-1.6.1-x4pro.bin", "browser_download_url": "https://correct", "size": 100}, {"name": "crosspoint-1.6.0-x4pro.bin", "browser_download_url": "https://stale", "size": 200} ] })";
ReleaseJsonParser p; p.setFirmwareAssetName("crosspoint-1.6.1-x4pro.bin"); p.feed(json, strlen(json));
EXPECT_TRUE(p.foundFirmware()); EXPECT_STREQ(p.getFirmwareUrl(), "https://correct"); EXPECT_EQ(p.getFirmwareSize(), 100u);}
TEST(ReleaseJsonParser, ConfiguredFirmwareNameRejectsDifferentVersion) { const char* json = R"({ "tag_name": "1.6.1", "assets": [ {"name": "crosspoint-1.6.0-x4pro.bin", "browser_download_url": "https://stale", "size": 200} ] })";
ReleaseJsonParser p; p.setFirmwareAssetName("crosspoint-1.6.1-x4pro.bin"); p.feed(json, strlen(json));
EXPECT_FALSE(p.foundFirmware());}
TEST(ReleaseJsonParser, LargeSize) { // 16MB firmware (maximum flash size) const char* json = R"({"tag_name":"v1.0","assets":[{"name":"firmware.bin","browser_download_url":"https://fw","size":16777216}]})";
ReleaseJsonParser p; p.feed(json, strlen(json));
EXPECT_EQ(p.getFirmwareSize(), 16777216u);}
TEST(ReleaseJsonParser, SizeZero) { const char* json = R"({"tag_name":"v1.0","assets":[{"name":"firmware.bin","browser_download_url":"https://fw","size":0}]})";
ReleaseJsonParser p; p.feed(json, strlen(json));
EXPECT_TRUE(p.foundFirmware()); EXPECT_EQ(p.getFirmwareSize(), 0u);}
TEST(ReleaseJsonParser, MinimalValidJson) { const char* json = R"({"tag_name":"v0","assets":[{"name":"firmware.bin","browser_download_url":"u","size":1}]})";
ReleaseJsonParser p; p.feed(json, strlen(json));
EXPECT_TRUE(p.foundTag()); EXPECT_TRUE(p.foundFirmware()); EXPECT_STREQ(p.getTagName(), "v0"); EXPECT_STREQ(p.getFirmwareUrl(), "u"); EXPECT_EQ(p.getFirmwareSize(), 1u);}
TEST(ReleaseJsonParser, ChunkedRealisticEveryBoundary) { // Two-chunk split at every byte boundary on a compact JSON const char* json = R"({"tag_name":"v2.0","assets":[{"name":"firmware.bin","browser_download_url":"https://example.com/fw","size":9999}]})"; size_t len = strlen(json);
for (size_t split = 0; split <= len; ++split) { ReleaseJsonParser p; if (split > 0) p.feed(json, split); if (split < len) p.feed(json + split, len - split);
EXPECT_TRUE(p.foundTag()) << "split=" << split; EXPECT_TRUE(p.foundFirmware()) << "split=" << split; EXPECT_STREQ(p.getTagName(), "v2.0") << "split=" << split; EXPECT_STREQ(p.getFirmwareUrl(), "https://example.com/fw") << "split=" << split; EXPECT_EQ(p.getFirmwareSize(), 9999u) << "split=" << split; }}