X-Git-Url: https://git.giacomolli.uk/onechat/blobdiff_plain/d4dc9a3b10a6092d18fb30cfb36526556aa33227..4b9b22619008f0829b7f5a8685e7b1d2a9aeb6bb:/replay_test.go diff --git a/replay_test.go b/replay_test.go index f01cd63..32ffebe 100644 --- a/replay_test.go +++ b/replay_test.go @@ -5,6 +5,7 @@ import ( "math/rand/v2" "os" "path/filepath" + "runtime" "strings" "testing" "time" @@ -34,6 +35,7 @@ func TestReplaySyntheticLog(t *testing.T) { batches := 0 low := chatHigh + long := make([]bool, syntheticMessages) for id := range syntheticMessages { if id == syntheticMessages/2 { memory = requireRestart(t, memory, root, clock) @@ -41,11 +43,12 @@ func TestReplaySyntheticLog(t *testing.T) { clock.now = clock.now.Add(time.Minute) kind, text := syntheticMessage(random, id) + long[id] = len(kind)+len(": ")+len(text) > nodeBytes lines := viewLines(memory) _, err = memory.Append(kind, text) require.NoError(t, err) - size, merged := requireViews(t, memory, lines) + size, merged := requireViews(t, memory, lines, long, id%1000 == 0) if merged { batches++ low = min(low, size) @@ -59,9 +62,15 @@ func TestReplaySyntheticLog(t *testing.T) { require.Greater(t, batches, 0) require.NoError(t, memory.Wait(t.Context())) - t.Logf("%d messages, %d nodes, %d batches in %s", syntheticMessages, len(memory.tree.nodes), batches, time.Since(start).Round(time.Second)) - t.Logf("chat view: %d lines, %d bytes, lowest after a batch %d bytes", len(memory.chat.lines), memory.chat.Size(memory.tree), low) - t.Logf("compaction view: %d lines, %d bytes", len(memory.cview.lines), memory.cview.Size(memory.tree)) + t.Logf("%d messages, %d nodes, %d batches in %s", syntheticMessages, memory.tree.Count(), batches, time.Since(start).Round(time.Second)) + t.Logf("chat view: %d lines, %d bytes, lowest after a batch %d bytes", len(memory.chat.lines), memory.chat.Size(), low) + t.Logf("compaction view: %d lines, %d bytes", len(memory.cview.lines), memory.cview.Size()) + + runtime.GC() + + var stats runtime.MemStats + runtime.ReadMemStats(&stats) + t.Logf("heap in use: %d KB", stats.HeapInuse/1024) } // syntheticMessage returns the kind and the text of message id. Most messages @@ -96,44 +105,50 @@ func syntheticText(id, size int) string { return head + strings.Repeat(words, max(1, (size-len(head))/len(words))) } -// requireViews checks the views after a new message. Each view is at most its -// high limit, and a batch merges the chat view to its low limit. The leaf of -// the new message can be built after the append, so the sizes count it as the -// pending mark, as the append did. It returns the size of the chat view and if -// a batch merged it, from the number of lines before the message. -func requireViews(t *testing.T, memory *Memory, lines int) (int, bool) { +// requireViews checks the views after a new message, and with full compares +// them with the tree. Each view is at most its high limit, and a batch merges +// the chat view to its low limit. The leaf of the new message can be built +// after the append, so the sizes count it as the pending mark, as the append +// did. It returns the size of the chat view and if a batch merged it, from the +// number of lines before the message. +func requireViews(t *testing.T, memory *Memory, lines int, long []bool, full bool) (int, bool) { t.Helper() memory.mutex.Lock() defer memory.mutex.Unlock() - size := appendSize(memory.chat, memory.tree) + size := appendSize(memory.chat) require.LessOrEqual(t, size, chatHigh) - require.LessOrEqual(t, appendSize(memory.cview, memory.tree), compactionHigh) + require.LessOrEqual(t, appendSize(memory.cview), compactionHigh) merged := len(memory.chat.lines) < lines if merged { require.LessOrEqual(t, size, chatLow) } - requireOnlySummaries(t, memory) + requireOnlySummaries(t, memory, long, full) return size, merged } // requireOnlySummaries checks each line of the chat view as the model reads it. -// A built line holds its node, a long message has a summary and not its text, -// and an unbuilt line holds only the pending mark. The caller holds the lock of -// memory. -func requireOnlySummaries(t *testing.T, memory *Memory) { +// A built line holds its node, a long message has a summary of the fake +// summarizer and not its text, and an unbuilt line holds only the pending mark. +// With full, it also checks that the view keeps the node of each built line. +// The caller holds the lock of memory. +func requireOnlySummaries(t *testing.T, memory *Memory, long []bool, full bool) { t.Helper() - rendered := strings.Split(memory.chat.Render(memory.tree), "\n") + rendered := strings.Split(memory.chat.Render(), "\n") require.Len(t, rendered, len(memory.chat.lines)+2) for n, line := range memory.chat.lines { head := fmt.Sprintf("%d+%d|", line.index< nodeBytes { - require.NotEqual(t, source, node.Text, "message %d", line.index) - } + if line.level == 0 && long[line.index] { + require.True(t, strings.HasPrefix(node.Text, "summary "), "message %d", line.index) } } } // appendSize returns the size of view with its last line as the pending mark. -func appendSize(view *View, tree nodeReader) int { +func appendSize(view *View) int { last := view.lines[len(view.lines)-1] - return view.Size(tree) - lineSize(tree, last) + len(pending) + return view.Size() - view.lineSize(last) + len(pending) } // viewLines returns the number of lines in the chat view of memory.