Add a framework for regression tests

This commit is contained in:
Ruihan Li 2024-03-20 14:55:17 +08:00 committed by Tate, Hongliang Tian
parent e8a991048f
commit b53980da25
5 changed files with 298 additions and 1 deletions

View File

@ -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.

View File

@ -0,0 +1,79 @@
// SPDX-License-Identifier: MPL-2.0
#include <unistd.h>
#include <sys/signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#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()

View File

@ -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 <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/** 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;
}

View File

@ -0,0 +1,52 @@
// SPDX-License-Identifier: MPL-2.0
#include <unistd.h>
#include <sys/signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#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()

View File

@ -21,5 +21,7 @@ echo "Start network test......"
# ./send_buf_full
./http_server &
./http_client
./tcp_err
./udp_err
echo "All network test passed"