Implement http-module (#637)

Create http-module and implement it in App Hub and App Hub Details apps.
This commit is contained in:
Ken Van Hoeylandt
2026-08-28 23:09:59 +02:00
committed by GitHub
parent d2442bedb4
commit bcbf18e363
27 changed files with 1265 additions and 77 deletions
+16
View File
@@ -0,0 +1,16 @@
project(HttpModuleTests)
enable_language(C CXX ASM)
file(GLOB_RECURSE TEST_SOURCES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/source/*.cpp)
add_executable(HttpModuleTests EXCLUDE_FROM_ALL ${TEST_SOURCES})
target_include_directories(HttpModuleTests PRIVATE ${DOCTESTINC})
add_test(NAME HttpModuleTests COMMAND HttpModuleTests)
target_link_libraries(HttpModuleTests PUBLIC
http-module
TactilityKernel
freertos_kernel
)
@@ -0,0 +1,90 @@
#include "doctest.h"
#include <http/download.h>
TEST_CASE("http_download_start requires a subscribed subscription") {
HttpDownloadSubscription sub {};
CHECK_EQ(http_download_start("http://example.com/file", "/cert.pem", "/target.bin", &sub), ERROR_INVALID_ARGUMENT);
}
TEST_CASE("http_download_poll times out before the download finishes") {
TaskEventGroup event_group {};
task_event_group_construct(&event_group);
HttpDownloadSubscription sub {};
CHECK_EQ(http_download_subscribe(&sub, &event_group), ERROR_NONE);
HttpDownloadEvent event {};
CHECK_EQ(http_download_poll(&sub, &event), ERROR_TIMEOUT);
http_download_unsubscribe(&sub);
task_event_group_destruct(&event_group);
}
TEST_CASE("http_download_start on this (non-ESP-IDF) platform always fails the download") {
TaskEventGroup event_group {};
task_event_group_construct(&event_group);
HttpDownloadSubscription sub {};
CHECK_EQ(http_download_subscribe(&sub, &event_group), ERROR_NONE);
CHECK_EQ(http_download_start("http://example.com/file", "/cert.pem", "/target.bin", &sub), ERROR_NONE);
CHECK_EQ(task_event_group_wait(&event_group, sub.bit, false, nullptr, pdMS_TO_TICKS(2000)), ERROR_NONE);
HttpDownloadEvent event {};
CHECK_EQ(http_download_poll(&sub, &event), ERROR_NONE);
CHECK_EQ(event.type, HTTP_DOWNLOAD_EVENT_ERROR);
http_download_unsubscribe(&sub);
task_event_group_destruct(&event_group);
}
TEST_CASE("http_download_unsubscribe reports ERROR_NOT_FOUND when not subscribed") {
HttpDownloadSubscription sub {};
CHECK_EQ(http_download_unsubscribe(&sub), ERROR_NOT_FOUND);
}
TEST_CASE("http_download_cancel reports ERROR_INVALID_STATE when not subscribed") {
HttpDownloadSubscription sub {};
CHECK_EQ(http_download_cancel(&sub), ERROR_INVALID_STATE);
}
TEST_CASE("http_download_unsubscribe is safe to call right after start, without waiting for the download to finish") {
TaskEventGroup event_group {};
task_event_group_construct(&event_group);
HttpDownloadSubscription sub {};
CHECK_EQ(http_download_subscribe(&sub, &event_group), ERROR_NONE);
CHECK_EQ(http_download_start("http://example.com/file", "/cert.pem", "/target.bin", &sub), ERROR_NONE);
// No cancel, no poll for the terminal event - just unsubscribe and tear down immediately,
// racing whatever the download task is doing.
CHECK_EQ(http_download_unsubscribe(&sub), ERROR_NONE);
CHECK_EQ(http_download_unsubscribe(&sub), ERROR_NOT_FOUND);
// The bit is free again immediately, even though the download task may still be running.
uint32_t reclaimed_bit;
CHECK_EQ(task_event_group_claim_bit(&event_group, &reclaimed_bit), ERROR_NONE);
task_event_group_release_bit(&event_group, reclaimed_bit);
task_event_group_destruct(&event_group);
}
TEST_CASE("http_download_cancel before start makes the download finish as cancelled") {
TaskEventGroup event_group {};
task_event_group_construct(&event_group);
HttpDownloadSubscription sub {};
CHECK_EQ(http_download_subscribe(&sub, &event_group), ERROR_NONE);
CHECK_EQ(http_download_cancel(&sub), ERROR_NONE);
CHECK_EQ(http_download_start("http://example.com/file", "/cert.pem", "/target.bin", &sub), ERROR_NONE);
CHECK_EQ(task_event_group_wait(&event_group, sub.bit, false, nullptr, pdMS_TO_TICKS(2000)), ERROR_NONE);
HttpDownloadEvent event {};
CHECK_EQ(http_download_poll(&sub, &event), ERROR_NONE);
CHECK_EQ(event.type, HTTP_DOWNLOAD_EVENT_CANCELLED);
http_download_unsubscribe(&sub);
task_event_group_destruct(&event_group);
}
+64
View File
@@ -0,0 +1,64 @@
#define DOCTEST_CONFIG_IMPLEMENT
#include "doctest.h"
#include <cstdio>
#include <cstdlib>
#include "FreeRTOS.h"
#include "task.h"
typedef struct {
int argc;
char** argv;
int result;
} TestTaskData;
void test_task(void* parameter) {
auto* data = (TestTaskData*)parameter;
doctest::Context context;
context.applyCommandLine(data->argc, data->argv);
// overrides
context.setOption("no-breaks", true); // don't break in the debugger when assertions fail
data->result = context.run();
vTaskEndScheduler();
vTaskDelete(nullptr);
}
int main(int argc, char** argv) {
TestTaskData data = {
.argc = argc,
.argv = argv,
.result = 0
};
BaseType_t task_result = xTaskCreate(
test_task,
"test_task",
8192,
&data,
1,
nullptr
);
if (task_result != pdPASS) {
return 1;
}
vTaskStartScheduler();
return data.result;
}
// NOTE: This is normally provided by the platform kernel module, but that's not loaded for http-module
extern "C" {
// Required for FreeRTOS
void vAssertCalled(unsigned long line, const char* const file) {
std::fprintf(stderr, "assert failed at %s:%lu\n", file, line);
std::abort();
}
}