d10df452ee
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>
572 lines
14 KiB
C
572 lines
14 KiB
C
#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;
|
|
}
|