Mercurial > louis > mq > lightsd
changeset 216:1314ac3aafd4
Meld patches
author | Louis Opter <kalessin@kalessin.fr> |
---|---|
date | Sun, 02 Aug 2015 19:02:55 -0700 |
parents | 7b980a75598d |
children | d320d75c9ca1 |
files | add_command_pipe.patch daemon_module.patch series testing_command_pipe.patch |
diffstat | 4 files changed, 1466 insertions(+), 1501 deletions(-) [+] |
line wrap: on
line diff
--- a/add_command_pipe.patch Sun Aug 02 18:59:24 2015 -0700 +++ b/add_command_pipe.patch Sun Aug 02 19:02:55 2015 -0700 @@ -1,9 +1,21 @@ # HG changeset patch -# Parent 0ded0e4a2bc8e4eb6d24e3f7f86ee6adf8b4c7e1 +# Parent 710a684b58e97eddc7d7a12d85b17b823d087a82 Add a command pipe to easily do actions from a shell NOTE: the command pipe is write only, responses aren't returned. +diff --git a/CMakeLists.txt b/CMakeLists.txt +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -43,6 +43,8 @@ + "-D_BSD_SOURCE=1" + "-D_DEFAULT_SOURCE=1" + ++ "-D_DARWIN_C_SOURCE=1" ++ + "-DLGTD_BIG_ENDIAN_SYSTEM=${BIG_ENDIAN_SYSTEM}" + "-DLGTD_SIZEOF_VOID_P=${CMAKE_SIZEOF_VOID_P}" + diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -259,7 +271,7 @@ new file mode 100644 --- /dev/null +++ b/core/pipe.c -@@ -0,0 +1,233 @@ +@@ -0,0 +1,247 @@ +// Copyright (c) 2015, Louis Opter <kalessin@kalessin.fr> +// +// This file is part of lighstd. @@ -410,6 +422,14 @@ + jsmn_init(&pipe->client.jsmn_ctx); +} + ++static mode_t ++lgtd_command_pipe_get_umask(void) ++{ ++ mode_t mask = umask(0); ++ umask(mask); ++ return mask; ++} ++ +bool +lgtd_command_pipe_open(const char *path) +{ @@ -432,8 +452,14 @@ + pipe->fd = -1; + + mode_t mode = S_IWUSR|S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IWGRP; -+ if (mkfifo(path, mode) && errno != EEXIST) { -+ goto error; ++ if (mkfifo(path, mode)) { ++ if (errno != EEXIST) { ++ goto error; ++ } ++ mode &= ~lgtd_command_pipe_get_umask(); ++ if (chmod(path, mode)) { ++ goto error; ++ } + } + + pipe->fd = open(path, O_RDONLY|O_NONBLOCK); @@ -594,10 +620,1342 @@ +{ + bufferevent_write(client->io, buf, bufsz); +} +diff --git a/tests/core/mock_event2.h b/tests/core/mock_event2.h +new file mode 100644 +--- /dev/null ++++ b/tests/core/mock_event2.h +@@ -0,0 +1,109 @@ ++#pragma once ++ ++#ifndef MOCKED_EVBUFFER_DRAIN ++int ++evbuffer_drain(struct evbuffer *buf, size_t len) ++{ ++ (void)buf; ++ (void)len; ++ return 0; ++} ++#endif ++ ++#ifndef MOCKED_EVBUFFER_NEW ++struct evbuffer * ++evbuffer_new(void) ++{ ++ return NULL; ++} ++#endif ++ ++#ifndef MOCKED_EVENT_FREE ++void ++evbuffer_free(struct evbuffer *buf) ++{ ++ (void)buf; ++} ++#endif ++ ++#ifndef MOCKED_EVBUFFER_GET_LENGTH ++size_t ++evbuffer_get_length(const struct evbuffer *buf) ++{ ++ (void)buf; ++ return 0; ++} ++#endif ++ ++#ifndef MOCKED_EVBUFFER_PULLUP ++unsigned char * ++evbuffer_pullup(struct evbuffer *buf, ev_ssize_t size) ++{ ++ (void)buf; ++ (void)size; ++ return NULL; ++} ++#endif ++ ++#ifndef MOCKED_EVBUFFER_READ ++int ++evbuffer_read(struct evbuffer *buffer, evutil_socket_t fd, int howmuch) ++{ ++ (void)buffer; ++ (void)fd; ++ return howmuch; ++} ++#endif ++ ++#ifndef MOCKED_EVENT_ADD ++int ++event_add(struct event *ev, const struct timeval *timeout) ++{ ++ (void)ev; ++ (void)timeout; ++ return 0; ++} ++#endif ++ ++#ifndef MOCKED_EVENT_DEL ++int ++event_del(struct event *ev) ++{ ++ (void)ev; ++ return 0; ++} ++#endif ++ ++#ifndef MOCKED_EVENT_FREE ++void ++event_free(struct event *ev) ++{ ++ (void)ev; ++} ++#endif ++ ++#ifndef MOCKED_EVENT_NEW ++struct event * ++event_new(struct event_base *base, ++ evutil_socket_t fd, ++ short events, ++ event_callback_fn cb, ++ void *ctx) ++{ ++ (void)base; ++ (void)fd; ++ (void)events; ++ (void)cb; ++ (void)ctx; ++ return NULL; ++} ++#endif ++ ++#ifndef MOCKED_EVUTIL_MAKE_SOCKET_NONBLOCKING ++int ++evutil_make_socket_nonblocking(evutil_socket_t fd) ++{ ++ (void)fd; ++ return 0; ++} ++#endif +diff --git a/tests/core/pipe/CMakeLists.txt b/tests/core/pipe/CMakeLists.txt +new file mode 100644 +--- /dev/null ++++ b/tests/core/pipe/CMakeLists.txt +@@ -0,0 +1,25 @@ ++INCLUDE_DIRECTORIES( ++ ${CMAKE_CURRENT_SOURCE_DIR} ++ ${CMAKE_CURRENT_BINARY_DIR} ++) ++ ++ADD_CORE_LIBRARY( ++ test_core_pipe STATIC ++ ${LIGHTSD_SOURCE_DIR}/core/jsmn.c ++ ${LIGHTSD_SOURCE_DIR}/core/log.c ++ ${LIGHTSD_SOURCE_DIR}/core/stats.c ++ ${LIGHTSD_SOURCE_DIR}/lifx/bulb.c ++ ${LIGHTSD_SOURCE_DIR}/lifx/tagging.c ++ ${LIGHTSD_SOURCE_DIR}/lifx/wire_proto.c ++ ${CMAKE_CURRENT_SOURCE_DIR}/../tests_shims.c ++ ${CMAKE_CURRENT_SOURCE_DIR}/../tests_utils.c ++) ++ ++FUNCTION(ADD_PIPE_TEST TEST_SOURCE) ++ ADD_TEST_FROM_C_SOURCES(${TEST_SOURCE} test_core_pipe) ++ENDFUNCTION() ++ ++FILE(GLOB TESTS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "test_*.c") ++FOREACH(TEST ${TESTS}) ++ ADD_PIPE_TEST(${TEST}) ++ENDFOREACH() +diff --git a/tests/core/pipe/test_pipe_close.c b/tests/core/pipe/test_pipe_close.c +new file mode 100644 +--- /dev/null ++++ b/tests/core/pipe/test_pipe_close.c +@@ -0,0 +1,117 @@ ++#include "pipe.c" ++ ++#include <sys/tree.h> ++#include <endian.h> ++#include <limits.h> ++ ++#include "lifx/wire_proto.h" ++ ++#define MOCKED_EVENT_NEW ++#define MOCKED_EVBUFFER_NEW ++#define MOCKED_EVENT_DEL ++#define MOCKED_EVBUFFER_FREE ++#define MOCKED_EVENT_FREE ++#include "mock_event2.h" ++#include "mock_gateway.h" ++#include "mock_daemon.h" ++ ++#include "tests_utils.h" ++#include "tests_pipe_utils.h" ++ ++char *tmpdir = NULL; ++ ++void ++cleanup_tmpdir(void) ++{ ++ lgtd_tests_remove_temp_dir(tmpdir); ++} ++ ++struct event * ++event_new(struct event_base *base, ++ evutil_socket_t fd, ++ short events, ++ event_callback_fn cb, ++ void *ctx) ++{ ++ (void)base; ++ (void)fd; ++ (void)events; ++ (void)cb; ++ (void)ctx; ++ ++ return (void *)1; ++} ++ ++struct evbuffer * ++evbuffer_new(void) ++{ ++ return (void *)2; ++} ++ ++static int event_del_call_count = 0; ++ ++int ++event_del(struct event *ev) ++{ ++ (void)ev; ++ ++ event_del_call_count++; ++ ++ return 0; ++} ++ ++static int evbuffer_free_call_count = 0; ++ ++void ++evbuffer_free(struct evbuffer *buf) ++{ ++ (void)buf; ++ ++ evbuffer_free_call_count++; ++} ++ ++static int event_free_call_count = 0; ++ ++void ++event_free(struct event *ev) ++{ ++ (void)ev; ++ ++ event_free_call_count++; ++} ++ ++int ++main(void) ++{ ++ tmpdir = lgtd_tests_make_temp_dir(); ++ atexit(cleanup_tmpdir); ++ ++ char path[PATH_MAX] = { 0 }; ++ snprintf(path, sizeof(path), "%s/lightsd.pipe", tmpdir); ++ if (!lgtd_command_pipe_open(path)) { ++ errx(1, "couldn't open pipe"); ++ } ++ ++ int pipe_fd = SLIST_FIRST(&lgtd_command_pipes)->fd; ++ ++ lgtd_command_pipe_close_all(); ++ ++ if (event_del_call_count != 1) { ++ errx(1, "event_del_call_count = %d", event_del_call_count); ++ } ++ if (evbuffer_free_call_count != 1) { ++ errx(1, "evbuffer_free_call_count = %d", evbuffer_free_call_count); ++ } ++ if (event_free_call_count != 1) { ++ errx(1, "event_free_call_count = %d", event_free_call_count); ++ } ++ struct stat sb; ++ if (fstat(pipe_fd, &sb) != -1 && errno != EBADF) { ++ errx(1, "the pipe file descriptor wasn't closed correctly"); ++ } ++ if (stat(path, &sb) != -1 && errno != ENOENT) { ++ errx(1, "the pipe wasn't removed correctly"); ++ } ++ ++ return 0; ++} +diff --git a/tests/core/pipe/test_pipe_open.c b/tests/core/pipe/test_pipe_open.c +new file mode 100644 +--- /dev/null ++++ b/tests/core/pipe/test_pipe_open.c +@@ -0,0 +1,171 @@ ++#include "pipe.c" ++ ++#include <sys/tree.h> ++#include <endian.h> ++#include <limits.h> ++ ++#include "lifx/wire_proto.h" ++ ++#define MOCKED_EVUTIL_MAKE_SOCKET_NONBLOCKING ++#define MOCKED_EVENT_NEW ++#define MOCKED_EVBUFFER_NEW ++#define MOCKED_EVENT_ADD ++#include "mock_event2.h" ++#include "mock_gateway.h" ++#include "mock_daemon.h" ++ ++#include "tests_utils.h" ++#define MOCKED_CLIENT_OPEN_FROM_PIPE ++#include "tests_pipe_utils.h" ++ ++char *tmpdir = NULL; ++ ++void ++cleanup_tmpdir(void) ++{ ++ lgtd_tests_remove_temp_dir(tmpdir); ++} ++ ++static bool make_socket_nonblocking_call_count = 0; ++ ++int ++evutil_make_socket_nonblocking(evutil_socket_t fd) ++{ ++ if (fd <= 0) { ++ errx(1, "got invalid fd %d in make_socket_nonblocking", fd); ++ } ++ ++ make_socket_nonblocking_call_count++; ++ ++ return 0; ++} ++ ++static int event_new_call_count = 0; ++ ++struct event * ++event_new(struct event_base *base, ++ evutil_socket_t fd, ++ short events, ++ event_callback_fn cb, ++ void *ctx) ++{ ++ if (base != lgtd_ev_base) { ++ errx( ++ 1, "unexpected lgtd_ev_base = %p (expected %p)", ++ base, lgtd_ev_base ++ ); ++ } ++ if (fd <= 0) { ++ errx(1, "got invalid fd %d in event_new", fd); ++ } ++ if (events != (EV_READ|EV_PERSIST)) { ++ errx(1, "got events %#x (expected %#x)", events, EV_READ|EV_PERSIST); ++ } ++ if (cb != lgtd_command_pipe_read_callback) { ++ errx(1, "the read callback wasn't set correctly"); ++ } ++ if (!ctx) { ++ errx(1, "the callback context wasn't set correctly"); ++ } ++ ++ event_new_call_count++; ++ ++ return (void *)1; ++} ++ ++static int evbuffer_new_call_count = 0; ++ ++struct evbuffer * ++evbuffer_new(void) ++{ ++ evbuffer_new_call_count++; ++ ++ return (void *)2; ++} ++ ++static int event_add_call_count = 0; ++ ++int ++event_add(struct event *ev, const struct timeval *timeout) ++{ ++ if (ev != (void *)1) { ++ errx(1, "got unexpected event %p (expected %p)", ev, (void*)1); ++ } ++ ++ if (timeout) { ++ errx(1, "a timeout shouldn't have been passed"); ++ } ++ ++ event_add_call_count++; ++ ++ return 0; ++} ++ ++static int client_open_from_pipe_call_count = 0; ++ ++void ++lgtd_client_open_from_pipe(struct lgtd_client *pipe_client) ++{ ++ if (!pipe_client) { ++ errx(1, "missing pipe_client"); ++ } ++ ++ client_open_from_pipe_call_count++; ++} ++ ++int ++main(void) ++{ ++ tmpdir = lgtd_tests_make_temp_dir(); ++ atexit(cleanup_tmpdir); ++ ++ char path[PATH_MAX] = { 0 }; ++ snprintf(path, sizeof(path), "%s/lightsd.pipe", tmpdir); ++ if (!lgtd_command_pipe_open(path)) { ++ errx(1, "couldn't open pipe"); ++ } ++ ++ if (make_socket_nonblocking_call_count != 1) { ++ errx( ++ 1, "make_socket_nonblocking_call_count = %d", ++ make_socket_nonblocking_call_count ++ ); ++ } ++ if (event_new_call_count != 1) { ++ errx(1, "event_new_call_count = %d", event_new_call_count); ++ } ++ if (evbuffer_new_call_count != 1) { ++ errx(1, "evbuffer_new_call_count = %d", evbuffer_new_call_count); ++ } ++ if (event_add_call_count != 1) { ++ errx(1, "event_add_call_count = %d", event_add_call_count); ++ } ++ if (SLIST_EMPTY(&lgtd_command_pipes)) { ++ errx(1, "the list of command pipes shouldn't be empty"); ++ } ++ ++ struct stat sb; ++ if (stat(path, &sb)) { ++ errx(1, "can't stat pipe %s", path); ++ } ++ ++ mode_t expected_mode; ++ expected_mode = S_IFIFO|S_IWUSR|S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IWGRP; ++ expected_mode &= ~lgtd_command_pipe_get_umask(); ++ if (sb.st_mode != expected_mode) { ++ errx( ++ 1, "unexpected mode %o (expected %o)", ++ sb.st_mode, expected_mode ++ ); ++ } ++ ++ // make sure it's idempotent: ++ if (!lgtd_command_pipe_open(path)) { ++ errx(1, "couldn't open pipe"); ++ } ++ if (event_new_call_count != 1) { ++ errx(1, "event_new_call_count = %d", event_new_call_count); ++ } ++ ++ return 0; ++} +diff --git a/tests/core/pipe/test_pipe_open_fifo_already_exists.c b/tests/core/pipe/test_pipe_open_fifo_already_exists.c +new file mode 100644 +--- /dev/null ++++ b/tests/core/pipe/test_pipe_open_fifo_already_exists.c +@@ -0,0 +1,176 @@ ++#include "pipe.c" ++ ++#include <sys/tree.h> ++#include <endian.h> ++#include <limits.h> ++ ++#include "lifx/wire_proto.h" ++ ++#define MOCKED_EVUTIL_MAKE_SOCKET_NONBLOCKING ++#define MOCKED_EVENT_NEW ++#define MOCKED_EVBUFFER_NEW ++#define MOCKED_EVENT_ADD ++#include "mock_event2.h" ++#include "mock_gateway.h" ++#include "mock_daemon.h" ++ ++#include "tests_utils.h" ++#define MOCKED_CLIENT_OPEN_FROM_PIPE ++#include "tests_pipe_utils.h" ++ ++char *tmpdir = NULL; ++ ++void ++cleanup_tmpdir(void) ++{ ++ lgtd_tests_remove_temp_dir(tmpdir); ++} ++ ++static bool make_socket_nonblocking_call_count = 0; ++ ++int ++evutil_make_socket_nonblocking(evutil_socket_t fd) ++{ ++ if (fd <= 0) { ++ errx(1, "got invalid fd %d in make_socket_nonblocking", fd); ++ } ++ ++ make_socket_nonblocking_call_count++; ++ ++ return 0; ++} ++ ++static int event_new_call_count = 0; ++ ++struct event * ++event_new(struct event_base *base, ++ evutil_socket_t fd, ++ short events, ++ event_callback_fn cb, ++ void *ctx) ++{ ++ if (base != lgtd_ev_base) { ++ errx( ++ 1, "unexpected lgtd_ev_base = %p (expected %p)", ++ base, lgtd_ev_base ++ ); ++ } ++ if (fd <= 0) { ++ errx(1, "got invalid fd %d in event_new", fd); ++ } ++ if (events != (EV_READ|EV_PERSIST)) { ++ errx(1, "got events %#x (expected %#x)", events, EV_READ|EV_PERSIST); ++ } ++ if (cb != lgtd_command_pipe_read_callback) { ++ errx(1, "the read callback wasn't set correctly"); ++ } ++ if (!ctx) { ++ errx(1, "the callback context wasn't set correctly"); ++ } ++ ++ event_new_call_count++; ++ ++ return (void *)1; ++} ++ ++static int evbuffer_new_call_count = 0; ++ ++struct evbuffer * ++evbuffer_new(void) ++{ ++ evbuffer_new_call_count++; ++ ++ return (void *)2; ++} ++ ++static int event_add_call_count = 0; ++ ++int ++event_add(struct event *ev, const struct timeval *timeout) ++{ ++ if (ev != (void *)1) { ++ errx(1, "got unexpected event %p (expected %p)", ev, (void*)1); ++ } ++ ++ if (timeout) { ++ errx(1, "a timeout shouldn't have been passed"); ++ } ++ ++ event_add_call_count++; ++ ++ return 0; ++} ++ ++static int client_open_from_pipe_call_count = 0; ++ ++void ++lgtd_client_open_from_pipe(struct lgtd_client *pipe_client) ++{ ++ if (!pipe_client) { ++ errx(1, "missing pipe_client"); ++ } ++ ++ client_open_from_pipe_call_count++; ++} ++ ++int ++main(void) ++{ ++ tmpdir = lgtd_tests_make_temp_dir(); ++ atexit(cleanup_tmpdir); ++ ++ char path[PATH_MAX] = { 0 }; ++ snprintf(path, sizeof(path), "%s/lightsd.pipe", tmpdir); ++ ++ if (mkfifo(path, 0)) { ++ errx(1, "can't open fifo"); ++ } ++ ++ if (!lgtd_command_pipe_open(path)) { ++ errx(1, "couldn't open pipe"); ++ } ++ ++ if (make_socket_nonblocking_call_count != 1) { ++ errx( ++ 1, "make_socket_nonblocking_call_count = %d", ++ make_socket_nonblocking_call_count ++ ); ++ } ++ if (event_new_call_count != 1) { ++ errx(1, "event_new_call_count = %d", event_new_call_count); ++ } ++ if (evbuffer_new_call_count != 1) { ++ errx(1, "evbuffer_new_call_count = %d", evbuffer_new_call_count); ++ } ++ if (event_add_call_count != 1) { ++ errx(1, "event_add_call_count = %d", event_add_call_count); ++ } ++ if (SLIST_EMPTY(&lgtd_command_pipes)) { ++ errx(1, "the list of command pipes shouldn't be empty"); ++ } ++ ++ struct stat sb; ++ if (stat(path, &sb)) { ++ errx(1, "can't stat pipe %s", path); ++ } ++ ++ mode_t expected_mode; ++ expected_mode = S_IFIFO|S_IWUSR|S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IWGRP; ++ expected_mode &= ~lgtd_command_pipe_get_umask(); ++ if (sb.st_mode != expected_mode) { ++ errx( ++ 1, "unexpected mode %o (expected %o)", ++ sb.st_mode, expected_mode ++ ); ++ } ++ ++ // make sure it's idempotent: ++ if (!lgtd_command_pipe_open(path)) { ++ errx(1, "couldn't open pipe"); ++ } ++ if (event_new_call_count != 1) { ++ errx(1, "event_new_call_count = %d", event_new_call_count); ++ } ++ ++ return 0; ++} +diff --git a/tests/core/pipe/test_pipe_read_callback.c b/tests/core/pipe/test_pipe_read_callback.c +new file mode 100644 +--- /dev/null ++++ b/tests/core/pipe/test_pipe_read_callback.c +@@ -0,0 +1,192 @@ ++#include "pipe.c" ++ ++#include <sys/tree.h> ++#include <endian.h> ++#include <limits.h> ++ ++#include "lifx/wire_proto.h" ++ ++#define MOCKED_EVENT_NEW ++#define MOCKED_EVBUFFER_NEW ++#define MOCKED_EVBUFFER_READ ++#define MOCKED_EVBUFFER_PULLUP ++#define MOCKED_EVBUFFER_GET_LENGTH ++#define MOCKED_EVBUFFER_DRAIN ++#include "mock_event2.h" ++#include "mock_gateway.h" ++#include "mock_daemon.h" ++ ++#include "tests_utils.h" ++#define MOCKED_JSONRPC_DISPATCH_REQUEST ++#include "tests_pipe_utils.h" ++ ++static unsigned char request[] = ("{" ++ "\"jsonrpc\": \"2.0\"," ++ "\"method\": \"get_light_state\"," ++ "\"params\": [\"*\"]," ++ "\"id\": 42" ++"}"); ++ ++static char *tmpdir = NULL; ++ ++void ++cleanup_tmpdir(void) ++{ ++ lgtd_tests_remove_temp_dir(tmpdir); ++} ++ ++static int jsonrpc_dispatch_request_call_count = 0; ++ ++void ++lgtd_jsonrpc_dispatch_request(struct lgtd_client *client, int parsed) ++{ ++ (void)client; ++ (void)parsed; ++ ++ if (!parsed) { ++ errx(1, "number of parsed json tokens not passed in"); ++ } ++ ++ if (memcmp(client->json, request, sizeof(request))) { ++ errx(1, "got unexpected json"); ++ } ++ ++ jsonrpc_dispatch_request_call_count++; ++} ++ ++struct event * ++event_new(struct event_base *base, ++ evutil_socket_t fd, ++ short events, ++ event_callback_fn cb, ++ void *ctx) ++{ ++ (void)base; ++ (void)fd; ++ (void)events; ++ (void)cb; ++ (void)ctx; ++ ++ return (void *)1; ++} ++ ++static int ++get_nbytes_read(int call_count) ++{ ++ switch (call_count) { ++ case 0: ++ return sizeof(request) - 1; // we don't return the '\0' ++ default: ++ return 0; ++ } ++} ++ ++struct evbuffer * ++evbuffer_new(void) ++{ ++ return (void *)2; ++} ++ ++static int evbuffer_drain_call_count = 0; ++ ++int ++evbuffer_drain(struct evbuffer *buf, size_t len) ++{ ++ if (buf != (void *)2) { ++ errx(1, "got unexpected buf %p (expected %p)", buf, (void *)2); ++ } ++ ++ jsmn_parser jsmn_ctx; ++ jsmn_init(&jsmn_ctx); ++ struct lgtd_command_pipe *pipe = SLIST_FIRST(&lgtd_command_pipes); ++ if (memcmp(&pipe->client.jsmn_ctx, &jsmn_ctx, sizeof(jsmn_ctx))) { ++ errx(1, "the client json parser context wasn't re-initialized"); ++ } ++ ++ ++ switch (evbuffer_drain_call_count) { ++ case 0: ++ if (len != sizeof(request) - 1) { ++ errx( ++ 1, "trying to drain %ju bytes (expected %ju)", ++ (uintmax_t)len, (uintmax_t)sizeof(request) - 1 ++ ); ++ } ++ break; ++ default: ++ break; ++ } ++ evbuffer_drain_call_count++; ++ ++ return 0; ++} ++ ++static int evbuffer_pullup_call_count = 0; ++ ++unsigned char * ++evbuffer_pullup(struct evbuffer *buf, ev_ssize_t size) ++{ ++ if (buf != (void *)2) { ++ errx(1, "got unexpected buf %p (expected %p)", buf, (void *)2); ++ } ++ ++ if (size != -1) { ++ errx(1, "got unexpected size %ld in pullup (expected -1)", size); ++ } ++ ++ return &request[evbuffer_pullup_call_count++ ? sizeof(request) - 1 : 0]; ++} ++ ++static int evbuffer_get_length_call_count = 0; ++ ++size_t ++evbuffer_get_length(const struct evbuffer *buf) ++{ ++ if (buf != (void *)2) { ++ errx(1, "got unexpected buf %p (expected %p)", buf, (void *)2); ++ } ++ ++ return get_nbytes_read(evbuffer_get_length_call_count++); ++} ++ ++static int evbuffer_read_call_count = 0; ++ ++int ++evbuffer_read(struct evbuffer *buf, evutil_socket_t fd, int howmuch) ++{ ++ if (buf != (void *)2) { ++ errx(1, "got unexpected buf %p (expected %p)", buf, (void *)2); ++ } ++ ++ struct lgtd_command_pipe *pipe = SLIST_FIRST(&lgtd_command_pipes); ++ if (fd != pipe->fd) { ++ errx(1, "got unexpected fd %d (expected %d)", fd, pipe->fd); ++ } ++ ++ if (howmuch != -1) { ++ errx( ++ 1, "got unexpected howmuch bytes to read %d (expected -1)", howmuch ++ ); ++ } ++ ++ return get_nbytes_read(evbuffer_read_call_count++); ++} ++ ++int ++main(void) ++{ ++ tmpdir = lgtd_tests_make_temp_dir(); ++ atexit(cleanup_tmpdir); ++ ++ char path[PATH_MAX] = { 0 }; ++ snprintf(path, sizeof(path), "%s/lightsd.pipe", tmpdir); ++ if (!lgtd_command_pipe_open(path)) { ++ errx(1, "couldn't open pipe"); ++ } ++ ++ struct lgtd_command_pipe *pipe = SLIST_FIRST(&lgtd_command_pipes); ++ ++ lgtd_command_pipe_read_callback(pipe->fd, EV_READ, pipe); ++ ++ return 0; ++} +diff --git a/tests/core/pipe/test_pipe_read_callback_extra_data.c b/tests/core/pipe/test_pipe_read_callback_extra_data.c +new file mode 100644 +--- /dev/null ++++ b/tests/core/pipe/test_pipe_read_callback_extra_data.c +@@ -0,0 +1,219 @@ ++#include "pipe.c" ++ ++#include <sys/tree.h> ++#include <endian.h> ++#include <limits.h> ++ ++#include "lifx/wire_proto.h" ++ ++#define MOCKED_EVENT_NEW ++#define MOCKED_EVBUFFER_NEW ++#define MOCKED_EVBUFFER_READ ++#define MOCKED_EVBUFFER_PULLUP ++#define MOCKED_EVBUFFER_GET_LENGTH ++#define MOCKED_EVBUFFER_DRAIN ++#include "mock_event2.h" ++#include "mock_gateway.h" ++#include "mock_daemon.h" ++ ++#include "tests_utils.h" ++#define MOCKED_JSONRPC_DISPATCH_REQUEST ++#include "tests_pipe_utils.h" ++ ++#define REQUEST_1 "{" \ ++ "\"jsonrpc\": \"2.0\"," \ ++ "\"method\": \"get_light_state\"," \ ++ "\"params\": [\"*\"]," \ ++ "\"id\": 42" \ ++"}" ++#define EXTRA_DATA "BLUBLBULBUBUHIFESHFUSsoundsaboutright" ++ ++static unsigned char request[] = ( ++ REQUEST_1 ++ EXTRA_DATA ++); ++ ++static char *tmpdir = NULL; ++ ++void ++cleanup_tmpdir(void) ++{ ++ lgtd_tests_remove_temp_dir(tmpdir); ++} ++ ++static int jsonrpc_dispatch_request_call_count = 0; ++ ++void ++lgtd_jsonrpc_dispatch_request(struct lgtd_client *client, int parsed) ++{ ++ (void)client; ++ (void)parsed; ++ ++ if (!parsed) { ++ errx(1, "number of parsed json tokens not passed in"); ++ } ++ ++ if (memcmp(client->json, request, sizeof(request))) { ++ errx(1, "got unexpected json"); ++ } ++ ++ jsonrpc_dispatch_request_call_count++; ++} ++ ++struct event * ++event_new(struct event_base *base, ++ evutil_socket_t fd, ++ short events, ++ event_callback_fn cb, ++ void *ctx) ++{ ++ (void)base; ++ (void)fd; ++ (void)events; ++ (void)cb; ++ (void)ctx; ++ ++ return (void *)1; ++} ++ ++static int ++get_nbytes_read(int call_count) ++{ ++ switch (call_count) { ++ case 0: ++ return sizeof(request) - 1; // we don't return the '\0' ++ default: ++ return 0; ++ } ++} ++ ++struct evbuffer * ++evbuffer_new(void) ++{ ++ return (void *)2; ++} ++ ++static int evbuffer_drain_call_count = 0; ++ ++int ++evbuffer_drain(struct evbuffer *buf, size_t len) ++{ ++ if (buf != (void *)2) { ++ errx(1, "got unexpected buf %p (expected %p)", buf, (void *)2); ++ } ++ ++ jsmn_parser jsmn_ctx; ++ jsmn_init(&jsmn_ctx); ++ struct lgtd_command_pipe *pipe = SLIST_FIRST(&lgtd_command_pipes); ++ if (memcmp(&pipe->client.jsmn_ctx, &jsmn_ctx, sizeof(jsmn_ctx))) { ++ errx(1, "the client json parser context wasn't re-initialized"); ++ } ++ ++ switch (evbuffer_drain_call_count) { ++ case 0: ++ if (len != sizeof(REQUEST_1) - 1) { ++ errx( ++ 1, "trying to drain %ju bytes (expected %ju)", ++ (uintmax_t)len, (uintmax_t)sizeof(request) - 1 ++ ); ++ } ++ break; ++ case 1: ++ if (len != sizeof(request) - sizeof(REQUEST_1)) { ++ errx( ++ 1, "trying to drain %ju bytes (expected %ju)", ++ (uintmax_t)len, sizeof(request) - sizeof(REQUEST_1) ++ ); ++ } ++ break; ++ default: ++ break; ++ } ++ evbuffer_drain_call_count++; ++ ++ return 0; ++} ++ ++static int evbuffer_pullup_call_count = 0; ++ ++unsigned char * ++evbuffer_pullup(struct evbuffer *buf, ev_ssize_t size) ++{ ++ if (buf != (void *)2) { ++ errx(1, "got unexpected buf %p (expected %p)", buf, (void *)2); ++ } ++ ++ if (size != -1) { ++ errx(1, "got unexpected size %ld in pullup (expected -1)", size); ++ } ++ ++ return &request[evbuffer_pullup_call_count++ ? sizeof(request) - 1 : 0]; ++} ++ ++static int evbuffer_get_length_call_count = 0; ++ ++size_t ++evbuffer_get_length(const struct evbuffer *buf) ++{ ++ if (buf != (void *)2) { ++ errx(1, "got unexpected buf %p (expected %p)", buf, (void *)2); ++ } ++ ++ size_t len; ++ switch (evbuffer_get_length_call_count) { ++ case 0: ++ len = sizeof(request) - 1; ++ break; ++ case 1: ++ len = sizeof(request) - sizeof(REQUEST_1); ++ break; ++ default: ++ len = 0; ++ break; ++ } ++ evbuffer_get_length_call_count++; ++ ++ return len; ++} ++ ++static int evbuffer_read_call_count = 0; ++ ++int ++evbuffer_read(struct evbuffer *buf, evutil_socket_t fd, int howmuch) ++{ ++ if (buf != (void *)2) { ++ errx(1, "got unexpected buf %p (expected %p)", buf, (void *)2); ++ } ++ ++ struct lgtd_command_pipe *pipe = SLIST_FIRST(&lgtd_command_pipes); ++ if (fd != pipe->fd) { ++ errx(1, "got unexpected fd %d (expected %d)", fd, pipe->fd); ++ } ++ ++ if (howmuch != -1) { ++ errx( ++ 1, "got unexpected howmuch bytes to read %d (expected -1)", howmuch ++ ); ++ } ++ ++ return get_nbytes_read(evbuffer_read_call_count++); ++} ++ ++int ++main(void) ++{ ++ tmpdir = lgtd_tests_make_temp_dir(); ++ atexit(cleanup_tmpdir); ++ ++ char path[PATH_MAX] = { 0 }; ++ snprintf(path, sizeof(path), "%s/lightsd.pipe", tmpdir); ++ if (!lgtd_command_pipe_open(path)) { ++ errx(1, "couldn't open pipe"); ++ } ++ ++ struct lgtd_command_pipe *pipe = SLIST_FIRST(&lgtd_command_pipes); ++ ++ lgtd_command_pipe_read_callback(pipe->fd, EV_READ, pipe); ++ ++ return 0; ++} +diff --git a/tests/core/pipe/test_pipe_read_callback_multiple_requests.c b/tests/core/pipe/test_pipe_read_callback_multiple_requests.c +new file mode 100644 +--- /dev/null ++++ b/tests/core/pipe/test_pipe_read_callback_multiple_requests.c +@@ -0,0 +1,259 @@ ++#include "pipe.c" ++ ++#include <sys/tree.h> ++#include <endian.h> ++#include <limits.h> ++ ++#include "lifx/wire_proto.h" ++ ++#define MOCKED_EVENT_NEW ++#define MOCKED_EVBUFFER_NEW ++#define MOCKED_EVBUFFER_READ ++#define MOCKED_EVBUFFER_PULLUP ++#define MOCKED_EVBUFFER_GET_LENGTH ++#define MOCKED_EVBUFFER_DRAIN ++#include "mock_event2.h" ++#include "mock_gateway.h" ++#include "mock_daemon.h" ++ ++#include "tests_utils.h" ++#define MOCKED_JSONRPC_DISPATCH_REQUEST ++#include "tests_pipe_utils.h" ++ ++#define REQUEST_1 "{" \ ++ "\"jsonrpc\": \"2.0\"," \ ++ "\"method\": \"get_light_state\"," \ ++ "\"params\": [\"*\"]," \ ++ "\"id\": 42" \ ++"}" ++ ++#define REQUEST_2 "{" \ ++ "\"jsonrpc\": \"2.0\"," \ ++ "\"method\": \"power_on\"," \ ++ "\"params\": [\"*\"]," \ ++ "\"id\": 43" \ ++"}" ++ ++static unsigned char request[] = ( ++ REQUEST_1 ++ REQUEST_2 ++); ++ ++static char *tmpdir = NULL; ++ ++void ++cleanup_tmpdir(void) ++{ ++ lgtd_tests_remove_temp_dir(tmpdir); ++} ++ ++static int jsonrpc_dispatch_request_call_count = 0; ++ ++void ++lgtd_jsonrpc_dispatch_request(struct lgtd_client *client, int parsed) ++{ ++ (void)client; ++ (void)parsed; ++ ++ if (!parsed) { ++ errx(1, "number of parsed json tokens not passed in"); ++ } ++ ++ switch (jsonrpc_dispatch_request_call_count) { ++ case 0: ++ if (memcmp(client->json, request, sizeof(request) - 1)) { ++ errx( ++ 1, "got unexpected json %s (expected %s)", ++ client->json, request ++ ); ++ } ++ break; ++ case 1: ++ if (memcmp(client->json, REQUEST_2, sizeof(REQUEST_2) - 1)) { ++ errx( ++ 1, "got unexpected json %s (expected %s)", ++ client->json, REQUEST_2 ++ ); ++ } ++ break; ++ default: ++ errx( ++ 1, "jsonrpc_dispatch_request_call_count = %d", ++ jsonrpc_dispatch_request_call_count ++ ); ++ break; ++ } ++ ++ jsonrpc_dispatch_request_call_count++; ++} ++ ++struct event * ++event_new(struct event_base *base, ++ evutil_socket_t fd, ++ short events, ++ event_callback_fn cb, ++ void *ctx) ++{ ++ (void)base; ++ (void)fd; ++ (void)events; ++ (void)cb; ++ (void)ctx; ++ ++ return (void *)1; ++} ++ ++struct evbuffer * ++evbuffer_new(void) ++{ ++ return (void *)2; ++} ++ ++static int evbuffer_drain_call_count = 0; ++ ++int ++evbuffer_drain(struct evbuffer *buf, size_t len) ++{ ++ if (buf != (void *)2) { ++ errx(1, "got unexpected buf %p (expected %p)", buf, (void *)2); ++ } ++ ++ jsmn_parser jsmn_ctx; ++ jsmn_init(&jsmn_ctx); ++ struct lgtd_command_pipe *pipe = SLIST_FIRST(&lgtd_command_pipes); ++ if (memcmp(&pipe->client.jsmn_ctx, &jsmn_ctx, sizeof(jsmn_ctx))) { ++ errx(1, "the client json parser context wasn't re-initialized"); ++ } ++ ++ switch (evbuffer_drain_call_count) { ++ case 0: ++ if (len != sizeof(REQUEST_1) - 1) { ++ errx( ++ 1, "trying to drain %ju bytes (expected %ju)", ++ (uintmax_t)len, (uintmax_t)sizeof(REQUEST_1) - 1 ++ ); ++ } ++ break; ++ case 1: ++ if (len != sizeof(REQUEST_2) - 1) { ++ errx( ++ 1, "trying to drain %ju bytes (expected %ju)", ++ (uintmax_t)len, (uintmax_t)sizeof(REQUEST_2) - 1 ++ ); ++ } ++ break; ++ default: ++ errx(1, "evbuffer_drain_call_count = %d", evbuffer_drain_call_count); ++ break; ++ } ++ evbuffer_drain_call_count++; ++ ++ return 0; ++} ++ ++static int evbuffer_pullup_call_count = 0; ++ ++unsigned char * ++evbuffer_pullup(struct evbuffer *buf, ev_ssize_t size) ++{ ++ if (buf != (void *)2) { ++ errx(1, "got unexpected buf %p (expected %p)", buf, (void *)2); ++ } ++ ++ if (size != -1) { ++ errx(1, "got unexpected size %ld in pullup (expected -1)", size); ++ } ++ ++ int offset; ++ switch (evbuffer_pullup_call_count) { ++ case 0: ++ offset = 0; ++ break; ++ case 1: ++ offset = sizeof(REQUEST_1) - 1; ++ break; ++ default: ++ offset = sizeof(request); ++ break; ++ } ++ evbuffer_pullup_call_count++; ++ ++ return &request[offset]; ++} ++ ++static int evbuffer_get_length_call_count = 0; ++ ++size_t ++evbuffer_get_length(const struct evbuffer *buf) ++{ ++ if (buf != (void *)2) { ++ errx(1, "got unexpected buf %p (expected %p)", buf, (void *)2); ++ } ++ ++ size_t len; ++ switch (evbuffer_get_length_call_count) { ++ case 0: ++ len = sizeof(request) - 1; ++ break; ++ case 1: ++ len = sizeof(request) - sizeof(REQUEST_1); ++ break; ++ default: ++ len = 0; ++ break; ++ } ++ evbuffer_get_length_call_count++; ++ ++ return len; ++} ++ ++static int evbuffer_read_call_count = 0; ++ ++int ++evbuffer_read(struct evbuffer *buf, evutil_socket_t fd, int howmuch) ++{ ++ if (buf != (void *)2) { ++ errx(1, "got unexpected buf %p (expected %p)", buf, (void *)2); ++ } ++ ++ struct lgtd_command_pipe *pipe = SLIST_FIRST(&lgtd_command_pipes); ++ if (fd != pipe->fd) { ++ errx(1, "got unexpected fd %d (expected %d)", fd, pipe->fd); ++ } ++ ++ if (howmuch != -1) { ++ errx( ++ 1, "got unexpected howmuch bytes to read %d (expected -1)", howmuch ++ ); ++ } ++ ++ int rv = 0; ++ switch (evbuffer_read_call_count) { ++ case 0: ++ rv = sizeof(request) - 1; ++ default: ++ break; ++ } ++ evbuffer_read_call_count++; ++ ++ return rv; ++} ++ ++int ++main(void) ++{ ++ tmpdir = lgtd_tests_make_temp_dir(); ++ atexit(cleanup_tmpdir); ++ ++ char path[PATH_MAX] = { 0 }; ++ snprintf(path, sizeof(path), "%s/lightsd.pipe", tmpdir); ++ if (!lgtd_command_pipe_open(path)) { ++ errx(1, "couldn't open pipe"); ++ } ++ ++ struct lgtd_command_pipe *pipe = SLIST_FIRST(&lgtd_command_pipes); ++ ++ lgtd_command_pipe_read_callback(pipe->fd, EV_READ, pipe); ++ ++ return 0; ++} +diff --git a/tests/core/pipe/tests_pipe_utils.h b/tests/core/pipe/tests_pipe_utils.h +new file mode 100644 +--- /dev/null ++++ b/tests/core/pipe/tests_pipe_utils.h +@@ -0,0 +1,19 @@ ++#pragma once ++ ++#ifndef MOCKED_CLIENT_OPEN_FROM_PIPE ++void ++lgtd_client_open_from_pipe(struct lgtd_client *pipe_client) ++{ ++ memset(pipe_client, 0, sizeof(*pipe_client)); ++ jsmn_init(&pipe_client->jsmn_ctx); ++} ++#endif ++ ++#ifndef MOCKED_JSONRPC_DISPATCH_REQUEST ++void ++lgtd_jsonrpc_dispatch_request(struct lgtd_client *client, int parsed) ++{ ++ (void)client; ++ (void)parsed; ++} ++#endif diff --git a/tests/core/proto/tests_proto_utils.h b/tests/core/proto/tests_proto_utils.h --- a/tests/core/proto/tests_proto_utils.h +++ b/tests/core/proto/tests_proto_utils.h -@@ -16,7 +16,7 @@ +@@ -18,7 +18,7 @@ void lgtd_client_send_response(struct lgtd_client *client, const char *msg) { @@ -606,3 +1964,93 @@ } #endif +diff --git a/tests/core/tests_shims.c b/tests/core/tests_shims.c +--- a/tests/core/tests_shims.c ++++ b/tests/core/tests_shims.c +@@ -24,7 +24,7 @@ + .verbosity = LGTD_DEBUG + }; + +-struct event_base *lgtd_ev_base = NULL; ++struct event_base *lgtd_ev_base = (void *)0x1234; + + const char *lgtd_binds = NULL; + +diff --git a/tests/core/tests_utils.c b/tests/core/tests_utils.c +--- a/tests/core/tests_utils.c ++++ b/tests/core/tests_utils.c +@@ -2,13 +2,18 @@ + #include <sys/tree.h> + #include <sys/socket.h> + #include <assert.h> ++#include <dirent.h> + #include <endian.h> ++#include <err.h> ++#include <limits.h> + #include <netinet/in.h> + #include <stdarg.h> + #include <stdbool.h> + #include <stdlib.h> + #include <stdint.h> ++#include <stdio.h> + #include <string.h> ++#include <unistd.h> + + #include <event2/util.h> + +@@ -112,3 +117,42 @@ + + return site; + } ++ ++char * ++lgtd_tests_make_temp_dir(void) ++{ ++ char buf[PATH_MAX] = { 0 }; ++ int n = snprintf(buf, sizeof(buf), "%slightsd.test.XXXXXXXX", P_tmpdir); ++ if (n >= (int)sizeof(buf)) { ++ errx(1, "cannot allocate temporary directory"); ++ } ++ return strdup(mkdtemp(buf)); ++} ++ ++void ++lgtd_tests_remove_temp_dir(char *path) ++{ ++ DIR *tmpdir = opendir(path); ++ if (!tmpdir) { ++ return; ++ } ++ ++ struct dirent db; ++ struct dirent *entry = &db; ++ while (!readdir_r(tmpdir, entry, &entry) && entry) { ++ if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) { ++ continue; ++ } ++ char buf[PATH_MAX] = { 0 }; ++ snprintf(buf, sizeof(buf), "%s/%s", path, entry->d_name); ++ unlink(buf); ++ } ++ ++ closedir(tmpdir); ++ ++ if (rmdir(path)) { ++ warn("couldn't remove tempdir %s", path); ++ } ++ ++ free(path); ++} +diff --git a/tests/core/tests_utils.h b/tests/core/tests_utils.h +--- a/tests/core/tests_utils.h ++++ b/tests/core/tests_utils.h +@@ -30,6 +30,9 @@ + return true; + } + ++char *lgtd_tests_make_temp_dir(void); ++void lgtd_tests_remove_temp_dir(char *); ++ + struct lgtd_lifx_gateway *lgtd_tests_insert_mock_gateway(int); + struct lgtd_lifx_bulb *lgtd_tests_insert_mock_bulb(struct lgtd_lifx_gateway *, uint64_t); + struct lgtd_proto_target_list *lgtd_tests_build_target_list(const char *, ...);
--- a/daemon_module.patch Sun Aug 02 18:59:24 2015 -0700 +++ b/daemon_module.patch Sun Aug 02 19:02:55 2015 -0700 @@ -1,5 +1,5 @@ # HG changeset patch -# Parent c4724f39ca4bfe9e0356a7e56e38d7201f614d04 +# Parent d7a2d37c150198e9b0626332d1cc57c538bd5447 diff --git a/README.rst b/README.rst --- a/README.rst @@ -490,7 +490,7 @@ SLIST_HEAD_INITIALIZER(&lgtd_command_pipes); static void -@@ -166,6 +166,7 @@ +@@ -174,6 +174,7 @@ return false; } @@ -568,8 +568,8 @@ + ${LIGHTSD_SOURCE_DIR}/core/log.c + ${LIGHTSD_SOURCE_DIR}/core/stats.c + ${LIGHTSD_SOURCE_DIR}/lifx/bulb.c ++ ${LIGHTSD_SOURCE_DIR}/lifx/tagging.c + ${LIGHTSD_SOURCE_DIR}/lifx/wire_proto.c -+ ${LIGHTSD_SOURCE_DIR}/lifx/tagging.c + ${CMAKE_CURRENT_SOURCE_DIR}/../tests_shims.c + ${CMAKE_CURRENT_SOURCE_DIR}/../tests_utils.c +) @@ -1002,7 +1002,7 @@ diff --git a/tests/core/tests_utils.c b/tests/core/tests_utils.c --- a/tests/core/tests_utils.c +++ b/tests/core/tests_utils.c -@@ -19,6 +19,9 @@ +@@ -24,6 +24,9 @@ #include "core/jsonrpc.h" #include "core/client.h" #include "core/proto.h" @@ -1012,7 +1012,7 @@ #include "lifx/bulb.h" #include "lifx/gateway.h" #include "tests_utils.h" -@@ -26,6 +29,9 @@ +@@ -31,6 +34,9 @@ struct lgtd_lifx_gateway_list lgtd_lifx_gateways = LIST_HEAD_INITIALIZER(&lgtd_lifx_gateways); @@ -1022,7 +1022,7 @@ struct lgtd_lifx_gateway * lgtd_tests_insert_mock_gateway(int id) { -@@ -36,6 +42,8 @@ +@@ -41,6 +47,8 @@ LIST_INSERT_HEAD(&lgtd_lifx_gateways, gw, link); @@ -1031,11 +1031,10 @@ return gw; } -@@ -112,3 +120,14 @@ - +@@ -118,6 +126,17 @@ return site; } -+ + +struct lgtd_listen * +lgtd_tests_insert_mock_listener(const char *addr, const char *port) +{ @@ -1046,10 +1045,14 @@ + + return listener; +} ++ + char * + lgtd_tests_make_temp_dir(void) + { diff --git a/tests/core/tests_utils.h b/tests/core/tests_utils.h --- a/tests/core/tests_utils.h +++ b/tests/core/tests_utils.h -@@ -37,3 +37,4 @@ +@@ -40,3 +40,4 @@ struct lgtd_lifx_site *lgtd_tests_add_tag_to_gw(struct lgtd_lifx_tag *, struct lgtd_lifx_gateway *, int);
--- a/series Sun Aug 02 18:59:24 2015 -0700 +++ b/series Sun Aug 02 19:02:55 2015 -0700 @@ -4,4 +4,3 @@ add_command_pipe.patch fix_usage_and_version.patch daemon_module.patch -testing_command_pipe.patch
--- a/testing_command_pipe.patch Sun Aug 02 18:59:24 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1485 +0,0 @@ -# HG changeset patch -# Parent 2781c3dbb72d815271a446c90a08da3aef0936d6 - -diff --git a/CMakeLists.txt b/CMakeLists.txt ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -43,6 +43,8 @@ - "-D_BSD_SOURCE=1" - "-D_DEFAULT_SOURCE=1" - -+ "-D_DARWIN_C_SOURCE=1" -+ - "-DLGTD_BIG_ENDIAN_SYSTEM=${BIG_ENDIAN_SYSTEM}" - "-DLGTD_SIZEOF_VOID_P=${CMAKE_SIZEOF_VOID_P}" - -diff --git a/core/pipe.c b/core/pipe.c ---- a/core/pipe.c -+++ b/core/pipe.c -@@ -148,6 +148,14 @@ - jsmn_init(&pipe->client.jsmn_ctx); - } - -+static mode_t -+lgtd_command_pipe_get_umask(void) -+{ -+ mode_t mask = umask(0); -+ umask(mask); -+ return mask; -+} -+ - bool - lgtd_command_pipe_open(const char *path) - { -@@ -171,8 +179,14 @@ - pipe->fd = -1; - - mode_t mode = S_IWUSR|S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IWGRP; -- if (mkfifo(path, mode) && errno != EEXIST) { -- goto error; -+ if (mkfifo(path, mode)) { -+ if (errno != EEXIST) { -+ goto error; -+ } -+ mode &= ~lgtd_command_pipe_get_umask(); -+ if (chmod(path, mode)) { -+ goto error; -+ } - } - - pipe->fd = open(path, O_RDONLY|O_NONBLOCK); -diff --git a/tests/core/daemon/CMakeLists.txt b/tests/core/daemon/CMakeLists.txt ---- a/tests/core/daemon/CMakeLists.txt -+++ b/tests/core/daemon/CMakeLists.txt -@@ -8,8 +8,8 @@ - ${LIGHTSD_SOURCE_DIR}/core/log.c - ${LIGHTSD_SOURCE_DIR}/core/stats.c - ${LIGHTSD_SOURCE_DIR}/lifx/bulb.c -+ ${LIGHTSD_SOURCE_DIR}/lifx/tagging.c - ${LIGHTSD_SOURCE_DIR}/lifx/wire_proto.c -- ${LIGHTSD_SOURCE_DIR}/lifx/tagging.c - ${CMAKE_CURRENT_SOURCE_DIR}/../tests_shims.c - ${CMAKE_CURRENT_SOURCE_DIR}/../tests_utils.c - ) -diff --git a/tests/core/mock_event2.h b/tests/core/mock_event2.h -new file mode 100644 ---- /dev/null -+++ b/tests/core/mock_event2.h -@@ -0,0 +1,109 @@ -+#pragma once -+ -+#ifndef MOCKED_EVBUFFER_DRAIN -+int -+evbuffer_drain(struct evbuffer *buf, size_t len) -+{ -+ (void)buf; -+ (void)len; -+ return 0; -+} -+#endif -+ -+#ifndef MOCKED_EVBUFFER_NEW -+struct evbuffer * -+evbuffer_new(void) -+{ -+ return NULL; -+} -+#endif -+ -+#ifndef MOCKED_EVENT_FREE -+void -+evbuffer_free(struct evbuffer *buf) -+{ -+ (void)buf; -+} -+#endif -+ -+#ifndef MOCKED_EVBUFFER_GET_LENGTH -+size_t -+evbuffer_get_length(const struct evbuffer *buf) -+{ -+ (void)buf; -+ return 0; -+} -+#endif -+ -+#ifndef MOCKED_EVBUFFER_PULLUP -+unsigned char * -+evbuffer_pullup(struct evbuffer *buf, ev_ssize_t size) -+{ -+ (void)buf; -+ (void)size; -+ return NULL; -+} -+#endif -+ -+#ifndef MOCKED_EVBUFFER_READ -+int -+evbuffer_read(struct evbuffer *buffer, evutil_socket_t fd, int howmuch) -+{ -+ (void)buffer; -+ (void)fd; -+ return howmuch; -+} -+#endif -+ -+#ifndef MOCKED_EVENT_ADD -+int -+event_add(struct event *ev, const struct timeval *timeout) -+{ -+ (void)ev; -+ (void)timeout; -+ return 0; -+} -+#endif -+ -+#ifndef MOCKED_EVENT_DEL -+int -+event_del(struct event *ev) -+{ -+ (void)ev; -+ return 0; -+} -+#endif -+ -+#ifndef MOCKED_EVENT_FREE -+void -+event_free(struct event *ev) -+{ -+ (void)ev; -+} -+#endif -+ -+#ifndef MOCKED_EVENT_NEW -+struct event * -+event_new(struct event_base *base, -+ evutil_socket_t fd, -+ short events, -+ event_callback_fn cb, -+ void *ctx) -+{ -+ (void)base; -+ (void)fd; -+ (void)events; -+ (void)cb; -+ (void)ctx; -+ return NULL; -+} -+#endif -+ -+#ifndef MOCKED_EVUTIL_MAKE_SOCKET_NONBLOCKING -+int -+evutil_make_socket_nonblocking(evutil_socket_t fd) -+{ -+ (void)fd; -+ return 0; -+} -+#endif -diff --git a/tests/core/pipe/CMakeLists.txt b/tests/core/pipe/CMakeLists.txt -new file mode 100644 ---- /dev/null -+++ b/tests/core/pipe/CMakeLists.txt -@@ -0,0 +1,25 @@ -+INCLUDE_DIRECTORIES( -+ ${CMAKE_CURRENT_SOURCE_DIR} -+ ${CMAKE_CURRENT_BINARY_DIR} -+) -+ -+ADD_CORE_LIBRARY( -+ test_core_pipe STATIC -+ ${LIGHTSD_SOURCE_DIR}/core/jsmn.c -+ ${LIGHTSD_SOURCE_DIR}/core/log.c -+ ${LIGHTSD_SOURCE_DIR}/core/stats.c -+ ${LIGHTSD_SOURCE_DIR}/lifx/bulb.c -+ ${LIGHTSD_SOURCE_DIR}/lifx/tagging.c -+ ${LIGHTSD_SOURCE_DIR}/lifx/wire_proto.c -+ ${CMAKE_CURRENT_SOURCE_DIR}/../tests_shims.c -+ ${CMAKE_CURRENT_SOURCE_DIR}/../tests_utils.c -+) -+ -+FUNCTION(ADD_PIPE_TEST TEST_SOURCE) -+ ADD_TEST_FROM_C_SOURCES(${TEST_SOURCE} test_core_pipe) -+ENDFUNCTION() -+ -+FILE(GLOB TESTS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "test_*.c") -+FOREACH(TEST ${TESTS}) -+ ADD_PIPE_TEST(${TEST}) -+ENDFOREACH() -diff --git a/tests/core/pipe/test_pipe_close.c b/tests/core/pipe/test_pipe_close.c -new file mode 100644 ---- /dev/null -+++ b/tests/core/pipe/test_pipe_close.c -@@ -0,0 +1,117 @@ -+#include "pipe.c" -+ -+#include <sys/tree.h> -+#include <endian.h> -+#include <limits.h> -+ -+#include "lifx/wire_proto.h" -+ -+#define MOCKED_EVENT_NEW -+#define MOCKED_EVBUFFER_NEW -+#define MOCKED_EVENT_DEL -+#define MOCKED_EVBUFFER_FREE -+#define MOCKED_EVENT_FREE -+#include "mock_event2.h" -+#include "mock_gateway.h" -+#include "mock_daemon.h" -+ -+#include "tests_utils.h" -+#include "tests_pipe_utils.h" -+ -+char *tmpdir = NULL; -+ -+void -+cleanup_tmpdir(void) -+{ -+ lgtd_tests_remove_temp_dir(tmpdir); -+} -+ -+struct event * -+event_new(struct event_base *base, -+ evutil_socket_t fd, -+ short events, -+ event_callback_fn cb, -+ void *ctx) -+{ -+ (void)base; -+ (void)fd; -+ (void)events; -+ (void)cb; -+ (void)ctx; -+ -+ return (void *)1; -+} -+ -+struct evbuffer * -+evbuffer_new(void) -+{ -+ return (void *)2; -+} -+ -+static int event_del_call_count = 0; -+ -+int -+event_del(struct event *ev) -+{ -+ (void)ev; -+ -+ event_del_call_count++; -+ -+ return 0; -+} -+ -+static int evbuffer_free_call_count = 0; -+ -+void -+evbuffer_free(struct evbuffer *buf) -+{ -+ (void)buf; -+ -+ evbuffer_free_call_count++; -+} -+ -+static int event_free_call_count = 0; -+ -+void -+event_free(struct event *ev) -+{ -+ (void)ev; -+ -+ event_free_call_count++; -+} -+ -+int -+main(void) -+{ -+ tmpdir = lgtd_tests_make_temp_dir(); -+ atexit(cleanup_tmpdir); -+ -+ char path[PATH_MAX] = { 0 }; -+ snprintf(path, sizeof(path), "%s/lightsd.pipe", tmpdir); -+ if (!lgtd_command_pipe_open(path)) { -+ errx(1, "couldn't open pipe"); -+ } -+ -+ int pipe_fd = SLIST_FIRST(&lgtd_command_pipes)->fd; -+ -+ lgtd_command_pipe_close_all(); -+ -+ if (event_del_call_count != 1) { -+ errx(1, "event_del_call_count = %d", event_del_call_count); -+ } -+ if (evbuffer_free_call_count != 1) { -+ errx(1, "evbuffer_free_call_count = %d", evbuffer_free_call_count); -+ } -+ if (event_free_call_count != 1) { -+ errx(1, "event_free_call_count = %d", event_free_call_count); -+ } -+ struct stat sb; -+ if (fstat(pipe_fd, &sb) != -1 && errno != EBADF) { -+ errx(1, "the pipe file descriptor wasn't closed correctly"); -+ } -+ if (stat(path, &sb) != -1 && errno != ENOENT) { -+ errx(1, "the pipe wasn't removed correctly"); -+ } -+ -+ return 0; -+} -diff --git a/tests/core/pipe/test_pipe_open.c b/tests/core/pipe/test_pipe_open.c -new file mode 100644 ---- /dev/null -+++ b/tests/core/pipe/test_pipe_open.c -@@ -0,0 +1,171 @@ -+#include "pipe.c" -+ -+#include <sys/tree.h> -+#include <endian.h> -+#include <limits.h> -+ -+#include "lifx/wire_proto.h" -+ -+#define MOCKED_EVUTIL_MAKE_SOCKET_NONBLOCKING -+#define MOCKED_EVENT_NEW -+#define MOCKED_EVBUFFER_NEW -+#define MOCKED_EVENT_ADD -+#include "mock_event2.h" -+#include "mock_gateway.h" -+#include "mock_daemon.h" -+ -+#include "tests_utils.h" -+#define MOCKED_CLIENT_OPEN_FROM_PIPE -+#include "tests_pipe_utils.h" -+ -+char *tmpdir = NULL; -+ -+void -+cleanup_tmpdir(void) -+{ -+ lgtd_tests_remove_temp_dir(tmpdir); -+} -+ -+static bool make_socket_nonblocking_call_count = 0; -+ -+int -+evutil_make_socket_nonblocking(evutil_socket_t fd) -+{ -+ if (fd <= 0) { -+ errx(1, "got invalid fd %d in make_socket_nonblocking", fd); -+ } -+ -+ make_socket_nonblocking_call_count++; -+ -+ return 0; -+} -+ -+static int event_new_call_count = 0; -+ -+struct event * -+event_new(struct event_base *base, -+ evutil_socket_t fd, -+ short events, -+ event_callback_fn cb, -+ void *ctx) -+{ -+ if (base != lgtd_ev_base) { -+ errx( -+ 1, "unexpected lgtd_ev_base = %p (expected %p)", -+ base, lgtd_ev_base -+ ); -+ } -+ if (fd <= 0) { -+ errx(1, "got invalid fd %d in event_new", fd); -+ } -+ if (events != (EV_READ|EV_PERSIST)) { -+ errx(1, "got events %#x (expected %#x)", events, EV_READ|EV_PERSIST); -+ } -+ if (cb != lgtd_command_pipe_read_callback) { -+ errx(1, "the read callback wasn't set correctly"); -+ } -+ if (!ctx) { -+ errx(1, "the callback context wasn't set correctly"); -+ } -+ -+ event_new_call_count++; -+ -+ return (void *)1; -+} -+ -+static int evbuffer_new_call_count = 0; -+ -+struct evbuffer * -+evbuffer_new(void) -+{ -+ evbuffer_new_call_count++; -+ -+ return (void *)2; -+} -+ -+static int event_add_call_count = 0; -+ -+int -+event_add(struct event *ev, const struct timeval *timeout) -+{ -+ if (ev != (void *)1) { -+ errx(1, "got unexpected event %p (expected %p)", ev, (void*)1); -+ } -+ -+ if (timeout) { -+ errx(1, "a timeout shouldn't have been passed"); -+ } -+ -+ event_add_call_count++; -+ -+ return 0; -+} -+ -+static int client_open_from_pipe_call_count = 0; -+ -+void -+lgtd_client_open_from_pipe(struct lgtd_client *pipe_client) -+{ -+ if (!pipe_client) { -+ errx(1, "missing pipe_client"); -+ } -+ -+ client_open_from_pipe_call_count++; -+} -+ -+int -+main(void) -+{ -+ tmpdir = lgtd_tests_make_temp_dir(); -+ atexit(cleanup_tmpdir); -+ -+ char path[PATH_MAX] = { 0 }; -+ snprintf(path, sizeof(path), "%s/lightsd.pipe", tmpdir); -+ if (!lgtd_command_pipe_open(path)) { -+ errx(1, "couldn't open pipe"); -+ } -+ -+ if (make_socket_nonblocking_call_count != 1) { -+ errx( -+ 1, "make_socket_nonblocking_call_count = %d", -+ make_socket_nonblocking_call_count -+ ); -+ } -+ if (event_new_call_count != 1) { -+ errx(1, "event_new_call_count = %d", event_new_call_count); -+ } -+ if (evbuffer_new_call_count != 1) { -+ errx(1, "evbuffer_new_call_count = %d", evbuffer_new_call_count); -+ } -+ if (event_add_call_count != 1) { -+ errx(1, "event_add_call_count = %d", event_add_call_count); -+ } -+ if (SLIST_EMPTY(&lgtd_command_pipes)) { -+ errx(1, "the list of command pipes shouldn't be empty"); -+ } -+ -+ struct stat sb; -+ if (stat(path, &sb)) { -+ errx(1, "can't stat pipe %s", path); -+ } -+ -+ mode_t expected_mode; -+ expected_mode = S_IFIFO|S_IWUSR|S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IWGRP; -+ expected_mode &= ~lgtd_command_pipe_get_umask(); -+ if (sb.st_mode != expected_mode) { -+ errx( -+ 1, "unexpected mode %o (expected %o)", -+ sb.st_mode, expected_mode -+ ); -+ } -+ -+ // make sure it's idempotent: -+ if (!lgtd_command_pipe_open(path)) { -+ errx(1, "couldn't open pipe"); -+ } -+ if (event_new_call_count != 1) { -+ errx(1, "event_new_call_count = %d", event_new_call_count); -+ } -+ -+ return 0; -+} -diff --git a/tests/core/pipe/test_pipe_open_fifo_already_exists.c b/tests/core/pipe/test_pipe_open_fifo_already_exists.c -new file mode 100644 ---- /dev/null -+++ b/tests/core/pipe/test_pipe_open_fifo_already_exists.c -@@ -0,0 +1,176 @@ -+#include "pipe.c" -+ -+#include <sys/tree.h> -+#include <endian.h> -+#include <limits.h> -+ -+#include "lifx/wire_proto.h" -+ -+#define MOCKED_EVUTIL_MAKE_SOCKET_NONBLOCKING -+#define MOCKED_EVENT_NEW -+#define MOCKED_EVBUFFER_NEW -+#define MOCKED_EVENT_ADD -+#include "mock_event2.h" -+#include "mock_gateway.h" -+#include "mock_daemon.h" -+ -+#include "tests_utils.h" -+#define MOCKED_CLIENT_OPEN_FROM_PIPE -+#include "tests_pipe_utils.h" -+ -+char *tmpdir = NULL; -+ -+void -+cleanup_tmpdir(void) -+{ -+ lgtd_tests_remove_temp_dir(tmpdir); -+} -+ -+static bool make_socket_nonblocking_call_count = 0; -+ -+int -+evutil_make_socket_nonblocking(evutil_socket_t fd) -+{ -+ if (fd <= 0) { -+ errx(1, "got invalid fd %d in make_socket_nonblocking", fd); -+ } -+ -+ make_socket_nonblocking_call_count++; -+ -+ return 0; -+} -+ -+static int event_new_call_count = 0; -+ -+struct event * -+event_new(struct event_base *base, -+ evutil_socket_t fd, -+ short events, -+ event_callback_fn cb, -+ void *ctx) -+{ -+ if (base != lgtd_ev_base) { -+ errx( -+ 1, "unexpected lgtd_ev_base = %p (expected %p)", -+ base, lgtd_ev_base -+ ); -+ } -+ if (fd <= 0) { -+ errx(1, "got invalid fd %d in event_new", fd); -+ } -+ if (events != (EV_READ|EV_PERSIST)) { -+ errx(1, "got events %#x (expected %#x)", events, EV_READ|EV_PERSIST); -+ } -+ if (cb != lgtd_command_pipe_read_callback) { -+ errx(1, "the read callback wasn't set correctly"); -+ } -+ if (!ctx) { -+ errx(1, "the callback context wasn't set correctly"); -+ } -+ -+ event_new_call_count++; -+ -+ return (void *)1; -+} -+ -+static int evbuffer_new_call_count = 0; -+ -+struct evbuffer * -+evbuffer_new(void) -+{ -+ evbuffer_new_call_count++; -+ -+ return (void *)2; -+} -+ -+static int event_add_call_count = 0; -+ -+int -+event_add(struct event *ev, const struct timeval *timeout) -+{ -+ if (ev != (void *)1) { -+ errx(1, "got unexpected event %p (expected %p)", ev, (void*)1); -+ } -+ -+ if (timeout) { -+ errx(1, "a timeout shouldn't have been passed"); -+ } -+ -+ event_add_call_count++; -+ -+ return 0; -+} -+ -+static int client_open_from_pipe_call_count = 0; -+ -+void -+lgtd_client_open_from_pipe(struct lgtd_client *pipe_client) -+{ -+ if (!pipe_client) { -+ errx(1, "missing pipe_client"); -+ } -+ -+ client_open_from_pipe_call_count++; -+} -+ -+int -+main(void) -+{ -+ tmpdir = lgtd_tests_make_temp_dir(); -+ atexit(cleanup_tmpdir); -+ -+ char path[PATH_MAX] = { 0 }; -+ snprintf(path, sizeof(path), "%s/lightsd.pipe", tmpdir); -+ -+ if (mkfifo(path, 0)) { -+ errx(1, "can't open fifo"); -+ } -+ -+ if (!lgtd_command_pipe_open(path)) { -+ errx(1, "couldn't open pipe"); -+ } -+ -+ if (make_socket_nonblocking_call_count != 1) { -+ errx( -+ 1, "make_socket_nonblocking_call_count = %d", -+ make_socket_nonblocking_call_count -+ ); -+ } -+ if (event_new_call_count != 1) { -+ errx(1, "event_new_call_count = %d", event_new_call_count); -+ } -+ if (evbuffer_new_call_count != 1) { -+ errx(1, "evbuffer_new_call_count = %d", evbuffer_new_call_count); -+ } -+ if (event_add_call_count != 1) { -+ errx(1, "event_add_call_count = %d", event_add_call_count); -+ } -+ if (SLIST_EMPTY(&lgtd_command_pipes)) { -+ errx(1, "the list of command pipes shouldn't be empty"); -+ } -+ -+ struct stat sb; -+ if (stat(path, &sb)) { -+ errx(1, "can't stat pipe %s", path); -+ } -+ -+ mode_t expected_mode; -+ expected_mode = S_IFIFO|S_IWUSR|S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IWGRP; -+ expected_mode &= ~lgtd_command_pipe_get_umask(); -+ if (sb.st_mode != expected_mode) { -+ errx( -+ 1, "unexpected mode %o (expected %o)", -+ sb.st_mode, expected_mode -+ ); -+ } -+ -+ // make sure it's idempotent: -+ if (!lgtd_command_pipe_open(path)) { -+ errx(1, "couldn't open pipe"); -+ } -+ if (event_new_call_count != 1) { -+ errx(1, "event_new_call_count = %d", event_new_call_count); -+ } -+ -+ return 0; -+} -diff --git a/tests/core/pipe/test_pipe_read_callback.c b/tests/core/pipe/test_pipe_read_callback.c -new file mode 100644 ---- /dev/null -+++ b/tests/core/pipe/test_pipe_read_callback.c -@@ -0,0 +1,192 @@ -+#include "pipe.c" -+ -+#include <sys/tree.h> -+#include <endian.h> -+#include <limits.h> -+ -+#include "lifx/wire_proto.h" -+ -+#define MOCKED_EVENT_NEW -+#define MOCKED_EVBUFFER_NEW -+#define MOCKED_EVBUFFER_READ -+#define MOCKED_EVBUFFER_PULLUP -+#define MOCKED_EVBUFFER_GET_LENGTH -+#define MOCKED_EVBUFFER_DRAIN -+#include "mock_event2.h" -+#include "mock_gateway.h" -+#include "mock_daemon.h" -+ -+#include "tests_utils.h" -+#define MOCKED_JSONRPC_DISPATCH_REQUEST -+#include "tests_pipe_utils.h" -+ -+static unsigned char request[] = ("{" -+ "\"jsonrpc\": \"2.0\"," -+ "\"method\": \"get_light_state\"," -+ "\"params\": [\"*\"]," -+ "\"id\": 42" -+"}"); -+ -+static char *tmpdir = NULL; -+ -+void -+cleanup_tmpdir(void) -+{ -+ lgtd_tests_remove_temp_dir(tmpdir); -+} -+ -+static int jsonrpc_dispatch_request_call_count = 0; -+ -+void -+lgtd_jsonrpc_dispatch_request(struct lgtd_client *client, int parsed) -+{ -+ (void)client; -+ (void)parsed; -+ -+ if (!parsed) { -+ errx(1, "number of parsed json tokens not passed in"); -+ } -+ -+ if (memcmp(client->json, request, sizeof(request))) { -+ errx(1, "got unexpected json"); -+ } -+ -+ jsonrpc_dispatch_request_call_count++; -+} -+ -+struct event * -+event_new(struct event_base *base, -+ evutil_socket_t fd, -+ short events, -+ event_callback_fn cb, -+ void *ctx) -+{ -+ (void)base; -+ (void)fd; -+ (void)events; -+ (void)cb; -+ (void)ctx; -+ -+ return (void *)1; -+} -+ -+static int -+get_nbytes_read(int call_count) -+{ -+ switch (call_count) { -+ case 0: -+ return sizeof(request) - 1; // we don't return the '\0' -+ default: -+ return 0; -+ } -+} -+ -+struct evbuffer * -+evbuffer_new(void) -+{ -+ return (void *)2; -+} -+ -+static int evbuffer_drain_call_count = 0; -+ -+int -+evbuffer_drain(struct evbuffer *buf, size_t len) -+{ -+ if (buf != (void *)2) { -+ errx(1, "got unexpected buf %p (expected %p)", buf, (void *)2); -+ } -+ -+ jsmn_parser jsmn_ctx; -+ jsmn_init(&jsmn_ctx); -+ struct lgtd_command_pipe *pipe = SLIST_FIRST(&lgtd_command_pipes); -+ if (memcmp(&pipe->client.jsmn_ctx, &jsmn_ctx, sizeof(jsmn_ctx))) { -+ errx(1, "the client json parser context wasn't re-initialized"); -+ } -+ -+ -+ switch (evbuffer_drain_call_count) { -+ case 0: -+ if (len != sizeof(request) - 1) { -+ errx( -+ 1, "trying to drain %ju bytes (expected %ju)", -+ (uintmax_t)len, (uintmax_t)sizeof(request) - 1 -+ ); -+ } -+ break; -+ default: -+ break; -+ } -+ evbuffer_drain_call_count++; -+ -+ return 0; -+} -+ -+static int evbuffer_pullup_call_count = 0; -+ -+unsigned char * -+evbuffer_pullup(struct evbuffer *buf, ev_ssize_t size) -+{ -+ if (buf != (void *)2) { -+ errx(1, "got unexpected buf %p (expected %p)", buf, (void *)2); -+ } -+ -+ if (size != -1) { -+ errx(1, "got unexpected size %ld in pullup (expected -1)", size); -+ } -+ -+ return &request[evbuffer_pullup_call_count++ ? sizeof(request) - 1 : 0]; -+} -+ -+static int evbuffer_get_length_call_count = 0; -+ -+size_t -+evbuffer_get_length(const struct evbuffer *buf) -+{ -+ if (buf != (void *)2) { -+ errx(1, "got unexpected buf %p (expected %p)", buf, (void *)2); -+ } -+ -+ return get_nbytes_read(evbuffer_get_length_call_count++); -+} -+ -+static int evbuffer_read_call_count = 0; -+ -+int -+evbuffer_read(struct evbuffer *buf, evutil_socket_t fd, int howmuch) -+{ -+ if (buf != (void *)2) { -+ errx(1, "got unexpected buf %p (expected %p)", buf, (void *)2); -+ } -+ -+ struct lgtd_command_pipe *pipe = SLIST_FIRST(&lgtd_command_pipes); -+ if (fd != pipe->fd) { -+ errx(1, "got unexpected fd %d (expected %d)", fd, pipe->fd); -+ } -+ -+ if (howmuch != -1) { -+ errx( -+ 1, "got unexpected howmuch bytes to read %d (expected -1)", howmuch -+ ); -+ } -+ -+ return get_nbytes_read(evbuffer_read_call_count++); -+} -+ -+int -+main(void) -+{ -+ tmpdir = lgtd_tests_make_temp_dir(); -+ atexit(cleanup_tmpdir); -+ -+ char path[PATH_MAX] = { 0 }; -+ snprintf(path, sizeof(path), "%s/lightsd.pipe", tmpdir); -+ if (!lgtd_command_pipe_open(path)) { -+ errx(1, "couldn't open pipe"); -+ } -+ -+ struct lgtd_command_pipe *pipe = SLIST_FIRST(&lgtd_command_pipes); -+ -+ lgtd_command_pipe_read_callback(pipe->fd, EV_READ, pipe); -+ -+ return 0; -+} -diff --git a/tests/core/pipe/test_pipe_read_callback_extra_data.c b/tests/core/pipe/test_pipe_read_callback_extra_data.c -new file mode 100644 ---- /dev/null -+++ b/tests/core/pipe/test_pipe_read_callback_extra_data.c -@@ -0,0 +1,219 @@ -+#include "pipe.c" -+ -+#include <sys/tree.h> -+#include <endian.h> -+#include <limits.h> -+ -+#include "lifx/wire_proto.h" -+ -+#define MOCKED_EVENT_NEW -+#define MOCKED_EVBUFFER_NEW -+#define MOCKED_EVBUFFER_READ -+#define MOCKED_EVBUFFER_PULLUP -+#define MOCKED_EVBUFFER_GET_LENGTH -+#define MOCKED_EVBUFFER_DRAIN -+#include "mock_event2.h" -+#include "mock_gateway.h" -+#include "mock_daemon.h" -+ -+#include "tests_utils.h" -+#define MOCKED_JSONRPC_DISPATCH_REQUEST -+#include "tests_pipe_utils.h" -+ -+#define REQUEST_1 "{" \ -+ "\"jsonrpc\": \"2.0\"," \ -+ "\"method\": \"get_light_state\"," \ -+ "\"params\": [\"*\"]," \ -+ "\"id\": 42" \ -+"}" -+#define EXTRA_DATA "BLUBLBULBUBUHIFESHFUSsoundsaboutright" -+ -+static unsigned char request[] = ( -+ REQUEST_1 -+ EXTRA_DATA -+); -+ -+static char *tmpdir = NULL; -+ -+void -+cleanup_tmpdir(void) -+{ -+ lgtd_tests_remove_temp_dir(tmpdir); -+} -+ -+static int jsonrpc_dispatch_request_call_count = 0; -+ -+void -+lgtd_jsonrpc_dispatch_request(struct lgtd_client *client, int parsed) -+{ -+ (void)client; -+ (void)parsed; -+ -+ if (!parsed) { -+ errx(1, "number of parsed json tokens not passed in"); -+ } -+ -+ if (memcmp(client->json, request, sizeof(request))) { -+ errx(1, "got unexpected json"); -+ } -+ -+ jsonrpc_dispatch_request_call_count++; -+} -+ -+struct event * -+event_new(struct event_base *base, -+ evutil_socket_t fd, -+ short events, -+ event_callback_fn cb, -+ void *ctx) -+{ -+ (void)base; -+ (void)fd; -+ (void)events; -+ (void)cb; -+ (void)ctx; -+ -+ return (void *)1; -+} -+ -+static int -+get_nbytes_read(int call_count) -+{ -+ switch (call_count) { -+ case 0: -+ return sizeof(request) - 1; // we don't return the '\0' -+ default: -+ return 0; -+ } -+} -+ -+struct evbuffer * -+evbuffer_new(void) -+{ -+ return (void *)2; -+} -+ -+static int evbuffer_drain_call_count = 0; -+ -+int -+evbuffer_drain(struct evbuffer *buf, size_t len) -+{ -+ if (buf != (void *)2) { -+ errx(1, "got unexpected buf %p (expected %p)", buf, (void *)2); -+ } -+ -+ jsmn_parser jsmn_ctx; -+ jsmn_init(&jsmn_ctx); -+ struct lgtd_command_pipe *pipe = SLIST_FIRST(&lgtd_command_pipes); -+ if (memcmp(&pipe->client.jsmn_ctx, &jsmn_ctx, sizeof(jsmn_ctx))) { -+ errx(1, "the client json parser context wasn't re-initialized"); -+ } -+ -+ switch (evbuffer_drain_call_count) { -+ case 0: -+ if (len != sizeof(REQUEST_1) - 1) { -+ errx( -+ 1, "trying to drain %ju bytes (expected %ju)", -+ (uintmax_t)len, (uintmax_t)sizeof(request) - 1 -+ ); -+ } -+ break; -+ case 1: -+ if (len != sizeof(request) - sizeof(REQUEST_1)) { -+ errx( -+ 1, "trying to drain %ju bytes (expected %ju)", -+ (uintmax_t)len, sizeof(request) - sizeof(REQUEST_1) -+ ); -+ } -+ break; -+ default: -+ break; -+ } -+ evbuffer_drain_call_count++; -+ -+ return 0; -+} -+ -+static int evbuffer_pullup_call_count = 0; -+ -+unsigned char * -+evbuffer_pullup(struct evbuffer *buf, ev_ssize_t size) -+{ -+ if (buf != (void *)2) { -+ errx(1, "got unexpected buf %p (expected %p)", buf, (void *)2); -+ } -+ -+ if (size != -1) { -+ errx(1, "got unexpected size %ld in pullup (expected -1)", size); -+ } -+ -+ return &request[evbuffer_pullup_call_count++ ? sizeof(request) - 1 : 0]; -+} -+ -+static int evbuffer_get_length_call_count = 0; -+ -+size_t -+evbuffer_get_length(const struct evbuffer *buf) -+{ -+ if (buf != (void *)2) { -+ errx(1, "got unexpected buf %p (expected %p)", buf, (void *)2); -+ } -+ -+ size_t len; -+ switch (evbuffer_get_length_call_count) { -+ case 0: -+ len = sizeof(request) - 1; -+ break; -+ case 1: -+ len = sizeof(request) - sizeof(REQUEST_1); -+ break; -+ default: -+ len = 0; -+ break; -+ } -+ evbuffer_get_length_call_count++; -+ -+ return len; -+} -+ -+static int evbuffer_read_call_count = 0; -+ -+int -+evbuffer_read(struct evbuffer *buf, evutil_socket_t fd, int howmuch) -+{ -+ if (buf != (void *)2) { -+ errx(1, "got unexpected buf %p (expected %p)", buf, (void *)2); -+ } -+ -+ struct lgtd_command_pipe *pipe = SLIST_FIRST(&lgtd_command_pipes); -+ if (fd != pipe->fd) { -+ errx(1, "got unexpected fd %d (expected %d)", fd, pipe->fd); -+ } -+ -+ if (howmuch != -1) { -+ errx( -+ 1, "got unexpected howmuch bytes to read %d (expected -1)", howmuch -+ ); -+ } -+ -+ return get_nbytes_read(evbuffer_read_call_count++); -+} -+ -+int -+main(void) -+{ -+ tmpdir = lgtd_tests_make_temp_dir(); -+ atexit(cleanup_tmpdir); -+ -+ char path[PATH_MAX] = { 0 }; -+ snprintf(path, sizeof(path), "%s/lightsd.pipe", tmpdir); -+ if (!lgtd_command_pipe_open(path)) { -+ errx(1, "couldn't open pipe"); -+ } -+ -+ struct lgtd_command_pipe *pipe = SLIST_FIRST(&lgtd_command_pipes); -+ -+ lgtd_command_pipe_read_callback(pipe->fd, EV_READ, pipe); -+ -+ return 0; -+} -diff --git a/tests/core/pipe/test_pipe_read_callback_multiple_requests.c b/tests/core/pipe/test_pipe_read_callback_multiple_requests.c -new file mode 100644 ---- /dev/null -+++ b/tests/core/pipe/test_pipe_read_callback_multiple_requests.c -@@ -0,0 +1,259 @@ -+#include "pipe.c" -+ -+#include <sys/tree.h> -+#include <endian.h> -+#include <limits.h> -+ -+#include "lifx/wire_proto.h" -+ -+#define MOCKED_EVENT_NEW -+#define MOCKED_EVBUFFER_NEW -+#define MOCKED_EVBUFFER_READ -+#define MOCKED_EVBUFFER_PULLUP -+#define MOCKED_EVBUFFER_GET_LENGTH -+#define MOCKED_EVBUFFER_DRAIN -+#include "mock_event2.h" -+#include "mock_gateway.h" -+#include "mock_daemon.h" -+ -+#include "tests_utils.h" -+#define MOCKED_JSONRPC_DISPATCH_REQUEST -+#include "tests_pipe_utils.h" -+ -+#define REQUEST_1 "{" \ -+ "\"jsonrpc\": \"2.0\"," \ -+ "\"method\": \"get_light_state\"," \ -+ "\"params\": [\"*\"]," \ -+ "\"id\": 42" \ -+"}" -+ -+#define REQUEST_2 "{" \ -+ "\"jsonrpc\": \"2.0\"," \ -+ "\"method\": \"power_on\"," \ -+ "\"params\": [\"*\"]," \ -+ "\"id\": 43" \ -+"}" -+ -+static unsigned char request[] = ( -+ REQUEST_1 -+ REQUEST_2 -+); -+ -+static char *tmpdir = NULL; -+ -+void -+cleanup_tmpdir(void) -+{ -+ lgtd_tests_remove_temp_dir(tmpdir); -+} -+ -+static int jsonrpc_dispatch_request_call_count = 0; -+ -+void -+lgtd_jsonrpc_dispatch_request(struct lgtd_client *client, int parsed) -+{ -+ (void)client; -+ (void)parsed; -+ -+ if (!parsed) { -+ errx(1, "number of parsed json tokens not passed in"); -+ } -+ -+ switch (jsonrpc_dispatch_request_call_count) { -+ case 0: -+ if (memcmp(client->json, request, sizeof(request) - 1)) { -+ errx( -+ 1, "got unexpected json %s (expected %s)", -+ client->json, request -+ ); -+ } -+ break; -+ case 1: -+ if (memcmp(client->json, REQUEST_2, sizeof(REQUEST_2) - 1)) { -+ errx( -+ 1, "got unexpected json %s (expected %s)", -+ client->json, REQUEST_2 -+ ); -+ } -+ break; -+ default: -+ errx( -+ 1, "jsonrpc_dispatch_request_call_count = %d", -+ jsonrpc_dispatch_request_call_count -+ ); -+ break; -+ } -+ -+ jsonrpc_dispatch_request_call_count++; -+} -+ -+struct event * -+event_new(struct event_base *base, -+ evutil_socket_t fd, -+ short events, -+ event_callback_fn cb, -+ void *ctx) -+{ -+ (void)base; -+ (void)fd; -+ (void)events; -+ (void)cb; -+ (void)ctx; -+ -+ return (void *)1; -+} -+ -+struct evbuffer * -+evbuffer_new(void) -+{ -+ return (void *)2; -+} -+ -+static int evbuffer_drain_call_count = 0; -+ -+int -+evbuffer_drain(struct evbuffer *buf, size_t len) -+{ -+ if (buf != (void *)2) { -+ errx(1, "got unexpected buf %p (expected %p)", buf, (void *)2); -+ } -+ -+ jsmn_parser jsmn_ctx; -+ jsmn_init(&jsmn_ctx); -+ struct lgtd_command_pipe *pipe = SLIST_FIRST(&lgtd_command_pipes); -+ if (memcmp(&pipe->client.jsmn_ctx, &jsmn_ctx, sizeof(jsmn_ctx))) { -+ errx(1, "the client json parser context wasn't re-initialized"); -+ } -+ -+ switch (evbuffer_drain_call_count) { -+ case 0: -+ if (len != sizeof(REQUEST_1) - 1) { -+ errx( -+ 1, "trying to drain %ju bytes (expected %ju)", -+ (uintmax_t)len, (uintmax_t)sizeof(REQUEST_1) - 1 -+ ); -+ } -+ break; -+ case 1: -+ if (len != sizeof(REQUEST_2) - 1) { -+ errx( -+ 1, "trying to drain %ju bytes (expected %ju)", -+ (uintmax_t)len, (uintmax_t)sizeof(REQUEST_2) - 1 -+ ); -+ } -+ break; -+ default: -+ errx(1, "evbuffer_drain_call_count = %d", evbuffer_drain_call_count); -+ break; -+ } -+ evbuffer_drain_call_count++; -+ -+ return 0; -+} -+ -+static int evbuffer_pullup_call_count = 0; -+ -+unsigned char * -+evbuffer_pullup(struct evbuffer *buf, ev_ssize_t size) -+{ -+ if (buf != (void *)2) { -+ errx(1, "got unexpected buf %p (expected %p)", buf, (void *)2); -+ } -+ -+ if (size != -1) { -+ errx(1, "got unexpected size %ld in pullup (expected -1)", size); -+ } -+ -+ int offset; -+ switch (evbuffer_pullup_call_count) { -+ case 0: -+ offset = 0; -+ break; -+ case 1: -+ offset = sizeof(REQUEST_1) - 1; -+ break; -+ default: -+ offset = sizeof(request); -+ break; -+ } -+ evbuffer_pullup_call_count++; -+ -+ return &request[offset]; -+} -+ -+static int evbuffer_get_length_call_count = 0; -+ -+size_t -+evbuffer_get_length(const struct evbuffer *buf) -+{ -+ if (buf != (void *)2) { -+ errx(1, "got unexpected buf %p (expected %p)", buf, (void *)2); -+ } -+ -+ size_t len; -+ switch (evbuffer_get_length_call_count) { -+ case 0: -+ len = sizeof(request) - 1; -+ break; -+ case 1: -+ len = sizeof(request) - sizeof(REQUEST_1); -+ break; -+ default: -+ len = 0; -+ break; -+ } -+ evbuffer_get_length_call_count++; -+ -+ return len; -+} -+ -+static int evbuffer_read_call_count = 0; -+ -+int -+evbuffer_read(struct evbuffer *buf, evutil_socket_t fd, int howmuch) -+{ -+ if (buf != (void *)2) { -+ errx(1, "got unexpected buf %p (expected %p)", buf, (void *)2); -+ } -+ -+ struct lgtd_command_pipe *pipe = SLIST_FIRST(&lgtd_command_pipes); -+ if (fd != pipe->fd) { -+ errx(1, "got unexpected fd %d (expected %d)", fd, pipe->fd); -+ } -+ -+ if (howmuch != -1) { -+ errx( -+ 1, "got unexpected howmuch bytes to read %d (expected -1)", howmuch -+ ); -+ } -+ -+ int rv = 0; -+ switch (evbuffer_read_call_count) { -+ case 0: -+ rv = sizeof(request) - 1; -+ default: -+ break; -+ } -+ evbuffer_read_call_count++; -+ -+ return rv; -+} -+ -+int -+main(void) -+{ -+ tmpdir = lgtd_tests_make_temp_dir(); -+ atexit(cleanup_tmpdir); -+ -+ char path[PATH_MAX] = { 0 }; -+ snprintf(path, sizeof(path), "%s/lightsd.pipe", tmpdir); -+ if (!lgtd_command_pipe_open(path)) { -+ errx(1, "couldn't open pipe"); -+ } -+ -+ struct lgtd_command_pipe *pipe = SLIST_FIRST(&lgtd_command_pipes); -+ -+ lgtd_command_pipe_read_callback(pipe->fd, EV_READ, pipe); -+ -+ return 0; -+} -diff --git a/tests/core/pipe/tests_pipe_utils.h b/tests/core/pipe/tests_pipe_utils.h -new file mode 100644 ---- /dev/null -+++ b/tests/core/pipe/tests_pipe_utils.h -@@ -0,0 +1,19 @@ -+#pragma once -+ -+#ifndef MOCKED_CLIENT_OPEN_FROM_PIPE -+void -+lgtd_client_open_from_pipe(struct lgtd_client *pipe_client) -+{ -+ memset(pipe_client, 0, sizeof(*pipe_client)); -+ jsmn_init(&pipe_client->jsmn_ctx); -+} -+#endif -+ -+#ifndef MOCKED_JSONRPC_DISPATCH_REQUEST -+void -+lgtd_jsonrpc_dispatch_request(struct lgtd_client *client, int parsed) -+{ -+ (void)client; -+ (void)parsed; -+} -+#endif -diff --git a/tests/core/tests_shims.c b/tests/core/tests_shims.c ---- a/tests/core/tests_shims.c -+++ b/tests/core/tests_shims.c -@@ -24,7 +24,7 @@ - .verbosity = LGTD_DEBUG - }; - --struct event_base *lgtd_ev_base = NULL; -+struct event_base *lgtd_ev_base = (void *)0x1234; - - const char *lgtd_binds = NULL; - -diff --git a/tests/core/tests_utils.c b/tests/core/tests_utils.c ---- a/tests/core/tests_utils.c -+++ b/tests/core/tests_utils.c -@@ -2,13 +2,18 @@ - #include <sys/tree.h> - #include <sys/socket.h> - #include <assert.h> -+#include <dirent.h> - #include <endian.h> -+#include <err.h> -+#include <limits.h> - #include <netinet/in.h> - #include <stdarg.h> - #include <stdbool.h> - #include <stdlib.h> - #include <stdint.h> -+#include <stdio.h> - #include <string.h> -+#include <unistd.h> - - #include <event2/util.h> - -@@ -131,3 +136,42 @@ - - return listener; - } -+ -+char * -+lgtd_tests_make_temp_dir(void) -+{ -+ char buf[PATH_MAX] = { 0 }; -+ int n = snprintf(buf, sizeof(buf), "%slightsd.test.XXXXXXXX", P_tmpdir); -+ if (n >= (int)sizeof(buf)) { -+ errx(1, "cannot allocate temporary directory"); -+ } -+ return strdup(mkdtemp(buf)); -+} -+ -+void -+lgtd_tests_remove_temp_dir(char *path) -+{ -+ DIR *tmpdir = opendir(path); -+ if (!tmpdir) { -+ return; -+ } -+ -+ struct dirent db; -+ struct dirent *entry = &db; -+ while (!readdir_r(tmpdir, entry, &entry) && entry) { -+ if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) { -+ continue; -+ } -+ char buf[PATH_MAX] = { 0 }; -+ snprintf(buf, sizeof(buf), "%s/%s", path, entry->d_name); -+ unlink(buf); -+ } -+ -+ closedir(tmpdir); -+ -+ if (rmdir(path)) { -+ warn("couldn't remove tempdir %s", path); -+ } -+ -+ free(path); -+} -diff --git a/tests/core/tests_utils.h b/tests/core/tests_utils.h ---- a/tests/core/tests_utils.h -+++ b/tests/core/tests_utils.h -@@ -30,6 +30,9 @@ - return true; - } - -+char *lgtd_tests_make_temp_dir(void); -+void lgtd_tests_remove_temp_dir(char *); -+ - struct lgtd_lifx_gateway *lgtd_tests_insert_mock_gateway(int); - struct lgtd_lifx_bulb *lgtd_tests_insert_mock_bulb(struct lgtd_lifx_gateway *, uint64_t); - struct lgtd_proto_target_list *lgtd_tests_build_target_list(const char *, ...);