diff --git a/packages/kilo-jetbrains/AGENTS.md b/packages/kilo-jetbrains/AGENTS.md index d9a13a7427e..3bb2c6abab0 100644 --- a/packages/kilo-jetbrains/AGENTS.md +++ b/packages/kilo-jetbrains/AGENTS.md @@ -268,6 +268,22 @@ Tests for retained Swing components should assert: - `update(model)` changes existing labels/body text without duplicating components. - Updates while collapsed do not eagerly create lazy bodies. - No-op updates, empty deltas, repeated hover values, and toggling non-expandable cards do not repaint/revalidate the whole view. +- Streaming/rebuilding surfaces additionally require stress + leak tests (see below). + +### Stress and Leak Tests for Streaming UI + +Session/transcript UI that streams updates or rebuilds its component tree (markdown +views, code blocks, transcript parts, collapsible cards) must ship stress + leak tests in +addition to behavior tests. These tests must: + +- Drive many updates (hundreds of streamed deltas or `set` cycles) through the public API. +- Assert that retained component instances stay identical across updates (`assertSame`). +- Assert the component count stays bounded — no growth per update. +- Assert disposable-backed resources return to baseline after churn + clear/dispose. + For code editors, compare `EditorFactory.getInstance().allEditors.size` against a + baseline captured before the loop. + +See `MdViewHybridStressTest` for the reference pattern. ### Platform Components and Utilities diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/ui/md/MdViewHybridStressTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/ui/md/MdViewHybridStressTest.kt new file mode 100644 index 00000000000..e9d40248cf6 --- /dev/null +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/ui/md/MdViewHybridStressTest.kt @@ -0,0 +1,180 @@ +package ai.kilocode.client.ui.md + +import ai.kilocode.client.session.ui.style.SessionEditorStyle +import com.intellij.openapi.editor.EditorFactory +import com.intellij.openapi.util.Disposer +import com.intellij.testFramework.fixtures.BasePlatformTestCase +import com.intellij.ui.EditorTextField +import com.intellij.ui.components.JBHtmlPane +import com.intellij.ui.components.JBScrollPane +import com.intellij.util.ui.UIUtil +import javax.swing.Box +import javax.swing.JPanel + +/** + * Stress + leak coverage for the hybrid markdown renderer. + * + * These tests drive many updates through the public [MdView] API and inspect the real + * Swing component tree to prove that: + * - retained component instances survive heavy streaming, + * - the component tree stays bounded (no per-update growth), + * - editors created for code blocks are released (no leak) after churn + clear. + */ +@Suppress("UnstableApiUsage") +class MdViewHybridStressTest : BasePlatformTestCase() { + private lateinit var view: MdView + private var disposed = false + + override fun setUp() { + super.setUp() + view = MdViewFactory.hybrid() + disposed = false + } + + override fun tearDown() { + try { + if (this::view.isInitialized && !disposed) Disposer.dispose(view) + } finally { + super.tearDown() + } + } + + fun `test streaming a large mixed document token by token stays consistent`() { + val doc = buildString { + append("# Heading\n\n") + append("Intro paragraph with **bold** text.\n\n") + append("- one\n- two\n- three\n\n") + append("```kotlin\nval x = 1\n```\n\n") + append("middle prose paragraph\n\n") + append("```java\nclass A {}\n```\n\n") + append("closing prose") + } + + for (token in doc.chunked(3)) view.append(token) + + assertEquals(doc, view.markdown()) + assertEquals(3, htmls().size) + assertEquals(2, scrolls().size) + assertEquals(4, struts().size) // blocks - 1 + assertEquals(9, panel().componentCount) // 5 blocks + 4 struts = 2*5 - 1 + + val html = view.html() + assertTrue(html.contains("