Add engine tests

This commit is contained in:
2025-05-12 22:23:33 +02:00
parent 2d4f062917
commit f725e63fb0
10 changed files with 638 additions and 182 deletions
+73 -4
View File
@@ -1,9 +1,76 @@
#include <stdlib.h>
#include <string.h>
#include "../http.h"
#include "test.h"
#define TEST(X) {if (!(X)) __builtin_trap(); }
static const char *statestr(HTTP_EngineState state)
{
switch (state) {
case HTTP_ENGINE_STATE_NONE : return "NONE";
case HTTP_ENGINE_STATE_CLIENT_PREP_URL : return "CLIENT_PREP_URL";
case HTTP_ENGINE_STATE_CLIENT_PREP_HEADER : return "CLIENT_PREP_HEADER";
case HTTP_ENGINE_STATE_CLIENT_PREP_BODY_BUF: return "CLIENT_PREP_BODY_BUF";
case HTTP_ENGINE_STATE_CLIENT_PREP_BODY_ACK: return "CLIENT_PREP_BODY_ACK";
case HTTP_ENGINE_STATE_CLIENT_PREP_ERROR : return "CLIENT_PREP_ERROR";
case HTTP_ENGINE_STATE_CLIENT_SEND_BUF : return "CLIENT_SEND_BUF";
case HTTP_ENGINE_STATE_CLIENT_SEND_ACK : return "CLIENT_SEND_ACK";
case HTTP_ENGINE_STATE_CLIENT_RECV_BUF : return "CLIENT_RECV_BUF";
case HTTP_ENGINE_STATE_CLIENT_RECV_ACK : return "CLIENT_RECV_ACK";
case HTTP_ENGINE_STATE_CLIENT_READY : return "CLIENT_READY";
case HTTP_ENGINE_STATE_CLIENT_CLOSED : return "CLIENT_CLOSED";
case HTTP_ENGINE_STATE_SERVER_RECV_BUF : return "SERVER_RECV_BUF";
case HTTP_ENGINE_STATE_SERVER_RECV_ACK : return "SERVER_RECV_ACK";
case HTTP_ENGINE_STATE_SERVER_PREP_STATUS : return "SERVER_PREP_STATUS";
case HTTP_ENGINE_STATE_SERVER_PREP_HEADER : return "SERVER_PREP_HEADER";
case HTTP_ENGINE_STATE_SERVER_PREP_BODY_BUF: return "SERVER_PREP_BODY_BUF";
case HTTP_ENGINE_STATE_SERVER_PREP_BODY_ACK: return "SERVER_PREP_BODY_ACK";
case HTTP_ENGINE_STATE_SERVER_PREP_ERROR : return "SERVER_PREP_ERROR";
case HTTP_ENGINE_STATE_SERVER_SEND_BUF : return "SERVER_SEND_BUF";
case HTTP_ENGINE_STATE_SERVER_SEND_ACK : return "SERVER_SEND_ACK";
case HTTP_ENGINE_STATE_SERVER_CLOSED : return "SERVER_CLOSED";
}
return "???";
}
void test_branch_coverage(void);
void testeq_engstate(HTTP_EngineState l, HTTP_EngineState r, HTTP_String uneval_l, HTTP_String uneval_r, const char *file, int line)
{
if (l != r) {
printf("Test failed at %s:%d\n", file, line);
printf(" TEST_EQ(%.*s, %.*s) -> TEST_EQ(%s, %s)\n",
(int) uneval_l.len, uneval_l.ptr,
(int) uneval_r.len, uneval_r.ptr,
statestr(l), statestr(r));
abort();
}
}
void testeq_int(int l, int r, HTTP_String uneval_l, HTTP_String uneval_r, const char *file, int line)
{
if (l != r) {
printf("Test failed at %s:%d\n", file, line);
printf(" TEST_EQ(%.*s, %.*s) -> TEST_EQ(%d, %d)\n",
(int) uneval_l.len, uneval_l.ptr,
(int) uneval_r.len, uneval_r.ptr,
l, r);
abort();
}
}
void testeq_str(HTTP_String l, HTTP_String r, HTTP_String uneval_l, HTTP_String uneval_r, const char *file, int line)
{
if (!http_streq(l, r)) {
printf("Test failed at %s:%d\n", file, line);
printf(" TEST_EQ(\"%.*s\", \"%.*s\") -> TEST_EQ(%.*s, %.*s)\n",
(int) uneval_l.len, uneval_l.ptr,
(int) uneval_r.len, uneval_r.ptr,
(int) l.len, l.ptr,
(int) r.len, r.ptr);
abort();
}
}
void test_branch_coverage_parse(void);
void test_branch_coverage_engine(void);
void test_fuzz_engine(void);
int main(void)
{
@@ -16,7 +83,9 @@ int main(void)
TEST(ret == 0);
}
test_branch_coverage();
//test_branch_coverage_parse();
test_branch_coverage_engine();
//test_fuzz_engine();
return 0;
}
+12
View File
@@ -0,0 +1,12 @@
#include <stdio.h>
#include "../tinyhttp.h"
#define S HTTP_STR
#define COUNT(X) (int) (sizeof(X)/sizeof((X)[0]))
#define TEST(X) {if (!(X)) { fprintf(stderr, "Failed test at %s:%d\n", __FILE__, __LINE__); __builtin_trap(); }}
void testeq_engstate(HTTP_EngineState l, HTTP_EngineState r, HTTP_String uneval_l, HTTP_String uneval_r, const char *file, int line);
void testeq_int(int l, int r, HTTP_String uneval_l, HTTP_String uneval_r, const char *file, int line);
void testeq_str(HTTP_String l, HTTP_String r, HTTP_String uneval_l, HTTP_String uneval_r, const char *file, int line);
#define TEST_EQ(X, Y) _Generic((X), HTTP_String: testeq_str, int: testeq_int, HTTP_EngineState: testeq_engstate)((X), (Y), S(#X), S(#Y), __FILE__, __LINE__)
+138
View File
@@ -0,0 +1,138 @@
#include <stdlib.h>
#include <string.h>
#include "test.h"
static void *memfunc(HTTP_MemoryFuncTag tag,
void *ptr, int len, void *data)
{
(void) data;
switch (tag) {
case HTTP_MEMFUNC_MALLOC:
return malloc(len);
case HTTP_MEMFUNC_FREE:
free(ptr);
return NULL;
}
return NULL;
}
static void test_engine_server(void)
{
HTTP_Engine eng;
{
int client = 0;
memset(&eng, 0, sizeof(eng));
http_engine_init(&eng, client, memfunc, NULL);
TEST_EQ((int) http_engine_state(&eng), HTTP_ENGINE_STATE_SERVER_RECV_BUF);
http_engine_free(&eng);
}
{
int client = 1;
memset(&eng, 0, sizeof(eng));
http_engine_init(&eng, client, memfunc, NULL);
TEST_EQ((int) http_engine_state(&eng), HTTP_ENGINE_STATE_CLIENT_PREP_URL);
http_engine_free(&eng);
}
}
static int read_from_client_engine(HTTP_Engine *eng, char *dst, int cap)
{
TEST_EQ(http_engine_state(eng), HTTP_ENGINE_STATE_CLIENT_SEND_BUF);
int num = 0;
do {
int len;
char *src = http_engine_sendbuf(eng, &len);
TEST_EQ(http_engine_state(eng), HTTP_ENGINE_STATE_CLIENT_SEND_ACK);
TEST(src != NULL);
TEST(cap - num >= len);
memcpy(dst + num, src, len);
num += len;
http_engine_sendack(eng, len);
} while (http_engine_state(eng) == HTTP_ENGINE_STATEBIT_SEND_BUF);
return num;
}
static void send_into_client_engine(HTTP_Engine *eng, char *src, int len)
{
int num = 0;
while (num < len) {
TEST_EQ(http_engine_state(eng), HTTP_ENGINE_STATE_CLIENT_RECV_BUF);
int cap;
char *dst = http_engine_recvbuf(eng, &cap);
TEST_EQ(http_engine_state(eng), HTTP_ENGINE_STATE_CLIENT_RECV_ACK);
TEST(dst != NULL);
TEST(cap > 0);
int cpy = len - num;
if (cpy > cap) cpy = cap;
memcpy(dst, src + num, cpy);
num += cpy;
http_engine_recvack(eng, cpy);
}
}
static void test_engine_client_basic_flow(void)
{
int client = 1;
HTTP_Engine eng;
http_engine_init(&eng, client, memfunc, NULL);
TEST_EQ(http_engine_state(&eng), HTTP_ENGINE_STATE_CLIENT_PREP_URL);
http_engine_url(&eng, HTTP_METHOD_GET, "http://some.url.com/some/endpoint", 1);
TEST_EQ(http_engine_state(&eng), HTTP_ENGINE_STATE_CLIENT_PREP_HEADER);
http_engine_header(&eng, "headerA: valueA", -1);
TEST_EQ(http_engine_state(&eng), HTTP_ENGINE_STATE_CLIENT_PREP_HEADER);
http_engine_body(&eng, "Hello, world!", -1);
TEST_EQ(http_engine_state(&eng), HTTP_ENGINE_STATE_CLIENT_PREP_BODY_BUF);
http_engine_done(&eng);
TEST_EQ(http_engine_state(&eng), HTTP_ENGINE_STATE_CLIENT_SEND_BUF);
{
char sendbuf[1<<10];
int sendnum;
sendnum = read_from_client_engine(&eng, sendbuf, (int) sizeof(sendbuf));
char expect[] =
"GET /some/endpoint HTTP/1.1\r\n"
"Host: some.url.com\r\n"
"headerA: valueA\r\n"
"Connection: Keep-Alive\r\n"
"Content-Length: 13 \r\n"
"\r\n"
"Hello, world!";
TEST_EQ(sendnum, strlen(expect));
TEST_EQ(memcmp(expect, sendbuf, strlen(expect)), 0);
}
{
char response[] =
"HTTP/1.1 200 OK\r\n"
"\r\n";
send_into_client_engine(&eng, response, strlen(response));
}
TEST_EQ(http_engine_state(&eng), HTTP_ENGINE_STATE_CLIENT_READY);
http_engine_free(&eng);
}
void test_branch_coverage_engine(void)
{
test_engine_server();
test_engine_client_basic_flow();
}
@@ -1,37 +1,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../http.h"
#define COUNT(X) (int) (sizeof(X)/sizeof((X)[0]))
#define TEST(X) {if (!(X)) { fprintf(stderr, "Failed test at %s:%d\n", __FILE__, __LINE__); __builtin_trap(); }}
#define TEST_EQ(X, Y) _Generic((X), HTTP_String: testeq_str, int: testeq_int)((X), (Y), S(#X), S(#Y), __FILE__, __LINE__)
static void testeq_int(int l, int r, HTTP_String uneval_l, HTTP_String uneval_r, const char *file, int line)
{
if (l != r) {
printf("Test failed at %s:%d\n", file, line);
printf(" TEST_EQ(%.*s, %.*s) -> TEST_EQ(%d, %d)\n",
(int) uneval_l.len, uneval_l.ptr,
(int) uneval_r.len, uneval_r.ptr,
l, r);
abort();
}
}
static void testeq_str(HTTP_String l, HTTP_String r, HTTP_String uneval_l, HTTP_String uneval_r, const char *file, int line)
{
if (!http_streq(l, r)) {
printf("Test failed at %s:%d\n", file, line);
printf(" TEST_EQ(\"%.*s\", \"%.*s\") -> TEST_EQ(%.*s, %.*s)\n",
(int) uneval_l.len, uneval_l.ptr,
(int) uneval_r.len, uneval_r.ptr,
(int) l.len, l.ptr,
(int) r.len, r.ptr);
abort();
}
}
#include "test.h"
static void test_branch_coverage_parse_request(void)
{
@@ -314,8 +283,6 @@ static void test_branch_coverage_parse_response(void)
#include <arpa/inet.h>
#endif
#define S HTTP_STR
typedef struct {
char *buf;
int cap;
@@ -510,7 +477,7 @@ static void test_branch_coverage_parse_url(void)
}
}
void test_branch_coverage(void)
void test_branch_coverage_parse(void)
{
test_branch_coverage_parse_request();
test_branch_coverage_parse_response();
+230
View File
@@ -0,0 +1,230 @@
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include "test.h"
static void *memfunc(HTTP_MemoryFuncTag tag,
void *ptr, int len, void *data)
{
(void) data;
switch (tag) {
case HTTP_MEMFUNC_MALLOC:
{
void *ptr = NULL;
int x = rand() & 31;
if (x == 0)
ptr = malloc(len);
return ptr;
}
case HTTP_MEMFUNC_FREE:
free(ptr);
return NULL;
}
return NULL;
}
const char *http_statestr(int state)
{
switch (state) {
case HTTP_ENGINE_STATE_NONE : return "NONE";
case HTTP_ENGINE_STATE_CLIENT_PREP_URL : return "CLIENT_PREP_URL";
case HTTP_ENGINE_STATE_CLIENT_PREP_HEADER : return "CLIENT_PREP_HEADER";
case HTTP_ENGINE_STATE_CLIENT_PREP_BODY_BUF: return "CLIENT_PREP_BODY_BUF";
case HTTP_ENGINE_STATE_CLIENT_PREP_BODY_ACK: return "CLIENT_PREP_BODY_ACK";
case HTTP_ENGINE_STATE_CLIENT_PREP_ERROR : return "CLIENT_PREP_ERROR";
case HTTP_ENGINE_STATE_CLIENT_SEND_BUF : return "CLIENT_SEND_BUF";
case HTTP_ENGINE_STATE_CLIENT_SEND_ACK : return "CLIENT_SEND_ACK";
case HTTP_ENGINE_STATE_CLIENT_RECV_BUF : return "CLIENT_RECV_BUF";
case HTTP_ENGINE_STATE_CLIENT_RECV_ACK : return "CLIENT_RECV_ACK";
case HTTP_ENGINE_STATE_CLIENT_READY : return "CLIENT_READY";
case HTTP_ENGINE_STATE_CLIENT_CLOSED : return "CLIENT_CLOSED";
case HTTP_ENGINE_STATE_SERVER_RECV_BUF : return "SERVER_RECV_BUF";
case HTTP_ENGINE_STATE_SERVER_RECV_ACK : return "SERVER_RECV_ACK";
case HTTP_ENGINE_STATE_SERVER_PREP_STATUS : return "SERVER_PREP_STATUS";
case HTTP_ENGINE_STATE_SERVER_PREP_HEADER : return "SERVER_PREP_HEADER";
case HTTP_ENGINE_STATE_SERVER_PREP_BODY_BUF: return "SERVER_PREP_BODY_BUF";
case HTTP_ENGINE_STATE_SERVER_PREP_BODY_ACK: return "SERVER_PREP_BODY_ACK";
case HTTP_ENGINE_STATE_SERVER_PREP_ERROR : return "SERVER_PREP_ERROR";
case HTTP_ENGINE_STATE_SERVER_SEND_BUF : return "SERVER_SEND_BUF";
case HTTP_ENGINE_STATE_SERVER_SEND_ACK : return "SERVER_SEND_ACK";
case HTTP_ENGINE_STATE_SERVER_CLOSED : return "SERVER_CLOSED";
}
return "???";
}
static sig_atomic_t stop = 0;
static void handle_signal(int dummy)
{
stop = 1;
}
void test_fuzz_engine(void)
{
signal(SIGINT, handle_signal);
char txt1[] = "";
char txt2[] = "GET / HTTP/1.1\r\n\r\n";
char txt3[] = "GET / HTTP/1.1\n";
char txt4[] = "HTTP/1.1 200 OK\r\n";
char txt5[] = "HTTP/1.1 200 OK\r\n\r\n";
int next_recvack = 0;
int next_sendack = 0;
int next_bodyack = 0;
int curr_bodycap = 0;
int client = 0;
HTTP_Engine eng;
http_engine_init(&eng, client, memfunc, NULL);
while (!stop) {
//printf("%s\n", http_statestr(http_engine_state(&eng)));
switch (rand() % 21) {
int max;
char *buf;
case 0:
//printf("http_engine_free/http_engine_init\n");
client = !client;
http_engine_free(&eng);
http_engine_init(&eng, client, memfunc, NULL);
next_recvack = 0;
next_sendack = 0;
next_bodyack = 0;
curr_bodycap = 0;
break;
case 1:
//printf("http_engine_recvbuf (1)\n");
buf = http_engine_recvbuf(&eng, &max);
memcpy(buf, txt1, strlen(txt1));
next_recvack = strlen(txt1);
break;
case 2:
//printf("http_engine_recvbuf (2)\n");
buf = http_engine_recvbuf(&eng, &max);
if (buf) {
memcpy(buf, txt2, strlen(txt2));
next_recvack = strlen(txt2);
}
break;
case 3:
//printf("http_engine_recvbuf (3)\n");
buf = http_engine_recvbuf(&eng, &max);
if (buf) {
memcpy(buf, txt3, strlen(txt3));
next_recvack = strlen(txt3);
}
break;
case 4:
//printf("http_engine_recvbuf (4)\n");
buf = http_engine_recvbuf(&eng, &max);
if (buf) {
memcpy(buf, txt4, strlen(txt4));
next_recvack = strlen(txt4);
}
break;
case 5:
//printf("http_engine_recvbuf (5)\n");
buf = http_engine_recvbuf(&eng, &max);
if (buf) {
memcpy(buf, txt5, strlen(txt5));
next_recvack = strlen(txt5);
}
break;
case 6:
//printf("http_engine_recvack\n");
http_engine_recvack(&eng, next_recvack);
next_recvack = 0;
break;
case 7:
//printf("http_engine_sendbuf\n");
buf = http_engine_sendbuf(&eng, &max);
if (max)
next_sendack = rand() % max;
break;
case 8:
//printf("http_engine_sendack\n");
http_engine_sendack(&eng, next_sendack);
next_sendack = 0;
break;
case 9:
//printf("http_engine_getreq\n");
http_engine_getreq(&eng);
break;
case 10:
//printf("http_engine_getres\n");
http_engine_getres(&eng);
break;
case 11:
//printf("http_engine_url\n");
http_engine_url(&eng, HTTP_METHOD_GET, "", 0);
break;
case 12:
//printf("http_engine_url\n");
http_engine_url(&eng, HTTP_METHOD_GET, "http://127.0.0.1/hello", 0);
break;
case 13:
//printf("http_engine_status\n");
http_engine_status(&eng, 200);
break;
case 14:
//printf("http_engine_header\n");
http_engine_header(&eng, "x:y", -1);
break;
case 15:
//printf("http_engine_bodycap\n");
http_engine_bodycap(&eng, rand() % 1000);
break;
case 16:
//printf("http_engine_bodybuf\n");
buf = http_engine_bodybuf(&eng, &max);
if (buf && max) {
max = rand() % max;
memset(buf, 0xFF, max);
next_bodyack = max;
}
break;
case 17:
//printf("http_engine_bodyack\n");
http_engine_bodyack(&eng, next_bodyack);
next_bodyack = 0;
break;
case 18:
//printf("http_engine_done\n");
http_engine_done(&eng);
break;
case 19:
//printf("http_engine_undo\n");
http_engine_undo(&eng);
break;
case 20:
//printf("http_engine_close\n");
http_engine_close(&eng);
break;
}
}
http_engine_free(&eng);
}