From 9002d3f0058b1dbebf207d3d24dc377ab4afa0b1 Mon Sep 17 00:00:00 2001 From: Alon Levy Date: Wed, 10 Nov 2010 19:36:47 +0200 Subject: server/tests: add test_display_no_ssl updates taken from spice vga mode updates, i.e. non cacheable, glz compressed (depends on whatever settings you apply to the server) opaque draw operations. + completed the SpiceCoreInterface implementation (timers) v1->v2: removed test_util.c (Hans) replaced mallocz with calloc (Hans) --- server/tests/Makefile.am | 15 +- server/tests/basic_event_loop.c | 320 +++++++++++++++++++++++++++++++------ server/tests/test_display_no_ssl.c | 291 +++++++++++++++++++++++++++++++++ server/tests/test_util.h | 12 ++ 4 files changed, 588 insertions(+), 50 deletions(-) create mode 100644 server/tests/test_display_no_ssl.c create mode 100644 server/tests/test_util.h diff --git a/server/tests/Makefile.am b/server/tests/Makefile.am index 09fdc264..f91dfa46 100644 --- a/server/tests/Makefile.am +++ b/server/tests/Makefile.am @@ -1,14 +1,19 @@ NULL = -INCLUDES = \ - -I.. \ - $(PROTOCOL_CFLAGS) \ - $(SPICE_NONPKGCONFIG_CFLAGS) \ +INCLUDES = \ + -I.. \ + -I../../common \ + $(PROTOCOL_CFLAGS) \ + $(SPICE_NONPKGCONFIG_CFLAGS) \ $(NULL) LDFLAGS = -L../.libs -lspice-server -noinst_PROGRAMS = test_just_sockets_no_ssl test_empty_success test_fail_on_null_core_interface +noinst_PROGRAMS = test_just_sockets_no_ssl test_empty_success test_fail_on_null_core_interface test_display_no_ssl + +test_display_no_ssl_SOURCES = test_display_no_ssl.c basic_event_loop.c + +test_display_no_ssl_LDFLAGS = $(LDFLAGS) test_just_sockets_no_ssl_SOURCES = test_just_sockets_no_ssl.c basic_event_loop.c basic_event_loop.h diff --git a/server/tests/basic_event_loop.c b/server/tests/basic_event_loop.c index 14dec512..f6570ff5 100644 --- a/server/tests/basic_event_loop.c +++ b/server/tests/basic_event_loop.c @@ -1,89 +1,287 @@ #include #include +#include +#include "test_util.h" #include "basic_event_loop.h" +int debug = 0; + +#define DPRINTF(x, format, ...) { \ + if (x <= debug) { \ + printf("%s: " format "\n" , __FUNCTION__, ## __VA_ARGS__); \ + } \ +} + +/* From ring.h */ +typedef struct Ring RingItem; +typedef struct Ring { + RingItem *prev; + RingItem *next; +} Ring; + +static inline void ring_init(Ring *ring) +{ + ring->next = ring->prev = ring; +} + +static inline void ring_item_init(RingItem *item) +{ + item->next = item->prev = NULL; +} + +static inline int ring_item_is_linked(RingItem *item) +{ + return !!item->next; +} + +static inline int ring_is_empty(Ring *ring) +{ + ASSERT(ring->next != NULL && ring->prev != NULL); + return ring == ring->next; +} + +static inline void ring_add(Ring *ring, RingItem *item) +{ + ASSERT(ring->next != NULL && ring->prev != NULL); + ASSERT(item->next == NULL && item->prev == NULL); + + item->next = ring->next; + item->prev = ring; + ring->next = item->next->prev = item; +} + +static inline void __ring_remove(RingItem *item) +{ + item->next->prev = item->prev; + item->prev->next = item->next; + item->prev = item->next = 0; +} + +static inline void ring_remove(RingItem *item) +{ + ASSERT(item->next != NULL && item->prev != NULL); + ASSERT(item->next != item); + + __ring_remove(item); +} + +static inline RingItem *ring_get_head(Ring *ring) +{ + RingItem *ret; + + ASSERT(ring->next != NULL && ring->prev != NULL); + + if (ring_is_empty(ring)) { + return NULL; + } + ret = ring->next; + return ret; +} + +static inline RingItem *ring_get_tail(Ring *ring) +{ + RingItem *ret; + + ASSERT(ring->next != NULL && ring->prev != NULL); + + if (ring_is_empty(ring)) { + return NULL; + } + ret = ring->prev; + return ret; +} + +static inline RingItem *ring_next(Ring *ring, RingItem *pos) +{ + RingItem *ret; + + ASSERT(ring->next != NULL && ring->prev != NULL); + ASSERT(pos); + ASSERT(pos->next != NULL && pos->prev != NULL); + ret = pos->next; + return (ret == ring) ? NULL : ret; +} + +static inline RingItem *ring_prev(Ring *ring, RingItem *pos) +{ + RingItem *ret; + + ASSERT(ring->next != NULL && ring->prev != NULL); + ASSERT(pos); + ASSERT(pos->next != NULL && pos->prev != NULL); + ret = pos->prev; + return (ret == ring) ? NULL : ret; +} + +#define RING_FOREACH_SAFE(var, next, ring) \ + for ((var) = ring_get_head(ring), \ + (next) = (var) ? ring_next(ring, (var)) : NULL; \ + (var); \ + (var) = (next), \ + (next) = (var) ? ring_next(ring, (var)) : NULL) + +/**/ + #define NOT_IMPLEMENTED printf("%s not implemented\n", __func__); static SpiceCoreInterface core; +typedef struct SpiceTimer { + RingItem link; + SpiceTimerFunc func; + struct timeval tv_start; + int ms; + void *opaque; +} Timer; + +Ring timers; + static SpiceTimer* timer_add(SpiceTimerFunc func, void *opaque) { - NOT_IMPLEMENTED + SpiceTimer *timer = calloc(sizeof(SpiceTimer), 1); + + timer->func = func; + timer->opaque = opaque; + ring_add(&timers, &timer->link); + return timer; +} + +static void add_ms_to_timeval(struct timeval *tv, int ms) +{ + tv->tv_usec += 1000 * ms; + while (tv->tv_usec >= 1000000) { + tv->tv_sec++; + tv->tv_usec -= 1000000; + } } static void timer_start(SpiceTimer *timer, uint32_t ms) { - NOT_IMPLEMENTED + gettimeofday(&timer->tv_start, NULL); + timer->ms = ms; + // already add ms to timer value + add_ms_to_timeval(&timer->tv_start, ms); + ASSERT(timer->ms); } static void timer_cancel(SpiceTimer *timer) { - NOT_IMPLEMENTED + timer->ms = 0; } static void timer_remove(SpiceTimer *timer) { - NOT_IMPLEMENTED + ring_remove(&timer->link); } struct SpiceWatch { - int id; -}; - -typedef struct Watch { - SpiceWatch id; + RingItem link; int fd; int event_mask; SpiceWatchFunc func; void *opaque; -} Watch; + int remove; +}; + +Ring watches; -Watch watches[100]; int watch_count = 0; -int next_id = 1; static SpiceWatch *watch_add(int fd, int event_mask, SpiceWatchFunc func, void *opaque) { - watches[watch_count].fd = fd; - watches[watch_count].event_mask = event_mask; - watches[watch_count].func = func; - watches[watch_count].opaque = opaque; - watches[watch_count].id.id = next_id++; - return &watches[watch_count++].id; + SpiceWatch *watch = malloc(sizeof(SpiceWatch)); + + DPRINTF(0, "adding %p, fd=%d at %d", watch, + fd, watch_count); + watch->fd = fd; + watch->event_mask = event_mask; + watch->func = func; + watch->opaque = opaque; + watch->remove = FALSE; + ring_item_init(&watch->link); + ring_add(&watches, &watch->link); + watch_count++; + return watch; } static void watch_update_mask(SpiceWatch *watch, int event_mask) { - int i; - Watch *my_watch; - - for (i = 0 ; i < watch_count; ++i) { - if (watches[i].id.id == watch->id) { - my_watch = &watches[i]; - if (my_watch->event_mask != event_mask) { - my_watch->event_mask = event_mask; - } - return; - } - } + DPRINTF(0, "fd %d to %d", watch->fd, event_mask); + watch->event_mask = event_mask; } static void watch_remove(SpiceWatch *watch) { - int i; + DPRINTF(0, "remove %p (fd %d)", watch, watch->fd); + ring_remove(&watch->link); + watch->remove = TRUE; + watch_count--; +} - for (i = 0 ; i < watch_count ; ++i) { - if (watches[i].id.id == watch->id) { - watches[i] = watches[--watch_count]; - return; +static void channel_event(int event, SpiceChannelEventInfo *info) +{ + NOT_IMPLEMENTED +} + +SpiceTimer *get_next_timer(void) +{ + SpiceTimer *next, *min; + + if (ring_is_empty(&timers)) { + return NULL; + } + min = next = (SpiceTimer*)ring_get_head(&timers); + while ((next=(SpiceTimer*)ring_next(&timers, &next->link)) != NULL) { + if (next->ms && + (next->tv_start.tv_sec < min->tv_start.tv_sec || + (next->tv_start.tv_sec == min->tv_start.tv_sec && + next->tv_start.tv_usec < min->tv_start.tv_usec))) { + min = next; } } + return min; } -static void channel_event(int event, SpiceChannelEventInfo *info) +struct timeval now; + +void tv_b_minus_a_return_le_zero(struct timeval *a, struct timeval *b, struct timeval *dest) { - NOT_IMPLEMENTED + dest->tv_usec = b->tv_usec - a->tv_usec; + dest->tv_sec = b->tv_sec - a->tv_sec; + while (dest->tv_usec < 0) { + dest->tv_usec += 1000000; + dest->tv_sec--; + } + if (dest->tv_sec < 0) { + dest->tv_sec = 0; + dest->tv_usec = 0; + } +} + +void calc_next_timeout(SpiceTimer *next, struct timeval *timeout) +{ + gettimeofday(&now, NULL); + tv_b_minus_a_return_le_zero(&now, &next->tv_start, timeout); +} + +void timeout_timers() +{ + SpiceTimer *next; + struct timeval left; + int count = 0; + + next = (SpiceTimer*)ring_get_head(&timers); + while (next != NULL) { + tv_b_minus_a_return_le_zero(&now, &next->tv_start, &left); + if (next->ms && left.tv_usec == 0 && left.tv_sec == 0) { + count++; + DPRINTF(1, "calling timer"); + next->func(next->opaque); + } + next = (SpiceTimer*)ring_next(&timers, &next->link); + } + DPRINTF(1, "called %d timers", count); } void basic_event_loop_mainloop(void) @@ -92,12 +290,20 @@ void basic_event_loop_mainloop(void) int max_fd = -1; int i; int retval; + SpiceWatch *watch; + SpiceTimer *next_timer; + RingItem *link; + RingItem *next; + struct timeval next_timer_timeout; + struct timeval *timeout; while (1) { FD_ZERO(&rfds); FD_ZERO(&wfds); - for (i = 0 ; i < watch_count; ++i) { - Watch* watch = &watches[i]; + watch = (SpiceWatch*)watches.next; + i = 0; + RING_FOREACH_SAFE(link, next, &watches) { + watch = (SpiceWatch*)link; if (watch->event_mask & SPICE_WATCH_EVENT_READ) { FD_SET(watch->fd, &rfds); max_fd = watch->fd > max_fd ? watch->fd : max_fd; @@ -106,21 +312,43 @@ void basic_event_loop_mainloop(void) FD_SET(watch->fd, &wfds); max_fd = watch->fd > max_fd ? watch->fd : max_fd; } + i++; + } + if ((next_timer = get_next_timer()) != NULL) { + calc_next_timeout(next_timer, &next_timer_timeout); + timeout = &next_timer_timeout; + DPRINTF(1, "timeout of %d.%06d", + timeout->tv_sec, timeout->tv_usec); + } else { + timeout = NULL; + } + DPRINTF(1, "watching %d fds", i); + retval = select(max_fd + 1, &rfds, &wfds, NULL, timeout); + if (timeout != NULL) { + calc_next_timeout(next_timer, &next_timer_timeout); + if (next_timer_timeout.tv_sec == 0 && + next_timer_timeout.tv_usec == 0) { + timeout_timers(); + } } - retval = select(max_fd + 1, &rfds, &wfds, NULL, NULL); if (retval == -1) { printf("error in select - exiting\n"); - exit(-1); + abort(); } if (retval) { - for (i = 0 ; i < watch_count; ++i) { - Watch* watch = &watches[i]; - if ((watch->event_mask & SPICE_WATCH_EVENT_READ) && FD_ISSET(watch->fd, &rfds)) { + RING_FOREACH_SAFE(link, next, &watches) { + watch = (SpiceWatch*)link; + if ((watch->event_mask & SPICE_WATCH_EVENT_READ) + && FD_ISSET(watch->fd, &rfds)) { watch->func(watch->fd, SPICE_WATCH_EVENT_READ, watch->opaque); } - if ((watch->event_mask & SPICE_WATCH_EVENT_WRITE) && FD_ISSET(watch->fd, &wfds)) { + if (!watch->remove && (watch->event_mask & SPICE_WATCH_EVENT_WRITE) + && FD_ISSET(watch->fd, &wfds)) { watch->func(watch->fd, SPICE_WATCH_EVENT_WRITE, watch->opaque); } + if (watch->remove) { + free(watch); + } } } } @@ -128,6 +356,8 @@ void basic_event_loop_mainloop(void) SpiceCoreInterface *basic_event_loop_init(void) { + ring_init(&watches); + ring_init(&timers); bzero(&core, sizeof(core)); core.base.major_version = SPICE_INTERFACE_CORE_MAJOR; core.base.minor_version = SPICE_INTERFACE_CORE_MINOR; // anything less then 3 and channel_event isn't called diff --git a/server/tests/test_display_no_ssl.c b/server/tests/test_display_no_ssl.c new file mode 100644 index 00000000..7edc8dc7 --- /dev/null +++ b/server/tests/test_display_no_ssl.c @@ -0,0 +1,291 @@ +#include +#include +#include +#include +#include +#include +#include +#include "red_channel.h" +#include "test_util.h" +#include "basic_event_loop.h" + +/* Parts cribbed from spice-display.h/.c/qxl.c */ + +typedef struct SimpleSpiceUpdate { + QXLDrawable drawable; + QXLImage image; + QXLCommandExt ext; + uint8_t *bitmap; +} SimpleSpiceUpdate; + +void test_spice_destroy_update(SimpleSpiceUpdate *update) +{ + free(update->bitmap); + free(update); +} + +#define WIDTH 800 +#define HEIGHT 600 + +static float angle = 0; +static int unique = 1; +static int color = 0; + +SimpleSpiceUpdate *test_spice_create_update() +{ + SimpleSpiceUpdate *update; + QXLDrawable *drawable; + QXLImage *image; + QXLCommand *cmd; + QXLRect bbox = { + .top = HEIGHT/2 + (HEIGHT/3)*cos(angle), + .left = WIDTH/2 + (WIDTH/3)*sin(angle), + }; + uint8_t *dst; + int bw, bh; + int i; + + angle += 0.1; + color = (color + 1) % 10; + unique++; + + update = calloc(sizeof(*update), 1); + drawable = &update->drawable; + image = &update->image; + cmd = &update->ext.cmd; + + bw = 64; + bh = 48; + bbox.right = bbox.left + bw; + bbox.bottom = bbox.top + bh; + update->bitmap = malloc(bw * bh * 4); + //printf("allocated %p, %p\n", update, update->bitmap); + + drawable->bbox = bbox; + drawable->clip.type = SPICE_CLIP_TYPE_NONE; + drawable->effect = QXL_EFFECT_OPAQUE; + drawable->release_info.id = (intptr_t)update; + drawable->type = QXL_DRAW_COPY; + drawable->surfaces_dest[0] = -1; + drawable->surfaces_dest[1] = -1; + drawable->surfaces_dest[2] = -1; + + drawable->u.copy.rop_descriptor = SPICE_ROPD_OP_PUT; + drawable->u.copy.src_bitmap = (intptr_t)image; + drawable->u.copy.src_area.right = bw; + drawable->u.copy.src_area.bottom = bh; + + QXL_SET_IMAGE_ID(image, QXL_IMAGE_GROUP_DEVICE, unique); + image->descriptor.type = SPICE_IMAGE_TYPE_BITMAP; + image->bitmap.flags = QXL_BITMAP_DIRECT | QXL_BITMAP_TOP_DOWN; + image->bitmap.stride = bw * 4; + image->descriptor.width = image->bitmap.x = bw; + image->descriptor.height = image->bitmap.y = bh; + image->bitmap.data = (intptr_t)(update->bitmap); + image->bitmap.palette = 0; + image->bitmap.format = SPICE_BITMAP_FMT_32BIT; + + dst = update->bitmap; + for (i = 0 ; i < bh * bw ; ++i, dst+=4) { + *dst = (color+i % 255); + *(dst+1) = 255 - color; + *(dst+2) = (color * (color + i)) & 0xff; + *(dst+3) = 0; + } + + cmd->type = QXL_CMD_DRAW; + cmd->data = (intptr_t)drawable; + + return update; +} + +#define MEM_SLOT_GROUP_ID 0 + +static QXLWorker *qxl_worker = NULL; +static uint8_t primary_surface[HEIGHT * WIDTH * 4]; + +void create_test_primary_surface(QXLWorker *worker) +{ + QXLDevSurfaceCreate surface; + + surface.format = SPICE_SURFACE_FMT_32_xRGB; + surface.width = WIDTH; + surface.height = HEIGHT; + surface.stride = -WIDTH * 4; + surface.mouse_mode = TRUE; + surface.flags = 0; + surface.type = 0; + surface.mem = (intptr_t)&primary_surface; + surface.group_id = MEM_SLOT_GROUP_ID; + + qxl_worker->create_primary_surface(qxl_worker, 0, &surface); +} + +QXLDevMemSlot slot = { +.slot_group_id = MEM_SLOT_GROUP_ID, +.slot_id = 0, +.generation = 0, +.virt_start = 0, +.virt_end = ~0, +.addr_delta = 0, +.qxl_ram_size = ~0, +}; + +void attache_worker(QXLInstance *qin, QXLWorker *_qxl_worker) +{ + static int count = 0; + if (++count > 1) { + printf("%s ignored\n", __func__); + return; + } + printf("%s\n", __func__); + qxl_worker = _qxl_worker; + qxl_worker->add_memslot(qxl_worker, &slot); + create_test_primary_surface(qxl_worker); + qxl_worker->start(qxl_worker); +} + +void set_compression_level(QXLInstance *qin, int level) +{ + printf("%s\n", __func__); +} + +void set_mm_time(QXLInstance *qin, uint32_t mm_time) +{ +} + +void get_init_info(QXLInstance *qin, QXLDevInitInfo *info) +{ + bzero(info, sizeof(*info)); + info->num_memslots = 1; + info->num_memslots_groups = 1; + info->memslot_id_bits = 1; + info->memslot_gen_bits = 1; + info->n_surfaces = 1; +} + +#define NOTIFY_BATCH 5 + +int notify = NOTIFY_BATCH; + +int get_command(QXLInstance *qin, struct QXLCommandExt *ext) +{ + SimpleSpiceUpdate *update; + + if (!notify--) { + return FALSE; + } + update = test_spice_create_update(); + *ext = update->ext; + notify = FALSE; + return TRUE; +} + + +SpiceTimer *wakeup_timer; +int wakeup_ms = 500; + +int req_cmd_notification(QXLInstance *qin) +{ + core->timer_start(wakeup_timer, wakeup_ms); + return TRUE; +} + +void do_wakeup() +{ + notify = NOTIFY_BATCH; + qxl_worker->wakeup(qxl_worker); +} + +void release_resource(QXLInstance *qin, struct QXLReleaseInfoExt release_info) +{ + //printf("%s\n", __func__); + ASSERT(release_info.group_id == MEM_SLOT_GROUP_ID); + test_spice_destroy_update((void*)release_info.info->id); +} + +int get_cursor_command(QXLInstance *qin, struct QXLCommandExt *cmd) +{ + //printf("%s\n", __func__); + return FALSE; +} + +int req_cursor_notification(QXLInstance *qin) +{ + printf("%s\n", __func__); + return TRUE; +} + +void notify_update(QXLInstance *qin, uint32_t update_id) +{ + printf("%s\n", __func__); +} + +int flush_resources(QXLInstance *qin) +{ + printf("%s\n", __func__); +} + +QXLInterface display_sif = { + .base = { + .type = SPICE_INTERFACE_QXL, + .description = "test", + .major_version = SPICE_INTERFACE_QXL_MAJOR, + .minor_version = SPICE_INTERFACE_QXL_MINOR + }, + .attache_worker = attache_worker, + .set_compression_level = set_compression_level, + .set_mm_time = set_mm_time, + .get_init_info = get_init_info, + .get_command = get_command, + .req_cmd_notification = req_cmd_notification, + .release_resource = release_resource, + .get_cursor_command = get_cursor_command, + .req_cursor_notification = req_cursor_notification, + .notify_update = notify_update, + .flush_resources = flush_resources, +}; + +QXLInstance display_sin = { + .base = { + .sif = &display_sif.base, + }, + .id = 0, +}; + +SpiceServer *server; +SpiceCoreInterface *core; +SpiceTimer *ping_timer; + +void show_channels(SpiceServer *server); + +int ping_ms = 100; + +void pinger(void *opaque) +{ + // show_channels is not thread safe - fails if disconnections / connections occur + //show_channels(server); + + core->timer_start(ping_timer, ping_ms); +} + +int main() +{ + core = basic_event_loop_init(); + server = spice_server_new(); + bzero(primary_surface, sizeof(primary_surface)); + spice_server_set_port(server, 5912); + spice_server_set_noauth(server); + spice_server_init(server, core); + //spice_server_set_image_compression(server, SPICE_IMAGE_COMPRESS_OFF); + spice_server_add_interface(server, &display_sin.base); + + ping_timer = core->timer_add(pinger, NULL); + wakeup_timer = core->timer_add(do_wakeup, NULL); + core->timer_start(ping_timer, ping_ms); + + basic_event_loop_mainloop(); + + return 0; +} + diff --git a/server/tests/test_util.h b/server/tests/test_util.h new file mode 100644 index 00000000..ac1442dd --- /dev/null +++ b/server/tests/test_util.h @@ -0,0 +1,12 @@ +#ifndef __TEST_UTIL_H__ +#define __TEST_UTIL_H__ + +#define TRUE 1 +#define FALSE 0 + +#define ASSERT(x) if (!(x)) { \ + printf("%s: ASSERT %s failed\n", __FUNCTION__, #x); \ + abort(); \ +} + +#endif // __TEST_UTIL_H__ -- cgit