Add tests for some utils

This commit is contained in:
2022-12-10 01:33:16 +01:00
parent 933aac0f09
commit 0f75aeea7a
10 changed files with 248 additions and 52 deletions

2
.gitignore vendored
View File

@ -3,6 +3,8 @@
!src/**
!doc
!doc/**
!test
!test/**
!Makefile
!.gitignore
!README.md

View File

@ -5,7 +5,7 @@ LIBS=-lssl -lcrypto -lmagic -lz -lmaxminddb -lbrotlienc
DEBIAN_OPTS=-D CACHE_MAGIC_FILE="\"/usr/share/file/magic.mgc\"" -D PHP_FPM_SOCKET="\"/var/run/php/php7.4-fpm.sock\""
.PHONY: all prod debug default permit clean
.PHONY: all prod debug default permit clean test
all: prod
default: bin bin/lib bin/libsesimos.so bin/sesimos
prod: CFLAGS += -O3
@ -14,6 +14,10 @@ debug: default
debian: CFLAGS += $(DEBIAN_OPTS)
debian: prod
test: CFLAGS += -include test/mock_*.h
test: bin bin/test
bin/test
bin:
mkdir -p bin
@ -22,6 +26,10 @@ bin/lib:
mkdir -p bin/lib
bin/test: test/mock_*.c test/test_*.c src/lib/utils.c src/lib/sock.c
$(CC) -o $@ $(CFLAGS) $^ -lcriterion
bin/%.o: src/%.c
$(CC) -c -o $@ $(CFLAGS) $<

View File

@ -179,8 +179,7 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
int change_proto = strncmp(uri.uri, "/.well-known/", 13) != 0 && !client->enc;
if (strcmp(uri.uri, buf0) != 0 || change_proto) {
res.status = http_get_status(308);
size = sizeof(buf0);
url_encode(uri.uri, buf0, &size);
size = url_encode(uri.uri, strlen(uri.uri), buf0, sizeof(buf0));
if (change_proto) {
p_len = snprintf(buf1, sizeof(buf1), "https://%s%s", host, buf0);
if (p_len < 0 || p_len >= sizeof(buf1)) {

View File

@ -15,69 +15,77 @@
char *log_prefix;
static const char base64_encode_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const int base64_mod_table[3] = {0, 2, 1};
char *format_duration(unsigned long micros, char *buf) {
if (micros < 10000) {
sprintf(buf, "%.1f ms", (double) micros / 1000);
} else if (micros < 1000000) {
sprintf(buf, "%li ms", micros / 1000);
} else if (micros < 60000000) {
} else if (micros < 1000000 - 1000) {
sprintf(buf, "%.0f ms", (double) micros / 1000);
} else if (micros < 60000000 - 1000000) {
sprintf(buf, "%.1f s", (double) micros / 1000000);
} else if (micros < 6000000000) {
sprintf(buf, "%.1f min", (double) micros / 1000000 / 60);
} else {
sprintf(buf, "%li min", micros / 1000000 / 60);
sprintf(buf, "%.0f min", (double) micros / 1000000 / 60);
}
return buf;
}
int url_encode_component(const char *str, char *enc, long *size) {
char *ptr = enc;
char ch;
memset(enc, 0, *size);
for (int i = 0; i < strlen(str); i++, ptr++) {
if ((ptr - enc) >= *size) {
return -1;
}
ch = str[i];
if (ch == ':' || ch == '/' || ch == '?' || ch == '#' || ch == '[' || ch == ']' || ch == '@' || ch == '!' ||
ch == '$' || ch == '&' || ch == '\'' || ch == '(' || ch == ')' || ch == '*' || ch == '+' || ch == ',' ||
ch == ';' || ch == '=' || ch < ' ' || ch > '~') {
if ((ptr - enc + 2) >= *size) {
return -1;
}
sprintf(ptr, "%%%02X", ch);
ptr += 2;
} else if (ch == ' ') {
ptr[0] = '+';
} else {
ptr[0] = ch;
}
}
*size = ptr - enc;
return 0;
int url_encode_component(const void *in, size_t size_in, char *out, size_t size_out) {
int size = 0;
// Encode control characters
for (int i = 0; i < size_in; i++) {
unsigned char ch = ((unsigned char *) in)[i];
if (ch == ' ') {
ch = '+';
} else if (
ch <= 0x20 || ch >= 0x7F ||
!((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') ||
ch == '-' || ch == '_' || ch == '.' || ch == '!' || ch == '~' || ch == '*' || ch == '\'' ||
ch == '(' || ch == ')')
) {
size += 3;
if (size < size_out) sprintf(out + size - 3, "%%%02X", ch);
ch = 0;
}
int url_encode(const char *str, char *enc, long *size) {
char *ptr = enc;
unsigned char ch;
memset(enc, 0, *size);
for (int i = 0; i < strlen(str); i++, ptr++) {
if ((ptr - enc) >= *size) {
return -1;
if (ch != 0) {
size++;
if (size < size_out) out[size - 1] = (char) ch;
}
ch = str[i];
if (ch > 0x7F || ch == ' ') {
if ((ptr - enc + 2) >= *size) {
return -1;
}
sprintf(ptr, "%%%02X", ch);
ptr += 2;
// Set terminating null byte
if (size_out > 0) out[size < size_out ? size : size_out - 1] = 0;
// Return theoretical size
return size;
}
int url_encode(const void *in, size_t size_in, char *out, size_t size_out) {
int size = 0;
// Encode control characters
for (int i = 0; i < size_in; i++) {
unsigned char ch = ((unsigned char *) in)[i];
if (ch <= 0x20 || ch >= 0x7F) {
size += 3;
if (size < size_out) sprintf(out + size - 3, "%%%02X", ch);
} else {
ptr[0] = (char) ch;
size++;
if (size < size_out) out[size - 1] = (char) ch;
}
}
*size = ptr - enc;
return 0;
// Set terminating null byte
if (size_out > 0) out[size < size_out ? size : size_out - 1] = 0;
// Return theoretical size
return size;
}
int url_decode(const char *str, char *dec, long *size) {

View File

@ -21,9 +21,6 @@
extern char *log_prefix;
static const char base64_encode_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const int base64_mod_table[3] = {0, 2, 1};
#define out_1(fmt) fprintf(stdout, "%s" fmt "\n", log_prefix)
#define out_2(fmt, args...) fprintf(stdout, "%s" fmt "\n", log_prefix, args)
@ -37,9 +34,9 @@ static const int base64_mod_table[3] = {0, 2, 1};
char *format_duration(unsigned long micros, char *buf);
int url_encode_component(const char *str, char *enc, long *size);
int url_encode_component(const void *in, size_t size_in, char *out, size_t size_out);
int url_encode(const char *str, char *enc, long *size);
int url_encode(const void *in, size_t size_in, char *out, size_t size_out);
int url_decode(const char *str, char *dec, long *size);

28
test/mock_socket.c Normal file
View File

@ -0,0 +1,28 @@
#include <stdio.h>
#include <errno.h>
#include "mock_socket.h"
int mock_socket_send_mode;
static int sockets[256] = {0};
static int n_sockets = 0;
int mock_socket(int domain, int type, int protocol) {
printf("SOCKET\n");
return (n_sockets++) + 100;
}
ssize_t mock_send(int fd, const void *buf, size_t n, int flags) {
printf("SEND\n");
if (mock_socket_send_mode == MOCK_SOCKET_MODE_EINTR) {
errno = EINTR;
return rand() % ((ssize_t) n) - 1;
} else if (mock_socket_send_mode == MOCK_SOCKET_MODE_CLOSED) {
errno = 0; // TODO
return -1;
}
return (ssize_t) n;
}

20
test/mock_socket.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef SESIMOS_MOCK_SOCKET_H
#define SESIMOS_MOCK_SOCKET_H
#include <stdlib.h>
#define MOCK_SOCKET_MODE_SUCCESS 0
#define MOCK_SOCKET_MODE_EINTR 1
#define MOCK_SOCKET_MODE_CLOSED 2
#define socket(args...) mock_socket(args)
#define send(args...) mock_send(args)
extern int mock_socket_send_mode;
int mock_socket(int domain, int type, int protocol);
ssize_t mock_send(int fd, const void *buf, size_t n, int flags);
#endif //SESIMOS_MOCK_SOCKET_H

33
test/mock_ssl.c Normal file
View File

@ -0,0 +1,33 @@
#include <openssl/crypto.h>
int SSL_write(SSL *ssl, const void *buf, int num) {
return num;
}
int SSL_read(SSL *ssl, void *buf, int num) {
return num;
}
int SSL_peek(SSL *ssl, void *buf, int num) {
return num;
}
int SSL_get_error(const SSL *s, int ret_code) {
return 0;
}
const char *ERR_reason_error_string(unsigned long e) {
return "";
}
int SSL_shutdown(SSL *s) {
return 0;
}
void SSL_free(SSL *ssl) {}
unsigned long ERR_get_error(void) {
return 0;
}

15
test/test_sock.c Normal file
View File

@ -0,0 +1,15 @@
#include <criterion/criterion.h>
#include "mock_socket.h"
#include "../src/lib/sock.h"
Test(sock, sock_send_1) {
int fd = socket(AF_INET6, SOCK_STREAM, 0);
sock s;
s.enc = 0;
s.socket = fd;
long ret = sock_send(&s, "Hello", 5, 0);
cr_assert_eq(ret, 5);
}

86
test/test_utils.c Normal file
View File

@ -0,0 +1,86 @@
#include <criterion/criterion.h>
#include <criterion/parameterized.h>
#include "../src/lib/utils.h"
struct url_encode_t {
long in_size;
char in[256];
long exp_size;
char exp[256];
};
struct format_duration_t {
unsigned long micros;
char exp[16];
};
ParameterizedTestParameters(utils, url_encode) {
static struct url_encode_t params[] = {
{0, "", 0, ""},
{9, "Test Text", 11, "Test%20Text"},
{21, "Text\0with\0null\0bytes\0", 29, "Text%00with%00null%00bytes%00"},
{59, "Text&with+some/strange_symbols-or#something?I%don't|know...", 59, "Text&with+some/strange_symbols-or#something?I%don't|know..."},
{33, "Data\x12With\x13Some" "\xFF" "Control" "\xFE" "Characters", 41, "Data%12With%13Some%FFControl%FECharacters"}
};
return cr_make_param_array(struct url_encode_t, params, sizeof(params) / sizeof(struct url_encode_t));
}
ParameterizedTest(struct url_encode_t *param, utils, url_encode) {
char out[256];
cr_assert_eq(url_encode(param->in, param->in_size, out, sizeof(out)), param->exp_size);
cr_assert_arr_eq(out, param->exp, param->exp_size + 1);
}
Test(utils, url_encode_bytes) {
char out[4];
char exp[4];
for (int i = 0; i < 256; i++) {
unsigned char ch = i;
if (ch <= 0x20 || ch >= 0x7F) {
cr_assert_eq(url_encode(&ch, 1, out, sizeof(out)), 3);
sprintf(exp, "%%%02X", ch);
cr_assert_str_eq(out, exp);
} else {
cr_assert_eq(url_encode(&ch, 1, out, sizeof(out)), 1);
sprintf(exp, "%c", ch);
cr_assert_str_eq(out, exp);
}
}
}
Test(utils, url_encode_invalid) {
cr_assert_eq(url_encode("Hello", 5, NULL, 0), 5);
}
ParameterizedTestParameters(utils, format_duration) {
static struct format_duration_t params[] = {
{0, "0.0 ms"},
{1, "0.0 ms"},
{90, "0.1 ms"},
{100, "0.1 ms"},
{110, "0.1 ms"},
{900, "0.9 ms"},
{1000, "1.0 ms"},
{9000, "9.0 ms"},
{9899, "9.9 ms"},
{9999, "10.0 ms"},
{10000, "10 ms"},
{11999, "12 ms"},
{999999, "1.0 s"},
{1000000, "1.0 s"},
{3000000, "3.0 s"},
{1000000 * 60, "1.0 min"},
{1000000 * 60 * 30L - 30000000, "29.5 min"},
{1000000 * 60 * 60L, "60.0 min"},
{1000000 * 60 * 120L, "120 min"},
};
return cr_make_param_array(struct format_duration_t, params, sizeof(params) / sizeof(struct format_duration_t));
}
ParameterizedTest(struct format_duration_t *param, utils, format_duration) {
char buf[16];
cr_assert_str_eq(format_duration(param->micros, buf), param->exp);
}