summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCendio <cendio@pairstation.lkpg.cendio.se>2017-12-07 11:07:23 +0100
committerCendio <cendio@pairstation.lkpg.cendio.se>2017-12-07 11:15:03 +0100
commitd10df452ee2e9f1c8b36bd1ff984b1fb2497830e (patch)
treee41047631288a9156da489659fc9a14a90677132
parent85c10b5bc0806684cd1404cebf73c1fa75e5cb28 (diff)
downloadrdesktop-d10df452ee2e9f1c8b36bd1ff984b1fb2497830e.zip
Ensure (partial) resize functionality with automated tests
This test suite was built with cgreen, and has various tests that tries to ensure that we didn't break something while developing the dynamic resize feature. Signed-off-by: Henrik Andersson <hean01@cendio.com> Signed-off-by: Karl Mikaelsson <derfian@cendio.se> Signed-off-by: Thomas Nilefalk <thoni56@cendio.se>
-rw-r--r--.gitignore2
-rw-r--r--tests/Makefile49
-rw-r--r--tests/README.md26
-rw-r--r--tests/bitmap_mock.c7
-rw-r--r--tests/cache_mock.c46
-rw-r--r--tests/channels_mock.c7
-rw-r--r--tests/ctrl_mock.c14
-rw-r--r--tests/dvc_mock.c26
-rw-r--r--tests/ewmh_mock.c87
-rw-r--r--tests/licence_mock.c9
-rw-r--r--tests/mcs_mock.c51
-rw-r--r--tests/mppc_mock.c8
-rw-r--r--tests/orders_mock.c12
-rw-r--r--tests/pstcache_mock.c12
-rw-r--r--tests/rdesktop_mock.c13
-rw-r--r--tests/rdp5_mock.c7
-rw-r--r--tests/rdp_mock.c15
-rw-r--r--tests/rdp_test.c141
-rw-r--r--tests/rdpdr_mock.c14
-rw-r--r--tests/rdpedisp_mock.c14
-rw-r--r--tests/resize_test.c571
-rw-r--r--tests/seamless_mock.c58
-rw-r--r--tests/secure_mock.c32
-rw-r--r--tests/ssl_mock.c108
-rw-r--r--tests/tcp_mock.c7
-rw-r--r--tests/ui_mock.c72
-rw-r--r--tests/utils_test.c179
-rw-r--r--tests/x11_mock.c10
-rw-r--r--tests/xclip_mock.c37
-rw-r--r--tests/xkeymap_mock.c63
-rw-r--r--tests/xwin_test.c216
31 files changed, 1912 insertions, 1 deletions
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 <cgreen/mocks.h>
+#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 <cgreen/mocks.h>
+#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 <cgreen/mocks.h>
+#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 <cgreen/mocks.h>
+#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 <cgreen/mocks.h>
+#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 <cgreen/mocks.h>
+#include "../rdesktop.h"
+#include <X11/Xlib.h>
+
+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 <cgreen/mocks.h>
+#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 <cgreen/mocks.h>
+#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 <cgreen/mocks.h>
+#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 <cgreen/mocks.h>
+#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 <cgreen/mocks.h>
+#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 <cgreen/mocks.h>
+#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 <cgreen/mocks.h>
+#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 <cgreen/mocks.h>
+#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 <cgreen/cgreen.h>
+#include <cgreen/mocks.h>
+#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 <cgreen/mocks.h>
+#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 <cgreen/mocks.h>
+#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 <cgreen/cgreen.h>
+#include <cgreen/mocks.h>
+#include <X11/Xlib.h>
+#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 <cgreen/mocks.h>
+#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 <cgreen/mocks.h>
+#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 <cgreen/mocks.h>
+#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 <cgreen/mocks.h>
+#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 <cgreen/mocks.h>
+#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 <cgreen/cgreen.h>
+#include <cgreen/mocks.h>
+#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 <cgreen/mocks.h>
+#include "../rdesktop.h"
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+
+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 <cgreen/mocks.h>
+#include "../rdesktop.h"
+
+#include <X11/Xlib.h>
+
+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 <cgreen/mocks.h>
+#include "../rdesktop.h"
+
+#include <X11/Xlib.h>
+
+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 <cgreen/cgreen.h>
+#include <cgreen/mocks.h>
+#include "../rdesktop.h"
+#include "../proto.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xproto.h>
+#include <X11/Xatom.h>
+
+/* 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