==> tests/mock.c <==1/* crates/mesh-top tests/snap.rs mock_snapshot_round_trips, plus the control port spoken raw
2 * (the client's own tests drive it through the endpoint thread). */
3#include "mesh/check.h"
4#include "mesh/top_mock.h"
5
6#include <arpa/inet.h>
7#include <netinet/in.h>
8#include <stdlib.h>
9#include <string.h>
10#include <sys/socket.h>
11#include <unistd.h>
12
13static void round_trips(void) {
14 mt_mock_world w;
15 mt_mock_world_init(&w, "m", MESH_PEER_NONE);
16 mt_snapshot s = mt_mock_synth(&w);
17 mt_snapshot again;
18 char *err = NULL;
19 CHECK(mt_snapshot_parse(s.bytes, s.len, &again, &err));
20 const char *want[5] = {"doom", "train", "infer", "http", "hello"};
21 CHECK_U(again.nsections, 5);
22 for (size_t i = 0; i < 5 && i < again.nsections; i++) {
23 char name[3 * MESH_NAME_BYTES + 1];
24 mt_section_name(&again.sections[i], name);
25 CHECK_S(name, want[i]);
26 }
27 CHECK_U(mt_section_view_len(mt_snapshot_by_name(&again, "doom")), MESH_DOOM_SNAP_BYTES);
28 CHECK(memcmp(mt_section_view(mt_snapshot_by_name(&again, "hello")), "HELO", 4) == 0);
29 mt_snapshot_free(&again);
30 mt_snapshot_free(&s);
31 mt_mock_world_free(&w);
32}
33
34static int dial(const char *addr) {
35 const char *colon = strrchr(addr, ':');
36 struct sockaddr_in sa = {.sin_family = AF_INET, .sin_port = htons((uint16_t)atoi(colon + 1))};
37 inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr);
38 int fd = socket(AF_INET, SOCK_STREAM, 0);
39 if (connect(fd, (struct sockaddr *)&sa, sizeof sa) != 0) {
40 close(fd);
41 return -1;
42 }
43 return fd;
44}
45
46static bool read_n(int fd, uint8_t *b, size_t n) {
47 while (n) {
48 ssize_t k = recv(fd, b, n, 0);
49 if (k <= 0) return false;
50 b += k;
51 n -= (size_t)k;
52 }
53 return true;
54}
55
56static uint8_t *framed(int fd, const char *req, size_t *len) {
57 send(fd, req, strlen(req), 0);
58 uint8_t h[4];
59 if (!read_n(fd, h, 4)) return NULL;
60 *len = (size_t)h[0] | (size_t)h[1] << 8 | (size_t)h[2] << 16 | (size_t)h[3] << 24;
61 uint8_t *b = malloc(*len + 1);
62 if (!read_n(fd, b, *len)) {
63 free(b);
64 return NULL;
65 }
66 b[*len] = 0;
67 return b;
68}
69
70static void line(int fd, const char *req, char *out, size_t cap) {
71 send(fd, req, strlen(req), 0);
72 size_t n = 0;
73 while (n + 1 < cap) {
74 char c;
75 if (recv(fd, &c, 1, 0) != 1 || c == '\n') break;
76 out[n++] = c;
77 }
78 out[n] = 0;
79}
80
81static void server(void) {
82 mt_mock *m = mt_mock_start("127.0.0.1:0", "mock-a", 0);
83 CHECK(m != NULL);
84 if (!m) return;
85 int fd = dial(mt_mock_addr(m));
86 CHECK(fd >= 0);
87 size_t len;
88 uint8_t *b = framed(fd, "snap\n", &len);
89 CHECK(b != NULL);
90 mt_snapshot s;
91 char *err = NULL;
92 CHECK(b && mt_snapshot_parse(b, len, &s, &err));
93 if (b && !err) {
94 char host[97];
95 mt_snapshot_hostname(&s, host);
96 CHECK_S(host, "mock-a");
97 CHECK_U(s.nsections, 5);
98 mt_snapshot_free(&s);
99 }
100 free(b);
101 char t[128];
102 line(fd, "stop train\n", t, sizeof t);
103 CHECK_S(t, "ok");
104 b = framed(fd, "http /hi\n", &len);
105 CHECK(b && strncmp((char *)b, "HTTP/1.0 200 OK", 15) == 0);
106 free(b);
107 line(fd, "bogus\n", t, sizeof t);
108 CHECK(strncmp(t, "err", 3) == 0);
109 b = framed(fd, "catalog\n", &len);
110 CHECK(b != NULL);
111 if (b) {
112 char want[256] = "";
113 for (int i = 0; i < MT_MOCK_CATALOG_LEN; i++) strcat(strcat(want, MT_MOCK_CATALOG[i]), "\n");
114 CHECK_S((char *)b, want);
115 }
116 free(b);
117 b = framed(fd, "programs\n", &len);
118 CHECK(b && strstr((char *)b, "\n4 hello running 2 ") && strstr((char *)b, "fields hello magic last_arg last_result"));
119 free(b);
120 mt_mock_lock(m);
121 CHECK_U(mt_mock_prog_named(mt_mock_world_of(m), "train")->state, MESH_ST_LOADED);
122 mt_mock_unlock(m);
123 CHECK(mt_mock_saw(m, "http /hi"));
124 CHECK(!mt_mock_saw(m, "http /nope"));
125 mt_mock_stop(m);
126 char c;
127 CHECK(recv(fd, &c, 1, 0) <= 0); /* the server closed us */
128 close(fd);
129}
130
131int main(void) {
132 mt_clock_virtual(true, 1000000000000u);
133 round_trips();
134 server();
135 return mesh_check_done();
136}