8 #include <sys/select.h>
13 #include <spice/macros.h>
14 #include <spice/qxl_dev.h>
16 #include "test_display_base.h"
17 //#include "red_channel.h"
19 #define MEM_SLOT_GROUP_ID 0
21 #define NOTIFY_DISPLAY_BATCH (SINGLE_PART/2)
22 #define NOTIFY_CURSOR_BATCH 10
24 /* Parts cribbed from spice-display.h/.c/qxl.c */
26 typedef struct SimpleSpiceUpdate
{
27 QXLCommandExt ext
; // first
33 typedef struct SimpleSurfaceCmd
{
34 QXLCommandExt ext
; // first
35 QXLSurfaceCmd surface_cmd
;
38 static void test_spice_destroy_update(SimpleSpiceUpdate
*update
)
43 if (update
->drawable
.clip
.type
!= SPICE_CLIP_TYPE_NONE
) {
44 uint8_t *ptr
= (uint8_t*)update
->drawable
.clip
.data
;
51 #define DEFAULT_WIDTH 640
52 #define DEFAULT_HEIGHT 320
55 static const int angle_parts
= 64 / SINGLE_PART
;
56 static int unique
= 1;
57 static int color
= -1;
60 /* Used for automated tests */
61 static int control
= 3; //used to know when we can take a screenshot
62 static int rects
= 16; //number of rects that will be draw
63 static int has_automated_tests
= 0; //automated test flag
65 __attribute__((noreturn
))
66 static void sigchld_handler(int signal_num
) // wait for the child process and exit
73 static void regression_test(void)
90 char *envp
[] = {buf
, NULL
};
92 snprintf(buf
, sizeof(buf
), "PATH=%s", getenv("PATH"));
93 execve("regression_test.py", NULL
, envp
);
99 static void set_cmd(QXLCommandExt
*ext
, uint32_t type
, QXLPHYSICAL data
)
101 ext
->cmd
.type
= type
;
102 ext
->cmd
.data
= data
;
103 ext
->cmd
.padding
= 0;
104 ext
->group_id
= MEM_SLOT_GROUP_ID
;
108 static void simple_set_release_info(QXLReleaseInfo
*info
, intptr_t ptr
)
111 //info->group_id = MEM_SLOT_GROUP_ID;
114 typedef struct Path
{
120 static void path_init(Path
*path
, int min
, int max
)
127 static void path_progress(Path
*path
)
129 path
->t
= (path
->t
+1)% (path
->max_t
- path
->min_t
) + path
->min_t
;
134 static void draw_pos(Test
*test
, int t
, int *x
, int *y
)
137 *y
= test
->primary_height
/2 + (test
->primary_height
/3)*cos(t
*2*M_PI
/angle_parts
);
138 *x
= test
->primary_width
/2 + (test
->primary_width
/3)*sin(t
*2*M_PI
/angle_parts
);
140 *y
= test
->primary_height
*(t
% SINGLE_PART
)/SINGLE_PART
;
141 *x
= ((test
->primary_width
/SINGLE_PART
)*(t
/ SINGLE_PART
)) % test
->primary_width
;
145 /* bitmap and rects are freed, so they must be allocated with malloc */
146 SimpleSpiceUpdate
*test_spice_create_update_from_bitmap(uint32_t surface_id
,
149 uint32_t num_clip_rects
,
152 SimpleSpiceUpdate
*update
;
153 QXLDrawable
*drawable
;
157 bh
= bbox
.bottom
- bbox
.top
;
158 bw
= bbox
.right
- bbox
.left
;
160 update
= calloc(sizeof(*update
), 1);
161 update
->bitmap
= bitmap
;
162 drawable
= &update
->drawable
;
163 image
= &update
->image
;
165 drawable
->surface_id
= surface_id
;
167 drawable
->bbox
= bbox
;
168 if (num_clip_rects
== 0) {
169 drawable
->clip
.type
= SPICE_CLIP_TYPE_NONE
;
171 QXLClipRects
*cmd_clip
;
173 cmd_clip
= calloc(sizeof(QXLClipRects
) + num_clip_rects
*sizeof(QXLRect
), 1);
174 cmd_clip
->num_rects
= num_clip_rects
;
175 cmd_clip
->chunk
.data_size
= num_clip_rects
*sizeof(QXLRect
);
176 cmd_clip
->chunk
.prev_chunk
= cmd_clip
->chunk
.next_chunk
= 0;
177 memcpy(cmd_clip
+ 1, clip_rects
, cmd_clip
->chunk
.data_size
);
179 drawable
->clip
.type
= SPICE_CLIP_TYPE_RECTS
;
180 drawable
->clip
.data
= (intptr_t)cmd_clip
;
184 drawable
->effect
= QXL_EFFECT_OPAQUE
;
185 simple_set_release_info(&drawable
->release_info
, (intptr_t)update
);
186 drawable
->type
= QXL_DRAW_COPY
;
187 drawable
->surfaces_dest
[0] = -1;
188 drawable
->surfaces_dest
[1] = -1;
189 drawable
->surfaces_dest
[2] = -1;
191 drawable
->u
.copy
.rop_descriptor
= SPICE_ROPD_OP_PUT
;
192 drawable
->u
.copy
.src_bitmap
= (intptr_t)image
;
193 drawable
->u
.copy
.src_area
.right
= bw
;
194 drawable
->u
.copy
.src_area
.bottom
= bh
;
196 QXL_SET_IMAGE_ID(image
, QXL_IMAGE_GROUP_DEVICE
, unique
);
197 image
->descriptor
.type
= SPICE_IMAGE_TYPE_BITMAP
;
198 image
->bitmap
.flags
= QXL_BITMAP_DIRECT
| QXL_BITMAP_TOP_DOWN
;
199 image
->bitmap
.stride
= bw
* 4;
200 image
->descriptor
.width
= image
->bitmap
.x
= bw
;
201 image
->descriptor
.height
= image
->bitmap
.y
= bh
;
202 image
->bitmap
.data
= (intptr_t)bitmap
;
203 image
->bitmap
.palette
= 0;
204 image
->bitmap
.format
= SPICE_BITMAP_FMT_32BIT
;
206 set_cmd(&update
->ext
, QXL_CMD_DRAW
, (intptr_t)drawable
);
211 static SimpleSpiceUpdate
*test_spice_create_update_solid(uint32_t surface_id
, QXLRect bbox
, uint32_t color
)
219 bw
= bbox
.right
- bbox
.left
;
220 bh
= bbox
.bottom
- bbox
.top
;
222 bitmap
= malloc(bw
* bh
* 4);
223 dst
= (uint32_t *)bitmap
;
225 for (i
= 0 ; i
< bh
* bw
; ++i
, ++dst
) {
229 return test_spice_create_update_from_bitmap(surface_id
, bbox
, bitmap
, 0, NULL
);
232 static SimpleSpiceUpdate
*test_spice_create_update_draw(Test
*test
, uint32_t surface_id
, int t
)
241 draw_pos(test
, t
, &left
, &top
);
242 if ((t
% angle_parts
) == 0) {
246 if (surface_id
!= 0) {
247 color
= (color
+ 1) % 2;
254 bw
= test
->primary_width
/SINGLE_PART
;
257 bitmap
= dst
= malloc(bw
* bh
* 4);
258 //printf("allocated %p\n", dst);
260 for (i
= 0 ; i
< bh
* bw
; ++i
, dst
+=4) {
261 *dst
= (color
+i
% 255);
262 *(dst
+((1+c_i
)%3)) = 255 - color
;
263 *(dst
+((2+c_i
)%3)) = (color
* (color
+ i
)) & 0xff;
264 *(dst
+((3+c_i
)%3)) = 0;
267 bbox
.left
= left
; bbox
.top
= top
;
268 bbox
.right
= left
+ bw
; bbox
.bottom
= top
+ bh
;
269 return test_spice_create_update_from_bitmap(surface_id
, bbox
, bitmap
, 0, NULL
);
272 static SimpleSpiceUpdate
*test_spice_create_update_copy_bits(Test
*test
, uint32_t surface_id
)
274 SimpleSpiceUpdate
*update
;
275 QXLDrawable
*drawable
;
282 update
= calloc(sizeof(*update
), 1);
283 drawable
= &update
->drawable
;
285 bw
= test
->primary_width
/SINGLE_PART
;
287 bbox
.right
= bbox
.left
+ bw
;
288 bbox
.bottom
= bbox
.top
+ bh
;
289 //printf("allocated %p, %p\n", update, update->bitmap);
291 drawable
->surface_id
= surface_id
;
293 drawable
->bbox
= bbox
;
294 drawable
->clip
.type
= SPICE_CLIP_TYPE_NONE
;
295 drawable
->effect
= QXL_EFFECT_OPAQUE
;
296 simple_set_release_info(&drawable
->release_info
, (intptr_t)update
);
297 drawable
->type
= QXL_COPY_BITS
;
298 drawable
->surfaces_dest
[0] = -1;
299 drawable
->surfaces_dest
[1] = -1;
300 drawable
->surfaces_dest
[2] = -1;
302 drawable
->u
.copy_bits
.src_pos
.x
= 0;
303 drawable
->u
.copy_bits
.src_pos
.y
= 0;
305 set_cmd(&update
->ext
, QXL_CMD_DRAW
, (intptr_t)drawable
);
310 static int format_to_bpp(int format
)
313 case SPICE_SURFACE_FMT_8_A
:
315 case SPICE_SURFACE_FMT_16_555
:
316 case SPICE_SURFACE_FMT_16_565
:
318 case SPICE_SURFACE_FMT_32_xRGB
:
319 case SPICE_SURFACE_FMT_32_ARGB
:
325 static SimpleSurfaceCmd
*create_surface(int surface_id
, int format
, int width
, int height
, uint8_t *data
)
327 SimpleSurfaceCmd
*simple_cmd
= calloc(sizeof(SimpleSurfaceCmd
), 1);
328 QXLSurfaceCmd
*surface_cmd
= &simple_cmd
->surface_cmd
;
329 int bpp
= format_to_bpp(format
);
331 set_cmd(&simple_cmd
->ext
, QXL_CMD_SURFACE
, (intptr_t)surface_cmd
);
332 simple_set_release_info(&surface_cmd
->release_info
, (intptr_t)simple_cmd
);
333 surface_cmd
->type
= QXL_SURFACE_CMD_CREATE
;
334 surface_cmd
->flags
= 0; // ?
335 surface_cmd
->surface_id
= surface_id
;
336 surface_cmd
->u
.surface_create
.format
= format
;
337 surface_cmd
->u
.surface_create
.width
= width
;
338 surface_cmd
->u
.surface_create
.height
= height
;
339 surface_cmd
->u
.surface_create
.stride
= -width
* bpp
;
340 surface_cmd
->u
.surface_create
.data
= (intptr_t)data
;
344 static SimpleSurfaceCmd
*destroy_surface(int surface_id
)
346 SimpleSurfaceCmd
*simple_cmd
= calloc(sizeof(SimpleSurfaceCmd
), 1);
347 QXLSurfaceCmd
*surface_cmd
= &simple_cmd
->surface_cmd
;
349 set_cmd(&simple_cmd
->ext
, QXL_CMD_SURFACE
, (intptr_t)surface_cmd
);
350 simple_set_release_info(&surface_cmd
->release_info
, (intptr_t)simple_cmd
);
351 surface_cmd
->type
= QXL_SURFACE_CMD_DESTROY
;
352 surface_cmd
->flags
= 0; // ?
353 surface_cmd
->surface_id
= surface_id
;
357 static void create_primary_surface(Test
*test
, uint32_t width
,
360 QXLWorker
*qxl_worker
= test
->qxl_worker
;
361 QXLDevSurfaceCreate surface
= { 0, };
363 g_assert(height
<= MAX_HEIGHT
);
364 g_assert(width
<= MAX_WIDTH
);
365 g_assert(height
> 0);
368 surface
.format
= SPICE_SURFACE_FMT_32_xRGB
;
369 surface
.width
= test
->primary_width
= width
;
370 surface
.height
= test
->primary_height
= height
;
371 surface
.stride
= -width
* 4; /* negative? */
372 surface
.mouse_mode
= TRUE
; /* unused by red_worker */
374 surface
.type
= 0; /* unused by red_worker */
375 surface
.position
= 0; /* unused by red_worker */
376 surface
.mem
= (uint64_t)&test
->primary_surface
;
377 surface
.group_id
= MEM_SLOT_GROUP_ID
;
380 test
->height
= height
;
382 qxl_worker
->create_primary_surface(qxl_worker
, 0, &surface
);
385 QXLDevMemSlot slot
= {
386 .slot_group_id
= MEM_SLOT_GROUP_ID
,
395 static void attache_worker(QXLInstance
*qin
, QXLWorker
*_qxl_worker
)
397 Test
*test
= SPICE_CONTAINEROF(qin
, Test
, qxl_instance
);
399 if (test
->qxl_worker
) {
400 if (test
->qxl_worker
!= _qxl_worker
)
401 printf("%s ignored, %p is set, ignoring new %p\n", __func__
,
402 test
->qxl_worker
, _qxl_worker
);
404 printf("%s ignored, redundant\n", __func__
);
407 printf("%s\n", __func__
);
408 test
->qxl_worker
= _qxl_worker
;
409 test
->qxl_worker
->add_memslot(test
->qxl_worker
, &slot
);
410 create_primary_surface(test
, DEFAULT_WIDTH
, DEFAULT_HEIGHT
);
411 test
->qxl_worker
->start(test
->qxl_worker
);
414 static void set_compression_level(QXLInstance
*qin
, int level
)
416 printf("%s\n", __func__
);
419 static void set_mm_time(QXLInstance
*qin
, uint32_t mm_time
)
423 // we now have a secondary surface
424 #define MAX_SURFACE_NUM 2
426 static void get_init_info(QXLInstance
*qin
, QXLDevInitInfo
*info
)
428 memset(info
, 0, sizeof(*info
));
429 info
->num_memslots
= 1;
430 info
->num_memslots_groups
= 1;
431 info
->memslot_id_bits
= 1;
432 info
->memslot_gen_bits
= 1;
433 info
->n_surfaces
= MAX_SURFACE_NUM
;
436 // We shall now have a ring of commands, so that we can update
437 // it from a separate thread - since get_command is called from
438 // the worker thread, and we need to sometimes do an update_area,
439 // which cannot be done from red_worker context (not via dispatcher,
440 // since you get a deadlock, and it isn't designed to be done
441 // any other way, so no point testing that).
442 int commands_end
= 0;
443 int commands_start
= 0;
444 struct QXLCommandExt
* commands
[1024];
446 #define COMMANDS_SIZE COUNT(commands)
448 static void push_command(QXLCommandExt
*ext
)
450 g_assert(commands_end
- commands_start
< COMMANDS_SIZE
);
451 commands
[commands_end
% COMMANDS_SIZE
] = ext
;
455 static struct QXLCommandExt
*get_simple_command(void)
457 struct QXLCommandExt
*ret
= commands
[commands_start
% COMMANDS_SIZE
];
458 g_assert(commands_start
< commands_end
);
463 static int get_num_commands(void)
465 return commands_end
- commands_start
;
468 // called from spice_server thread (i.e. red_worker thread)
469 static int get_command(QXLInstance
*qin
, struct QXLCommandExt
*ext
)
471 if (get_num_commands() == 0) {
474 *ext
= *get_simple_command();
478 static void produce_command(Test
*test
)
481 QXLWorker
*qxl_worker
= test
->qxl_worker
;
483 g_assert(qxl_worker
);
485 if (test
->has_secondary
)
486 test
->target_surface
= 1;
488 if (!test
->num_commands
) {
493 command
= &test
->commands
[test
->cmd_index
];
495 command
->cb(test
, command
);
497 switch (command
->command
) {
499 printf("sleep %u seconds\n", command
->sleep
.secs
);
500 sleep(command
->sleep
.secs
);
503 path_progress(&path
);
505 case SIMPLE_UPDATE
: {
508 .right
= (test
->target_surface
== 0 ? test
->primary_width
: test
->width
),
510 .bottom
= (test
->target_surface
== 0 ? test
->primary_height
: test
->height
)
512 if (rect
.right
> 0 && rect
.bottom
> 0) {
513 qxl_worker
->update_area(qxl_worker
, test
->target_surface
, &rect
, NULL
, 0, 1);
518 /* Drawing commands, they all push a command to the command ring */
519 case SIMPLE_COPY_BITS
:
520 case SIMPLE_DRAW_SOLID
:
521 case SIMPLE_DRAW_BITMAP
:
523 SimpleSpiceUpdate
*update
;
525 if (has_automated_tests
)
534 switch (command
->command
) {
535 case SIMPLE_COPY_BITS
:
536 update
= test_spice_create_update_copy_bits(test
, 0);
539 update
= test_spice_create_update_draw(test
, 0, path
.t
);
541 case SIMPLE_DRAW_BITMAP
:
542 update
= test_spice_create_update_from_bitmap(command
->bitmap
.surface_id
,
543 command
->bitmap
.bbox
, command
->bitmap
.bitmap
,
544 command
->bitmap
.num_clip_rects
, command
->bitmap
.clip_rects
);
546 case SIMPLE_DRAW_SOLID
:
547 update
= test_spice_create_update_solid(command
->solid
.surface_id
,
548 command
->solid
.bbox
, command
->solid
.color
);
551 push_command(&update
->ext
);
555 case SIMPLE_CREATE_SURFACE
: {
556 SimpleSurfaceCmd
*update
;
557 if (command
->create_surface
.data
) {
558 g_assert(command
->create_surface
.surface_id
> 0);
559 g_assert(command
->create_surface
.surface_id
< MAX_SURFACE_NUM
);
560 g_assert(command
->create_surface
.surface_id
== 1);
561 update
= create_surface(command
->create_surface
.surface_id
,
562 command
->create_surface
.format
,
563 command
->create_surface
.width
,
564 command
->create_surface
.height
,
565 command
->create_surface
.data
);
567 update
= create_surface(test
->target_surface
, SPICE_SURFACE_FMT_32_xRGB
,
568 SURF_WIDTH
, SURF_HEIGHT
,
569 test
->secondary_surface
);
571 push_command(&update
->ext
);
572 test
->has_secondary
= 1;
576 case SIMPLE_DESTROY_SURFACE
: {
577 SimpleSurfaceCmd
*update
;
578 test
->has_secondary
= 0;
579 update
= destroy_surface(test
->target_surface
);
580 test
->target_surface
= 0;
581 push_command(&update
->ext
);
585 case DESTROY_PRIMARY
:
586 qxl_worker
->destroy_primary_surface(qxl_worker
, 0);
590 create_primary_surface(test
,
591 command
->create_primary
.width
, command
->create_primary
.height
);
594 test
->cmd_index
= (test
->cmd_index
+ 1) % test
->num_commands
;
597 static int req_cmd_notification(QXLInstance
*qin
)
599 Test
*test
= SPICE_CONTAINEROF(qin
, Test
, qxl_instance
);
601 test
->core
->timer_start(test
->wakeup_timer
, test
->wakeup_ms
);
605 static void do_wakeup(void *opaque
)
610 test
->cursor_notify
= NOTIFY_CURSOR_BATCH
;
611 for (notify
= NOTIFY_DISPLAY_BATCH
; notify
> 0;--notify
) {
612 produce_command(test
);
615 test
->core
->timer_start(test
->wakeup_timer
, test
->wakeup_ms
);
616 test
->qxl_worker
->wakeup(test
->qxl_worker
);
619 static void release_resource(QXLInstance
*qin
, struct QXLReleaseInfoExt release_info
)
621 QXLCommandExt
*ext
= (QXLCommandExt
*)(unsigned long)release_info
.info
->id
;
622 //printf("%s\n", __func__);
623 g_assert(release_info
.group_id
== MEM_SLOT_GROUP_ID
);
624 switch (ext
->cmd
.type
) {
626 test_spice_destroy_update((void*)ext
);
628 case QXL_CMD_SURFACE
:
631 case QXL_CMD_CURSOR
: {
632 QXLCursorCmd
*cmd
= (QXLCursorCmd
*)(unsigned long)ext
->cmd
.data
;
633 if (cmd
->type
== QXL_CURSOR_SET
) {
644 #define CURSOR_WIDTH 32
645 #define CURSOR_HEIGHT 32
649 uint8_t data
[CURSOR_WIDTH
* CURSOR_HEIGHT
* 4]; // 32bit per pixel
652 static void cursor_init()
654 cursor
.cursor
.header
.unique
= 0;
655 cursor
.cursor
.header
.type
= SPICE_CURSOR_TYPE_COLOR32
;
656 cursor
.cursor
.header
.width
= CURSOR_WIDTH
;
657 cursor
.cursor
.header
.height
= CURSOR_HEIGHT
;
658 cursor
.cursor
.header
.hot_spot_x
= 0;
659 cursor
.cursor
.header
.hot_spot_y
= 0;
660 cursor
.cursor
.data_size
= CURSOR_WIDTH
* CURSOR_HEIGHT
* 4;
662 // X drivers addes it to the cursor size because it could be
663 // cursor data information or another cursor related stuffs.
664 // Otherwise, the code will break in client/cursor.cpp side,
665 // that expect the data_size plus cursor information.
666 // Blame cursor protocol for this. :-)
667 cursor
.cursor
.data_size
+= 128;
668 cursor
.cursor
.chunk
.data_size
= cursor
.cursor
.data_size
;
669 cursor
.cursor
.chunk
.prev_chunk
= cursor
.cursor
.chunk
.next_chunk
= 0;
672 static int get_cursor_command(QXLInstance
*qin
, struct QXLCommandExt
*ext
)
674 Test
*test
= SPICE_CONTAINEROF(qin
, Test
, qxl_instance
);
675 static int color
= 0;
677 static int x
= 0, y
= 0;
678 QXLCursorCmd
*cursor_cmd
;
681 if (!test
->cursor_notify
) {
685 test
->cursor_notify
--;
686 cmd
= calloc(sizeof(QXLCommandExt
), 1);
687 cursor_cmd
= calloc(sizeof(QXLCursorCmd
), 1);
689 cursor_cmd
->release_info
.id
= (unsigned long)cmd
;
692 cursor_cmd
->type
= QXL_CURSOR_SET
;
693 cursor_cmd
->u
.set
.position
.x
= 0;
694 cursor_cmd
->u
.set
.position
.y
= 0;
695 cursor_cmd
->u
.set
.visible
= TRUE
;
696 cursor_cmd
->u
.set
.shape
= (unsigned long)&cursor
;
697 // Only a white rect (32x32) as cursor
698 memset(cursor
.data
, 255, sizeof(cursor
.data
));
701 cursor_cmd
->type
= QXL_CURSOR_MOVE
;
702 cursor_cmd
->u
.position
.x
= x
++ % test
->primary_width
;
703 cursor_cmd
->u
.position
.y
= y
++ % test
->primary_height
;
706 cmd
->cmd
.data
= (unsigned long)cursor_cmd
;
707 cmd
->cmd
.type
= QXL_CMD_CURSOR
;
708 cmd
->group_id
= MEM_SLOT_GROUP_ID
;
711 //printf("%s\n", __func__);
715 static int req_cursor_notification(QXLInstance
*qin
)
717 printf("%s\n", __func__
);
721 static void notify_update(QXLInstance
*qin
, uint32_t update_id
)
723 printf("%s\n", __func__
);
726 static int flush_resources(QXLInstance
*qin
)
728 printf("%s\n", __func__
);
732 static int client_monitors_config(QXLInstance
*qin
,
733 VDAgentMonitorsConfig
*monitors_config
)
735 if (!monitors_config
) {
736 printf("%s: NULL monitors_config\n", __func__
);
738 printf("%s: %d\n", __func__
, monitors_config
->num_of_monitors
);
743 static void set_client_capabilities(QXLInstance
*qin
,
744 uint8_t client_present
,
747 Test
*test
= SPICE_CONTAINEROF(qin
, Test
, qxl_instance
);
749 printf("%s: present %d caps %d\n", __func__
, client_present
, caps
[0]);
750 if (test
->on_client_connected
&& client_present
) {
751 test
->on_client_connected(test
);
753 if (test
->on_client_disconnected
&& !client_present
) {
754 test
->on_client_disconnected(test
);
758 QXLInterface display_sif
= {
760 .type
= SPICE_INTERFACE_QXL
,
761 .description
= "test",
762 .major_version
= SPICE_INTERFACE_QXL_MAJOR
,
763 .minor_version
= SPICE_INTERFACE_QXL_MINOR
765 .attache_worker
= attache_worker
,
766 .set_compression_level
= set_compression_level
,
767 .set_mm_time
= set_mm_time
,
768 .get_init_info
= get_init_info
,
770 /* the callbacks below are called from spice server thread context */
771 .get_command
= get_command
,
772 .req_cmd_notification
= req_cmd_notification
,
773 .release_resource
= release_resource
,
774 .get_cursor_command
= get_cursor_command
,
775 .req_cursor_notification
= req_cursor_notification
,
776 .notify_update
= notify_update
,
777 .flush_resources
= flush_resources
,
778 .client_monitors_config
= client_monitors_config
,
779 .set_client_capabilities
= set_client_capabilities
,
782 /* interface for tests */
783 void test_add_display_interface(Test
* test
)
785 spice_server_add_interface(test
->server
, &test
->qxl_instance
.base
);
788 static int vmc_write(SpiceCharDeviceInstance
*sin
, const uint8_t *buf
, int len
)
790 printf("%s: %d\n", __func__
, len
);
794 static int vmc_read(SpiceCharDeviceInstance
*sin
, uint8_t *buf
, int len
)
796 printf("%s: %d\n", __func__
, len
);
800 static void vmc_state(SpiceCharDeviceInstance
*sin
, int connected
)
802 printf("%s: %d\n", __func__
, connected
);
805 static SpiceCharDeviceInterface vdagent_sif
= {
806 .base
.type
= SPICE_INTERFACE_CHAR_DEVICE
,
807 .base
.description
= "test spice virtual channel char device",
808 .base
.major_version
= SPICE_INTERFACE_CHAR_DEVICE_MAJOR
,
809 .base
.minor_version
= SPICE_INTERFACE_CHAR_DEVICE_MINOR
,
815 SpiceCharDeviceInstance vdagent_sin
= {
817 .sif
= &vdagent_sif
.base
,
819 .subtype
= "vdagent",
822 void test_add_agent_interface(SpiceServer
*server
)
824 spice_server_add_interface(server
, &vdagent_sin
.base
);
827 void test_set_simple_command_list(Test
*test
, int *simple_commands
, int num_commands
)
832 test
->commands
= malloc(sizeof(*test
->commands
) * num_commands
);
833 memset(test
->commands
, 0, sizeof(*test
->commands
) * num_commands
);
834 test
->num_commands
= num_commands
;
835 for (i
= 0 ; i
< num_commands
; ++i
) {
836 test
->commands
[i
].command
= simple_commands
[i
];
840 void test_set_command_list(Test
*test
, Command
*commands
, int num_commands
)
842 test
->commands
= commands
;
843 test
->num_commands
= num_commands
;
847 Test
*test_new(SpiceCoreInterface
*core
)
850 Test
*test
= g_new0(Test
, 1);
851 SpiceServer
* server
= spice_server_new();
853 test
->qxl_instance
.base
.sif
= &display_sif
.base
;
854 test
->qxl_instance
.id
= 0;
857 test
->server
= server
;
858 test
->wakeup_ms
= 50;
859 test
->cursor_notify
= NOTIFY_CURSOR_BATCH
;
860 // some common initialization for all display tests
861 printf("TESTER: listening on port %d (unsecure)\n", port
);
862 spice_server_set_port(server
, port
);
863 spice_server_set_noauth(server
);
864 spice_server_init(server
, core
);
867 path_init(&path
, 0, angle_parts
);
868 test
->has_secondary
= 0;
869 test
->wakeup_timer
= core
->timer_add(do_wakeup
, test
);
873 void init_automated()
877 memset(&sa
, 0, sizeof sa
);
878 sa
.sa_handler
= &sigchld_handler
;
879 sigaction(SIGCHLD
, &sa
, NULL
);
882 __attribute__((noreturn
))
883 void usage(const char *argv0
, const int exitcode
)
885 #ifdef AUTOMATED_TESTS
886 const char *autoopt
=" [--automated-tests]";
888 const char *autoopt
="";
891 printf("usage: %s%s\n", argv0
, autoopt
);
895 void spice_test_config_parse_args(int argc
, char **argv
)
897 struct option options
[] = {
898 #ifdef AUTOMATED_TESTS
899 {"automated-tests", no_argument
, &has_automated_tests
, 1},
906 while ((val
= getopt_long(argc
, argv
, "", options
, &option_index
)) != -1) {
909 printf("unrecognized option '%s'\n", argv
[optind
- 1]);
910 usage(argv
[0], EXIT_FAILURE
);
917 printf("unknown argument '%s'\n", argv
[optind
]);
918 usage(argv
[0], EXIT_FAILURE
);
920 if (has_automated_tests
) {