Skip to content

Commit

Permalink
Add unit test setup with php_network_connect_socket test
Browse files Browse the repository at this point in the history
  • Loading branch information
bukka committed Nov 28, 2024
1 parent 34edb29 commit 319fc93
Show file tree
Hide file tree
Showing 2 changed files with 285 additions and 0 deletions.
31 changes: 31 additions & 0 deletions tests/unit/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
CC = gcc
CFLAGS = -g -Wall -I../../ -I../../Zend -I../../main -I../../TSRM -I. -I..
COMMON_LDFLAGS = ../../.libs/libphp.a -lcmocka -lpthread -lm -ldl -lresolv -lutil

TESTS = main/test_network
main/test_network_SRC = main/test_network.c
main/test_network_LDFLAGS = $(COMMON_LDFLAGS) -Wl,--wrap=connect,--wrap=poll,--wrap=getsockopt,--wrap=gettimeofday


# Build all tests
all: $(TESTS)

# Build rule for each test
$(TESTS):
$(CC) $(CFLAGS) -o $@.out $($(basename $@)_SRC) $($(basename $@)_LDFLAGS)

# Run all tests
.PHONY: test
test: $(TESTS)
@echo "Running all tests..."
@for test in $(TESTS); do \
echo "Running $$test..."; \
$$test.out || exit 1; \
done

# Clean tests
.PHONY: clean
clean:
@for test in $(TESTS); do \
rm -f $$test.out; \
done
254 changes: 254 additions & 0 deletions tests/unit/main/test_network.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
#include "php.h"
#include "php_network.h"
#include <cmocka.h>

// Mocked poll
int __wrap_poll(struct pollfd *ufds, nfds_t nfds, int timeout)
{
function_called();
check_expected(timeout);

int n = mock_type(int);
if (n > 0) {
ufds->revents = 1;
} else if (n < 0) {
errno = -n;
n = -1;
}

return n;
}

// Mocked connect
int __wrap_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
{
function_called();
errno = mock_type(int);
return errno != 0 ? -1 : 0;
}

// Mocked getsockopt
int __wrap_getsockopt(int fd, int level, int optname, void *optval, socklen_t *optlen)
{
function_called();
int *error = (int *) optval;
*error = mock_type(int);
return mock_type(int);
}

// Mocked gettimeofday
int __wrap_gettimeofday(struct timeval *time_Info, struct timezone *timezone_Info)
{
function_called();
struct timeval *now = mock_ptr_type(struct timeval *);
if (now) {
time_Info->tv_sec = now->tv_sec;
time_Info->tv_usec = now->tv_usec;
}
return mock_type(int);
}

// Test successful connection
static void test_php_network_connect_socket_immediate_success(void **state) {
struct timeval timeout = { .tv_sec = 2, .tv_usec = 500000 };
php_socket_t sockfd = 12;
int error_code = 0;

expect_function_call(__wrap_connect);
will_return(__wrap_connect, 0);

int result = php_network_connect_socket(sockfd, NULL, 0, 0, &timeout, NULL, &error_code);

assert_int_equal(result, 0);
assert_int_equal(error_code, 0);
}

// Test successful connection in progress followed by poll
static void test_php_network_connect_socket_progress_success(void **state) {
struct timeval timeout_tv = { .tv_sec = 2, .tv_usec = 500000 };
php_socket_t sockfd = 12;
int error_code = 0;

// Mock connect setting EINPROGRESS errno
expect_function_call(__wrap_connect);
will_return(__wrap_connect, EINPROGRESS);

// Mock time setting - ignored
expect_function_call(__wrap_gettimeofday);
will_return(__wrap_gettimeofday, NULL);
will_return(__wrap_gettimeofday, 0);

// Mock poll to return success
expect_function_call(__wrap_poll);
expect_value(__wrap_poll, timeout, 2500);
will_return(__wrap_poll, 1);

// Mock no socket error
expect_function_call(__wrap_getsockopt);
will_return(__wrap_getsockopt, 0); // optval saved result
will_return(__wrap_getsockopt, 0); // actual return value

int result = php_network_connect_socket(sockfd, NULL, 0, 0, &timeout_tv, NULL, &error_code);

assert_int_equal(result, 0);
assert_int_equal(error_code, 0);
}

static void test_php_network_connect_socket_eintr_t1(void **state) {
struct timeval timeout_tv = { .tv_sec = 2, .tv_usec = 500000 };
struct timeval start_time = { .tv_sec = 1000, .tv_usec = 0 }; // Initial time
struct timeval retry_time = { .tv_sec = 1001, .tv_usec = 200000 }; // Time after EINTR
php_socket_t sockfd = 12;
int error_code = 0;

// Mock connect to set EINPROGRESS
expect_function_call(__wrap_connect);
will_return(__wrap_connect, EINPROGRESS);

// Mock gettimeofday for initial call
expect_function_call(__wrap_gettimeofday);
will_return(__wrap_gettimeofday, &start_time); // time to set
will_return(__wrap_gettimeofday, 0); // actual return value

// Mock poll to return EINTR first
expect_function_call(__wrap_poll);
expect_value(__wrap_poll, timeout, 2500);
will_return(__wrap_poll, -EINTR);

// Mock gettimeofday after EINTR
expect_function_call(__wrap_gettimeofday);
will_return(__wrap_gettimeofday, &retry_time); // time to set
will_return(__wrap_gettimeofday, 0); // actual return value

// Mock poll to succeed on retry
expect_function_call(__wrap_poll);
expect_value(__wrap_poll, timeout, 1300);
will_return(__wrap_poll, 1);

// Mock no socket error
expect_function_call(__wrap_getsockopt);
will_return(__wrap_getsockopt, 0); // optval saved result
will_return(__wrap_getsockopt, 0); // actual return value

int result = php_network_connect_socket(sockfd, NULL, 0, 0, &timeout_tv, NULL, &error_code);

// Ensure the function succeeds
assert_int_equal(result, 0);
assert_int_equal(error_code, 0);
}

static void test_php_network_connect_socket_eintr_t2(void **state) {
struct timeval timeout_tv = { .tv_sec = 2, .tv_usec = 1500000 };
struct timeval start_time = { .tv_sec = 1000, .tv_usec = 300000 }; // Initial time
struct timeval retry_time = { .tv_sec = 1001, .tv_usec = 200000 }; // Time after EINTR
php_socket_t sockfd = 12;
int error_code = 0;

// Mock connect to set EINPROGRESS
expect_function_call(__wrap_connect);
will_return(__wrap_connect, EINPROGRESS);

// Mock gettimeofday for initial call
expect_function_call(__wrap_gettimeofday);
will_return(__wrap_gettimeofday, &start_time);
will_return(__wrap_gettimeofday, 0); // Return 0 (success)

// Mock poll to return EINTR first
expect_function_call(__wrap_poll);
expect_value(__wrap_poll, timeout, 3500);
will_return(__wrap_poll, -EINTR);

// Mock gettimeofday after EINTR
expect_function_call(__wrap_gettimeofday);
will_return(__wrap_gettimeofday, &retry_time);
will_return(__wrap_gettimeofday, 0); // Return 0 (success)

// Mock poll to succeed on retry
expect_function_call(__wrap_poll);
expect_value(__wrap_poll, timeout, 2600);
will_return(__wrap_poll, 1);

// Mock no socket error
expect_function_call(__wrap_getsockopt);
will_return(__wrap_getsockopt, 0); // optval saved result
will_return(__wrap_getsockopt, 0); // actual return value

int result = php_network_connect_socket(sockfd, NULL, 0, 0, &timeout_tv, NULL, &error_code);

// Ensure the function succeeds
assert_int_equal(result, 0);
assert_int_equal(error_code, 0);
}

static void test_php_network_connect_socket_eintr_t3(void **state) {
struct timeval timeout_tv = { .tv_sec = 2, .tv_usec = 500000 };
struct timeval start_time = { .tv_sec = 1002, .tv_usec = 300000 }; // Initial time
struct timeval retry_time = { .tv_sec = 1001, .tv_usec = 2200000 }; // Time after EINTR
php_socket_t sockfd = 12;
int error_code = 0;

// Mock connect to set EINPROGRESS
expect_function_call(__wrap_connect);
will_return(__wrap_connect, EINPROGRESS);

// Mock gettimeofday for initial call
expect_function_call(__wrap_gettimeofday);
will_return(__wrap_gettimeofday, &start_time);
will_return(__wrap_gettimeofday, 0); // Return 0 (success)

// Mock poll to return EINTR first
expect_function_call(__wrap_poll);
expect_value(__wrap_poll, timeout, 2500);
will_return(__wrap_poll, -EINTR);

// Mock gettimeofday after EINTR
expect_function_call(__wrap_gettimeofday);
will_return(__wrap_gettimeofday, &retry_time);
will_return(__wrap_gettimeofday, 0); // Return 0 (success)

// Mock poll to succeed on retry
expect_function_call(__wrap_poll);
expect_value(__wrap_poll, timeout, 1600);
will_return(__wrap_poll, 1);

// Mock no socket error
expect_function_call(__wrap_getsockopt);
will_return(__wrap_getsockopt, 0); // optval saved result
will_return(__wrap_getsockopt, 0); // actual return value

int result = php_network_connect_socket(sockfd, NULL, 0, 0, &timeout_tv, NULL, &error_code);

// Ensure the function succeeds
assert_int_equal(result, 0);
assert_int_equal(error_code, 0);
}

// Test connection error (ECONNREFUSED)
static void test_php_network_connect_socket_connect_error(void **state) {
struct timeval timeout = { .tv_sec = 2, .tv_usec = 500000 };
php_socket_t sockfd = 12;
int error_code = 0;

// Mock connect to set ECONNREFUSED
expect_function_call(__wrap_connect);
will_return(__wrap_connect, ECONNREFUSED);

int result = php_network_connect_socket(sockfd, NULL, 0, 0, &timeout, NULL, &error_code);

// Ensure the function returns an error
assert_int_equal(result, -1);
assert_int_equal(error_code, ECONNREFUSED);
}


int main(void) {
const struct CMUnitTest tests[] = {
cmocka_unit_test(test_php_network_connect_socket_immediate_success),
cmocka_unit_test(test_php_network_connect_socket_progress_success),
cmocka_unit_test(test_php_network_connect_socket_eintr_t1),
cmocka_unit_test(test_php_network_connect_socket_eintr_t2),
cmocka_unit_test(test_php_network_connect_socket_eintr_t3),
cmocka_unit_test(test_php_network_connect_socket_connect_error),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}

0 comments on commit 319fc93

Please sign in to comment.