diff --git a/.licenserc.yaml b/.licenserc.yaml index ba31cfd32..b7673e4fe 100644 --- a/.licenserc.yaml +++ b/.licenserc.yaml @@ -9,6 +9,7 @@ header: - '**/*.S' - '**/*.s' - '**/*.c' + - '**/*.h' - '**/*.sh' - '**/Makefile' - '**/Dockerfile.*' @@ -58,4 +59,3 @@ header: content: | Licensed under the Apache License, Version 2.0 or the MIT License. Copyright (C) 2023-2024 Ant Group. - \ No newline at end of file diff --git a/regression/apps/network/tcp_err.c b/regression/apps/network/tcp_err.c new file mode 100644 index 000000000..3317f184e --- /dev/null +++ b/regression/apps/network/tcp_err.c @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include + +#include "test.h" + +static struct sockaddr_in sk_addr; + +#define C_PORT htons(0x1234) +#define S_PORT htons(0x1235) + +FN_SETUP(general) +{ + sk_addr.sin_family = AF_INET; + sk_addr.sin_port = htons(8080); + CHECK(inet_aton("127.0.0.1", &sk_addr.sin_addr)); + + signal(SIGPIPE, SIG_IGN); +} +END_SETUP() + +static int sk_unbound; +static int sk_bound; +static int sk_listen; +static int sk_connected; +static int sk_accepted; + +FN_SETUP(unbound) +{ + sk_unbound = CHECK(socket(PF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0)); +} +END_SETUP() + +FN_SETUP(bound) +{ + sk_bound = CHECK(socket(PF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0)); + + sk_addr.sin_port = C_PORT; + CHECK(bind(sk_bound, (struct sockaddr *)&sk_addr, sizeof(sk_addr))); +} +END_SETUP() + +FN_SETUP(listen) +{ + sk_listen = CHECK(socket(PF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0)); + + sk_addr.sin_port = S_PORT; + CHECK(bind(sk_listen, (struct sockaddr *)&sk_addr, sizeof(sk_addr))); + + CHECK(listen(sk_listen, 2)); +} +END_SETUP() + +FN_SETUP(connected) +{ + sk_connected = CHECK(socket(PF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0)); + + sk_addr.sin_port = S_PORT; + CHECK_WITH(connect(sk_connected, (struct sockaddr *)&sk_addr, + sizeof(sk_addr)), + _ret == 0 || errno == EINPROGRESS); +} +END_SETUP() + +FN_SETUP(accpected) +{ + struct sockaddr addr; + socklen_t addrlen = sizeof(addr); + + do { + sk_accepted = CHECK_WITH(accept(sk_listen, &addr, &addrlen), + _ret >= 0 || errno == EAGAIN); + } while (sk_accepted < 0); +} +END_SETUP() diff --git a/regression/apps/network/test.h b/regression/apps/network/test.h new file mode 100644 index 000000000..86b3353f6 --- /dev/null +++ b/regression/apps/network/test.h @@ -0,0 +1,164 @@ +/* SPDX-License-Identifier: MPL-2.0 */ + +/* + * A framework for writing regression tests. + * + * A regression test typically consists of two parts, the setup part and the + * test part. The setup part contains setup functions that set up the context + * for the subsequent tests to run. The setup functions cannot fail, and if they + * do, execution is aborted because the subsequent tests will not work as + * expected either. The test functions, on the other hand, can fail, and if they + * do, they are reported as test failures. + * + * The framework provides basic utilities for writing regression tests: + * + * - To define a setup function or a test function, FN_SETUP() or FN_TEST() can + * be used. These functions are automatically executed in the order of their + * definiton. Note that the order of execution is _not_ related to whether a + * function is a setup function or a test function. + * + * - Within a setup function, CHECK() can be used to write a setup expression + * that must succeed. If the expression fails, a fatal error will be reported + * and the execution will be aborted. + * + * - Within a test function, TEST_SUCC() can be used to write a test expression + * that should succeed. If the expression fails, a test failure will be reported + * but the execution will continue. + * + * - The number of successful and failed tests is tracked. When a test function + * finishes, a summary sentence is printed describing the number of test + * failures. The program will exit with a non-zero code if and only if there is + * at least one test failure. + */ + +#include +#include +#include +#include + +/** Starts the definition of a setup function. */ +#define FN_SETUP(name) \ + void setup_##name(void) __attribute__((constructor(__LINE__ + 200))); \ + \ + void setup_##name(void) \ + { +/** Ends the definition of a setup function. */ +#define END_SETUP() } + +#define __CHECK(func, cond) \ + errno = 0; \ + long _ret = (func); \ + if (!(cond)) { \ + fprintf(stderr, \ + "fatal error: %s: `" #cond "` is false after `" #func \ + "` [got %s]\n", \ + __func__, strerror(errno)); \ + exit(EXIT_FAILURE); \ + } + +/** + * Makes a fucntion call and checks its return value is positive. + * + * The execution will be aborted if the check fails. + */ +#define CHECK(func) \ + ({ \ + __CHECK(func, _ret >= 0); \ + _ret; \ + }) + +/** + * Makes a function call and checks its result with the specified condition. + * + * The execution will be aborted if the check fails. + * + * The return value of the function can be accessed with a local variable named + * _ret. + */ +#define CHECK_WITH(func, cond) \ + ({ \ + __CHECK(func, cond); \ + _ret; \ + }) + +static int __total_failures; + +/** Starts the definition of a test function. */ +#define FN_TEST(name) \ + void test_##name(void) __attribute__((constructor(__LINE__ + 200))); \ + \ + void test_##name(void) \ + { \ + int __tests_passed = 0, __tests_failed = 0; + +/** Ends the definition of a test function. */ +#define END_TEST() \ + fprintf(stderr, "%s summary: %d tests passed, %d tests failed\n", \ + __func__, __tests_passed, __tests_failed); \ + __total_failures += __tests_failed; \ + } + +#define __TEST(func, err, cond) \ + errno = 0; \ + long _ret = (func); \ + if (errno != (err)) { \ + __tests_failed++; \ + fprintf(stderr, \ + "%s: `" #func "` failed [got %s, but expected %s]\n", \ + __func__, strerror(errno), strerror(err)); \ + } else if (!(cond)) { \ + __tests_failed++; \ + fprintf(stderr, \ + "%s: `" #func "` failed [got %s, but `" #cond \ + "` is false]\n", \ + __func__, strerror(errno)); \ + } else { \ + __tests_passed++; \ + fprintf(stderr, "%s: `" #func "` passed [got %s]\n", __func__, \ + strerror(errno)); \ + } + +/** + * Makes a function call and checks its result. + * + * A test failure will be reported if the function does not set the specified + * errno or the specified condition does not meet. + * + * The return value of the function can be accessed with a local variable named + * _ret. + */ +#define TEST(func, err, cond) \ + ({ \ + __TEST(func, err, cond); \ + _ret; \ + }) +/** + * Makes a function call and checks whether it succeeds. + * + * A test failure will be reported if the function sets a non-zero errno. + */ +#define TEST_SUCC(func) TEST(func, 0, 1) + +/** + * Makes a function call and checks whether it fails in the specified way. + * + * A test failure will be reported if the function does not set the specified + * errno. + */ +#define TEST_ERRNO(func, err) TEST(func, err, 1) + +/** + * Makes a function call and checks whether it produces expected results. + * + * A test failure will be reported if the function sets a non-zero errno or the + * specified condition does not meet. + * + * The return value of the function can be accessed with a local variable named + * _ret. + */ +#define TEST_RES(func, cond) TEST(func, 0, cond) + +int main(void) +{ + return __total_failures ? 1 : 0; +} diff --git a/regression/apps/network/udp_err.c b/regression/apps/network/udp_err.c new file mode 100644 index 000000000..6169c2bc9 --- /dev/null +++ b/regression/apps/network/udp_err.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include + +#include "test.h" + +static struct sockaddr_in sk_addr; + +#define C_PORT htons(0x1234) + +FN_SETUP(general) +{ + sk_addr.sin_family = AF_INET; + sk_addr.sin_port = htons(8080); + CHECK(inet_aton("127.0.0.1", &sk_addr.sin_addr)); + + signal(SIGPIPE, SIG_IGN); +} +END_SETUP() + +static int sk_unbound; +static int sk_bound; +static int sk_connected; + +FN_SETUP(unbound) +{ + sk_unbound = CHECK(socket(PF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0)); +} +END_SETUP() + +FN_SETUP(bound) +{ + sk_bound = CHECK(socket(PF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0)); + + sk_addr.sin_port = C_PORT; + CHECK(bind(sk_bound, (struct sockaddr *)&sk_addr, sizeof(sk_addr))); +} +END_SETUP() + +FN_SETUP(connected) +{ + sk_connected = CHECK(socket(PF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0)); + + sk_addr.sin_port = C_PORT; + CHECK(connect(sk_connected, (struct sockaddr *)&sk_addr, + sizeof(sk_addr))); +} +END_SETUP() diff --git a/regression/apps/scripts/network.sh b/regression/apps/scripts/network.sh index be90abf3c..bf0efb3ea 100755 --- a/regression/apps/scripts/network.sh +++ b/regression/apps/scripts/network.sh @@ -21,5 +21,7 @@ echo "Start network test......" # ./send_buf_full ./http_server & ./http_client +./tcp_err +./udp_err echo "All network test passed"