Private
Public Access
Release 0.13.0
This commit is contained in:
1 parent
7f6eeab39e
commit
c0d891b372
49 files changed
+1523
-478
No files matched your search
@@ -0,0 +1,181 @@
|
||||
#define _DEFAULT_SOURCE
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <signal.h>
|
||||
#include <sys/wait.h>
|
||||
#include <time.h>
|
||||
#include "target.h"
|
||||
#include "layout.h"
|
||||
#include "receiver_output.h"
|
||||
static unsigned request_tan(const char *request) {
|
||||
const char *p = strstr(request, "\"tan\":");
|
||||
assert(p);
|
||||
return (unsigned)strtoul(p + 6, NULL, 10);
|
||||
}
|
||||
static void ack(int fd, unsigned id, bool layout) {
|
||||
char data[256];
|
||||
int n = snprintf(data, sizeof(data),
|
||||
layout ? "{\"success\":true,\"tan\":%u,\"info\":{\"leds\":[{\"hmin\":0,"
|
||||
"\"hmax\":1,\"vmin\":0,\"vmax\":0.1}]}}\n"
|
||||
: "{\"success\":true,\"tan\":%u}\n",
|
||||
id);
|
||||
assert(send(fd, data, 7, MSG_NOSIGNAL) == 7);
|
||||
usleep(1000);
|
||||
assert(send(fd, data + 7, n - 7, MSG_NOSIGNAL) == n - 7);
|
||||
}
|
||||
static void fixture(PlaybackJson *json, int mode) {
|
||||
int server = socket(AF_INET, SOCK_STREAM, 0);
|
||||
assert(server >= 0);
|
||||
struct sockaddr_in address = {.sin_family = AF_INET, .sin_addr = {htonl(INADDR_LOOPBACK)}};
|
||||
assert(!bind(server, (void *)&address, sizeof(address)) && !listen(server, 1));
|
||||
socklen_t size = sizeof(address);
|
||||
assert(!getsockname(server, (void *)&address, &size));
|
||||
TargetConfig config = target;
|
||||
config.port = ntohs(address.sin_port);
|
||||
strcpy(config.token, "fixture");
|
||||
pid_t child = fork();
|
||||
assert(child >= 0);
|
||||
if (!child) {
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
int fd = accept(server, NULL, NULL);
|
||||
assert(fd >= 0);
|
||||
FILE *input = fdopen(dup(fd), "r");
|
||||
char line[1024];
|
||||
assert(fgets(line, sizeof(line), input));
|
||||
assert(strstr(line, "authorize"));
|
||||
ack(fd, request_tan(line), false);
|
||||
assert(fgets(line, sizeof(line), input) && strstr(line, "serverinfo"));
|
||||
unsigned id = request_tan(line);
|
||||
if (mode == 1) {
|
||||
send(fd, "{\"success\":true", 15, MSG_NOSIGNAL);
|
||||
goto done;
|
||||
}
|
||||
if (mode == 2) {
|
||||
send(fd, "{} garbage\n", 11, MSG_NOSIGNAL);
|
||||
goto done;
|
||||
}
|
||||
if (mode == 3) {
|
||||
send(fd, "[]\n", 3, MSG_NOSIGNAL);
|
||||
goto done;
|
||||
}
|
||||
if (mode == 4) {
|
||||
char *huge = malloc(2 * 1024 * 1024 + 64);
|
||||
assert(huge);
|
||||
memset(huge, 'x', 2 * 1024 * 1024 + 64);
|
||||
size_t sent = 0;
|
||||
while (sent < 2 * 1024 * 1024 + 64) {
|
||||
ssize_t n = send(fd, huge + sent, 2 * 1024 * 1024 + 64 - sent, MSG_NOSIGNAL);
|
||||
if (n <= 0)
|
||||
break;
|
||||
sent += (size_t)n;
|
||||
}
|
||||
free(huge);
|
||||
goto done;
|
||||
}
|
||||
if (mode == 6) {
|
||||
const char bytes[] = "{}\0ignored\n";
|
||||
send(fd, bytes, sizeof(bytes) - 1, MSG_NOSIGNAL);
|
||||
goto done;
|
||||
}
|
||||
if (mode == 5) {
|
||||
char bad[128];
|
||||
int n = snprintf(bad, sizeof(bad), "{\"success\":\"true\",\"tan\":%u}\n", id);
|
||||
send(fd, bad, n, MSG_NOSIGNAL);
|
||||
goto done;
|
||||
}
|
||||
ack(fd, id, true);
|
||||
assert(fgets(line, sizeof(line), input) && strstr(line, "[1,2,3]"));
|
||||
id = request_tan(line);
|
||||
/* Unrelated/stale acknowledgements must not release the in-flight frame. */
|
||||
ack(fd, id + 99, false);
|
||||
usleep(50000);
|
||||
ack(fd, id, false);
|
||||
assert(fgets(line, sizeof(line), input) && strstr(line, "[7,8,9]"));
|
||||
ack(fd, request_tan(line), false);
|
||||
assert(fgets(line, sizeof(line), input) && strstr(line, "clear"));
|
||||
ack(fd, request_tan(line), false);
|
||||
done:
|
||||
usleep(20000);
|
||||
fclose(input);
|
||||
close(fd);
|
||||
close(server);
|
||||
_exit(0);
|
||||
}
|
||||
ReceiverOutput output = {.fd = -1};
|
||||
assert(output_begin(&output, &config, 1000));
|
||||
bool first = false, queued = false, stopping = false;
|
||||
unsigned iterations = 0;
|
||||
for (; iterations < 5000; iterations++) {
|
||||
output_tick(&output, json, 1000 + iterations);
|
||||
if (output.acknowledgements == 1 && queued && !stopping)
|
||||
assert(output.awaiting && !output.latest);
|
||||
if (output.stage == OUTPUT_FAILED || (stopping && output.stage == OUTPUT_IDLE))
|
||||
break;
|
||||
if (output.stage == OUTPUT_READY && !first) {
|
||||
first = true;
|
||||
assert(output_frame(&output, "{\"command\":\"color\",\"color\":[1,2,3]}\n"));
|
||||
} else if (first && output.awaiting && !queued) {
|
||||
queued = true;
|
||||
assert(output_frame(&output, "{\"command\":\"color\",\"color\":[4,5,6]}\n"));
|
||||
assert(output_frame(&output, "{\"command\":\"color\",\"color\":[7,8,9]}\n"));
|
||||
}
|
||||
if (output.acknowledgements == 2 && !stopping) {
|
||||
stopping = true;
|
||||
output_stop(&output, 1000 + iterations, true);
|
||||
}
|
||||
usleep(1000);
|
||||
}
|
||||
assert(iterations < 5000);
|
||||
if (mode)
|
||||
assert(output.stage == OUTPUT_FAILED);
|
||||
else
|
||||
assert(first && queued && stopping && output.stage == OUTPUT_IDLE);
|
||||
output_close(&output);
|
||||
int status;
|
||||
waitpid(child, &status, 0);
|
||||
assert(WIFEXITED(status) && !WEXITSTATUS(status));
|
||||
close(server);
|
||||
}
|
||||
int main(void) {
|
||||
(void)target_open;
|
||||
(void)target_parse;
|
||||
(void)target_load;
|
||||
(void)target_save;
|
||||
PlaybackJson json = {0};
|
||||
assert(playback_json_init(&json));
|
||||
for (int mode = 0; mode <= 6; mode++)
|
||||
fixture(&json, mode);
|
||||
ReceiverOutput output = {.fd = -1};
|
||||
int sockets[2];
|
||||
assert(!socketpair(AF_UNIX, SOCK_STREAM, 0, sockets));
|
||||
output.fd = sockets[0];
|
||||
output.stage = OUTPUT_READY;
|
||||
output.incoming = malloc(2 * 1024 * 1024 + 1);
|
||||
assert(output.incoming);
|
||||
assert(!fcntl(sockets[0], F_SETFL, O_NONBLOCK));
|
||||
char block[4096] = {0};
|
||||
while (send(sockets[0], block, sizeof(block), MSG_NOSIGNAL) > 0) {
|
||||
}
|
||||
assert(errno == EAGAIN || errno == EWOULDBLOCK);
|
||||
assert(output_frame(&output, "{\"command\":\"color\",\"color\":[0,0,0]}\n"));
|
||||
output_tick(&output, &json, 1000);
|
||||
assert(output.stage == OUTPUT_READY && output.request && output.sent == 0);
|
||||
output_tick(&output, &json, 4000);
|
||||
assert(output.stage == OUTPUT_FAILED);
|
||||
close(sockets[1]);
|
||||
output_close(&output);
|
||||
assert(!socketpair(AF_UNIX, SOCK_STREAM, 0, sockets));
|
||||
output.fd = sockets[0];
|
||||
output.stage = OUTPUT_READY;
|
||||
output.incoming = malloc(2 * 1024 * 1024 + 1);
|
||||
assert(output.incoming);
|
||||
close(sockets[1]);
|
||||
assert(output_frame(&output, "{\"command\":\"clear\"}\n"));
|
||||
output_tick(&output, &json, 5000);
|
||||
assert(output.stage == OUTPUT_FAILED);
|
||||
output_close(&output);
|
||||
dlclose(json.lib);
|
||||
puts("PASS: asynchronous login/layout, fragmented and unrelated replies, latest-frame "
|
||||
"coalescing, clear/stop, truncation, invalid JSON/types, oversized responses, send "
|
||||
"backpressure, timeout and disconnect");
|
||||
}
|
||||
Reference in new issue
Block a user