Skip to content
Snippets Groups Projects
Commit 5340c99f authored by Marek Vavruša's avatar Marek Vavruša
Browse files

tests/integration: split mock calls library, faking time now works

todo: change the cache behaviour to pass cname cache test
parent 63c4183b
No related branches found
No related tags found
No related merge requests found
......@@ -10,6 +10,9 @@ AM_SILENT_RULES([yes])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIR([m4])
# Platform-specific
AM_CONDITIONAL([HOST_DARWIN], [test "${host_os}" != "${host_os/darwin/}"])
# Set compiler compatibility flags
AC_PROG_CC_C99
AM_PROG_CC_C_O
......
......@@ -35,6 +35,7 @@ lib/utils.h
lib/zonecut.c
lib/zonecut.h
tests/Makefile.am
tests/mock_calls.c
tests/pydnstest/__init__.py
tests/pydnstest/scenario.py
tests/pydnstest/test.py
......
......@@ -33,26 +33,35 @@ endif
# Integration tests
if BUILD_INTEGRATION
check_LTLIBRARIES = _test_integration.la
check_LTLIBRARIES = libmock_calls.la _test_integration.la
libmock_calls_la_SOURCES = mock_calls.c
libmock_calls_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_CPPFLAGS)
libmock_calls_la_LDFLAGS = $(LDFLAGS) -rpath $(abs_builddir)/tests -shared -avoid-version
libmock_calls_la_LIBADD = $(libknot_LIBS) $(top_builddir)/lib/libkresolve_static.la $(PYTHON_LIBS)
_test_integration_la_SOURCES = test_integration.c
_test_integration_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_CPPFLAGS)
_test_integration_la_LDFLAGS = $(LDFLAGS) -rpath $(abs_builddir) -module -shared -avoid-version --wrap=gettimeofday
_test_integration_la_LIBADD = $(top_builddir)/lib/libkresolve_static.la $(libknot_LIBS) $(PYTHON_LIBS)
_test_integration_la_LDFLAGS = $(LDFLAGS) -rpath $(abs_builddir) -module -shared -avoid-version
_test_integration_la_LIBADD = libmock_calls.la $(PYTHON_LIBS)
check_cmd = $(abs_builddir)/test_integration.py testdata
convenience-link: $(check_LTLIBRARIES)
@for soname in `echo | $(EGREP) "^dlname=" $(check_LTLIBRARIES) | $(SED) -e "s|^dlname='\(.*\)'|\1|"`; do \
for soname in `echo | $(EGREP) "^dlname=" $(check_LTLIBRARIES) | $(SED) -e "s|\(.*\)dlname='\(.*\)'|\2|"`; do \
rm -f $(abs_builddir)/$$soname; $(LN_S) $(abs_builddir)/.libs/$$soname $$soname || true;\
done
check-local-integration: convenience-link
@echo "---- Executing integration tests ----"
@cd $(top_builddir)
@$(top_builddir)/tests/test_integration.py testdata
if HOST_DARWIN
@DYLD_FORCE_FLAT_NAMESPACE=1 DYLD_INSERT_LIBRARIES=$(abs_builddir)/.libs/libmock_calls.dylib $(check_cmd)
else
@LD_PRELOAD=$(abs_builddir)/.libs/libmock_calls.so $(check_cmd)
endif
clean-local:
@for soname in `echo | $(EGREP) "^dlname=" $(check_LTLIBRARIES) | $(SED) -e "s|^dlname='\(.*\)'|\1|"`; do \
test -L $(abs_builddir)/$$soname && rm -f $(abs_builddir)/$$soname || true; \
done
rm -f *.so *.dylib || true
else
check-local-integration:
......
/* Copyright (C) 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <Python.h>
#include <libknot/descriptor.h>
#include <libknot/packet/pkt.h>
#include <libknot/internal/net.h>
/*
* Globals
*/
struct timeval g_mock_time; /* Mocked system time */
PyObject *g_mock_server = NULL; /* Mocked endpoint for recursive queries */
int gettimeofday(struct timeval *tv, struct timezone *tz)
{
memcpy(tv, &g_mock_time, sizeof(struct timeval));
return 0;
}
int tcp_recv_msg(int fd, uint8_t *buf, size_t len, struct timeval *timeout)
{
/* Unlock GIL and attempt to receive message. */
uint16_t msg_len = 0;
int rcvd = 0;
Py_BEGIN_ALLOW_THREADS
rcvd = read(fd, (char *)&msg_len, sizeof(msg_len));
if (rcvd == sizeof(msg_len)) {
msg_len = htons(msg_len);
rcvd = read(fd, buf, msg_len);
}
Py_END_ALLOW_THREADS
return rcvd;
}
int udp_recv_msg(int fd, uint8_t *buf, size_t len, struct timeval *timeout)
{
/* Tunnel via TCP. */
return tcp_recv_msg(fd, buf, len, timeout);
}
int tcp_send_msg(int fd, const uint8_t *msg, size_t len)
{
/* Unlock GIL and attempt to send message over. */
uint16_t msg_len = htons(len);
int sent = 0;
Py_BEGIN_ALLOW_THREADS
sent = write(fd, (char *)&msg_len, sizeof(msg_len));
if (sent == sizeof(msg_len)) {
sent = write(fd, msg, len);
}
Py_END_ALLOW_THREADS
return sent;
}
int udp_send_msg(int fd, const uint8_t *msg, size_t msglen,
const struct sockaddr *addr)
{
/* Tunnel via TCP. */
return tcp_send_msg(fd, msg, msglen);
}
int net_connected_socket(int type, const struct sockaddr_storage *dst_addr,
const struct sockaddr_storage *src_addr, unsigned flags)
{
char addr_str[SOCKADDR_STRLEN];
sockaddr_tostr(addr_str, sizeof(addr_str), dst_addr);
PyObject *result = PyObject_CallMethod(g_mock_server, "client", "s", addr_str);
if (result == NULL) {
return -1;
}
/* Refcount decrement is going to close the fd, dup() it */
int fd = dup(PyObject_AsFileDescriptor(result));
Py_DECREF(result);
return fd;
}
int net_is_connected(int fd)
{
fprintf(stderr, "%s (%d)\n", __func__, fd);
return true;
}
......@@ -92,7 +92,8 @@ class TestServer:
def client(self, dst_address = None):
""" Return connected client. """
self.client_address = dst_address.split('@')[0]
if dst_address is not None:
self.client_address = dst_address.split('@')[0]
sock = socket.socket(self.sock_type, socket.SOCK_STREAM)
sock.connect(self.sock.getsockname())
return sock
......
......@@ -15,9 +15,6 @@
*/
#include <Python.h>
#include <libknot/descriptor.h>
#include <libknot/packet/pkt.h>
#include <libknot/internal/net.h>
#include "tests/test.h"
#include "lib/rplan.h"
......@@ -29,8 +26,12 @@
static mm_ctx_t global_mm; /* Test memory context */
static struct kr_context global_context; /* Resolution context */
static const char *global_tmpdir = NULL; /* Temporary directory */
static struct timeval _mock_time; /* Mocked system time */
static PyObject *mock_server = NULL; /* Mocked endpoint for recursive queries */
/*
* Test driver global variables.
*/
extern struct timeval g_mock_time; /* Mocked system time */
extern PyObject *g_mock_server; /* Mocked endpoint for recursive queries */
/*
* PyModule implementation.
......@@ -44,8 +45,8 @@ static PyObject* init(PyObject* self, PyObject* args)
}
/* Initialize mock variables */
memset(&_mock_time, 0, sizeof(struct timeval));
mock_server = NULL;
memset(&g_mock_time, 0, sizeof(struct timeval));
g_mock_server = NULL;
/* Initialize resolution context */
#define CACHE_SIZE 100*1024
......@@ -76,9 +77,9 @@ static PyObject* deinit(PyObject* self, PyObject* args)
kr_context_deinit(&global_context);
test_tmpdir_remove(global_tmpdir);
global_tmpdir = NULL;
if (mock_server) {
Py_XDECREF(mock_server);
mock_server = NULL;
if (g_mock_server) {
Py_XDECREF(g_mock_server);
g_mock_server = NULL;
}
return Py_BuildValue("");
......@@ -122,8 +123,8 @@ static PyObject* set_time(PyObject *self, PyObject *args)
return NULL;
}
_mock_time.tv_sec = arg_time;
_mock_time.tv_usec = 0;
g_mock_time.tv_sec = arg_time;
g_mock_time.tv_usec = 0;
return Py_BuildValue("");
}
......@@ -138,60 +139,18 @@ static PyObject* set_server(PyObject *self, PyObject *args)
/* Swap the server implementation. */
Py_XINCREF(arg_client);
Py_XDECREF(mock_server);
mock_server = arg_client;
Py_XDECREF(g_mock_server);
g_mock_server = arg_client;
return Py_BuildValue("");
}
static PyObject* test_connect(PyObject *self, PyObject *args)
{
/* Fetch a new client */
struct sockaddr_storage addr;
sockaddr_set(&addr, AF_INET, "127.0.0.1", 0);
int sock = net_connected_socket(SOCK_STREAM, &addr, NULL, 0);
if (sock < 0) {
return NULL;
}
int ret = 0;
bool test_passed = true;
knot_pkt_t *query = NULL, *reply = NULL;
/* Send and receive a query. */
query = knot_pkt_new(NULL, 512, NULL);
knot_pkt_put_question(query, (const uint8_t *)"", KNOT_CLASS_IN, KNOT_RRTYPE_NS);
ret = tcp_send_msg(sock, query->wire, query->size);
if (ret != query->size) {
test_passed = false;
goto finish;
}
reply = knot_pkt_new(NULL, 512, NULL);
ret = tcp_recv_msg(sock, reply->wire, reply->max_size, NULL);
if (ret <= 0) {
test_passed = false;
goto finish;
}
finish:
close(sock);
knot_pkt_free(&query);
knot_pkt_free(&reply);
if (test_passed) {
return Py_BuildValue("");
} else {
return NULL;
}
}
static PyMethodDef module_methods[] = {
{"init", init, METH_VARARGS, "Initialize resolution context."},
{"deinit", deinit, METH_VARARGS, "Clean up resolution context."},
{"resolve", resolve, METH_VARARGS, "Resolve query."},
{"set_time", set_time, METH_VARARGS, "Set mock system time."},
{"set_server", set_server, METH_VARARGS, "Set fake server object."},
{"test_connect", test_connect, METH_VARARGS, "Test server connection."},
{NULL, NULL, 0, NULL}
};
......@@ -199,82 +158,3 @@ PyMODINIT_FUNC init_test_integration(void)
{
(void) Py_InitModule("_test_integration", module_methods);
}
/*
* Mock symbol reimplementation.
* These effectively allow to manipulate time/networking during resolution.
*/
int __wrap_gettimeofday(struct timeval *tv, struct timezone *tz)
{
fprintf(stderr, "gettimeofday = %ld\n", tv->tv_sec);
memcpy(tv, &_mock_time, sizeof(struct timeval));
return 0;
}
int tcp_recv_msg(int fd, uint8_t *buf, size_t len, struct timeval *timeout)
{
/* Unlock GIL and attempt to receive message. */
uint16_t msg_len = 0;
int rcvd = 0;
Py_BEGIN_ALLOW_THREADS
rcvd = read(fd, (char *)&msg_len, sizeof(msg_len));
if (rcvd == sizeof(msg_len)) {
msg_len = htons(msg_len);
rcvd = read(fd, buf, msg_len);
}
Py_END_ALLOW_THREADS
return rcvd;
}
int udp_recv_msg(int fd, uint8_t *buf, size_t len, struct timeval *timeout)
{
/* Tunnel via TCP. */
return tcp_recv_msg(fd, buf, len, timeout);
}
int tcp_send_msg(int fd, const uint8_t *msg, size_t len)
{
/* Unlock GIL and attempt to send message over. */
uint16_t msg_len = htons(len);
int sent = 0;
Py_BEGIN_ALLOW_THREADS
sent = write(fd, (char *)&msg_len, sizeof(msg_len));
if (sent == sizeof(msg_len)) {
sent = write(fd, msg, len);
}
Py_END_ALLOW_THREADS
return sent;
}
int udp_send_msg(int fd, const uint8_t *msg, size_t msglen,
const struct sockaddr *addr)
{
/* Tunnel via TCP. */
return tcp_send_msg(fd, msg, msglen);
}
int net_connected_socket(int type, const struct sockaddr_storage *dst_addr,
const struct sockaddr_storage *src_addr, unsigned flags)
{
char addr_str[SOCKADDR_STRLEN];
sockaddr_tostr(addr_str, sizeof(addr_str), dst_addr);
PyObject *result = PyObject_CallMethod(mock_server, "client", "s", addr_str);
if (result == NULL) {
return -1;
}
/* Refcount decrement is going to close the fd, dup() it */
int fd = dup(PyObject_AsFileDescriptor(result));
Py_DECREF(result);
return fd;
}
int net_is_connected(int fd)
{
fprintf(stderr, "%s (%d)\n", __func__, fd);
return true;
}
......@@ -146,28 +146,17 @@ def play_object(path):
server.stop()
mock_ctx.deinit()
def test_ipc(*args):
for arg in args:
print arg
""" Module self-test code. """
server = testserver.TestServer(None)
server.start()
mock_ctx.set_server(server)
try:
mock_ctx.test_connect()
finally:
server.stop()
def test_platform(*args):
if sys.platform == 'darwin':
raise Exception('ld -wrap is not supported on OS X')
if sys.platform == 'windows':
raise Exception('not supported at all on Windows')
if __name__ == '__main__':
# Self-tests first
test = test.Test()
test.add('integration/ipc', test_ipc)
test.add('integration/platform', test_platform)
test.add('testserver/sendrecv', testserver.test_sendrecv)
if test.run() != 0:
sys.exit(1)
else:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment