mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-13 23:36:48 +00:00
Add a framework for regression tests
This commit is contained in:
parent
e8a991048f
commit
b53980da25
@ -9,6 +9,7 @@ header:
|
|||||||
- '**/*.S'
|
- '**/*.S'
|
||||||
- '**/*.s'
|
- '**/*.s'
|
||||||
- '**/*.c'
|
- '**/*.c'
|
||||||
|
- '**/*.h'
|
||||||
- '**/*.sh'
|
- '**/*.sh'
|
||||||
- '**/Makefile'
|
- '**/Makefile'
|
||||||
- '**/Dockerfile.*'
|
- '**/Dockerfile.*'
|
||||||
@ -58,4 +59,3 @@ header:
|
|||||||
content: |
|
content: |
|
||||||
Licensed under the Apache License, Version 2.0 or the MIT License.
|
Licensed under the Apache License, Version 2.0 or the MIT License.
|
||||||
Copyright (C) 2023-2024 Ant Group.
|
Copyright (C) 2023-2024 Ant Group.
|
||||||
|
|
79
regression/apps/network/tcp_err.c
Normal file
79
regression/apps/network/tcp_err.c
Normal 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()
|
164
regression/apps/network/test.h
Normal file
164
regression/apps/network/test.h
Normal 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;
|
||||||
|
}
|
52
regression/apps/network/udp_err.c
Normal file
52
regression/apps/network/udp_err.c
Normal 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()
|
@ -21,5 +21,7 @@ echo "Start network test......"
|
|||||||
# ./send_buf_full
|
# ./send_buf_full
|
||||||
./http_server &
|
./http_server &
|
||||||
./http_client
|
./http_client
|
||||||
|
./tcp_err
|
||||||
|
./udp_err
|
||||||
|
|
||||||
echo "All network test passed"
|
echo "All network test passed"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user