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()
|
||||
|
||||
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)
|
||||
pkg_check_modules(AUDIO_TEST_SAMPLERATE REQUIRED IMPORTED_TARGET
|
||||
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