Pantheon/tests/test_tool_call_stream.c

602 lines
21 KiB
C

/* SPDX-License-Identifier: AGPL-3.0-or-later */
/* Copyright (C) 2026 David (Corey) Herrera */
/*
* Unit tests for incremental tool-call streaming:
* - json_canonical_stream.c: fed raw JSON in any split, its output is always a
* prefix of — and when complete, byte-identical to — jr_to_json(jr_parse(x)),
* and it rejects exactly what jr_parse rejects;
* - tool_call_stream.c: an ORNITH_JSON call opens on the wire as soon as its
* name is decided, its arguments stream while the model writes them, and
* the fragments concatenate to exactly the arguments the end-of-turn parse
* returns (repair, abandoned and multi-call cases included);
* - serve_emitter.c's tool tap: the streamer sees exactly the bytes that end
* up in tool_raw.
*
* The streaming-before-the-end assertions (early_*) are the ones that fail on
* the old behaviour, where nothing of a call left the serve until the turn
* ended. libc-only; no engine.
*/
#include "lib/test.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../src/serve/json_canonical_stream.h"
#include "../src/serve/json_reader.h"
#include "../src/serve/json_writer.h"
#include "../src/serve/serve_emitter.h"
#include "../src/serve/tool_call_stream.h"
#include "../src/serve/tool_calling.h"
/* ============================================================
* json_canonical_stream vs jr_parse + jr_to_json
* ============================================================ */
/* The DOM's canonical form of `doc`, or NULL when jr_parse rejects it. */
static char *dom_canonical(const char *doc)
{
jr_value *v = jr_parse(doc);
if (!v) return NULL;
json_writer jw;
jw_init(&jw);
jr_to_json(v, &jw);
jr_free(v);
return jw.data;
}
/* Feed `doc` in chunks of `chunk` bytes (0 = random sizes from `seed`).
* Returns 0 when jcs matches the DOM: same verdict, identical bytes when
* accepted, and every intermediate output a prefix of the final one. */
static int jcs_matches_dom(const char *doc, size_t chunk, unsigned seed)
{
char *want = dom_canonical(doc);
jcs_parser p;
if (!jcs_init(&p, NULL)) { free(want); return 1; }
size_t len = strlen(doc), at = 0;
int bad = 0;
while (at < len && p.status == JCS_RUNNING) {
size_t n = chunk;
if (!n) { seed = seed * 1103515245u + 12345u; n = 1 + (seed >> 16) % 7; }
if (n > len - at) n = len - at;
jcs_feed(&p, doc + at, n);
at += n;
if (want && (p.out.len > strlen(want) ||
memcmp(p.out.data, want, p.out.len) != 0)) { bad = 2; break; }
}
if (!bad) {
if (want) bad = (p.status == JCS_DONE && strcmp(p.out.data, want) == 0) ? 0 : 3;
else bad = (p.status == JCS_FAILED) ? 0 : 4;
}
if (bad)
printf(" jcs mismatch (%d) chunk=%zu doc=<<<%s>>> want=<<<%s>>> got=<<<%s>>>\n",
bad, chunk, doc, want ? want : "(rejected)", p.out.data);
jcs_free(&p);
free(want);
return bad;
}
static const char *const ACCEPTED_DOCS[] = {
"{}",
"{ }",
"[]",
"{\"a\":1}",
" {\r\n\t\"a\" :\t[ 1 , 2.50 ,-3e2, 1E+2, 0.1, -0.0, 12345678901234567890 ] } ",
"{\"n\":[true,false,null],\"o\":{\"x\":{}},\"e\":[]}",
"{\"s\":\"plain text with spaces\"}",
"{\"s\":\"esc \\\" \\\\ \\/ \\b \\f \\n \\r \\t end\"}",
"{\"s\":\"\\u00e9\\u4e2d\\ud83d\\ude00 ok\"}",
"{\"s\":\"lone high \\ud83d then text\"}",
"{\"s\":\"high then escape \\ud83d\\n\"}",
"{\"s\":\"high then quote \\ud83d\\\"\"}",
"{\"s\":\"high at end \\ud83d\"}",
"{\"s\":\"bad low \\ud83d\\u0041\"}",
"{\"s\":\"nul \\u0000 cut here\",\"t\":\"after\"}",
"{\"k\\u0000ey\":1}",
"{\"s\":\"raw\ttab and \x01 ctl\"}",
"{\"s\":\"raw utf8 \xc3\xa9 \xe4\xb8\xad\"}",
"{\"num\":+1,\"hex\":0x1A,\"dot\":.5,\"inf\":1e400,\"Inf\":inf}",
"{\"a\":{\"b\":{\"c\":[{\"d\":[[[]]]}]}}}",
"{\"function\": \"write\", \"arguments\": {\"path\": \"a.py\", \"content\": \"def f():\\n return {\\\"x\\\": [1, 2]}\\n\"}}",
"{\"dup\":1,\"dup\":2}",
"\"top string\"",
"[1,[2,[3]]]",
};
static const char *const REJECTED_DOCS[] = {
"{\"a\":1,}",
"{\"a\" 1}",
"[1,]",
"[1 2]",
"{\"a\":tru}",
"{\"a\":truex}",
"{\"a\":nan}",
"{\"a\":\"\\q\"}",
"{\"a\":\"\\u12g4\"}",
"{\"a\":\"\\ud83d\\u12\"}",
"{\"a\":1.5x}",
"{\"a\":-}",
"{a:1}",
"{\"a\":\"x\" \"y\"}",
"{\"a\":1}}", /* the root closes, the trailing '}' is ignored */
"{\"a\":}",
"{,}",
"{\"a\":[}",
"{\"a\":1 \"b\":2}",
"{\"a\"::1}",
};
static void test_jcs_matches_dom_fixed_docs(void)
{
for (size_t i = 0; i < sizeof(ACCEPTED_DOCS) / sizeof(*ACCEPTED_DOCS); i++) {
char *want = dom_canonical(ACCEPTED_DOCS[i]);
ASSERT_NOT_NULL(want); /* the corpus really is accepted */
free(want);
size_t len = strlen(ACCEPTED_DOCS[i]);
for (size_t chunk = 1; chunk <= len; chunk++)
ASSERT_INT_EQ(jcs_matches_dom(ACCEPTED_DOCS[i], chunk, 0), 0);
ASSERT_INT_EQ(jcs_matches_dom(ACCEPTED_DOCS[i], 0, (unsigned)i), 0);
}
}
/* "{\"a\":1}}" is accepted by jr_parse (it ignores trailing bytes) and by jcs
* (DONE at the root's close) — kept in the list to pin that both agree. */
static void test_jcs_rejects_what_dom_rejects(void)
{
for (size_t i = 0; i < sizeof(REJECTED_DOCS) / sizeof(*REJECTED_DOCS); i++) {
for (size_t chunk = 1; chunk <= 3; chunk++)
ASSERT_INT_EQ(jcs_matches_dom(REJECTED_DOCS[i], chunk, 0), 0);
ASSERT_INT_EQ(jcs_matches_dom(REJECTED_DOCS[i], 0, (unsigned)i), 0);
}
}
/* ---- generated documents ---- */
static unsigned rng_state = 12345;
static unsigned rng(void)
{
rng_state = rng_state * 1103515245u + 12345u;
return rng_state >> 16;
}
static void gen_ws(json_writer *jw)
{
static const char ws[] = " \t\n\r";
int n = (int)(rng() % 3);
for (int i = 0; i < n; i++) jw_raw_n(jw, &ws[rng() % 4], 1);
}
static void gen_string(json_writer *jw)
{
static const char *const pieces[] = {
"a", "Z", " ", "0", "\\n", "\\t", "\\\"", "\\\\", "\\/", "\\b", "\\f",
"\\r", "\\u00e9", "\\u4E2D", "\\ud83d\\ude00", "\\ud83d", "\\uDC00",
"\\u0000", "\xc3\xa9", "\t", "{", "}", "[", ":", ",", "\\u001f",
};
jw_raw(jw, "\"");
int n = (int)(rng() % 12);
for (int i = 0; i < n; i++)
jw_raw(jw, pieces[rng() % (sizeof(pieces) / sizeof(*pieces))]);
jw_raw(jw, "\"");
}
static void gen_value(json_writer *jw, int depth)
{
static const char *const scalars[] = {
"0", "-1", "1.5", "2e3", "-0.25E-2", "123456789012345678901",
"3.141592653589793", "true", "false", "null", "1e400", "+7",
};
unsigned kind = rng() % (depth > 3 ? 2 : 4);
gen_ws(jw);
if (kind == 0) {
jw_raw(jw, scalars[rng() % (sizeof(scalars) / sizeof(*scalars))]);
} else if (kind == 1) {
gen_string(jw);
} else if (kind == 2) {
jw_raw(jw, "[");
int n = (int)(rng() % 4);
for (int i = 0; i < n; i++) {
if (i) { gen_ws(jw); jw_raw(jw, ","); }
gen_value(jw, depth + 1);
gen_ws(jw);
}
if (!n) gen_ws(jw);
jw_raw(jw, "]");
} else {
jw_raw(jw, "{");
int n = (int)(rng() % 4);
for (int i = 0; i < n; i++) {
if (i) jw_raw(jw, ",");
gen_ws(jw);
gen_string(jw);
gen_ws(jw);
jw_raw(jw, ":");
gen_value(jw, depth + 1);
gen_ws(jw);
}
if (!n) gen_ws(jw);
jw_raw(jw, "}");
}
}
static void test_jcs_matches_dom_generated_docs(void)
{
for (int i = 0; i < 3000; i++) {
json_writer jw;
jw_init(&jw);
jw_raw(&jw, "{\"k\":");
gen_value(&jw, 0);
jw_raw(&jw, "}");
ASSERT_INT_EQ(jcs_matches_dom(jw.data, 0, (unsigned)i), 0);
ASSERT_INT_EQ(jcs_matches_dom(jw.data, 1, 0), 0);
jw_free(&jw);
}
}
/* ============================================================
* tool_call_stream
* ============================================================ */
#define REC_MAX_CALLS 8
/* What a client saw: per wire index, the id/name of the opening delta and the
* concatenated argument fragments, plus how far the feed had got when each
* event happened. */
typedef struct {
int n_begin;
int index[REC_MAX_CALLS];
char name[REC_MAX_CALLS][32];
json_writer args[REC_MAX_CALLS];
int fragments[REC_MAX_CALLS];
size_t fed; /* bytes fed so far (set by the driver) */
size_t fed_at_begin[REC_MAX_CALLS];
size_t fed_at_first_fragment[REC_MAX_CALLS];
int progress;
} recorder;
static int rec_slot(recorder *r, int index)
{
for (int i = 0; i < r->n_begin; i++)
if (r->index[i] == index) return i;
return -1;
}
static void rec_begin(void *ud, int index, const char *id, const char *name)
{
recorder *r = ud;
(void)id;
if (r->n_begin >= REC_MAX_CALLS) return;
int s = r->n_begin++;
r->index[s] = index;
snprintf(r->name[s], sizeof(r->name[s]), "%s", name);
jw_init(&r->args[s]);
r->fed_at_begin[s] = r->fed;
r->fed_at_first_fragment[s] = (size_t)-1;
}
static void rec_args(void *ud, int index, const char *bytes, size_t n)
{
recorder *r = ud;
int s = rec_slot(r, index);
if (s < 0) return;
if (!r->fragments[s]) r->fed_at_first_fragment[s] = r->fed;
r->fragments[s]++;
jw_raw_n(&r->args[s], bytes, n);
}
static void rec_progress(void *ud) { ((recorder *)ud)->progress++; }
static void rec_free(recorder *r)
{
for (int i = 0; i < r->n_begin; i++) jw_free(&r->args[i]);
}
static const char TOOLS_JSON[] =
"[{\"type\":\"function\",\"function\":{\"name\":\"write\",\"parameters\":"
"{\"type\":\"object\",\"properties\":{\"path\":{\"type\":\"string\"},"
"\"content\":{\"type\":\"string\"}},\"required\":[\"path\",\"content\"]}}},"
"{\"type\":\"function\",\"function\":{\"name\":\"read\",\"parameters\":"
"{\"type\":\"object\",\"properties\":{\"path\":{\"type\":\"string\"}}}}}]";
/* Stream `text` through a streamer in `piece`-byte feeds, finish it against the
* end-of-turn parse (with repair, as collect_tool_calls runs it), and leave the
* wire record in `r`. Returns the number of calls the parse produced. */
static int stream_text(recorder *r, const char *text, size_t piece,
tc_dialect dialect, tc_tool_call **calls_out)
{
jr_value *tools = jr_parse(TOOLS_JSON);
tcs_streamer ts;
tcs_sink sink = { rec_begin, rec_args, rec_progress, r };
memset(r, 0, sizeof(*r));
tcs_init(&ts, dialect, tools, &sink);
size_t len = strlen(text);
for (size_t at = 0; at < len; at += piece) {
size_t n = piece < len - at ? piece : len - at;
r->fed = at + n;
tcs_feed(&ts, text + at, n);
}
tc_tool_call *calls = NULL;
int n = 0;
tc_parse_repair repair = {0};
tc_parse_tool_calls_diagnosed(text, tools, dialect, &calls, &n, NULL, &repair);
tc_parse_repair_free(&repair);
tcs_finish(&ts, calls, n);
tcs_free(&ts);
jr_free(tools);
*calls_out = calls;
return n;
}
/* The wire contract: the calls whose streamed arguments are valid JSON are
* exactly the parsed calls, in order, byte for byte; every other wire call
* carries arguments no client can parse. */
static int wire_matches_parse(const recorder *r, const tc_tool_call *calls, int n)
{
int j = 0;
for (int s = 0; s < r->n_begin; s++) {
jr_value *v = jr_parse(r->args[s].data);
bool valid = v && jr_kind(v) == JR_OBJECT;
jr_free(v);
if (!valid) continue;
if (j >= n) { printf(" extra valid wire call %d\n", r->index[s]); return 1; }
if (strcmp(r->name[s], calls[j].name) != 0 ||
strcmp(r->args[s].data, calls[j].arguments) != 0) {
printf(" wire call %d != parsed call %d\n wire <<<%s>>>\n"
" parse <<<%s>>>\n", r->index[s], j, r->args[s].data,
calls[j].arguments);
return 2;
}
j++;
}
if (j != n) { printf(" %d parsed calls, %d on the wire\n", n, j); return 3; }
return 0;
}
static const char LONG_CALL[] =
"```json\n"
"{\n \"function\": \"write\",\n \"arguments\": {\n"
" \"path\": \"inventory.py\",\n"
" \"content\": \"\\\"\\\"\\\"Inventory.\\\"\\\"\\\"\\n\\nimport csv\\n\\n"
"class Product:\\n def __init__(self, sku, qty):\\n self.sku = sku\\n"
" self.qty = qty\\n\\n def restock(self, n):\\n"
" if n <= 0:\\n raise ValueError(\\\"n must be > 0\\\")\\n"
" self.qty += n\\n\\n\\u00e9t\\u00e9 \\ud83d\\ude00 done\\n\"\n"
" }\n}\n```";
static void test_early_call_opens_before_the_object_closes(void)
{
recorder r;
tc_tool_call *calls;
int n = stream_text(&r, LONG_CALL, 3, TC_DIALECT_ORNITH_JSON, &calls);
ASSERT_INT_EQ(n, 1);
ASSERT_INT_EQ(r.n_begin, 1);
ASSERT_STR_EQ(r.name[0], "write");
/* Opened right after the name, long before the call ends. */
const char *after_name = strstr(LONG_CALL, "\"write\"") + strlen("\"write\"");
ASSERT_TRUE(r.fed_at_begin[0] <= (size_t)(after_name - LONG_CALL) + 3);
ASSERT_INT_EQ(wire_matches_parse(&r, calls, n), 0);
tc_tool_calls_free(calls, n);
rec_free(&r);
}
static void test_early_arguments_stream_while_written(void)
{
recorder r;
tc_tool_call *calls;
int n = stream_text(&r, LONG_CALL, 4, TC_DIALECT_ORNITH_JSON, &calls);
ASSERT_INT_EQ(n, 1);
/* Many fragments, the first long before the text ends. */
ASSERT_TRUE(r.fragments[0] > 20);
const char *content = strstr(LONG_CALL, "\"content\"");
ASSERT_TRUE(r.fed_at_first_fragment[0] < (size_t)(content - LONG_CALL) + 16);
ASSERT_INT_EQ(wire_matches_parse(&r, calls, n), 0);
tc_tool_calls_free(calls, n);
rec_free(&r);
}
/* Every feed size, token-like and byte-at-a-time: the bytes on the wire are the
* parse's bytes. */
static void test_fragments_concatenate_to_parsed_arguments(void)
{
for (size_t piece = 1; piece <= 13; piece++) {
recorder r;
tc_tool_call *calls;
int n = stream_text(&r, LONG_CALL, piece, TC_DIALECT_ORNITH_JSON, &calls);
ASSERT_INT_EQ(n, 1);
ASSERT_INT_EQ(wire_matches_parse(&r, calls, n), 0);
ASSERT_TRUE(r.progress > 0);
tc_tool_calls_free(calls, n);
rec_free(&r);
}
}
static void test_two_calls_two_indices(void)
{
const char *text =
"```json\n{\"function\":\"read\",\"arguments\":{\"path\":\"a\"}}\n```\n"
"```json\n{\"function\":\"write\",\"arguments\":{\"path\":\"b\",\"content\":\"x\"}}\n```";
recorder r;
tc_tool_call *calls;
int n = stream_text(&r, text, 2, TC_DIALECT_ORNITH_JSON, &calls);
ASSERT_INT_EQ(n, 2);
ASSERT_INT_EQ(r.n_begin, 2);
ASSERT_INT_EQ(r.index[0], 0);
ASSERT_INT_EQ(r.index[1], 1);
ASSERT_INT_EQ(wire_matches_parse(&r, calls, n), 0);
tc_tool_calls_free(calls, n);
rec_free(&r);
}
/* "name" can be outranked by a later "function" key, so it is held to the
* object's close — then sent whole, still before the turn ends. */
static void test_name_spelling_held_to_object_close(void)
{
const char *text =
"{\"name\":\"write\",\"arguments\":{\"path\":\"p\",\"content\":\"long\"}}\ntrailing";
recorder r;
tc_tool_call *calls;
int n = stream_text(&r, text, 1, TC_DIALECT_ORNITH_JSON, &calls);
ASSERT_INT_EQ(n, 1);
ASSERT_INT_EQ(r.n_begin, 1);
size_t close_at = (size_t)(strchr(text, '\n') - text);
ASSERT_INT_EQ((int)r.fed_at_begin[0], (int)close_at);
ASSERT_INT_EQ(wire_matches_parse(&r, calls, n), 0);
tc_tool_calls_free(calls, n);
rec_free(&r);
}
static void test_arguments_before_function_key(void)
{
const char *text =
"{\"arguments\":{\"path\":\"p\",\"content\":\"c\"},\"function\":\"write\"}";
recorder r;
tc_tool_call *calls;
int n = stream_text(&r, text, 1, TC_DIALECT_ORNITH_JSON, &calls);
ASSERT_INT_EQ(n, 1);
ASSERT_INT_EQ(wire_matches_parse(&r, calls, n), 0);
tc_tool_calls_free(calls, n);
rec_free(&r);
}
static void test_unknown_tool_never_opens(void)
{
const char *text = "{\"function\":\"rm_rf\",\"arguments\":{\"path\":\"/\"}}";
recorder r;
tc_tool_call *calls;
int n = stream_text(&r, text, 1, TC_DIALECT_ORNITH_JSON, &calls);
ASSERT_INT_EQ(n, 0);
ASSERT_INT_EQ(r.n_begin, 0);
tc_tool_calls_free(calls, n);
rec_free(&r);
}
/* Cut off mid-string (max_tokens): the parse repairs it, and the stream owes
* exactly the repaired tail. */
static void test_truncated_call_repaired(void)
{
const char *text =
"```json\n{\"function\":\"write\",\"arguments\":{\"path\":\"p\",\"content\":\"abc\\ndef";
recorder r;
tc_tool_call *calls;
int n = stream_text(&r, text, 2, TC_DIALECT_ORNITH_JSON, &calls);
ASSERT_INT_EQ(n, 1);
ASSERT_STR_EQ(calls[0].arguments, "{\"path\":\"p\",\"content\":\"abc\\ndef\"}");
ASSERT_INT_EQ(wire_matches_parse(&r, calls, n), 0);
tc_tool_calls_free(calls, n);
rec_free(&r);
}
/* Cut off where no repair is possible: the call was opened on the wire, the
* parse has none — its streamed arguments must not be valid JSON. */
static void test_truncated_unrepairable_left_unparseable(void)
{
const char *text = "{\"function\":\"write\",\"arguments\":{\"path\":";
recorder r;
tc_tool_call *calls;
int n = stream_text(&r, text, 1, TC_DIALECT_ORNITH_JSON, &calls);
ASSERT_INT_EQ(n, 0);
ASSERT_INT_EQ(r.n_begin, 1);
ASSERT_INT_EQ(wire_matches_parse(&r, calls, n), 0);
tc_tool_calls_free(calls, n);
rec_free(&r);
}
/* Arguments complete, then the object goes bad: the held final '}' is what
* keeps the abandoned call from looking runnable. */
static void test_malformed_after_arguments_left_unparseable(void)
{
const char *text =
"{\"function\":\"write\",\"arguments\":{\"path\":\"p\",\"content\":\"c\"},\"x\":tru}"
"\n{\"function\":\"read\",\"arguments\":{\"path\":\"q\"}}";
recorder r;
tc_tool_call *calls;
int n = stream_text(&r, text, 1, TC_DIALECT_ORNITH_JSON, &calls);
ASSERT_INT_EQ(n, 1);
ASSERT_STR_EQ(calls[0].name, "read");
ASSERT_INT_EQ(r.n_begin, 2);
ASSERT_STR_EQ(r.args[0].data, "{\"path\":\"p\",\"content\":\"c\"");
ASSERT_INT_EQ(wire_matches_parse(&r, calls, n), 0);
tc_tool_calls_free(calls, n);
rec_free(&r);
}
/* XML dialect: nothing streams early (values are typed and may be rewritten by
* recovery), progress still fires for the keepalive, and the end sends whole
* calls at indices 0.. exactly as before. */
static void test_xml_dialect_sent_whole_at_finish(void)
{
const char *text =
"<tool_call>\n<function=write>\n<parameter=path>\na.txt\n</parameter>\n"
"<parameter=content>\nhello\n</parameter>\n</function>\n</tool_call>";
recorder r;
tc_tool_call *calls;
size_t len = strlen(text);
int n = stream_text(&r, text, 5, TC_DIALECT_QWEN_XML, &calls);
ASSERT_INT_EQ(n, 1);
ASSERT_INT_EQ(r.n_begin, 1);
ASSERT_INT_EQ((int)r.fed_at_begin[0], (int)len); /* only at finish */
ASSERT_INT_EQ(r.index[0], 0);
ASSERT_INT_EQ(r.fragments[0], 1);
ASSERT_TRUE(r.progress >= (int)(len / 5));
ASSERT_INT_EQ(wire_matches_parse(&r, calls, n), 0);
tc_tool_calls_free(calls, n);
rec_free(&r);
}
/* ============================================================
* Emitter tap: the streamer sees exactly tool_raw.
* ============================================================ */
typedef struct { json_writer seen; } tap_sink;
static void tap_collect(const char *bytes, size_t n, void *ud)
{
jw_raw_n(&((tap_sink *)ud)->seen, bytes, n);
}
static void test_emitter_tap_sees_exactly_tool_raw(void)
{
static const char *const pieces[] = {
"Let me write it", ".\n\n``", "`js", "on\n{\"func", "tion\": \"wr", "ite\", ",
"\"arguments\": {\"path\": \"x\", \"content\": \"a\\nb\"}}\n``", "`",
};
tap_sink t;
jw_init(&t.seen);
emitter em;
em_init(&em, NULL, NULL, NULL, 0, true, TC_DIALECT_ORNITH_JSON);
em_set_tool_tap(&em, tap_collect, &t);
em_feed(&em, "</think>");
for (size_t i = 0; i < sizeof(pieces) / sizeof(*pieces); i++)
em_feed(&em, pieces[i]);
em_flush(&em);
ASSERT_NOT_NULL(em.tool.data);
ASSERT_STR_EQ(t.seen.data, em.tool.data);
ASSERT_TRUE(strncmp(em.tool.data, "```json", 7) == 0);
em_free(&em);
jw_free(&t.seen);
}
int main(void)
{
printf("\n tool-call streaming\n\n");
RUN_TEST(jcs_matches_dom_fixed_docs);
RUN_TEST(jcs_rejects_what_dom_rejects);
RUN_TEST(jcs_matches_dom_generated_docs);
RUN_TEST(early_call_opens_before_the_object_closes);
RUN_TEST(early_arguments_stream_while_written);
RUN_TEST(fragments_concatenate_to_parsed_arguments);
RUN_TEST(two_calls_two_indices);
RUN_TEST(name_spelling_held_to_object_close);
RUN_TEST(arguments_before_function_key);
RUN_TEST(unknown_tool_never_opens);
RUN_TEST(truncated_call_repaired);
RUN_TEST(truncated_unrepairable_left_unparseable);
RUN_TEST(malformed_after_arguments_left_unparseable);
RUN_TEST(xml_dialect_sent_whole_at_finish);
RUN_TEST(emitter_tap_sees_exactly_tool_raw);
TEST_REPORT();
return __ft_failed == 0 ? 0 : 1;
}