diff --git a/.gitignore b/.gitignore index 3f57c02..2d8dfd3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ rdesktop autom4te.cache -Makefile +/Makefile config.log config.status configure diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 0000000..8e52b47 --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,49 @@ +CC=gcc +CFLAGS=-fPIC -Wall -Wextra -ggdb -gdwarf-2 -g3 +CGREEN_RUNNER=cgreen-runner + +TESTS=resize rdp xwin utils + + +RDP_MOCKS=ui_mock.o bitmap_mock.o secure_mock.o ssl_mock.o mppc_mock.o \ + cache_mock.o pstcache_mock.o orders_mock.o rdesktop_mock.o \ + rdp5_mock.o xkeymap_mock.o tcp_mock.o + +XWIN_MOCKS=x11_mock.o cache_mock.o xclip_mock.o xkeymap_mock.o seamless_mock.o \ + ctrl_mock.o rdpdr_mock.o ewmh_mock.o rdpedisp_mock.o rdp_mock.o + +UTILS_MOCKS= + +RESIZE_MOCKS=x11_mock.o cache_mock.o xclip_mock.o xkeymap_mock.o seamless_mock.o \ + ctrl_mock.o rdpdr_mock.o ewmh_mock.o rdpedisp_mock.o bitmap_mock.o \ + ssl_mock.o mppc_mock.o pstcache_mock.o orders_mock.o rdesktop_mock.o rdp5_mock.o \ + tcp_mock.o licence_mock.o mcs_mock.o channels_mock.o + + +all: test + +.PHONY: test +test: $(foreach test, $(TESTS), runtest.$(test)) + + +.PHONY: runtest.% +runtest.%: % + $(CGREEN_RUNNER) $^ + + +rdp: rdp_test.o $(RDP_MOCKS) + $(CC) $(CFLAGS) -shared -lcgreen -o $@ $^ + +xwin: xwin_test.o $(XWIN_MOCKS) + $(CC) $(CFLAGS) -shared -lcgreen -o $@ $^ -lX11 -lXcursor + +utils: utils_test.o $(UTILS_MOCKS) + $(CC) $(CFLAGS) -shared -lcgreen -o $@ $^ + +resize: resize_test.o $(RESIZE_MOCKS) + $(CC) $(CFLAGS) -shared -lcgreen -o $@ $^ -lX11 -lXcursor + + +.PHONY: clean +clean: + rm -f $(TESTS) *_mock.o *_test.o diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..36cccc2 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,26 @@ +# Automated test suite + +An basic test suite was added while implementing the dynamic resize +feature. Improvements to this test suite are very welcome. + + +## Requirements + +In addition to the build requirements of rdesktop itself, you also need: + + * [cgreen](https://github.com/cgreen-devs/cgreen) + + +## Building and running the tests + + cd tests + make + +This will build and run each test in turn. Re-running `make` will +recompile the tests as necessary, and run them again. + + +## Cgreen documentation + +You can find the Cgreen documentation over +at [their github site](https://cgreen-devs.github.io). diff --git a/tests/bitmap_mock.c b/tests/bitmap_mock.c new file mode 100644 index 0000000..f61da24 --- /dev/null +++ b/tests/bitmap_mock.c @@ -0,0 +1,7 @@ +#include +#include "../rdesktop.h" + +RD_BOOL bitmap_decompress(uint8 * output, int width, int height, uint8 * input, int size, int Bpp) +{ + return mock(output, width, height, input, size, Bpp); +}; diff --git a/tests/cache_mock.c b/tests/cache_mock.c new file mode 100644 index 0000000..ba28194 --- /dev/null +++ b/tests/cache_mock.c @@ -0,0 +1,46 @@ +#include +#include "../rdesktop.h" + +RD_HCURSOR +cache_get_cursor(uint16 cache_idx) +{ + return (RD_HCURSOR)mock(cache_idx); +} + +void +cache_put_cursor(uint16 cache_idx, RD_HCURSOR cursor) +{ + mock(cache_idx, cursor); +} + + +FONTGLYPH * +cache_get_font(uint8 font, uint16 character) +{ + return (FONTGLYPH *) mock(font, character); +} + +DATABLOB * +cache_get_text(uint8 cache_id) +{ + return (DATABLOB *)mock(cache_id); +} + +void +cache_put_text(uint8 cache_id, void *data, int length) +{ + mock(cache_id, data, length); +} + +uint8 * +cache_get_desktop(uint32 offset, int cx, int cy, int bytes_per_pixel) +{ + return (uint8 *) mock(offset, cx, cy, bytes_per_pixel); +} + +void +cache_put_desktop(uint32 offset, int cx, int cy, int scanline, + int bytes_per_pixel, uint8 * data) +{ + mock(offset, cx, cy, scanline, bytes_per_pixel, data); +} diff --git a/tests/channels_mock.c b/tests/channels_mock.c new file mode 100644 index 0000000..d04ab11 --- /dev/null +++ b/tests/channels_mock.c @@ -0,0 +1,7 @@ +#include +#include "../rdesktop.h" + +void channel_process(STREAM s, uint16 mcs_channel) +{ + mock(s, mcs_channel); +} diff --git a/tests/ctrl_mock.c b/tests/ctrl_mock.c new file mode 100644 index 0000000..89e7684 --- /dev/null +++ b/tests/ctrl_mock.c @@ -0,0 +1,14 @@ +#include +#include "../rdesktop.h" + +void +ctrl_add_fds(int *n, fd_set * rfds) +{ + mock(n, rfds); +} + +void +ctrl_check_fds(fd_set * rfds, fd_set * wfds) +{ + mock(rfds, wfds); +} diff --git a/tests/dvc_mock.c b/tests/dvc_mock.c new file mode 100644 index 0000000..f887c2f --- /dev/null +++ b/tests/dvc_mock.c @@ -0,0 +1,26 @@ +#include +#include "../rdesktop.h" + +RD_BOOL +dvc_init(void) +{ + return mock(); +} + +RD_BOOL +dvc_channels_register(const char *name, dvc_channel_process_fn handler) +{ + return mock(name, handler); +} + +RD_BOOL +dvc_channels_is_available(const char *name) +{ + return mock(name); +} + +void +dvc_send(const char *name, STREAM s) +{ + mock(name, s); +} diff --git a/tests/ewmh_mock.c b/tests/ewmh_mock.c new file mode 100644 index 0000000..ddaed26 --- /dev/null +++ b/tests/ewmh_mock.c @@ -0,0 +1,87 @@ +#include +#include "../rdesktop.h" +#include + +int +ewmh_get_window_state(Window w) +{ + return mock(w); +} + +int +ewmh_change_state(Window wnd, int state) +{ + return mock(wnd, state); +} + +int +ewmh_move_to_desktop(Window wnd, unsigned int desktop) +{ + return mock(wnd, desktop); +} + +int +ewmh_get_window_desktop(Window wnd) +{ + return mock(wnd); +} + +void +ewmh_set_wm_name(Window wnd, const char *title) +{ + mock(wnd, title); +} + +void +ewmh_set_wm_pid(Window wnd, pid_t pid) +{ + mock(wnd, pid); +} + +int +ewmh_set_window_popup(Window wnd) +{ + return mock(wnd); +} + +int +ewmh_set_window_modal(Window wnd) +{ + return mock(wnd); +} + +void +ewmh_set_icon(Window wnd, int width, int height, const char *rgba_data) +{ + mock(wnd, width, height, rgba_data); +} + +void +ewmh_del_icon(Window wnd, int width, int height) +{ + mock(wnd, width, height); +} + +int +ewmh_set_window_above(Window wnd) +{ + return mock(wnd); +} + +RD_BOOL +ewmh_is_window_above(Window w) +{ + return mock(w); +} + +int +get_current_workarea(uint32 *x, uint32 *y, uint32 *width, uint32 *height) +{ + return mock(x, y, width, height); +} + +void +ewmh_init() +{ + mock(); +} diff --git a/tests/licence_mock.c b/tests/licence_mock.c new file mode 100644 index 0000000..33b823e --- /dev/null +++ b/tests/licence_mock.c @@ -0,0 +1,9 @@ +#include +#include "../rdesktop.h" + + +void +licence_process(STREAM s) +{ + mock(s); +} diff --git a/tests/mcs_mock.c b/tests/mcs_mock.c new file mode 100644 index 0000000..251049c --- /dev/null +++ b/tests/mcs_mock.c @@ -0,0 +1,51 @@ +#include +#include "../rdesktop.h" + +STREAM +mcs_init(int length) +{ + return (STREAM)mock(length); +} + +void +mcs_send_to_channel(STREAM s, uint16 channel) +{ + mock(s, channel); +} + +void +mcs_send(STREAM s) +{ + mock(s); +} + +STREAM +mcs_recv(uint16 * channel, uint8 * rdpver) +{ + return (STREAM)mock(channel, rdpver); +} + +RD_BOOL +mcs_connect_start(char *server, char *username, char *domain, char *password, + RD_BOOL reconnect, uint32 * selected_protocol) +{ + return mock(server,username,domain,password,reconnect,selected_protocol); +} + +RD_BOOL +mcs_connect_finalize(STREAM s) +{ + return mock(s); +} + +void +mcs_disconnect(void) +{ + mock(); +} + +void +mcs_reset_state(void) +{ + mock(); +} diff --git a/tests/mppc_mock.c b/tests/mppc_mock.c new file mode 100644 index 0000000..ca9d520 --- /dev/null +++ b/tests/mppc_mock.c @@ -0,0 +1,8 @@ +#include +#include "../rdesktop.h" + +int +mppc_expand(uint8 * data, uint32 clen, uint8 ctype, uint32 * roff, uint32 * rlen) +{ + return mock(data, clen, ctype, roff, rlen); +} diff --git a/tests/orders_mock.c b/tests/orders_mock.c new file mode 100644 index 0000000..f4f6d42 --- /dev/null +++ b/tests/orders_mock.c @@ -0,0 +1,12 @@ +#include +#include "../rdesktop.h" + +void process_orders(STREAM s, uint16 num_orders) +{ + mock(s, num_orders); +} + +void reset_order_state() +{ + mock(); +} diff --git a/tests/pstcache_mock.c b/tests/pstcache_mock.c new file mode 100644 index 0000000..43c836c --- /dev/null +++ b/tests/pstcache_mock.c @@ -0,0 +1,12 @@ +#include +#include "../rdesktop.h" + +int pstcache_enumerate(uint8 id, HASH_KEY * keylist) +{ + return mock(id, keylist); +} + +RD_BOOL pstcache_init(uint8 cache_id) +{ + return mock(cache_id); +} diff --git a/tests/rdesktop_mock.c b/tests/rdesktop_mock.c new file mode 100644 index 0000000..b3bd876 --- /dev/null +++ b/tests/rdesktop_mock.c @@ -0,0 +1,13 @@ +#include +#include "../rdesktop.h" + +void rd_create_ui(void) +{ + mock(); +} + + +void generate_random(uint8 * random) +{ + mock(random); +} diff --git a/tests/rdp5_mock.c b/tests/rdp5_mock.c new file mode 100644 index 0000000..40906c8 --- /dev/null +++ b/tests/rdp5_mock.c @@ -0,0 +1,7 @@ +#include +#include "../rdesktop.h" + +void process_ts_fp_updates(STREAM s) +{ + mock(s); +} diff --git a/tests/rdp_mock.c b/tests/rdp_mock.c new file mode 100644 index 0000000..45b0eec --- /dev/null +++ b/tests/rdp_mock.c @@ -0,0 +1,15 @@ +#include +#include "../rdesktop.h" + +void +rdp_send_input(uint32 time, uint16 message_type, uint16 device_flags, uint16 param1, uint16 param2) +{ + mock(time, message_type, device_flags, param1, param2); +} + +void +rdp_send_suppress_output_pdu(enum RDP_SUPPRESS_STATUS allowupdates) +{ + mock(allowupdates); +} + diff --git a/tests/rdp_test.c b/tests/rdp_test.c new file mode 100644 index 0000000..6ccdc87 --- /dev/null +++ b/tests/rdp_test.c @@ -0,0 +1,141 @@ +#include +#include +#include "../rdesktop.h" +#include "../proto.h" + +/* Boilerplate */ +Describe(RDP); +BeforeEach(RDP) {}; +AfterEach(RDP) {}; + +/* Global Variables.. :( */ +uint16 g_mcs_userid; +char *g_username; +char g_password[64]; +char g_codepage[16]; +RD_BOOL g_orders; +RD_BOOL g_encryption; +RD_BOOL g_desktop_save; +RD_BOOL g_polygon_ellipse_orders; +RDP_VERSION g_rdp_version; +uint16 g_server_rdp_version; +uint32 g_rdp5_performanceflags; +int g_server_depth; +uint32 g_initial_width; +uint32 g_initial_height; +RD_BOOL g_bitmap_cache; +RD_BOOL g_bitmap_cache_persist_enable; +RD_BOOL g_numlock_sync; +RD_BOOL g_pending_resize; +RD_BOOL g_network_error; +time_t g_wait_for_deactivate_ts; +RDPCOMP g_mppc_dict; +RD_BOOL g_redirect; +char *g_redirect_server; +uint32 g_redirect_server_len; +char *g_redirect_domain; +uint32 g_redirect_domain_len; +char *g_redirect_username; +uint32 g_redirect_username_len; +uint8 *g_redirect_lb_info; +uint32 g_redirect_lb_info_len; +uint8 *g_redirect_cookie; +uint32 g_redirect_cookie_len; +uint32 g_redirect_flags; +uint32 g_redirect_session_id; +uint32 g_reconnect_logonid; +char g_reconnect_random[16]; +time_t g_reconnect_random_ts; +RD_BOOL g_has_reconnect_random; +uint8 g_client_random[SEC_RANDOM_SIZE]; +RD_BOOL g_local_cursor; + +#include "../rdp.c" +#include "../utils.c" +#include "../stream.c" + +/* malloc; exit if out of memory */ +void * +xmalloc(int size) +{ + void *mem = malloc(size); + if (mem == NULL) + { + logger(Core, Error, "xmalloc, failed to allocate %d bytes", size); + exit(EX_UNAVAILABLE); + } + return mem; +} + +/* Exit on NULL pointer. Use to verify result from XGetImage etc */ +void +exit_if_null(void *ptr) +{ + if (ptr == NULL) + { + logger(Core, Error, "unexpected null pointer. Out of memory?"); + exit(EX_UNAVAILABLE); + } +} + +/* strdup */ +char * +xstrdup(const char *s) +{ + char *mem = strdup(s); + if (mem == NULL) + { + logger(Core, Error, "xstrdup(), strdup() failed: %s", strerror(errno)); + exit(EX_UNAVAILABLE); + } + return mem; +} + +/* realloc; exit if out of memory */ +void * +xrealloc(void *oldmem, size_t size) +{ + void *mem; + + if (size == 0) + size = 1; + mem = realloc(oldmem, size); + if (mem == NULL) + { + logger(Core, Error, "xrealloc, failed to reallocate %ld bytes", size); + exit(EX_UNAVAILABLE); + } + return mem; +} + +/* free */ +void +xfree(void *mem) +{ + free(mem); +} + + +/* Test function */ +Ensure(RDP, ProcessBitmapCapsCallsUiResizeWindow) { + struct stream s; + memset(&s, 0, sizeof(struct stream)); + + expect(ui_resize_window, + when(width, is_equal_to(1024)), + when(height, is_equal_to(768))); + + s_realloc(&s, 32); + s_reset(&s); + + out_uint16_le(&s, 32); /* depth */ + out_uint8s(&s, 6); /* pad? dunno */ + out_uint16_le(&s, 1024); + out_uint16_le(&s, 768); + s_mark_end(&s); + s_reset(&s); + + rdp_process_bitmap_caps(&s); + + free(s.data); +} diff --git a/tests/rdpdr_mock.c b/tests/rdpdr_mock.c new file mode 100644 index 0000000..b73c743 --- /dev/null +++ b/tests/rdpdr_mock.c @@ -0,0 +1,14 @@ +#include +#include "../rdesktop.h" + +void +rdpdr_add_fds(int *n, fd_set * rfds, fd_set * wfds, struct timeval *tv, RD_BOOL * timeout) +{ + mock(n, rfds, wfds, tv, timeout); +} + +void +rdpdr_check_fds(fd_set * rfds, fd_set * wfds, RD_BOOL timed_out) +{ + mock(rfds, wfds, timed_out); +} diff --git a/tests/rdpedisp_mock.c b/tests/rdpedisp_mock.c new file mode 100644 index 0000000..6e52377 --- /dev/null +++ b/tests/rdpedisp_mock.c @@ -0,0 +1,14 @@ +#include +#include "../rdesktop.h" + +RD_BOOL +rdpedisp_is_available() +{ + return mock(); +} + +void +rdpedisp_set_session_size(uint32 width, uint32 height) +{ + mock(width, height); +} diff --git a/tests/resize_test.c b/tests/resize_test.c new file mode 100644 index 0000000..6a12ef1 --- /dev/null +++ b/tests/resize_test.c @@ -0,0 +1,571 @@ +#include +#include +#include +#include "../rdesktop.h" + +/* Boilerplate */ +Describe(Resize); +BeforeEach(Resize) {}; +AfterEach(Resize) {}; + +/* globals driven by xwin.c */ +RD_BOOL g_user_quit; +RD_BOOL g_exit_mainloop; +int g_sizeopt; +uint32 g_initial_width; +uint32 g_initial_height; +uint16 g_session_width; +uint16 g_session_height; +int g_xpos; +int g_ypos; +int g_pos; +RD_BOOL g_sendmotion; +RD_BOOL g_fullscreen; +RD_BOOL g_grab_keyboard; +RD_BOOL g_hide_decorations; +RD_BOOL g_pending_resize; +char g_title[] = "MyTitle"; +char g_seamless_spawn_cmd[] = ""; +int g_server_depth; +int g_win_button_size; +RD_BOOL g_seamless_persistent_mode; +RD_BOOL g_seamless_rdp; +RD_BOOL g_seamless_persistent_mode; +uint32 g_embed_wnd; +Atom g_net_wm_state_atom; +Atom g_net_wm_desktop_atom; +Atom g_net_wm_ping_atom; +RD_BOOL g_ownbackstore; +RD_BOOL g_rdpsnd; +RD_BOOL g_owncolmap; +RD_BOOL g_local_cursor; + +/* globals driven by utils.c */ +char g_codepage[16] = ""; + +/* global driven by rdp.c */ +uint16 g_mcs_userid; +char *g_username; +char g_password[64]; +char g_codepage[16]; +RD_BOOL g_orders; +RD_BOOL g_encryption; +RD_BOOL g_desktop_save; +RD_BOOL g_polygon_ellipse_orders; +RDP_VERSION g_rdp_version; +uint16 g_server_rdp_version; +uint32 g_rdp5_performanceflags; +int g_server_depth; +uint32 g_initial_width; +uint32 g_initial_height; +RD_BOOL g_bitmap_cache; +RD_BOOL g_bitmap_cache_persist_enable; +RD_BOOL g_numlock_sync; +RD_BOOL g_pending_resize; +RD_BOOL g_network_error; +time_t g_wait_for_deactivate_ts; +RDPCOMP g_mppc_dict; +RD_BOOL g_redirect; +char *g_redirect_server; +uint32 g_redirect_server_len; +char *g_redirect_domain; +uint32 g_redirect_domain_len; +char *g_redirect_username; +uint32 g_redirect_username_len; +uint8 *g_redirect_lb_info; +uint32 g_redirect_lb_info_len; +uint8 *g_redirect_cookie; +uint32 g_redirect_cookie_len; +uint32 g_redirect_flags; +uint32 g_redirect_session_id; +uint32 g_reconnect_logonid; +char g_reconnect_random[16]; +time_t g_reconnect_random_ts; +RD_BOOL g_has_reconnect_random; +uint8 g_client_random[SEC_RANDOM_SIZE]; +RD_BOOL g_local_cursor; + +/* globals from secure.c */ +char g_hostname[16]; +uint32 g_initial_width; +uint32 g_initial_height; +int g_dpi; +unsigned int g_keylayout; +int g_keyboard_type; +int g_keyboard_subtype; +int g_keyboard_functionkeys; +RD_BOOL g_encryption; +RD_BOOL g_licence_issued; +RD_BOOL g_licence_error_result; +RDP_VERSION g_rdp_version; +RD_BOOL g_console_session; +uint32 g_redirect_session_id; +int g_server_depth; +VCHANNEL g_channels[1]; +unsigned int g_num_channels; +uint8 g_client_random[SEC_RANDOM_SIZE]; + +/* Xlib macros to mock functions */ +#undef DefaultRootWindow +Window DefaultRootWindow(Display *display) { return (Window) mock(display); } + +#undef WidthOfScreen +int WidthOfScreen(Screen* x) { return mock(x); } + +#undef HeightOfScreen +int HeightOfScreen(Screen *x) { return mock(x); } + + +#include "../xwin.c" +#include "../utils.c" +#include "../rdp.c" +#include "../stream.c" +#include "../secure.c" + +/* malloc; exit if out of memory */ +void * +xmalloc(int size) +{ + void *mem = malloc(size); + if (mem == NULL) + { + logger(Core, Error, "xmalloc, failed to allocate %d bytes", size); + exit(EX_UNAVAILABLE); + } + return mem; +} + +/* Exit on NULL pointer. Use to verify result from XGetImage etc */ +void +exit_if_null(void *ptr) +{ + if (ptr == NULL) + { + logger(Core, Error, "unexpected null pointer. Out of memory?"); + exit(EX_UNAVAILABLE); + } +} + +/* strdup */ +char * +xstrdup(const char *s) +{ + char *mem = strdup(s); + if (mem == NULL) + { + logger(Core, Error, "xstrdup(), strdup() failed: %s", strerror(errno)); + exit(EX_UNAVAILABLE); + } + return mem; +} + +/* realloc; exit if out of memory */ +void * +xrealloc(void *oldmem, size_t size) +{ + void *mem; + + if (size == 0) + size = 1; + mem = realloc(oldmem, size); + if (mem == NULL) + { + logger(Core, Error, "xrealloc, failed to reallocate %ld bytes", size); + exit(EX_UNAVAILABLE); + } + return mem; +} + +/* free */ +void +xfree(void *mem) +{ + free(mem); +} + +/* X11 mocks */ + +Status +XGetWindowAttributes(Display *display, Window wnd, XWindowAttributes *attr) +{ + return mock(display, wnd, attr); +} + +int +XResizeWindow(Display *display, Window wnd, unsigned width, unsigned height) +{ + return mock(display, wnd, width, height); +} + +XSizeHints * +XAllocSizeHints(void) +{ + return (XSizeHints *) mock(); +} + + +int +XSetClipRectangles(Display *display, GC gc, int clip_x_origin, int clip_y_origin, + XRectangle rectangles[], int n, int ordering) +{ + return mock(display, gc, clip_x_origin, clip_y_origin, rectangles, n, ordering); +} + +/* Test helpers */ + +struct stream +bitmap_caps_packet(int width, int height) +{ + struct stream s; + memset(&s, 0, sizeof(s)); + s_realloc(&s, 32); + s_reset(&s); + + out_uint16_le(&s, 32); /* depth */ + out_uint8s(&s, 6); /* pad? dunno */ + out_uint16_le(&s, width); + out_uint16_le(&s, height); + s_mark_end(&s); + s_reset(&s); + + return s; +} + +#define RDPEDISP True +#define RECONNECT False + +#define FULLSCREEN True +#define WINDOW False + +void setup_user_initiated_resize(int width, int height, RD_BOOL use_rdpedisp, RD_BOOL fullscreen) +{ + /* g_resize_timer = A second ago */ + struct timeval tv; + gettimeofday(&tv, NULL); + tv.tv_sec -= 1; + g_resize_timer = tv; + + g_fullscreen = fullscreen; + + g_window_width = width; + g_window_height = height; + + expect(rdpedisp_is_available, + will_return(use_rdpedisp)); +} + +struct stream setup_server_resize_response(int width, int height) { + expect(XGetWindowAttributes); + expect(XSetClipRectangles); + expect(XAllocSizeHints, + will_return(0)); + + return bitmap_caps_packet(width, height); +} + +Ensure(Resize, UsingRDPEDISP) +{ + struct stream s; + int user_wanted_width = 1280; + int user_wanted_height = 1024; + + /* Step 1 : Act on UI side initiated resize and tell server about it using RDPEDISP */ + setup_user_initiated_resize(user_wanted_width, user_wanted_height, RDPEDISP, WINDOW); + + expect(rdpedisp_set_session_size, + when(width, is_equal_to(user_wanted_width)), + when(height, is_equal_to(user_wanted_height))); + + /* FIXME: Move process_pending_resize out of X11 UI implementation */ + assert_that(process_pending_resize(), is_equal_to(False)); + + /* Step 2 : Handle a BITMAP_CAPS containing session size from server + so set window size accordingly */ + s = setup_server_resize_response(user_wanted_width, user_wanted_height); + + expect(XResizeWindow, + when(width, is_equal_to(user_wanted_width)), + when(height, is_equal_to(user_wanted_height))); + + rdp_process_bitmap_caps(&s); + free(s.data); +} + +Ensure(Resize, UsingRDPEDISPHonoursServerMaximumSessionSizeLimit) +{ + /* User has changed window size, X has resized, RDP needs to tell server. + RDPEDISP is available and a Windows 2012 server will limit session size + to maximum 8192x8192. We will set window size to whatever server says. + */ + struct stream s; + int user_wanted_width = 9000; + int user_wanted_height = 9000; + + /* Step 1 : Act on UI side initiated resize and tell server about it + using RDPEDISP */ + setup_user_initiated_resize(user_wanted_width, user_wanted_height, RDPEDISP, WINDOW); + + expect(rdpedisp_set_session_size, + when(width, is_equal_to(user_wanted_width)), + when(height, is_equal_to(user_wanted_height))); + + /* FIXME: Move process_pending_resize out of X11 UI implementation */ + assert_that(process_pending_resize(), is_equal_to(False)); + + /* Step 2 : Handle a BITMAP_CAPS containing session size from server + so set window size accordingly */ + + int resulting_server_width = 8192; + int resulting_server_height = 8192; + + s = setup_server_resize_response(resulting_server_width, resulting_server_height); + + expect(XResizeWindow, + when(width, is_equal_to(resulting_server_width)), + when(height, is_equal_to(resulting_server_height))); + + /* simulate response from server */ + rdp_process_bitmap_caps(&s); + free(s.data); +} + + +Ensure(Resize, UsingRDPEDISPHonoursServerMinimumSessionSizeLimit) +{ + /* User has changed window size, X has resized, RDP needs to tell server. + RDPEDISP is available and a Windows 2012 server will limit session size + to minimum 200x200. We will set window size to whatever server says. + */ + + struct stream s; + + /* Step 1 : Act on UI side initiated resize and tell server about it + using RDPEDISP */ + + int user_wanted_width = 100; + int user_wanted_height = 100; + + setup_user_initiated_resize(user_wanted_width, user_wanted_height, RDPEDISP, WINDOW); + + expect(rdpedisp_set_session_size, + when(width, is_equal_to(user_wanted_width)), + when(height, is_equal_to(user_wanted_height))); + + /* FIXME: Move process_pending_resize out of X11 UI implementation */ + assert_that(process_pending_resize(), is_equal_to(False)); + + /* Step 2 : Handle a BITMAP_CAPS containing session size from server + so set window size accordingly */ + + int resulting_server_width = 200; + int resulting_server_height = 200; + + s = setup_server_resize_response(resulting_server_width, resulting_server_height); + + expect(XResizeWindow, + when(width, is_equal_to(resulting_server_width)), + when(height, is_equal_to(resulting_server_height))); + + /* simulate response from server */ + rdp_process_bitmap_caps(&s); + free(s.data); +} + +Ensure(Resize, UsingRDPEDISPHonoursServerSessionWidthConstraintMustBeEven) +{ + /* User has changed window size, X has resized, RDP needs to tell server. + RDPEDISP is available and a Windows 2012 server will limit session size + to minimum 200x200. We will set window size to whatever server says. + */ + struct stream s; + + /* Step 1 : Act on UI side initiated resize and tell server about it + using RDPEDISP */ + + int user_wanted_width = 999; + int user_wanted_height = 900; + + setup_user_initiated_resize(user_wanted_width, user_wanted_height, RDPEDISP, WINDOW); + + expect(rdpedisp_set_session_size, + when(width, is_equal_to(user_wanted_width)), + when(height, is_equal_to(user_wanted_height))); + + /* FIXME: Move process_pending_resize out of X11 UI implementation */ + assert_that(process_pending_resize(), is_equal_to(False)); + + /* Step 2 : Handle a BITMAP_CAPS containing session size from server + so set window size accordingly */ + + /* FIXME: Does the server round up or down? */ + int resulting_server_width = 998; + int resulting_server_height = 900; + + s = setup_server_resize_response(resulting_server_width, resulting_server_height); + + expect(XResizeWindow, + when(width, is_equal_to(resulting_server_width)), + when(height, is_equal_to(resulting_server_height))); + + /* simulate response from server */ + rdp_process_bitmap_caps(&s); + free(s.data); +} + +/* FIXME: promote to actual function in stream.c */ +STREAM s_alloc(size_t capacity) +{ + STREAM s; + s = xmalloc(sizeof(struct stream)); + memset(s, 0, sizeof(struct stream)); + s_realloc(s, capacity); + s_reset(s); + return s; +} + +void get_width_and_height_from_mcs_connect_initial(int *width, int *height) +{ + STREAM s; + + /* Allocate stream and write mcs_connect_initial PDU to it */ + s = s_alloc(4096); + sec_out_mcs_connect_initial_pdu(s, 0); + + /* Rewind and extract the requested session size */ + s_reset(s); + in_skip(s, 31); + in_uint16_le(s, *width); /* desktopWidth */ + in_uint16_le(s, *height); /* desktopHeight */ + + s_free(s); +} + + +Ensure(Resize, UsingReconnect) +{ + struct stream s; + + int user_wanted_width = 1280; + int user_wanted_height = 1024; + + /* Step 1 : Act on UI side initiated resize */ + setup_user_initiated_resize(user_wanted_width, user_wanted_height, RECONNECT, WINDOW); + + assert_that(process_pending_resize(), + is_equal_to(True)); + + /* we assume that process_pending_resize returning True will exit the main loop and initiate a + reconnect */ + + + /* Step 2 : Simulate parts of the connection sequence where we send + width & height through a MCS Connect Initial packet to the server */ + + int sent_width, sent_height; + + get_width_and_height_from_mcs_connect_initial(&sent_width, &sent_height); + + assert_that(sent_width, is_equal_to(user_wanted_width)); + assert_that(sent_height, is_equal_to(user_wanted_height)); + + + /* Step 3 : Handle a BITMAP_CAPS containing session size from server + so set window size accordingly */ + + s = setup_server_resize_response(user_wanted_width, + user_wanted_height); + + expect(XResizeWindow, + when(width, is_equal_to(user_wanted_width)), + when(height, is_equal_to(user_wanted_height))); + + rdp_process_bitmap_caps(&s); + free(s.data); +} + + +Ensure(Resize, UsingReconnectHonoursServerMaximumSessionSizeLimit) +{ + struct stream s; + + int user_wanted_width = 9000; + int user_wanted_height = 9000; + + /* Step 1 : Act on UI side initiated resize */ + setup_user_initiated_resize(user_wanted_width, user_wanted_height, RECONNECT, WINDOW); + + /* FIXME: Move process_pending_resize out of X11 UI implementation */ + assert_that(process_pending_resize(), + is_equal_to(True)); + + /* We assume that process_pending_resize returning True exits the main + loop and initiates a reconnect */ + + + /* Step 2 : Simulate parts of the connection sequence where we send + width & height through a MCS Connect Initial packet to the server */ + + int sent_width, sent_height; + + get_width_and_height_from_mcs_connect_initial(&sent_width, &sent_height); + + assert_that(sent_width, is_equal_to(user_wanted_width)); + assert_that(sent_height, is_equal_to(user_wanted_height)); + + /* Step 3 : Handle a BITMAP_CAPS containing session size from server + so set window size accordingly */ + + int resulting_server_width = 4096; + int resulting_server_height = 2048; + + s = setup_server_resize_response(resulting_server_width, + resulting_server_height); + + expect(XResizeWindow, + when(width, is_equal_to(resulting_server_width)), + when(height, is_equal_to(resulting_server_height))); + + rdp_process_bitmap_caps(&s); + free(s.data); +} + +int +XNextEvent(Display *display, XEvent *event) +{ + return mock(display, event); +} + +int +XPending(Display *display) +{ + return mock(display); +} + +void +setup_user_initiated_root_window_resize(int width, int height, + RD_BOOL use_rdpedisp, RD_BOOL fullscreen) +{ + XEvent rootWindowResizeEvent; + memset(&rootWindowResizeEvent, 0, sizeof(XEvent)); + + rootWindowResizeEvent.xconfigure.type = ConfigureNotify; + rootWindowResizeEvent.xconfigure.window = DefaultRootWindow(g_display); + rootWindowResizeEvent.xconfigure.width = width; + rootWindowResizeEvent.xconfigure.height = height; + + /* one event to process */ + expect(XPending, will_return(1)); + + /* event is a ConfigureNotify event on root window */ + expect(XNextEvent, + will_set_contents_of_parameter(event, + &rootWindowResizeEvent, + sizeof(XConfigureEvent *))); + /* no more events to process */ + expect(XPending, + will_return(0)); + + + expect(rdpedisp_is_available, will_return(use_rdpedisp)); + g_fullscreen = fullscreen; +} diff --git a/tests/seamless_mock.c b/tests/seamless_mock.c new file mode 100644 index 0000000..8302483 --- /dev/null +++ b/tests/seamless_mock.c @@ -0,0 +1,58 @@ +#include +#include "../rdesktop.h" + +RD_BOOL seamless_init() +{ + return mock(); +} + +void seamless_reset_state() +{ + mock(); +} + +unsigned int seamless_send_sync(void) +{ + return mock(); +} + +unsigned int seamless_send_state(unsigned long id, unsigned int state, unsigned long flags) +{ + return mock(id, state, flags); +} + +unsigned int seamless_send_position(unsigned long id, int x, int y, + int width, int height, unsigned long flags) +{ + return mock(id, x, y, width, height, flags); +} + +void seamless_select_timeout(struct timeval *tv) +{ + mock(tv); +} + +unsigned int seamless_send_zchange(unsigned long id, unsigned long below, unsigned long flags) +{ + return mock(id, below, flags); +} + +unsigned int seamless_send_focus(unsigned long id, unsigned long flags) +{ + return mock(id, flags); +} + +unsigned int seamless_send_destroy(unsigned long id) +{ + return mock(id); +} + +unsigned int seamless_send_spawn(char *cmd) +{ + return mock(cmd); +} + +unsigned int seamless_send_persistent(RD_BOOL enable) +{ + return mock(enable); +} diff --git a/tests/secure_mock.c b/tests/secure_mock.c new file mode 100644 index 0000000..b219b16 --- /dev/null +++ b/tests/secure_mock.c @@ -0,0 +1,32 @@ +#include +#include "../rdesktop.h" + +STREAM sec_recv(uint8 * rdpver) +{ + return (STREAM)mock(rdpver); +} + +void sec_disconnect() +{ + mock(); +} + +STREAM sec_init(uint32 flags, int maxlen) +{ + return (STREAM) mock(flags, maxlen); +} + +RD_BOOL sec_connect(char *server, char *username, char *domain, char *password, RD_BOOL reconnect) +{ + return mock(server, username, domain, password, reconnect); +} + +void sec_reset_state() +{ + mock(); +} + +void sec_send(STREAM s, uint32 flags) +{ + mock(s, flags); +} diff --git a/tests/ssl_mock.c b/tests/ssl_mock.c new file mode 100644 index 0000000..9c5ad79 --- /dev/null +++ b/tests/ssl_mock.c @@ -0,0 +1,108 @@ +#include +#include "../rdesktop.h" +#include "../ssl.h" + +void rdssl_hmac_md5(const void *key, int key_len, + const unsigned char *msg, int msg_len, unsigned char *md) +{ + mock(key, key_len, msg, msg_len, md); +} + +void +rdssl_cert_free(RDSSL_CERT * cert) +{ + mock(cert); +} + +RDSSL_CERT * +rdssl_cert_read(uint8 *data, uint32 len) +{ + return (RDSSL_CERT *) mock(data, len); +} + +RD_BOOL +rdssl_certs_ok(RDSSL_CERT * server_cert, RDSSL_CERT * cacert) +{ + return mock(server_cert, cacert); +} + +RDSSL_RKEY * +rdssl_cert_to_rkey(RDSSL_CERT * cert, uint32 * key_len) +{ + return (RDSSL_RKEY *) mock(cert, key_len); +} + +void +rdssl_md5_init(RDSSL_MD5 * md5) +{ + mock(md5); +} + +void +rdssl_md5_update(RDSSL_MD5 * md5, uint8 * data, uint32 len) +{ + mock(md5, data, len); +} + +void +rdssl_md5_final(RDSSL_MD5 * md5, uint8 * out_data) +{ + mock(md5, out_data); +} + +void +rdssl_rc4_set_key(RDSSL_RC4 * rc4, uint8 * key, uint32 len) +{ + mock(rc4, key, len); +} + +void +rdssl_rc4_crypt(RDSSL_RC4 * rc4, uint8 * in_data, uint8 * out_data, uint32 len) +{ + mock(rc4, in_data, out_data, len); +} + +void +rdssl_rkey_free(RDSSL_RKEY * rkey) +{ + mock(rkey); +} + +int +rdssl_rkey_get_exp_mod(RDSSL_RKEY * rkey, uint8 * exponent, uint32 max_exp_len, uint8 * modulus, + uint32 max_mod_len) +{ + return mock(rkey, exponent, max_exp_len, modulus, max_mod_len); +} + +void +rdssl_rsa_encrypt(uint8 * out, uint8 * in, int len, uint32 modulus_size, uint8 * modulus, + uint8 * exponent) +{ + mock(out, in, len, modulus_size, modulus, exponent); +} + +void +rdssl_sha1_final(RDSSL_SHA1 * sha1, uint8 * out_data) +{ + mock(sha1, out_data); +} + +void +rdssl_sha1_init(RDSSL_SHA1 *sha1) +{ + mock(sha1); +} + +void +rdssl_sha1_update(RDSSL_SHA1 *sha1, uint8 *data, uint32 len) +{ + mock(sha1, data, len); +} + +RD_BOOL +rdssl_sig_ok(uint8 *exponent, uint32 exp_len, uint8 *modulus, uint32 mod_len, + uint8 *signature, uint32 sig_len) +{ + return mock(exponent, exp_len, modulus, mod_len, signature, sig_len); +} diff --git a/tests/tcp_mock.c b/tests/tcp_mock.c new file mode 100644 index 0000000..49a101c --- /dev/null +++ b/tests/tcp_mock.c @@ -0,0 +1,7 @@ +#include +#include "../rdesktop.h" + +char *tcp_get_address() +{ + return (char *) mock(); +} diff --git a/tests/ui_mock.c b/tests/ui_mock.c new file mode 100644 index 0000000..bb53320 --- /dev/null +++ b/tests/ui_mock.c @@ -0,0 +1,72 @@ +#include +#include "../rdesktop.h" + +time_t g_wait_for_deactivate_ts; + +/* Mock implementation of F1 */ +void ui_resize_window(uint32 width, uint32 height) +{ + mock(width, height); +} + +void ui_set_cursor(RD_HCURSOR cursor) +{ + mock(cursor); +} + +void ui_set_standard_cursor() +{ + mock(); +} + +void ui_bell() +{ + mock(); +} + +void ui_paint_bitmap(int x, int y, int cx, int cy, int width, int height, uint8 * data) +{ + mock(x,y,cx,cy,width,height,data); +} + +void ui_begin_update() +{ + mock(); +} + +RD_HCOLOURMAP ui_create_colourmap(COLOURMAP * colours) +{ + return (RD_HCOLOURMAP) mock(colours); +} + +RD_HCURSOR ui_create_cursor(unsigned int x, unsigned int y, uint32 width, uint32 height, uint8 * andmask, + uint8 * xormask, int bpp) +{ + return (RD_HCURSOR) mock(x, y, width, height, andmask, xormask, bpp); +} + +void ui_end_update() +{ + mock(); +} + +void ui_move_pointer(int x, int y) +{ + mock(x, y); +} + +void ui_set_colourmap(RD_HCOLOURMAP map) +{ + mock(map); +} + +void ui_set_null_cursor() +{ + mock(); +} + + +void ui_set_clip(int x,int y, int cx, int cy) +{ + mock(x,y,cx,cy); +} diff --git a/tests/utils_test.c b/tests/utils_test.c new file mode 100644 index 0000000..4544a57 --- /dev/null +++ b/tests/utils_test.c @@ -0,0 +1,179 @@ +#include +#include +#include "../rdesktop.h" + +/* Boilerplate */ +Describe(Utils); +BeforeEach(Utils) {}; +AfterEach(Utils) {}; + +/* globals */ +char g_codepage[16]; + +#include "../utils.c" + +/* malloc; exit if out of memory */ +void * +xmalloc(int size) +{ + void *mem = malloc(size); + if (mem == NULL) + { + logger(Core, Error, "xmalloc, failed to allocate %d bytes", size); + exit(EX_UNAVAILABLE); + } + return mem; +} + +/* Exit on NULL pointer. Use to verify result from XGetImage etc */ +void +exit_if_null(void *ptr) +{ + if (ptr == NULL) + { + logger(Core, Error, "unexpected null pointer. Out of memory?"); + exit(EX_UNAVAILABLE); + } +} + +/* strdup */ +char * +xstrdup(const char *s) +{ + char *mem = strdup(s); + if (mem == NULL) + { + logger(Core, Error, "xstrdup(), strdup() failed: %s", strerror(errno)); + exit(EX_UNAVAILABLE); + } + return mem; +} + +/* realloc; exit if out of memory */ +void * +xrealloc(void *oldmem, size_t size) +{ + void *mem; + + if (size == 0) + size = 1; + mem = realloc(oldmem, size); + if (mem == NULL) + { + logger(Core, Error, "xrealloc, failed to reallocate %ld bytes", size); + exit(EX_UNAVAILABLE); + } + return mem; +} + +/* free */ +void +xfree(void *mem) +{ + free(mem); +} + + +/* Test function */ +Ensure(Utils, CalculateDpiScaleFactorsWhenDpiIsZero) { + uint32 physical_width, physical_height, desktop_scale, device_scale; + + utils_calculate_dpi_scale_factors(1024, 768, 0, + &physical_width, &physical_height, + &desktop_scale, &device_scale); + + assert_that(physical_width, is_equal_to(0)); + assert_that(physical_height, is_equal_to(0)); + assert_that(desktop_scale, is_equal_to(0)); + assert_that(device_scale, is_equal_to(0)); +} + +Ensure(Utils, CalculateDpiScaleFactorsWhenDpiLessThan96) { + uint32 physical_width, physical_height, desktop_scale, device_scale; + + utils_calculate_dpi_scale_factors(1024, 768, 95, + &physical_width, &physical_height, + &desktop_scale, &device_scale); + + assert_that(physical_width, is_equal_to(273)); + assert_that(physical_height, is_equal_to(205)); + assert_that(desktop_scale, is_equal_to(100)); + assert_that(device_scale, is_equal_to(100)); +} + +Ensure(Utils, CalculateDpiScaleFactorsWhenDpiLessThan134) { + uint32 physical_width, physical_height, desktop_scale, device_scale; + + utils_calculate_dpi_scale_factors(1024, 768, 133, + &physical_width, &physical_height, + &desktop_scale, &device_scale); + + assert_that(physical_width, is_equal_to(195)); + assert_that(physical_height, is_equal_to(146)); + assert_that(desktop_scale, is_equal_to(139)); + assert_that(device_scale, is_equal_to(100)); +} + +Ensure(Utils, CalculateDpiScaleFactorsWhenDpiLessThan173) { + uint32 physical_width, physical_height, desktop_scale, device_scale; + + utils_calculate_dpi_scale_factors(1024, 768, 172, + &physical_width, &physical_height, + &desktop_scale, &device_scale); + + assert_that(physical_width, is_equal_to(151)); + assert_that(physical_height, is_equal_to(113)); + assert_that(desktop_scale, is_equal_to(179)); + assert_that(device_scale, is_equal_to(140)); +} + +Ensure(Utils, CalculateDpiScaleFactorsWhenDpiGreaterThanOrEqualTo173) { + uint32 physical_width, physical_height, desktop_scale, device_scale; + + utils_calculate_dpi_scale_factors(1024, 768, 173, + &physical_width, &physical_height, + &desktop_scale, &device_scale); + + assert_that(physical_width, is_equal_to(150)); + assert_that(physical_height, is_equal_to(112)); + assert_that(desktop_scale, is_equal_to(180)); + assert_that(device_scale, is_equal_to(180)); +} + + +Ensure(Utils, ApplySessionSizeLimitationLimitsWidthAndHeightToMax8192) +{ + uint32 width, height; + + width = height = 90000; + + utils_apply_session_size_limitations(&width, &height); + + assert_that(width, is_equal_to(8192)); + assert_that(height, is_equal_to(8192)); +} + + +Ensure(Utils, ApplySessionSizeLimitationLimitsWidthAndHeightToMin200) +{ + uint32 width, height; + + width = height = 100; + + utils_apply_session_size_limitations(&width, &height); + + assert_that(width, is_equal_to(200)); + assert_that(height, is_equal_to(200)); +} + +Ensure(Utils, ApplySessionSizeLimitationRoundsWidthToClosestSmallerEvenNumber) +{ + uint32 width, height; + + width = height = 201; + + utils_apply_session_size_limitations(&width, &height); + + assert_that(width, is_equal_to(200)); + assert_that(height, is_equal_to(201)); +} diff --git a/tests/x11_mock.c b/tests/x11_mock.c new file mode 100644 index 0000000..864f297 --- /dev/null +++ b/tests/x11_mock.c @@ -0,0 +1,10 @@ +#include +#include "../rdesktop.h" +#include +#include + + +XClassHint *XAllocClassHint() +{ + return (XClassHint *)mock(); +} diff --git a/tests/xclip_mock.c b/tests/xclip_mock.c new file mode 100644 index 0000000..0b30827 --- /dev/null +++ b/tests/xclip_mock.c @@ -0,0 +1,37 @@ +#include +#include "../rdesktop.h" + +#include + +void xclip_init() +{ + mock(); +} + +void xclip_deinit() +{ + mock(); +} + +void +xclip_handle_SelectionNotify(XSelectionEvent * event) +{ + mock(event); +} + +void +xclip_handle_SelectionRequest(XSelectionRequestEvent * xevent) +{ + mock(xevent); +} +void +xclip_handle_SelectionClear(void) +{ + mock(); +} + +void +xclip_handle_PropertyNotify(XPropertyEvent * xev) +{ + mock(xev); +} diff --git a/tests/xkeymap_mock.c b/tests/xkeymap_mock.c new file mode 100644 index 0000000..e815721 --- /dev/null +++ b/tests/xkeymap_mock.c @@ -0,0 +1,63 @@ +#include +#include "../rdesktop.h" + +#include + +unsigned int read_keyboard_state() +{ + return mock(); +} + +void +xkeymap_init() +{ + mock(); +} + +void +xkeymap_send_keys(uint32 keysym, unsigned int keycode, unsigned int state, uint32 ev_time, + RD_BOOL pressed, uint8 nesting) +{ + mock(keysym, keycode, state, ev_time, pressed, nesting); +} + +uint16 +xkeymap_translate_button(unsigned int button, uint16 * input_type) +{ + return mock(button, input_type); +} + +RD_BOOL +handle_special_keys(uint32 keysym, unsigned int state, uint32 ev_time, RD_BOOL pressed) +{ + return mock(keysym, state, ev_time, pressed); +} + +void +set_keypress_keysym(unsigned int keycode, KeySym keysym) +{ + mock(keycode, keysym); +} + +KeySym +reset_keypress_keysym(unsigned int keycode, KeySym keysym) +{ + return mock(keycode, keysym); +} + +void +reset_modifier_keys() +{ + mock(); +} + +char * +get_ksname(uint32 keysym) +{ + return (char *) mock(keysym); +} + +uint16 ui_get_numlock_state(unsigned int state) +{ + return mock(state); +} diff --git a/tests/xwin_test.c b/tests/xwin_test.c new file mode 100644 index 0000000..de6a202 --- /dev/null +++ b/tests/xwin_test.c @@ -0,0 +1,216 @@ +#include +#include +#include "../rdesktop.h" +#include "../proto.h" + +#include +#include +#include +#include + +/* Boilerplate */ +Describe(XWIN); +BeforeEach(XWIN) {}; +AfterEach(XWIN) {}; + +/* Global Variables.. :( */ +RD_BOOL g_user_quit; +RD_BOOL g_exit_mainloop; + +int g_sizeopt; +uint32 g_initial_width; +uint32 g_initial_height; +uint16 g_session_width; +uint16 g_session_height; +int g_xpos; +int g_ypos; +int g_pos; +RD_BOOL g_sendmotion; +RD_BOOL g_fullscreen; +RD_BOOL g_grab_keyboard; +RD_BOOL g_hide_decorations; +RD_BOOL g_pending_resize; +char g_title[64]; +char g_seamless_spawn_cmd[512]; +/* Color depth of the RDP session. + As of RDP 5.1, it may be 8, 15, 16 or 24. */ +int g_server_depth; +int g_win_button_size; +RD_BOOL g_seamless_rdp; +RD_BOOL g_seamless_persistent_mode; +uint32 g_embed_wnd; +Atom g_net_wm_state_atom; +Atom g_net_wm_desktop_atom; +Atom g_net_wm_ping_atom; +RD_BOOL g_ownbackstore; +RD_BOOL g_rdpsnd; +RD_BOOL g_owncolmap; +RD_BOOL g_local_cursor; +char g_codepage[16]; + +#include "../xwin.c" +#include "../utils.c" +#include "../stream.c" + +/* malloc; exit if out of memory */ +void * +xmalloc(int size) +{ + void *mem = malloc(size); + if (mem == NULL) + { + logger(Core, Error, "xmalloc, failed to allocate %d bytes", size); + exit(EX_UNAVAILABLE); + } + return mem; +} + +/* Exit on NULL pointer. Use to verify result from XGetImage etc */ +void +exit_if_null(void *ptr) +{ + if (ptr == NULL) + { + logger(Core, Error, "unexpected null pointer. Out of memory?"); + exit(EX_UNAVAILABLE); + } +} + +/* strdup */ +char * +xstrdup(const char *s) +{ + char *mem = strdup(s); + if (mem == NULL) + { + logger(Core, Error, "xstrdup(), strdup() failed: %s", strerror(errno)); + exit(EX_UNAVAILABLE); + } + return mem; +} + +/* realloc; exit if out of memory */ +void * +xrealloc(void *oldmem, size_t size) +{ + void *mem; + + if (size == 0) + size = 1; + mem = realloc(oldmem, size); + if (mem == NULL) + { + logger(Core, Error, "xrealloc, failed to reallocate %ld bytes", size); + exit(EX_UNAVAILABLE); + } + return mem; +} + +/* free */ +void +xfree(void *mem) +{ + free(mem); +} + +/* Special mocks */ + +int XResizeWindow(Display *display, Window wnd, unsigned int width, unsigned int height) +{ + return mock(display, wnd, width, height); +} + +Status +XGetWindowAttributes(Display *display, Window wnd, XWindowAttributes *attr) +{ + return mock(display, wnd, attr); +} + +void +XSetWMSizeHints(Display *display, Window wnd, XSizeHints *hints, Atom property) +{ + mock(display, wnd, hints, property); +} + +int +XSetClipRectangles(Display *display, GC gc, int clip_x_origin, + int clip_y_origin, XRectangle rectangles[], + int n, int ordering) +{ + return mock(display, gc, clip_x_origin, clip_y_origin, + rectangles, n, ordering); +} + +int XPending(Display *display) +{ + return mock(display); +} + +/* Test functions */ + +Ensure(XWIN, UiResizeWindowCallsXResizeWindow) { + int width = 1024; + int height = 768; + + /* stubs */ + expect(XGetWindowAttributes, will_return(True)); + expect(XSetWMSizeHints); + expect(XSetClipRectangles); + + /* expects */ + expect(XResizeWindow, + when(width, is_equal_to(width)), + when(height, is_equal_to(height))); + + ui_resize_window(width, height); +} + +/* FIXME: This test is broken */ +#if 0 +Ensure(XWIN, UiSelectCallsProcessPendingResizeIfGPendingResizeIsTrue) +{ + g_pending_resize = True; + + expect(rdpdr_add_fds); + expect(rdpdr_check_fds); + + expect(ctrl_add_fds); + expect(ctrl_check_fds); + + expect(seamless_select_timeout); + + expect(XPending, will_return(0)); + + expect(rdpedisp_is_available, will_return(False)); + + /* HELP! How do we mock functions within the same unit? We're + drawing blanks with plenty of "redefined function" or "multiple + definition" errors from the compiler and linker. + + The two attempts below does not work as intended. + */ + + /* Letting process_pending_resize return true will break the + ui_select loop. */ + expect(process_pending_resize, will_return(True)); + + /* process_fds returning True indicates there is data on rdp socket, + assumed this will break out of ui_select loop */ + //expect(process_fds, will_return(True)); + + ui_select(0); +} +#endif /* broken */ + + +#if 0 /* hackety-hackety-hack /k */ +Ensure(XWIN, UiSelectCallsRDPEDISPSetSessionSizeOnResize) +{ + g_pending_resize = True; + g_resize_timer = (struct timeval) {0, 0}; + expect(rdpedisp_is_available, will_return(True)); + expect(rdpedisp_set_session_size); + + ui_select(0); +} +#endif