Viewing is open to everyone. Downloading and starring need a GitHub sign-in. Sign in with GitHub

bridge_test.cpp

v0.1.0-build.85 Latest Commit ede38f5

Size
11 KB
Lines
228
Language
cpp
SHA-256
5e2ce99e772f…
View on GitHub

Sign in with GitHub to download. Each download is recorded with your GitHub username.

1// Native test of the Web SDK bridge (sdk/web/src/ckey_web.cpp), compiled with
2// the system C++ compiler against the same core sources the wasm build uses.
3// It exercises every function the JavaScript wrapper calls, so the bridge
4// logic is verified even where Emscripten is not installed.
5// scripts/test-native.sh (or see .github/workflows/sdk-web.yml)
6
7#include "ckey/ckey.h"
8#include "ckey/telemetry.h"
9
10#include <cstdint>
11#include <cstdio>
12#include <cstdlib>
13#include <cstring>
14#include <string>
15#include <vector>
16
17extern "C" {
18void ckw_free(void *);
19const char *ckw_last_error(void);
20uint32_t ckw_last_len(void);
21const char *ckw_software_version(void);
22uint32_t ckw_scalable_capacity(uint32_t, uint32_t, uint32_t);
23void *ckw_encode(const uint8_t *, uint32_t, uint32_t, uint32_t, uint32_t);
24void ckw_symbol_free(void *);
25void ckw_symbol_info(const void *, uint32_t *);
26void *ckw_style_new(uint32_t);
27void ckw_style_free(void *);
28int ckw_style_set_rgb(void *, uint32_t, uint32_t, uint32_t, uint32_t);
29int ckw_style_set_num(void *, uint32_t, double);
30int ckw_style_check(const void *, const void *);
31uint8_t *ckw_render_raster(const void *, const void *, uint32_t, uint32_t, uint32_t, uint32_t, double, uint32_t,
32 uint32_t, const uint8_t *, uint32_t, uint32_t);
33uint8_t *ckw_encode_png_rgba(const uint8_t *, uint32_t, uint32_t, double);
34char *ckw_render_svg(const void *, const void *, uint32_t);
35uint8_t *ckw_render_pdf(const void *, const void *, double, uint32_t);
36uint8_t *ckw_decode_rgba(const uint8_t *, uint32_t, uint32_t, uint32_t, uint32_t *);
37uint8_t *ckw_decode_gray(const uint8_t *, uint32_t, uint32_t, uint32_t, uint32_t *);
38char *ckw_safe_action(const uint8_t *, uint32_t, const uint8_t *, double, uint32_t);
39int ckw_tel_set_mode(int);
40int ckw_tel_get_mode(void);
41int ckw_tel_set_client(const char *, const char *, const char *);
42void ckw_tel_session_begin(double);
43void ckw_tel_frame(double, double);
44int ckw_tel_session_success(double, uint32_t);
45void ckw_tel_session_end(double);
46int ckw_tel_should_send(double);
47char *ckw_tel_take_batch(const char *);
48double ckw_tel_last_batch_id(void);
49int ckw_tel_batch_result(double, int, double);
50int ckw_tel_snapshot(double *);
51void ckw_tel_reset_for_testing(void);
52uint32_t ckw_has_certify(void);
53int ckw_certify(const void *, const void *, uint32_t, const uint8_t *, uint32_t, const uint8_t *, uint32_t, uint32_t,
54 double, double, double, double, uint32_t, double *, void *);
55}
56
57static int failures = 0;
58#define CHECK(c) \
59 do { \
60 if (!(c)) { std::fprintf(stderr, "FAIL %s:%d: %s\n", __FILE__, __LINE__, #c); ++failures; } \
61 } while (0)
62
63// Places an RGBA render in the middle of a larger white frame, like a photo.
64static std::vector<uint8_t> pad(const uint8_t *px, uint32_t size, uint32_t margin, uint32_t &side) {
65 side = size + 2 * margin;
66 std::vector<uint8_t> f(static_cast<size_t>(side) * side * 4, 255);
67 for (uint32_t y = 0; y < size; ++y)
68 std::memcpy(&f[(static_cast<size_t>(y + margin) * side + margin) * 4], px + static_cast<size_t>(y) * size * 4, size * 4);
69 return f;
70}
71
72static bool roundtrip(const std::string &text, uint32_t layout, uint32_t transparent, const void *style) {
73 void *sym = ckw_encode(reinterpret_cast<const uint8_t *>(text.data()), static_cast<uint32_t>(text.size()), 0, 1, layout);
74 if (!sym) { std::fprintf(stderr, "encode: %s\n", ckw_last_error()); return false; }
75 uint8_t *px = ckw_render_raster(sym, style, 512, 0, transparent, 4, 1.0, 1, 0, nullptr, 0, 0);
76 if (!px) { std::fprintf(stderr, "raster: %s\n", ckw_last_error()); ckw_symbol_free(sym); return false; }
77 uint32_t side = 0;
78 auto frame = pad(px, 512, 64, side);
79 uint32_t info[4] = {9, 9, 9, 9};
80 uint8_t *out = ckw_decode_rgba(frame.data(), side, side, 0, info);
81 bool ok = out && ckw_last_len() == text.size() && std::memcmp(out, text.data(), text.size()) == 0 &&
82 info[0] == (layout ? 1u : 0u) && info[2] == layout && info[1] >= 1;
83 if (!out) std::fprintf(stderr, "decode: %s (borders %u)\n", ckw_last_error(), info[3]);
84 ckw_free(out);
85 ckw_free(px);
86 ckw_symbol_free(sym);
87 return ok;
88}
89
90int main() {
91 CHECK(std::strcmp(ckw_software_version(), CKEY_SOFTWARE_VERSION) == 0);
92
93 // Encode + info.
94 const std::string url = "https://openckey.org";
95 void *sym = ckw_encode(reinterpret_cast<const uint8_t *>(url.data()), static_cast<uint32_t>(url.size()), 0, 1, 0);
96 CHECK(sym != nullptr);
97 uint32_t info[5] = {};
98 ckw_symbol_info(sym, info);
99 CHECK(info[0] >= 1 && info[0] <= 31);
100 CHECK(info[1] == 1 && info[2] == 0 && info[3] == url.size());
101 CHECK(ckw_scalable_capacity(info[0], 1, 0) >= url.size());
102
103 // Encode failure carries the core's message.
104 std::vector<uint8_t> big(20000, 'x');
105 CHECK(ckw_encode(big.data(), static_cast<uint32_t>(big.size()), 0, 1, 0) == nullptr);
106 CHECK(std::strlen(ckw_last_error()) > 0);
107 std::printf("oversize message: %s\n", ckw_last_error());
108
109 // Round trips through the reader pipeline (standard, print, transparent, concept).
110 CHECK(roundtrip(url, 0, 0, nullptr));
111 CHECK(roundtrip("Hello, CKey! Grüße", 1, 0, nullptr));
112 CHECK(roundtrip(url, 0, 1, nullptr));
113 void *concept_style = ckw_style_new(1);
114 CHECK(concept_style != nullptr);
115 CHECK(roundtrip(url, 0, 0, concept_style));
116
117 // Transparent corner has alpha 0; opaque corner is white.
118 uint8_t *px = ckw_render_raster(sym, nullptr, 64, 0, 1, 4, 1.0, 1, 0, nullptr, 0, 0);
119 CHECK(px && ckw_last_len() == 64u * 64u * 4u && px[3] == 0);
120 ckw_free(px);
121 px = ckw_render_raster(sym, nullptr, 64, 1, 0, 4, 1.0, 1, 0, nullptr, 0, 0);
122 CHECK(px && px[0] == 255 && px[3] == 255);
123 uint8_t *png = ckw_encode_png_rgba(px, 64, 64, 0);
124 CHECK(png && ckw_last_len() > 8 && std::memcmp(png, "\x89PNG\r\n\x1a\n", 8) == 0);
125 ckw_free(png);
126 ckw_free(px);
127 CHECK(ckw_render_raster(sym, nullptr, 8, 0, 0, 4, 1.0, 1, 0, nullptr, 0, 0) == nullptr); // size < 16
128
129 char *svg = ckw_render_svg(sym, nullptr, 16);
130 CHECK(svg && std::strncmp(svg, "<svg", 4) == 0 && std::strlen(svg) == ckw_last_len());
131 ckw_free(svg);
132 uint8_t *pdf = ckw_render_pdf(sym, nullptr, 144, 0);
133 CHECK(pdf && std::memcmp(pdf, "%PDF-", 5) == 0);
134 ckw_free(pdf);
135
136 // A low-contrast style is refused with an explanation.
137 void *bad = ckw_style_new(0);
138 CHECK(ckw_style_set_rgb(bad, 2, 250, 250, 250) == 1);
139 CHECK(ckw_style_set_rgb(bad, 3, 250, 250, 250) == 1);
140 CHECK(ckw_style_check(sym, bad) != 0);
141 std::printf("bad style message: %s\n", ckw_last_error());
142 CHECK(ckw_render_svg(sym, bad, 16) == nullptr);
143 CHECK(ckw_style_set_num(bad, 2, 7) == 0);
144#if CKEY_ABI_VERSION >= 4
145 // Override draws the refused style as given; the check still reports it.
146 CHECK(ckw_style_set_num(bad, 10, 2) == 1);
147 char *over = ckw_render_svg(sym, bad, 16);
148 CHECK(over != nullptr);
149 ckw_free(over);
150 CHECK(ckw_style_check(sym, bad) == 0 && std::strncmp(ckw_last_error(), "override", 8) == 0);
151 CHECK(ckw_style_set_num(bad, 10, 0) == 1);
152#endif
153 if (ckw_has_certify()) {
154 double cert[8] = {};
155 CHECK(ckw_certify(sym, nullptr, 0, nullptr, 0, nullptr, 0, 0, -1, -1, -1, -1, 0, cert, nullptr) == 0);
156 CHECK(cert[0] == 1 && cert[3] >= 1);
157 std::printf("certify default: certified=%g trials=%g/%g msg=%s\n", cert[0], cert[4], cert[3], ckw_last_error());
158 // Transparent with no backdrop: background unknown, never certified.
159 CHECK(ckw_certify(sym, nullptr, 1, nullptr, 0, nullptr, 0, 0, -1, -1, -1, -1, 0, cert, nullptr) != 0 || cert[0] == 0);
160 std::printf("certify transparent/no backdrop: certified=%g msg=%s\n", cert[0], ckw_last_error());
161 const uint8_t grey[3] = {40, 40, 40};
162 ckw_certify(sym, nullptr, 1, grey, 1, nullptr, 0, 0, -1, -1, -1, -1, 0, cert, nullptr);
163 std::printf("certify transparent on dark grey: certified=%g msg=%s\n", cert[0], ckw_last_error());
164 }
165 ckw_style_free(bad);
166 CHECK(ckw_style_new(9) == nullptr);
167 ckw_style_free(concept_style);
168
169 // Blank frame: nothing decoded.
170 std::vector<uint8_t> blank(100 * 100, 255);
171 uint32_t dinfo[4];
172 CHECK(ckw_decode_gray(blank.data(), 100, 100, 0, dinfo) == nullptr);
173
174 // Telemetry wrappers.
175 ckw_tel_reset_for_testing();
176 CHECK(ckw_tel_get_mode() == 1);
177 CHECK(ckw_tel_set_mode(3) != 0 && ckw_tel_get_mode() == 1);
178 CHECK(ckw_tel_set_client("web", CKEY_SOFTWARE_VERSION, "web") == 0);
179 ckw_tel_session_begin(1000);
180 ckw_tel_frame(1010, 7.6);
181 CHECK(ckw_tel_session_success(1020, 0) == 0);
182 ckw_tel_session_end(1030);
183 double snap[33] = {};
184 CHECK(ckw_tel_snapshot(snap) == 0);
185 CHECK(snap[0] == 1 && snap[1] == 1 && snap[3] == 1 && snap[4] == 1);
186 CHECK(ckw_tel_should_send(2000) == 0); // delta younger than a minute
187 CHECK(ckw_tel_should_send(1000 + 60000) != 0);
188 char *json = ckw_tel_take_batch("2026-11-05");
189 CHECK(json && std::strstr(json, "\"sdk\":\"web\"") && std::strstr(json, "\"platform\":\"web\""));
190 const double id = ckw_tel_last_batch_id();
191 CHECK(id > 0);
192 std::printf("batch: %s\n", json ? json : "(null)");
193 ckw_free(json);
194 CHECK(ckw_tel_take_batch("2026-11-05") == nullptr); // one in flight
195 CHECK(ckw_tel_batch_result(id, 204, 62000) == 0);
196 CHECK(ckw_tel_batch_result(id, 204, 62000) != 0); // reported once
197 ckw_tel_reset_for_testing();
198
199 { // Safe Actions: sign with the core, then parse and verify through the bridge.
200 uint8_t seed[32] = {0x9d, 0x61, 0xb1, 0x9d, 0xef, 0xfd, 0x5a, 0x60, 0xba, 0x84, 0x4a, 0xf4, 0x92, 0xec, 0x2c, 0xc4,
201 0x44, 0x49, 0xc5, 0x69, 0x7b, 0x32, 0x69, 0x19, 0x70, 0x3b, 0xac, 0x03, 0x1c, 0xae, 0x7f, 0x60};
202 uint8_t pk[32];
203 ckey_ed25519_public_key(seed, pk);
204 ckey_safe_action f{};
205 f.scope = CKEY_SCOPE_SAME_DEVICE;
206 std::strcpy(f.issuer, "citeory.com"); std::strcpy(f.app, "com.viadere"); std::strcpy(f.action, "open.library");
207 std::strcpy(f.key_id, "k1"); std::strcpy(f.request_id, "req-12345678");
208 f.issued_at = 1000; f.expires_at = 1100; f.params[0] = 'h'; f.params[1] = 'i'; f.params_len = 2;
209 char text[1024]; size_t n = 0; ckey_diagnostics diag{};
210 CHECK(ckey_safe_action_sign(&f, seed, text, sizeof text, &n, &diag) == CKEY_OK);
211 const auto *t = reinterpret_cast<const uint8_t *>(text);
212 char *parsed = ckw_safe_action(t, static_cast<uint32_t>(n), nullptr, 0, 0);
213 CHECK(parsed && std::strstr(parsed, "\"action\":\"open.library\"") && std::strstr(parsed, "\"params\":\"6869\""));
214 ckw_free(parsed);
215 char *ok = ckw_safe_action(t, static_cast<uint32_t>(n), pk, 1050, 600);
216 CHECK(ok && std::strstr(ok, "\"request_id\":\"req-12345678\"") && std::strstr(ok, "\"scope\":0"));
217 ckw_free(ok);
218 CHECK(ckw_safe_action(t, static_cast<uint32_t>(n), pk, 1200, 600) == nullptr);
219 CHECK(std::strstr(ckw_last_error(), "expired") != nullptr);
220 uint8_t other[32] = {1};
221 CHECK(ckw_safe_action(t, static_cast<uint32_t>(n), other, 1050, 600) == nullptr);
222 }
223
224 ckw_symbol_free(sym);
225 if (failures) { std::fprintf(stderr, "%d failure(s)\n", failures); return 1; }
226 std::printf("bridge native tests passed\n");
227 return 0;
228}