mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-22 07:01:30 +00:00
[client] control: test config and resize messages
This commit is contained in:
@@ -366,6 +366,44 @@ foreach(name IN LISTS FRAME_SCHEDULER_CASES)
|
|||||||
)
|
)
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
|
add_executable(config-message-tests
|
||||||
|
config_message_test.c
|
||||||
|
../src/message.c
|
||||||
|
../src/core.c
|
||||||
|
../src/config.c
|
||||||
|
)
|
||||||
|
target_compile_definitions(config-message-tests PRIVATE
|
||||||
|
CIMGUI_DEFINE_ENUMS_AND_STRUCTS=1
|
||||||
|
)
|
||||||
|
target_include_directories(config-message-tests PRIVATE
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/../src"
|
||||||
|
"${PROJECT_TOP}/repos/gui/cimgui"
|
||||||
|
"${PROJECT_TOP}/repos/gui/cimgui/imgui"
|
||||||
|
)
|
||||||
|
target_link_libraries(config-message-tests
|
||||||
|
${EXE_FLAGS}
|
||||||
|
lg_common
|
||||||
|
)
|
||||||
|
set(CONFIG_MESSAGE_CASES
|
||||||
|
debounce
|
||||||
|
latest
|
||||||
|
equal
|
||||||
|
reinit
|
||||||
|
control
|
||||||
|
cfg-values
|
||||||
|
cfg-position
|
||||||
|
cfg-size
|
||||||
|
cfg-precedence
|
||||||
|
)
|
||||||
|
foreach(name IN LISTS CONFIG_MESSAGE_CASES)
|
||||||
|
add_test(NAME config-message-${name}
|
||||||
|
COMMAND config-message-tests ${name}
|
||||||
|
)
|
||||||
|
set_tests_properties(config-message-${name} PROPERTIES
|
||||||
|
TIMEOUT 10
|
||||||
|
)
|
||||||
|
endforeach()
|
||||||
|
|
||||||
if(ENABLE_AUDIO)
|
if(ENABLE_AUDIO)
|
||||||
pkg_check_modules(AUDIO_TEST_SAMPLERATE REQUIRED IMPORTED_TARGET
|
pkg_check_modules(AUDIO_TEST_SAMPLERATE REQUIRED IMPORTED_TARGET
|
||||||
samplerate)
|
samplerate)
|
||||||
|
|||||||
473
client/tests/config_message_test.c
Normal file
473
client/tests/config_message_test.c
Normal file
@@ -0,0 +1,473 @@
|
|||||||
|
/**
|
||||||
|
* Looking Glass
|
||||||
|
* Copyright © 2017-2026 The Looking Glass Authors
|
||||||
|
* https://looking-glass.io
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation; either version 2 of the License, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||||
|
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "kb.h"
|
||||||
|
#include "main.h"
|
||||||
|
#include "message.h"
|
||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
#include "common/debug.h"
|
||||||
|
#include "common/option.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define MAX_CTL 16U
|
||||||
|
#define DEBOUNCE_US UINT64_C(500000)
|
||||||
|
#define TEST_ALARM_S 5U
|
||||||
|
|
||||||
|
struct LG_Transport
|
||||||
|
{
|
||||||
|
int unused;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Trace
|
||||||
|
{
|
||||||
|
uint64_t now;
|
||||||
|
unsigned int send;
|
||||||
|
LG_TransportStatus status;
|
||||||
|
LG_TransportControl ctl[MAX_CTL];
|
||||||
|
LG_TransportControlToken token[MAX_CTL];
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct Trace t;
|
||||||
|
static struct LG_Transport tr;
|
||||||
|
|
||||||
|
struct AppState g_state;
|
||||||
|
struct CursorState g_cursor;
|
||||||
|
struct AppParams g_params;
|
||||||
|
|
||||||
|
const char * linux_to_str[KEY_MAX] =
|
||||||
|
{
|
||||||
|
[KEY_A] = "KEY_A",
|
||||||
|
[KEY_RIGHTCTRL] = "KEY_RIGHTCTRL",
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char * renAName(void)
|
||||||
|
{
|
||||||
|
return "EGL";
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char * renBName(void)
|
||||||
|
{
|
||||||
|
return "OpenGL";
|
||||||
|
}
|
||||||
|
|
||||||
|
static LG_RendererOps renA =
|
||||||
|
{
|
||||||
|
.getName = renAName,
|
||||||
|
};
|
||||||
|
|
||||||
|
static LG_RendererOps renB =
|
||||||
|
{
|
||||||
|
.getName = renBName,
|
||||||
|
};
|
||||||
|
|
||||||
|
LG_RendererOps * LG_Renderers[] =
|
||||||
|
{
|
||||||
|
&renA,
|
||||||
|
&renB,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
bool lgTransport_isValid(const char * name)
|
||||||
|
{
|
||||||
|
return name &&
|
||||||
|
(strcmp(name, "lgmp") == 0 || strcmp(name, "spice") == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int clock_gettime(clockid_t id, struct timespec * ts)
|
||||||
|
{
|
||||||
|
const uint64_t ns = t.now * UINT64_C(1000);
|
||||||
|
ts->tv_sec = ns / UINT64_C(1000000000);
|
||||||
|
ts->tv_nsec = ns % UINT64_C(1000000000);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static LG_TransportStatus sendCtl(LG_Transport * transport,
|
||||||
|
const LG_TransportControl * control, LG_TransportControlToken * token)
|
||||||
|
{
|
||||||
|
CHECK(transport == &tr);
|
||||||
|
CHECK(control);
|
||||||
|
CHECK(token);
|
||||||
|
CHECK(t.send < MAX_CTL);
|
||||||
|
|
||||||
|
const unsigned int no = t.send++;
|
||||||
|
t.ctl[no] = *control;
|
||||||
|
t.token[no] = no + 1;
|
||||||
|
*token = t.token[no];
|
||||||
|
return t.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const LG_TransportOps trOps =
|
||||||
|
{
|
||||||
|
.name = "fake",
|
||||||
|
.sendControl = sendCtl,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void msgInit(void)
|
||||||
|
{
|
||||||
|
t.now = UINT64_C(1000000);
|
||||||
|
t.status = LG_TRANSPORT_OK;
|
||||||
|
g_state.transport.handle = &tr;
|
||||||
|
g_state.transport.ops = &trOps;
|
||||||
|
g_state.transportFeatures = LG_TRANSPORT_FEATURE_WINDOW_SIZE;
|
||||||
|
g_state.srcSize = (struct Point) { 1920, 1080 };
|
||||||
|
CHECK(lgMessage_init());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void post(unsigned int width, unsigned int height)
|
||||||
|
{
|
||||||
|
const LGMsg msg =
|
||||||
|
{
|
||||||
|
.type = LG_MSG_WINDOWSIZE,
|
||||||
|
.windowSize =
|
||||||
|
{
|
||||||
|
.width = width,
|
||||||
|
.height = height,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
lgMessage_post(&msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void step(uint64_t us)
|
||||||
|
{
|
||||||
|
t.now += us;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mature(void)
|
||||||
|
{
|
||||||
|
step(DEBOUNCE_US);
|
||||||
|
lgMessage_process();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void checkCtl(unsigned int no, unsigned int width,
|
||||||
|
unsigned int height)
|
||||||
|
{
|
||||||
|
CHECK(no < t.send);
|
||||||
|
CHECK(t.ctl[no].type == LG_TRANSPORT_CONTROL_WINDOW_SIZE);
|
||||||
|
CHECK(t.ctl[no].windowSize.width == width);
|
||||||
|
CHECK(t.ctl[no].windowSize.height == height);
|
||||||
|
CHECK(t.token[no] == no + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testDebounce(void)
|
||||||
|
{
|
||||||
|
msgInit();
|
||||||
|
post(800, 600);
|
||||||
|
lgMessage_process();
|
||||||
|
CHECK(t.send == 0);
|
||||||
|
step(DEBOUNCE_US - 1);
|
||||||
|
lgMessage_process();
|
||||||
|
CHECK(t.send == 0);
|
||||||
|
step(1);
|
||||||
|
lgMessage_process();
|
||||||
|
CHECK(t.send == 1);
|
||||||
|
checkCtl(0, 800, 600);
|
||||||
|
|
||||||
|
post(800, 600);
|
||||||
|
mature();
|
||||||
|
CHECK(t.send == 1);
|
||||||
|
post(801, 600);
|
||||||
|
mature();
|
||||||
|
CHECK(t.send == 2);
|
||||||
|
checkCtl(1, 801, 600);
|
||||||
|
lgMessage_deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testLatest(void)
|
||||||
|
{
|
||||||
|
msgInit();
|
||||||
|
post(800, 600);
|
||||||
|
step(100);
|
||||||
|
post(1024, 768);
|
||||||
|
lgMessage_process();
|
||||||
|
CHECK(t.send == 0);
|
||||||
|
|
||||||
|
step(DEBOUNCE_US - 1);
|
||||||
|
lgMessage_process();
|
||||||
|
CHECK(t.send == 0);
|
||||||
|
step(1);
|
||||||
|
lgMessage_process();
|
||||||
|
CHECK(t.send == 1);
|
||||||
|
checkCtl(0, 1024, 768);
|
||||||
|
lgMessage_deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testEqual(void)
|
||||||
|
{
|
||||||
|
msgInit();
|
||||||
|
post(800, 600);
|
||||||
|
post(1024, 768);
|
||||||
|
mature();
|
||||||
|
|
||||||
|
CHECK(t.send == 1);
|
||||||
|
/* Posting order decides when timestamps are equal. */
|
||||||
|
checkCtl(0, 1024, 768);
|
||||||
|
lgMessage_deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testReinit(void)
|
||||||
|
{
|
||||||
|
msgInit();
|
||||||
|
post(640, 480);
|
||||||
|
lgMessage_deinit();
|
||||||
|
CHECK(lgMessage_init());
|
||||||
|
mature();
|
||||||
|
CHECK(t.send == 0);
|
||||||
|
|
||||||
|
post(640, 480);
|
||||||
|
mature();
|
||||||
|
CHECK(t.send == 1);
|
||||||
|
lgMessage_deinit();
|
||||||
|
CHECK(lgMessage_init());
|
||||||
|
post(640, 480);
|
||||||
|
mature();
|
||||||
|
|
||||||
|
/* A new message instance must not inherit its old duplicate filter. */
|
||||||
|
CHECK(t.send == 2);
|
||||||
|
checkCtl(1, 640, 480);
|
||||||
|
lgMessage_deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testControl(void)
|
||||||
|
{
|
||||||
|
msgInit();
|
||||||
|
g_state.transportFeatures = 0;
|
||||||
|
post(100, 100);
|
||||||
|
mature();
|
||||||
|
CHECK(t.send == 0);
|
||||||
|
|
||||||
|
g_state.transportFeatures = LG_TRANSPORT_FEATURE_WINDOW_SIZE;
|
||||||
|
g_state.transport.handle = NULL;
|
||||||
|
post(200, 200);
|
||||||
|
mature();
|
||||||
|
CHECK(t.send == 0);
|
||||||
|
|
||||||
|
g_state.transport.handle = &tr;
|
||||||
|
g_state.srcSize = (struct Point) { 300, 300 };
|
||||||
|
post(300, 300);
|
||||||
|
mature();
|
||||||
|
CHECK(t.send == 0);
|
||||||
|
|
||||||
|
t.status = LG_TRANSPORT_ERROR;
|
||||||
|
post(400, 400);
|
||||||
|
mature();
|
||||||
|
CHECK(t.send == 1);
|
||||||
|
checkCtl(0, 400, 400);
|
||||||
|
step(DEBOUNCE_US);
|
||||||
|
lgMessage_process();
|
||||||
|
CHECK(t.send == 1);
|
||||||
|
|
||||||
|
t.status = LG_TRANSPORT_UNAVAILABLE;
|
||||||
|
post(500, 500);
|
||||||
|
mature();
|
||||||
|
CHECK(t.send == 2);
|
||||||
|
checkCtl(1, 500, 500);
|
||||||
|
|
||||||
|
t.status = LG_TRANSPORT_OK;
|
||||||
|
post(600, 600);
|
||||||
|
mature();
|
||||||
|
CHECK(t.send == 3);
|
||||||
|
checkCtl(2, 600, 600);
|
||||||
|
lgMessage_deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct Option * opt(const char * module, const char * name)
|
||||||
|
{
|
||||||
|
struct Option * result = option_get(module, name);
|
||||||
|
CHECK(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void checkStr(char * value, const char * expected)
|
||||||
|
{
|
||||||
|
CHECK(value);
|
||||||
|
CHECK(strcmp(value, expected) == 0);
|
||||||
|
free(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testCfgValues(void)
|
||||||
|
{
|
||||||
|
config_init();
|
||||||
|
|
||||||
|
struct Option * pos = opt("win", "position");
|
||||||
|
CHECK(pos->parser(pos, "center"));
|
||||||
|
CHECK(g_params.center);
|
||||||
|
checkStr(pos->toString(pos), "center");
|
||||||
|
CHECK(pos->parser(pos, "-10x20"));
|
||||||
|
CHECK(!g_params.center);
|
||||||
|
CHECK(g_params.x == -10);
|
||||||
|
CHECK(g_params.y == 20);
|
||||||
|
checkStr(pos->toString(pos), "-10x20");
|
||||||
|
|
||||||
|
struct Option * size = opt("win", "size");
|
||||||
|
CHECK(size->parser(size, "1920x1080"));
|
||||||
|
CHECK(g_params.w == 1920);
|
||||||
|
CHECK(g_params.h == 1080);
|
||||||
|
checkStr(size->toString(size), "1920x1080");
|
||||||
|
|
||||||
|
struct Option * mic = opt("audio", "micDefault");
|
||||||
|
CHECK(mic->parser(mic, "ALLOW"));
|
||||||
|
CHECK(g_params.micDefaultState == MIC_DEFAULT_ALLOW);
|
||||||
|
checkStr(mic->toString(mic), "allow");
|
||||||
|
CHECK(!mic->parser(mic, "sometimes"));
|
||||||
|
|
||||||
|
struct Option * resampler = opt("audio", "resampler");
|
||||||
|
CHECK(resampler->parser(resampler, "BACKEND"));
|
||||||
|
CHECK(g_params.audioResampler == AUDIO_RESAMPLER_BACKEND);
|
||||||
|
checkStr(resampler->toString(resampler), "backend");
|
||||||
|
CHECK(!resampler->parser(resampler, "linear"));
|
||||||
|
|
||||||
|
struct Option * renderer = opt("app", "renderer");
|
||||||
|
CHECK(renderer->parser(renderer, "OpenGL"));
|
||||||
|
CHECK(g_params.forceRenderer);
|
||||||
|
CHECK(g_params.forceRendererIndex == 1);
|
||||||
|
checkStr(renderer->toString(renderer), "OpenGL");
|
||||||
|
CHECK(renderer->parser(renderer, "auto"));
|
||||||
|
CHECK(!g_params.forceRenderer);
|
||||||
|
checkStr(renderer->toString(renderer), "auto");
|
||||||
|
|
||||||
|
struct Option * key = opt("input", "escapeKey");
|
||||||
|
CHECK(key->parser(key, "KEY_A"));
|
||||||
|
CHECK(key->value.x_int == KEY_A);
|
||||||
|
char * keyName = key->toString(key);
|
||||||
|
CHECK(keyName);
|
||||||
|
CHECK(strstr(keyName, "KEY_A"));
|
||||||
|
free(keyName);
|
||||||
|
|
||||||
|
struct Option * rotate = opt("win", "rotate");
|
||||||
|
const char * error = NULL;
|
||||||
|
rotate->value.x_int = 270;
|
||||||
|
CHECK(rotate->validator(rotate, &error));
|
||||||
|
rotate->value.x_int = 45;
|
||||||
|
CHECK(!rotate->validator(rotate, &error));
|
||||||
|
CHECK(error);
|
||||||
|
|
||||||
|
config_free();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testCfgPos(void)
|
||||||
|
{
|
||||||
|
config_init();
|
||||||
|
struct Option * pos = opt("win", "position");
|
||||||
|
CHECK(!pos->parser(pos, NULL));
|
||||||
|
CHECK(!pos->parser(pos, "10"));
|
||||||
|
CHECK(!pos->parser(pos, "10x20tail"));
|
||||||
|
config_free();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testCfgSize(void)
|
||||||
|
{
|
||||||
|
config_init();
|
||||||
|
struct Option * size = opt("win", "size");
|
||||||
|
CHECK(!size->parser(size, NULL));
|
||||||
|
CHECK(!size->parser(size, "0x20"));
|
||||||
|
CHECK(!size->parser(size, "-1x20"));
|
||||||
|
CHECK(!size->parser(size, "10x20tail"));
|
||||||
|
config_free();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void loadCfg(const char * text)
|
||||||
|
{
|
||||||
|
char path[] = "/tmp/lg-config-test-XXXXXX";
|
||||||
|
const int fd = mkstemp(path);
|
||||||
|
CHECK(fd >= 0);
|
||||||
|
const size_t size = strlen(text);
|
||||||
|
CHECK(write(fd, text, size) == (ssize_t)size);
|
||||||
|
CHECK(close(fd) == 0);
|
||||||
|
CHECK(option_load(path));
|
||||||
|
CHECK(unlink(path) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testCfgPrecedence(void)
|
||||||
|
{
|
||||||
|
config_init();
|
||||||
|
loadCfg("[win]\nsize=800x600\nposition=10x20\n");
|
||||||
|
CHECK(g_params.w == 800);
|
||||||
|
CHECK(g_params.h == 600);
|
||||||
|
CHECK(g_params.x == 10);
|
||||||
|
CHECK(g_params.y == 20);
|
||||||
|
|
||||||
|
char * argv[] =
|
||||||
|
{
|
||||||
|
"test",
|
||||||
|
"win:size=1024x768",
|
||||||
|
"win:position=center",
|
||||||
|
};
|
||||||
|
CHECK(option_parse(3, argv));
|
||||||
|
CHECK(g_params.w == 1024);
|
||||||
|
CHECK(g_params.h == 768);
|
||||||
|
CHECK(g_params.center);
|
||||||
|
|
||||||
|
loadCfg("[win]\nsize=1280x720\nposition=30x40\n");
|
||||||
|
CHECK(g_params.w == 1280);
|
||||||
|
CHECK(g_params.h == 720);
|
||||||
|
CHECK(g_params.x == 30);
|
||||||
|
CHECK(g_params.y == 40);
|
||||||
|
CHECK(!g_params.center);
|
||||||
|
CHECK(option_validate());
|
||||||
|
config_free();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Test
|
||||||
|
{
|
||||||
|
const char * name;
|
||||||
|
void (*run)(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct Test tests[] =
|
||||||
|
{
|
||||||
|
{ "debounce" , testDebounce },
|
||||||
|
{ "latest" , testLatest },
|
||||||
|
{ "equal" , testEqual },
|
||||||
|
{ "reinit" , testReinit },
|
||||||
|
{ "control" , testControl },
|
||||||
|
{ "cfg-values" , testCfgValues },
|
||||||
|
{ "cfg-position" , testCfgPos },
|
||||||
|
{ "cfg-size" , testCfgSize },
|
||||||
|
{ "cfg-precedence", testCfgPrecedence },
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char ** argv)
|
||||||
|
{
|
||||||
|
if (argc != 2)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "usage: %s <case>\n", argv[0]);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
alarm(TEST_ALARM_S);
|
||||||
|
debug_init();
|
||||||
|
for (unsigned int i = 0; i < sizeof(tests) / sizeof(tests[0]); ++i)
|
||||||
|
if (strcmp(argv[1], tests[i].name) == 0)
|
||||||
|
{
|
||||||
|
tests[i].run();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "unknown test: %s\n", argv[1]);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user