Skip to content
Commits on Source (9)
  • Daniel Stone's avatar
    CI: Skip certain fontconfig leaks · 6a06a069
    Daniel Stone authored
    
    
    For some reason, the Debian CI setup leaks fontconfig allocations in a
    way which it does not for me on Fedora. On the assumption that this has
    been fixed between our older CI Debian and Fedora 36, suppress the leak
    warning, because we do already call FcFini() which should free it.
    
    Signed-off-by: default avatarDaniel Stone <daniels@collabora.com>
    6a06a069
  • Daniel Stone's avatar
    CI: Disable ASan fast unwinding for suppressions · c5ed892b
    Daniel Stone authored
    
    
    Unfortunately just adding suppressions isn't enough; the build of Expat
    we have in our CI system does not have frame pointers, so ASan's fast
    unwinder can't see through it. This means that the suppressions we've
    added won't be taken into account.
    
    For now, disable the fast unwinder for the Xwayland test only. Disabling
    it globally is not practical as it massively increases the per-test
    runtime, so here (to avoid polluting the build system), we use a
    per-test wrapper to selectively choose the unwinder.
    
    Signed-off-by: default avatarDaniel Stone <daniels@collabora.com>
    c5ed892b
  • Daniel Stone's avatar
    CI: Never unload llvmpipe DSO whilst testing · 6c8ae362
    Daniel Stone authored
    
    
    This commit is truly horrible.
    
    We want to run ASan with leak checking enabled in CI so we can catch
    memory leaks before they're introduced. This works well with Pixman, and
    with NIR-only drivers like iris or Panfrost.
    
    But when we run under llvmpipe - which we do under CI - we start failing
    because:
      - Mesa pulls in llvmpipe via dlopen
      - llvmpipe pulls in LLVM itself via DT_NEEDED
      - initialising LLVM's global type/etc systems performs thread-local
        allocations
      - llvmpipe can't free those allocations since the application might
        also be using LLVM
      - Weston stops using GL and destroys all GL objects, leading to Mesa
        unloading llvmpipe like it should
      - with everything disappearing from the process's vmap, ASan can no
        longer keep track of still-reachable pointers
      - tests fail because LLVM is 'leaking'
    
    Usually, an alternative is to LD_PRELOAD a shim which overrides
    dlclose() to be a no-op. This is not usable here, because when
    $LD_PRELOAD is not empty and ASan is not first in it, ASan immediately
    errors out. Prepending ASan doesn't work, because we run our tests
    through Meson (which also invokes Ninja), leading to LSan exploding
    over CPython and Ninja, which is not what we're interested in.
    
    It would be possible to inject _both_ ASan and a dlclose-does-nothing
    shim DSO into the LD_PRELOAD environment for every test, but that seems
    even worse, especially as Meson strongly discourages globbing for random
    files in the root.
    
    So, here we are, doing what we can: finding where swrast_dri.so (aka
    llvmpipe) lives, stashing that in an environment variable, and
    deliberately leaking a dlopen handle which we never close to ensure that
    neither llvmpipe nor LLVM leave our process's address space before we
    exit.
    
    Signed-off-by: default avatarDaniel Stone <daniels@collabora.com>
    6c8ae362
  • Daniel Stone's avatar
    zuc: Delete support for forking tests · 759712ba
    Daniel Stone authored
    
    
    ZUC's default mode is to fork for every test case. Unfortunately on
    AArch64, fork in an ASan-traced program usually takes around 3.6 entire
    seconds. This was leading to the config-parser test timing out.
    
    As none of our remaining ZUC tests even need to fork, just remove all
    support for it.
    
    Signed-off-by: default avatarDaniel Stone <daniels@collabora.com>
    759712ba
  • Daniel Stone's avatar
    tests: Use memstream for config-parser test · f5223166
    Daniel Stone authored
    
    
    Using real files is unnecessarily heavy and error-prone. Fixes timeouts
    seen on CI with ASan.
    
    Signed-off-by: default avatarDaniel Stone <daniels@collabora.com>
    f5223166
  • Daniel Stone's avatar
    tests: Check requirements after setting up args · 23c8dc7b
    Daniel Stone authored
    
    
    Setting up the arguments may consume some of the arguments, e.g. if we
    provide a config file or extra modules, then the test harness is
    expected to be responsible for freeing those arguments.
    
    Checking the requirements and bailing first means that we never do that,
    and thus skipped tests result in leaks. Flip the order so we set up the
    args first and skip last, so we can consistently take ownership of all
    the provided setup parameters.
    
    Signed-off-by: default avatarDaniel Stone <daniels@collabora.com>
    23c8dc7b
  • Daniel Stone's avatar
    xwayland: Don't dup() displayfd pipe · 4aa885d4
    Daniel Stone authored
    
    
    For some reason, this causes the reads to get completely lost sometimes
    in CI.
    
    Signed-off-by: default avatarDaniel Stone <daniels@collabora.com>
    4aa885d4
  • Daniel Stone's avatar
    xwayland: Allow for old WM_NORMAL_HINTS · 5b11f406
    Daniel Stone authored
    
    
    There are two versions of WM_NORMAL_HINTS: the original pre-ICCCM
    version (standardised by Xlib itself?) provides 15 elements of 32 bits
    each, with the ICCCM v1 extending this by 3 additional elements.
    
    Since the flags are enough to identify which elements are present, and
    the structure is append-only, we only need to read the minimum length
    between what the user provided and what we support.
    
    Fixes a heap overrun found with ASan.
    
    Signed-off-by: default avatarDaniel Stone <daniels@collabora.com>
    5b11f406
  • Daniel Stone's avatar
    xwayland: Add compositor destroy listener to free allocation · 18897253
    Daniel Stone authored
    
    
    Otherwise we just leak this into the void. Not good.
    
    Signed-off-by: default avatarDaniel Stone <daniels@collabora.com>
    18897253
......@@ -202,7 +202,7 @@ aarch64-debian-container_prep:
- $BUILDDIR/weston-virtme
- $PREFIX
reports:
junit: $BUILDDIR/meson-logs/testlog.junit.xml
junit: $BUILDDIR/meson-logs/testlog-per-test-asan.sh.junit.xml
# Same as above, but without running any tests.
.build-no-test:
......
......@@ -6,3 +6,6 @@ leak:cairo_text_extents
# Pango thread-global state (not destroyable?)
leak:pango_language_get_default
# This leaks in Debian's fontconfig/Xwayland setup?
leak:FcConfigSubstituteWithPat
#!/bin/bash
# When running Debian's Xwayland and fontconfig, we hit memory leaks which
# aren't visible on other setups. We do have suppressions for these tests, but
# regrettably ASan can't see through omitted frame pointers in Expat, so for
# Xwayland specifically, we disable fast-unwind.
#
# Doing it globally makes the other tests far, far, too slow to run.
case "$1" in
*xwayland*)
export ASAN_OPTIONS="detect_leaks=0,fast_unwind_on_malloc=0"
;;
*)
export ASAN_OPTIONS="detect_leaks=0"
;;
esac
export ASAN_OPTIONS
exec "$@"
......@@ -26,13 +26,17 @@ export HOME=/root
export PATH=$HOME/.local/bin:$PATH
export PATH=/usr/local/bin:$PATH
export ASAN_OPTIONS=detect_leaks=0,atexit=1
export SEATD_LOGLEVEL=debug
# Terrible hack, per comment in weston-test-runner.c's main(): find Mesa's
# llvmpipe driver module location
export WESTON_CI_LEAK_DL_HANDLE=$(find /usr/local -name swrast_dri.so -print 2>/dev/null || true)
# run the tests and save the exit status
# we give ourselves a very generous timeout multiplier due to ASan overhead
echo 0x1f > /sys/module/drm/parameters/debug
seatd-launch -- meson test --no-rebuild --timeout-multiplier 4
seatd-launch -- meson test --no-rebuild --timeout-multiplier 4 \
--wrapper $(pwd)/../.gitlab-ci/virtme-scripts/per-test-asan.sh
# note that we need to store the return value from the tests in order to
# determine if the test suite ran successfully or not.
TEST_RES=$?
......
......@@ -45,6 +45,7 @@
struct wet_xwayland {
struct weston_compositor *compositor;
struct wl_listener compositor_destroy_listener;
const struct weston_xwayland_api *api;
struct weston_xwayland *xwayland;
struct wl_event_source *display_fd_source;
......@@ -134,11 +135,6 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd
return 1;
}
if (os_fd_set_cloexec(display_fd[1]) != 0) {
weston_log("failed setting Xwayland end of displayfd as cloexec\n");
return 1;
}
pid = fork();
switch (pid) {
case 0:
......@@ -153,12 +149,16 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd
ret &= dup_fd_to_string(abstract_fd_str, abstract_fd);
ret &= dup_fd_to_string(unix_fd_str, unix_fd);
ret &= dup_fd_to_string(wm_fd_str, wm[1]);
ret &= dup_fd_to_string(display_fd_str, display_fd[1]);
if (!ret)
_exit(EXIT_FAILURE);
setenv("WAYLAND_SOCKET", wayland_socket_str, 1);
if (snprintf(display_fd_str, sizeof(display_fd_str), "%d",
display_fd[1]) <= 0) {
_exit(EXIT_FAILURE);
}
if (execl(xserver,
xserver,
display,
......@@ -217,6 +217,21 @@ xserver_cleanup(struct weston_process *process, int status)
wxw->client = NULL;
}
static void
wxw_compositor_destroy(struct wl_listener *listener, void *data)
{
struct wet_xwayland *wxw =
wl_container_of(listener, wxw, compositor_destroy_listener);
/* Don't call xserver_exited because Xwayland's own destroy handler
* already does this for us ... */
if (wxw->client)
kill(wxw->process.pid, SIGTERM);
wl_list_remove(&wxw->process.link);
free(wxw);
}
int
wet_load_xwayland(struct weston_compositor *comp)
{
......@@ -246,9 +261,13 @@ wet_load_xwayland(struct weston_compositor *comp)
wxw->compositor = comp;
wxw->api = api;
wxw->xwayland = xwayland;
wl_list_init(&wxw->process.link);
wxw->process.cleanup = xserver_cleanup;
wxw->compositor_destroy_listener.notify = wxw_compositor_destroy;
if (api->listen(xwayland, wxw, spawn_xserver) < 0)
return -1;
wl_signal_add(&comp->destroy_signal, &wxw->compositor_destroy_listener);
return 0;
}
......@@ -88,6 +88,9 @@ weston_config_section_get_bool(struct weston_config_section *section,
const char *
weston_config_get_name_from_env(void);
struct weston_config *
weston_config_parse_fp(FILE *file);
struct weston_config *
weston_config_parse(const char *name);
......
......@@ -381,41 +381,15 @@ section_add_entry(struct weston_config_section *section,
return entry;
}
WL_EXPORT struct weston_config *
weston_config_parse(const char *name)
static bool
weston_config_parse_internal(struct weston_config *config, FILE *fp)
{
FILE *fp;
char line[512], *p;
struct stat filestat;
struct weston_config *config;
struct weston_config_section *section = NULL;
int i, fd;
config = zalloc(sizeof *config);
if (config == NULL)
return NULL;
char line[512], *p;
int i;
wl_list_init(&config->section_list);
fd = open_config_file(config, name);
if (fd == -1) {
free(config);
return NULL;
}
if (fstat(fd, &filestat) < 0 ||
!S_ISREG(filestat.st_mode)) {
close(fd);
free(config);
return NULL;
}
fp = fdopen(fd, "r");
if (fp == NULL) {
free(config);
return NULL;
}
while (fgets(line, sizeof line, fp)) {
switch (line[0]) {
case '#':
......@@ -426,9 +400,7 @@ weston_config_parse(const char *name)
if (!p || p[1] != '\n') {
fprintf(stderr, "malformed "
"section header: %s\n", line);
fclose(fp);
weston_config_destroy(config);
return NULL;
return false;
}
p[0] = '\0';
section = config_add_section(config, &line[1]);
......@@ -438,9 +410,7 @@ weston_config_parse(const char *name)
if (!p || p == line || !section) {
fprintf(stderr, "malformed "
"config line: %s\n", line);
fclose(fp);
weston_config_destroy(config);
return NULL;
return false;
}
p[0] = '\0';
......@@ -457,8 +427,67 @@ weston_config_parse(const char *name)
}
}
return true;
}
WESTON_EXPORT_FOR_TESTS struct weston_config *
weston_config_parse_fp(FILE *file)
{
struct weston_config *config = zalloc(sizeof(*config));
if (config == NULL)
return NULL;
if (!weston_config_parse_internal(config, file)) {
weston_config_destroy(config);
return NULL;
}
return config;
}
WL_EXPORT struct weston_config *
weston_config_parse(const char *name)
{
FILE *fp;
struct stat filestat;
struct weston_config *config;
int fd;
bool ret;
config = zalloc(sizeof *config);
if (config == NULL)
return NULL;
fd = open_config_file(config, name);
if (fd == -1) {
free(config);
return NULL;
}
if (fstat(fd, &filestat) < 0 ||
!S_ISREG(filestat.st_mode)) {
close(fd);
free(config);
return NULL;
}
fp = fdopen(fd, "r");
if (fp == NULL) {
close(fd);
free(config);
return NULL;
}
ret = weston_config_parse_internal(config, fp);
fclose(fp);
if (!ret) {
weston_config_destroy(config);
return NULL;
}
return config;
}
......
......@@ -48,23 +48,25 @@ static struct weston_config *
load_config(const char *text)
{
struct weston_config *config = NULL;
int len = 0;
int fd = -1;
char file[] = "/tmp/weston-config-parser-test-XXXXXX";
char *content = NULL;
size_t file_len = 0;
int write_len;
FILE *file;
ZUC_ASSERTG_NOT_NULL(text, out);
file = open_memstream(&content, &file_len);
ZUC_ASSERTG_NOT_NULL(file, out);
fd = mkstemp(file);
ZUC_ASSERTG_NE(-1, fd, out);
write_len = fwrite(text, 1, strlen(text), file);
ZUC_ASSERTG_EQ((int)strlen(text), write_len, out_close);
len = write(fd, text, strlen(text));
ZUC_ASSERTG_EQ((int)strlen(text), len, out_close);
ZUC_ASSERTG_EQ(fflush(file), 0, out_close);
fseek(file, 0L, SEEK_SET);
config = weston_config_parse(file);
config = weston_config_parse_fp(file);
out_close:
close(fd);
unlink(file);
fclose(file);
free(content);
out:
return config;
}
......
......@@ -14,6 +14,7 @@ lib_test_runner = static_library(
dependencies: [
dep_libweston_private_h_deps,
dep_wayland_client,
dep_libdl,
],
include_directories: common_inc,
install: false,
......@@ -106,8 +107,6 @@ lib_zuc = static_library(
'../tools/zunitc/inc/zunitc/zunitc_impl.h',
'../tools/zunitc/src/zuc_base_logger.c',
'../tools/zunitc/src/zuc_base_logger.h',
'../tools/zunitc/src/zuc_collector.c',
'../tools/zunitc/src/zuc_collector.h',
'../tools/zunitc/src/zuc_context.h',
'../tools/zunitc/src/zuc_event.h',
'../tools/zunitc/src/zuc_event_listener.h',
......
......@@ -291,48 +291,8 @@ execute_compositor(const struct compositor_setup *setup,
struct prog_args args;
char *tmp;
const char *ctmp, *drm_device;
int ret, lock_fd = -1;
if (setenv("WESTON_MODULE_MAP", WESTON_MODULE_MAP, 0) < 0 ||
setenv("WESTON_DATA_DIR", WESTON_DATA_DIR, 0) < 0) {
fprintf(stderr, "Error: environment setup failed.\n");
return RESULT_HARD_ERROR;
}
#ifndef BUILD_DRM_COMPOSITOR
if (setup->backend == WESTON_BACKEND_DRM) {
fprintf(stderr, "DRM-backend required but not built, skipping.\n");
return RESULT_SKIP;
}
#endif
#ifndef BUILD_RDP_COMPOSITOR
if (setup->backend == WESTON_BACKEND_RDP) {
fprintf(stderr, "RDP-backend required but not built, skipping.\n");
return RESULT_SKIP;
}
#endif
#ifndef BUILD_WAYLAND_COMPOSITOR
if (setup->backend == WESTON_BACKEND_WAYLAND) {
fprintf(stderr, "wayland-backend required but not built, skipping.\n");
return RESULT_SKIP;
}
#endif
#ifndef BUILD_X11_COMPOSITOR
if (setup->backend == WESTON_BACKEND_X11) {
fprintf(stderr, "X11-backend required but not built, skipping.\n");
return RESULT_SKIP;
}
#endif
#ifndef ENABLE_EGL
if (setup->renderer == RENDERER_GL) {
fprintf(stderr, "GL-renderer required but not built, skipping.\n");
return RESULT_SKIP;
}
#endif
int lock_fd = -1;
int ret = RESULT_OK;
prog_args_init(&args);
......@@ -424,7 +384,50 @@ execute_compositor(const struct compositor_setup *setup,
test_data.test_quirks = setup->test_quirks;
test_data.test_private_data = data;
prog_args_save(&args);
ret = wet_main(args.argc, args.argv, &test_data);
if (setenv("WESTON_MODULE_MAP", WESTON_MODULE_MAP, 0) < 0 ||
setenv("WESTON_DATA_DIR", WESTON_DATA_DIR, 0) < 0) {
fprintf(stderr, "Error: environment setup failed.\n");
ret = RESULT_HARD_ERROR;
}
#ifndef BUILD_DRM_COMPOSITOR
if (setup->backend == WESTON_BACKEND_DRM) {
fprintf(stderr, "DRM-backend required but not built, skipping.\n");
ret = RESULT_SKIP;
}
#endif
#ifndef BUILD_RDP_COMPOSITOR
if (setup->backend == WESTON_BACKEND_RDP) {
fprintf(stderr, "RDP-backend required but not built, skipping.\n");
ret = RESULT_SKIP;
}
#endif
#ifndef BUILD_WAYLAND_COMPOSITOR
if (setup->backend == WESTON_BACKEND_WAYLAND) {
fprintf(stderr, "wayland-backend required but not built, skipping.\n");
ret = RESULT_SKIP;
}
#endif
#ifndef BUILD_X11_COMPOSITOR
if (setup->backend == WESTON_BACKEND_X11) {
fprintf(stderr, "X11-backend required but not built, skipping.\n");
ret = RESULT_SKIP;
}
#endif
#ifndef ENABLE_EGL
if (setup->renderer == RENDERER_GL) {
fprintf(stderr, "GL-renderer required but not built, skipping.\n");
ret = RESULT_SKIP;
}
#endif
if (ret == RESULT_OK)
ret = wet_main(args.argc, args.argv, &test_data);
out:
prog_args_fini(&args);
......
......@@ -34,12 +34,18 @@
#include <errno.h>
#include <signal.h>
#include <getopt.h>
#include <dlfcn.h>
#include "test-config.h"
#include "weston-test-runner.h"
#include "weston-testsuite-data.h"
#include "shared/string-helpers.h"
/* This is a glibc extension; if we can't use it then make it harmless. */
#ifndef RTLD_NODELETE
#define RTLD_NODELETE 0
#endif
/**
* \defgroup testharness Test harness
* \defgroup testharness_private Test harness private
......@@ -627,9 +633,23 @@ main(int argc, char *argv[])
enum test_result_code ret;
enum test_result_code result = RESULT_OK;
const struct fixture_setup_array *fsa;
const char *leak_dl_handle;
int fi;
int fi_end;
/* This is horrific, but it gives us working leak checking. If we
* actually unload llvmpipe, then we also unload LLVM, and some global
* setup it's done - which llvmpipe can't tear down because the actual
* client might be using LLVM instead.
*
* Turns out if llvmpipe is always live, then the pointers are always
* reachable, so LeakSanitizer just tells us about our own code rather
* than LLVM's, so ...
*/
leak_dl_handle = getenv("WESTON_CI_LEAK_DL_HANDLE");
if (leak_dl_handle)
(void) dlopen(leak_dl_handle, RTLD_LAZY | RTLD_GLOBAL | RTLD_NODELETE);
harness = weston_test_harness_create(argc, argv);
fsa = fixture_setup_array_get_();
......
......@@ -239,16 +239,6 @@ zuc_set_repeat(int repeat);
void
zuc_set_random(int random);
/**
* Controls whether or not to run the tests as forked child processes.
* Defaults to true.
*
* @param spawn true to spawn each test in a forked child process,
* false to run tests directly.
*/
void
zuc_set_spawn(bool spawn);
/**
* Enables output in the JUnit XML format.
* Defaults to false.
......
/*
* Copyright © 2015 Samsung Electronics Co., Ltd
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "config.h"
#include "zuc_collector.h"
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <libweston/zalloc.h>
#include "zuc_event_listener.h"
#include "zunitc/zunitc_impl.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
/**
* @file
* General handling of collecting events during testing to pass back to
* main tracking of fork()'d tests.
*
* @note implementation of zuc_process_message() is included here so that
* all child->parent IPC is in a single source file for easier maintenance
* and updating.
*/
/**
* Internal data struct for processing.
*/
struct collector_data
{
int *fd; /**< file descriptor to output to. */
struct zuc_test *test; /**< current test, or NULL. */
};
/**
* Stores an int32_t into the given buffer.
*
* @param ptr the buffer to store to.
* @param val the value to store.
* @return a pointer to the position in the buffer after the stored value.
*/
static char *
pack_int32(char *ptr, int32_t val);
/**
* Stores an intptr_t into the given buffer.
*
* @param ptr the buffer to store to.
* @param val the value to store.
* @return a pointer to the position in the buffer after the stored value.
*/
static char *
pack_intptr_t(char *ptr, intptr_t val);
/**
* Extracts a int32_t from the given buffer.
*
* @param ptr the buffer to extract from.
* @param val the value to set.
* @return a pointer to the position in the buffer after the extracted
* value.
*/
static char const *
unpack_int32(char const *ptr, int32_t *val);
/**
* Extracts a intptr_t from the given buffer.
*
* @param ptr the buffer to extract from.
* @param val the value to set.
* @return a pointer to the position in the buffer after the extracted
* value.
*/
static char const *
unpack_intptr_t(char const *ptr, intptr_t *val);
/**
* Extracts a length-prefixed string from the given buffer.
*
* @param ptr the buffer to extract from.
* @param str the value to set.
* @return a pointer to the position in the buffer after the extracted
* value.
*/
static char const *
unpack_string(char const *ptr, char **str);
/**
* Extracts an event from the given buffer.
*
* @param ptr the buffer to extract from.
* @param len the length of the given buffer.
* @return an event that was packed in the buffer
*/
static struct zuc_event *
unpack_event(char const *ptr, int32_t len);
/**
* Handles an event by either attaching it directly or sending it over IPC
* as needed.
*/
static void
store_event(struct collector_data *cdata,
enum zuc_event_type event_type, char const *file, int line,
enum zuc_fail_state state, enum zuc_check_op op,
enum zuc_check_valtype valtype,
intptr_t val1, intptr_t val2, const char *expr1, const char *expr2);
static void
destroy(void *data);
static void
test_started(void *data, struct zuc_test *test);
static void
test_ended(void *data, struct zuc_test *test);
static void
check_triggered(void *data, char const *file, int line,
enum zuc_fail_state state, enum zuc_check_op op,
enum zuc_check_valtype valtype,
intptr_t val1, intptr_t val2,
const char *expr1, const char *expr2);
static void
collect_event(void *data, char const *file, int line, const char *expr1);
struct zuc_event_listener *
zuc_collector_create(int *pipe_fd)
{
struct zuc_event_listener *listener =
zalloc(sizeof(struct zuc_event_listener));
listener->data = zalloc(sizeof(struct collector_data));
((struct collector_data *)listener->data)->fd = pipe_fd;
listener->destroy = destroy;
listener->test_started = test_started;
listener->test_ended = test_ended;
listener->check_triggered = check_triggered;
listener->collect_event = collect_event;
return listener;
}
char *
pack_int32(char *ptr, int32_t val)
{
memcpy(ptr, &val, sizeof(val));
return ptr + sizeof(val);
}
char *
pack_intptr_t(char *ptr, intptr_t val)
{
memcpy(ptr, &val, sizeof(val));
return ptr + sizeof(val);
}
static char *
pack_cstr(char *ptr, intptr_t val, int len)
{
if (val == 0) { /* a null pointer */
ptr = pack_int32(ptr, -1);
} else {
ptr = pack_int32(ptr, len);
memcpy(ptr, (const char *)val, len);
ptr += len;
}
return ptr;
}
void
destroy(void *data)
{
free(data);
}
void
test_started(void *data, struct zuc_test *test)
{
struct collector_data *cdata = data;
cdata->test = test;
}
void
test_ended(void *data, struct zuc_test *test)
{
struct collector_data *cdata = data;
cdata->test = NULL;
}
void
check_triggered(void *data, char const *file, int line,
enum zuc_fail_state state, enum zuc_check_op op,
enum zuc_check_valtype valtype,
intptr_t val1, intptr_t val2,
const char *expr1, const char *expr2)
{
struct collector_data *cdata = data;
if (op != ZUC_OP_TRACEPOINT)
store_event(cdata, ZUC_EVENT_IMMEDIATE, file, line, state, op,
valtype,
val1, val2, expr1, expr2);
}
void
collect_event(void *data, char const *file, int line, const char *expr1)
{
struct collector_data *cdata = data;
store_event(cdata, ZUC_EVENT_DEFERRED, file, line, ZUC_CHECK_OK,
ZUC_OP_TRACEPOINT, ZUC_VAL_INT,
0, 0, expr1, "");
}
void
store_event(struct collector_data *cdata,
enum zuc_event_type event_type, char const *file, int line,
enum zuc_fail_state state, enum zuc_check_op op,
enum zuc_check_valtype valtype,
intptr_t val1, intptr_t val2, const char *expr1, const char *expr2)
{
struct zuc_event *event = zalloc(sizeof(*event));
event->file = strdup(file);
event->line = line;
event->state = state;
event->op = op;
event->valtype = valtype;
event->val1 = val1;
event->val2 = val2;
if (valtype == ZUC_VAL_CSTR) {
if (val1)
event->val1 = (intptr_t)strdup((const char *)val1);
if (val2)
event->val2 = (intptr_t)strdup((const char *)val2);
}
event->expr1 = strdup(expr1);
event->expr2 = strdup(expr2);
zuc_attach_event(cdata->test, event, event_type, false);
if (*cdata->fd == -1) {
} else {
/* Need to pass it back */
int sent;
int count;
int expr1_len = strlen(expr1);
int expr2_len = strlen(expr2);
int val1_len =
((valtype == ZUC_VAL_CSTR) && val1) ?
strlen((char *)val1) : 0;
int val2_len =
((valtype == ZUC_VAL_CSTR) && val2) ?
strlen((char *)val2) : 0;
int file_len = strlen(file);
int len = (sizeof(int32_t) * 9) + file_len
+ (sizeof(intptr_t) * 2)
+ ((valtype == ZUC_VAL_CSTR) ?
(sizeof(int32_t) * 2) + val1_len + val2_len : 0)
+ expr1_len + expr2_len;
char *buf = zalloc(len);
char *ptr = pack_int32(buf, len - 4);
ptr = pack_int32(ptr, event_type);
ptr = pack_int32(ptr, file_len);
memcpy(ptr, file, file_len);
ptr += file_len;
ptr = pack_int32(ptr, line);
ptr = pack_int32(ptr, state);
ptr = pack_int32(ptr, op);
ptr = pack_int32(ptr, valtype);
if (valtype == ZUC_VAL_CSTR) {
ptr = pack_cstr(ptr, val1, val1_len);
ptr = pack_cstr(ptr, val2, val2_len);
} else {
ptr = pack_intptr_t(ptr, val1);
ptr = pack_intptr_t(ptr, val2);
}
ptr = pack_int32(ptr, expr1_len);
if (expr1_len) {
memcpy(ptr, expr1, expr1_len);
ptr += expr1_len;
}
ptr = pack_int32(ptr, expr2_len);
if (expr2_len) {
memcpy(ptr, expr2, expr2_len);
ptr += expr2_len;
}
sent = 0;
while (sent < len) {
count = write(*cdata->fd, buf, len);
if (count == -1)
break;
sent += count;
}
free(buf);
}
}
char const *
unpack_int32(char const *ptr, int32_t *val)
{
memcpy(val, ptr, sizeof(*val));
return ptr + sizeof(*val);
}
char const *
unpack_intptr_t(char const *ptr, intptr_t *val)
{
memcpy(val, ptr, sizeof(*val));
return ptr + sizeof(*val);
}
char const *
unpack_string(char const *ptr, char **str)
{
int32_t len = 0;
ptr = unpack_int32(ptr, &len);
*str = zalloc(len + 1);
if (len)
memcpy(*str, ptr, len);
ptr += len;
return ptr;
}
static char const *
unpack_cstr(char const *ptr, char **str)
{
int32_t len = 0;
ptr = unpack_int32(ptr, &len);
if (len < 0) {
*str = NULL;
} else {
*str = zalloc(len + 1);
if (len)
memcpy(*str, ptr, len);
ptr += len;
}
return ptr;
}
struct zuc_event *
unpack_event(char const *ptr, int32_t len)
{
int32_t val = 0;
struct zuc_event *evt = zalloc(sizeof(*evt));
char const *tmp = unpack_string(ptr, &evt->file);
tmp = unpack_int32(tmp, &evt->line);
tmp = unpack_int32(tmp, &val);
evt->state = val;
tmp = unpack_int32(tmp, &val);
evt->op = val;
tmp = unpack_int32(tmp, &val);
evt->valtype = val;
if (evt->valtype == ZUC_VAL_CSTR) {
char *ptr = NULL;
tmp = unpack_cstr(tmp, &ptr);
evt->val1 = (intptr_t)ptr;
tmp = unpack_cstr(tmp, &ptr);
evt->val2 = (intptr_t)ptr;
} else {
tmp = unpack_intptr_t(tmp, &evt->val1);
tmp = unpack_intptr_t(tmp, &evt->val2);
}
tmp = unpack_string(tmp, &evt->expr1);
tmp = unpack_string(tmp, &evt->expr2);
return evt;
}
int
zuc_process_message(struct zuc_test *test, int fd)
{
char buf[4] = {};
int got = read(fd, buf, 4);
if (got == 4) {
enum zuc_event_type event_type = ZUC_EVENT_IMMEDIATE;
int32_t val = 0;
int32_t len = 0;
const char *tmp = NULL;
char *raw = NULL;
unpack_int32(buf, &len);
raw = zalloc(len);
got = read(fd, raw, len);
tmp = unpack_int32(raw, &val);
event_type = val;
struct zuc_event *evt = unpack_event(tmp, len - (tmp - raw));
zuc_attach_event(test, evt, event_type, true);
free(raw);
}
return got;
}
/*
* Copyright © 2015 Samsung Electronics Co., Ltd
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef ZUC_COLLECTOR_H
#define ZUC_COLLECTOR_H
struct zuc_event_listener;
struct zuc_test;
/**
* Creates a new instance of an event collector that will attach events
* to the current test directly or via connection from child to parent.
*
* @param pipe_fd pointer to the file descriptor to use for communication if
* needed. If the value is -1 the events will be attached directly to the
* current test. Otherwise events will be passed back via IPC over this
* pipe with the expectation that the payload will be handled in the parent
* process via zuc_process_message().
* @return a new collector instance.
* @see zuc_process_message()
*/
struct zuc_event_listener *
zuc_collector_create(int *pipe_fd);
/**
* Reads events from the given pipe and processes them.
*
* @param test the currently active test to attach events for.
* @param pipe_fd the file descriptor of the pipe to read from.
* @return a positive value if a message was received, 0 if the end has
* been reached and -1 if an error has occurred.
*/
int
zuc_process_message(struct zuc_test *test, int pipe_fd);
#endif /* ZUC_COLLECTOR_H */
......@@ -42,11 +42,9 @@ struct zuc_context {
int repeat;
int random;
unsigned int seed;
bool spawn;
bool break_on_failure;
bool output_tap;
bool output_junit;
int fds[2];
char *filter;
struct zuc_slinked *listeners;
......
......@@ -41,7 +41,6 @@
#include "zunitc/zunitc.h"
#include "zuc_base_logger.h"
#include "zuc_collector.h"
#include "zuc_context.h"
#include "zuc_event_listener.h"
#include "zuc_junit_reporter.h"
......@@ -82,9 +81,7 @@ static struct zuc_context g_ctx = {
.fatal = false,
.repeat = 0,
.random = 0,
.spawn = true,
.break_on_failure = false,
.fds = {-1, -1},
.listeners = NULL,
......@@ -141,12 +138,6 @@ zuc_set_random(int random)
g_ctx.random = random;
}
void
zuc_set_spawn(bool spawn)
{
g_ctx.spawn = spawn;
}
void
zuc_set_break_on_failure(bool break_on_failure)
{
......@@ -525,7 +516,6 @@ zuc_initialize(int *argc, char *argv[], bool *help_flagged)
{
int rc = EXIT_FAILURE;
bool opt_help = false;
bool opt_nofork = false;
bool opt_list = false;
int opt_repeat = 0;
int opt_random = 0;
......@@ -537,7 +527,6 @@ zuc_initialize(int *argc, char *argv[], bool *help_flagged)
int argc_in = *argc;
const struct weston_option options[] = {
{ WESTON_OPTION_BOOLEAN, "zuc-nofork", 0, &opt_nofork },
{ WESTON_OPTION_BOOLEAN, "zuc-list-tests", 0, &opt_list },
{ WESTON_OPTION_INTEGER, "zuc-repeat", 0, &opt_repeat },
{ WESTON_OPTION_INTEGER, "zuc-random", 0, &opt_random },
......@@ -633,7 +622,6 @@ zuc_initialize(int *argc, char *argv[], bool *help_flagged)
" --zuc-break-on-failure\n"
" --zuc-filter=FILTER\n"
" --zuc-list-tests\n"
" --zuc-nofork\n"
#if ENABLE_JUNIT_XML
" --zuc-output-xml\n"
#endif
......@@ -650,7 +638,6 @@ zuc_initialize(int *argc, char *argv[], bool *help_flagged)
} else {
zuc_set_repeat(opt_repeat);
zuc_set_random(opt_random);
zuc_set_spawn(!opt_nofork);
zuc_set_break_on_failure(opt_break_on_failure);
zuc_set_output_junit(opt_junit);
rc = EXIT_SUCCESS;
......@@ -937,11 +924,6 @@ zuc_cleanup(void)
free(g_ctx.filter);
g_ctx.filter = 0;
for (i = 0; i < 2; ++i)
if (g_ctx.fds[i] != -1) {
close(g_ctx.fds[i]);
g_ctx.fds[i] = -1;
}
if (g_ctx.listeners) {
struct zuc_slinked *curr = g_ctx.listeners;
......@@ -1017,108 +999,9 @@ zuc_list_tests(void)
}
}
static void
spawn_test(struct zuc_test *test, void *test_data,
void (*cleanup_fn)(void *data), void *cleanup_data)
{
pid_t pid = -1;
if (!test || (!test->fn && !test->fn_f))
return;
if (pipe2(g_ctx.fds, O_CLOEXEC)) {
printf("%s:%d: error: Unable to create pipe: %d\n",
__FILE__, __LINE__, errno);
mark_failed(test, ZUC_CHECK_ERROR);
return;
}
fflush(NULL); /* important. avoid duplication of output */
pid = fork();
switch (pid) {
case -1: /* Error forking */
printf("%s:%d: error: Problem with fork: %d\n",
__FILE__, __LINE__, errno);
mark_failed(test, ZUC_CHECK_ERROR);
close(g_ctx.fds[0]);
g_ctx.fds[0] = -1;
close(g_ctx.fds[1]);
g_ctx.fds[1] = -1;
break;
case 0: { /* child */
int rc = EXIT_SUCCESS;
close(g_ctx.fds[0]);
g_ctx.fds[0] = -1;
if (test->fn_f)
test->fn_f(test_data);
else
test->fn();
if (test_has_failure(test))
rc = EXIT_FAILURE;
else if (test_has_skip(test))
rc = ZUC_EXIT_SKIP;
/* Avoid confusing memory tools like valgrind */
if (cleanup_fn)
cleanup_fn(cleanup_data);
zuc_cleanup();
exit(rc);
}
default: { /* parent */
ssize_t rc = 0;
siginfo_t info = {};
close(g_ctx.fds[1]);
g_ctx.fds[1] = -1;
do {
rc = zuc_process_message(g_ctx.curr_test,
g_ctx.fds[0]);
} while (rc > 0);
close(g_ctx.fds[0]);
g_ctx.fds[0] = -1;
if (waitid(P_ALL, 0, &info, WEXITED)) {
printf("%s:%d: error: waitid failed. (%d)\n",
__FILE__, __LINE__, errno);
mark_failed(test, ZUC_CHECK_ERROR);
} else {
switch (info.si_code) {
case CLD_EXITED: {
int exit_code = info.si_status;
switch(exit_code) {
case EXIT_SUCCESS:
break;
case ZUC_EXIT_SKIP:
if (!test_has_skip(g_ctx.curr_test) &&
!test_has_failure(g_ctx.curr_test))
ZUC_SKIP("Child exited SKIP");
break;
default:
/* unexpected failure */
if (!test_has_failure(g_ctx.curr_test))
ZUC_ASSERT_EQ(0, exit_code);
}
break;
}
case CLD_KILLED:
case CLD_DUMPED:
printf("%s:%d: error: signaled: %d\n",
__FILE__, __LINE__, info.si_status);
mark_failed(test, ZUC_CHECK_ERROR);
break;
}
}
}
}
}
static void
run_single_test(struct zuc_test *test,const struct zuc_fixture *fxt,
void *case_data, bool spawn)
void *case_data)
{
long elapsed = 0;
struct timespec begin;
......@@ -1146,15 +1029,10 @@ run_single_test(struct zuc_test *test,const struct zuc_fixture *fxt,
/* Need to re-check these, as fixtures might have changed test state. */
if (!test->fatal && !test->skipped) {
if (spawn) {
spawn_test(test, test_data,
cleanup_fn, cleanup_data);
} else {
if (test->fn_f)
test->fn_f(test_data);
else
test->fn();
}
if (test->fn_f)
test->fn_f(test_data);
else
test->fn();
}
clock_gettime(TARGET_TIMER, &end);
......@@ -1204,8 +1082,7 @@ run_single_case(struct zuc_case *test_case)
if (curr->disabled) {
dispatch_test_disabled(&g_ctx, curr);
} else {
run_single_test(curr, fxt, case_data,
g_ctx.spawn);
run_single_test(curr, fxt, case_data);
if (curr->skipped)
test_case->skipped++;
if (curr->failed)
......@@ -1313,7 +1190,6 @@ zucimpl_run_tests(void)
return EXIT_FAILURE;
if (g_ctx.listeners == NULL) {
zuc_add_event_listener(zuc_collector_create(&(g_ctx.fds[1])));
zuc_add_event_listener(zuc_base_logger_create());
if (g_ctx.output_junit)
zuc_add_event_listener(zuc_junit_reporter_create());
......
......@@ -576,9 +576,13 @@ weston_wm_window_read_properties(struct weston_wm_window *window)
}
break;
case TYPE_WM_NORMAL_HINTS:
/* WM_NORMAL_HINTS can be either 15 or 18 CARD32s */
memset(&window->size_hints, 0,
sizeof(window->size_hints));
memcpy(&window->size_hints,
xcb_get_property_value(reply),
sizeof window->size_hints);
MIN(sizeof(window->size_hints),
reply->value_len * 4));
break;
case TYPE_NET_WM_STATE:
window->fullscreen = 0;
......