diff infinite_canvas/canvas_test.c @ 280:49e9e591c9bb

Add persistent dictation, prewarmed WebRTC speech input, Copilot SDK routing, animated conversation lifecycle controls, parking, and architecture coverage.
author MrJuneJune <me@mrjunejune.com>
date Tue, 18 Aug 2026 19:14:53 -0700
parents 8d560f50ed4c
children c57149ad216e
line wrap: on
line diff
--- a/infinite_canvas/canvas_test.c	Mon Aug 17 22:22:36 2026 -0700
+++ b/infinite_canvas/canvas_test.c	Tue Aug 18 19:14:53 2026 -0700
@@ -526,6 +526,133 @@
     Dowa_Arena_Free(p_arena);
 }
 
+static void Test_Dictation_Scratchpad_Is_Not_A_Routing_Target(void)
+{
+    Dowa_Arena *p_arena = Dowa_Arena_Create(ONE_MEGA_BYTE);
+    assert(p_arena);
+
+    Canvas_Camera camera;
+    Canvas_Camera_Init(&camera, 800, 600);
+    Canvas_Scene scene;
+    Canvas_Scene_Init(&scene, p_arena);
+    assert(Canvas_Scene_Add(
+        &scene,
+        CANVAS_ENTITY_TEXT_AREA,
+        (Vector2){-260.0f, -120.0f}));
+    snprintf(
+        scene.p_entities[0].label,
+        sizeof(scene.p_entities[0].label),
+        "Dictation");
+    snprintf(
+        scene.p_entities[0].text,
+        sizeof(scene.p_entities[0].text),
+        "Route this thought");
+    uint32 conversation_id = Canvas_Scene_Add_Conversation(
+        &scene,
+        "Visible session",
+        "Earlier thought",
+        "Earlier response",
+        (Vector2){40.0f, -120.0f});
+    assert(conversation_id != 0);
+
+    char context[4096];
+    Canvas_Context_Snapshot snapshot = Canvas_Scene_Build_Visible_Context(
+        &scene,
+        &camera,
+        context,
+        sizeof(context));
+    assert(snapshot.visible_count == 1);
+    assert(!strstr(context, "Route this thought"));
+    assert(strstr(context, "Visible session"));
+    assert(strstr(context, TextFormat("id=%u", conversation_id)));
+
+    Dowa_Arena_Free(p_arena);
+}
+
+static void Test_Conversation_Create_And_Append(void)
+{
+    Dowa_Arena *p_arena = Dowa_Arena_Create(ONE_MEGA_BYTE);
+    assert(p_arena);
+    Canvas_Scene scene;
+    Canvas_Scene_Init(&scene, p_arena);
+
+    uint32 id = Canvas_Scene_Add_Conversation(
+        &scene,
+        "Rendering architecture",
+        "How should images render?",
+        "Use a rich entity with typed resources.",
+        (Vector2){10.0f, 20.0f});
+    assert(id != 0);
+    assert(Dowa_Array_Length(scene.p_entities) == 1);
+    assert(scene.p_entities[0].type == CANVAS_ENTITY_CONVERSATION);
+    assert(strcmp(scene.p_entities[0].label, "Rendering architecture") == 0);
+    assert(strstr(scene.p_entities[0].text, "How should images render?"));
+
+    assert(Canvas_Scene_Append_Conversation(
+        &scene,
+        id,
+        "What about browser content?",
+        "Keep the resource typed and composited by its backend."));
+    assert(strstr(scene.p_entities[0].text, "What about browser content?"));
+    assert(strstr(scene.p_entities[0].text, "composited by its backend"));
+    assert(!Canvas_Scene_Append_Conversation(
+        &scene,
+        id + 1,
+        "Missing",
+        "Missing"));
+    Dowa_Arena_Free(p_arena);
+}
+
+static void Test_Conversation_Collapse_And_Parking(void)
+{
+    Dowa_Arena *p_arena = Dowa_Arena_Create(ONE_MEGA_BYTE);
+    assert(p_arena);
+    Canvas_Camera camera;
+    Canvas_Camera_Init(&camera, 800, 600);
+    Canvas_Scene scene;
+    Canvas_Scene_Init(&scene, p_arena);
+
+    Vector2 original = {20.0f, 30.0f};
+    uint32 id = Canvas_Scene_Add_Conversation(
+        &scene,
+        "Parkable session",
+        "Question",
+        "Answer",
+        original);
+    Canvas_Entity *p_conversation = &scene.p_entities[0];
+    assert(Near(p_conversation->animation_amount, 1.0f));
+    Canvas_Conversation_Set_Collapsed(p_conversation, TRUE);
+    assert(p_conversation->active);
+    Canvas_Conversation_Set_Collapsed(p_conversation, FALSE);
+    assert(!p_conversation->active);
+
+    assert(Canvas_Scene_Set_Conversation_Parked(&scene, id, TRUE));
+    assert(p_conversation->parked);
+    assert(Canvas_Scene_Parked_Conversation_Count(&scene) == 1);
+    assert(!Near(p_conversation->position.x, original.x));
+    char context[4096];
+    Canvas_Context_Snapshot snapshot = Canvas_Scene_Build_Visible_Context(
+        &scene,
+        &camera,
+        context,
+        sizeof(context));
+    assert(snapshot.visible_count == 0);
+    assert(Canvas_Scene_Show_Parking_Lot(&scene, &camera));
+    snapshot = Canvas_Scene_Build_Visible_Context(
+        &scene,
+        &camera,
+        context,
+        sizeof(context));
+    assert(snapshot.visible_count == 0);
+
+    assert(Canvas_Scene_Set_Conversation_Parked(&scene, id, FALSE));
+    assert(!p_conversation->parked);
+    assert(Canvas_Scene_Parked_Conversation_Count(&scene) == 0);
+    assert(Near(p_conversation->position.x, original.x));
+    assert(Near(p_conversation->position.y, original.y));
+    Dowa_Arena_Free(p_arena);
+}
+
 int main(void)
 {
     Test_Theme_Resolution();
@@ -543,8 +670,11 @@
     Test_Lucide_Gallery_Reuses_Entity_And_Fits_Camera();
     Test_Lucide_Search_Is_Case_Insensitive();
     Test_Demo_Contains_Every_Entity_Type();
+    Test_Conversation_Create_And_Append();
     Test_Pinned_Entity_Stays_In_Screen_Space();
     Test_Visible_Context_Tracks_Camera_And_Values();
+    Test_Dictation_Scratchpad_Is_Not_A_Routing_Target();
+    Test_Conversation_Collapse_And_Parking();
     printf("Infinite canvas tests passed.\n");
     return 0;
 }