From e9d04de3995d1795c12148be60d7abbab7f25f3c Mon Sep 17 00:00:00 2001 From: Yifeng Wang Date: Mon, 14 Apr 2025 11:07:48 +0800 Subject: [PATCH] test(editor): add tests for turbo renderer layout cache --- .../gfx/turbo-renderer/src/turbo-renderer.ts | 8 +-- ...enderer.spec.ts => turbo-renderer.spec.ts} | 53 ++++++++++++++----- 2 files changed, 43 insertions(+), 18 deletions(-) rename blocksuite/integration-test/src/__tests__/edgeless/{viewport-renderer.spec.ts => turbo-renderer.spec.ts} (56%) diff --git a/blocksuite/affine/gfx/turbo-renderer/src/turbo-renderer.ts b/blocksuite/affine/gfx/turbo-renderer/src/turbo-renderer.ts index b92c405d79..8465b4fae8 100644 --- a/blocksuite/affine/gfx/turbo-renderer/src/turbo-renderer.ts +++ b/blocksuite/affine/gfx/turbo-renderer/src/turbo-renderer.ts @@ -73,20 +73,16 @@ export const TurboRendererConfigFactory = export class ViewportTurboRendererExtension extends GfxExtension { static override key = 'viewportTurboRenderer'; - private readonly state$ = new BehaviorSubject('inactive'); + public readonly state$ = new BehaviorSubject('inactive'); public readonly canvas: HTMLCanvasElement = document.createElement('canvas'); + public layoutCacheData: ViewportLayoutTree | null = null; private readonly worker: Worker; private readonly disposables = new DisposableGroup(); - private layoutCacheData: ViewportLayoutTree | null = null; private layoutVersion = 0; private bitmap: ImageBitmap | null = null; private viewportElement: GfxViewportElement | null = null; private readonly refresh$ = new Subject(); - public get currentState(): RenderingState { - return this.state$.value; - } - constructor(gfx: GfxController) { super(gfx); diff --git a/blocksuite/integration-test/src/__tests__/edgeless/viewport-renderer.spec.ts b/blocksuite/integration-test/src/__tests__/edgeless/turbo-renderer.spec.ts similarity index 56% rename from blocksuite/integration-test/src/__tests__/edgeless/viewport-renderer.spec.ts rename to blocksuite/integration-test/src/__tests__/edgeless/turbo-renderer.spec.ts index 0ff58541b5..3ac3b4f58e 100644 --- a/blocksuite/integration-test/src/__tests__/edgeless/viewport-renderer.spec.ts +++ b/blocksuite/integration-test/src/__tests__/edgeless/turbo-renderer.spec.ts @@ -2,6 +2,7 @@ * Please refer to integration-test/README.md for commands to run tests. */ import { ParagraphLayoutHandlerExtension } from '@blocksuite/affine/blocks/paragraph'; +import { noop } from '@blocksuite/affine/global/utils'; import { TurboRendererConfigFactory, ViewportTurboRendererExtension, @@ -45,36 +46,64 @@ describe('viewport turbo renderer', () => { test('initial state should be pending', async () => { const renderer = getRenderer(); - expect(renderer.currentState).toBe('pending'); + expect(renderer.state$.value).toBe('pending'); }); test('zooming should change state to zooming', async () => { const renderer = getRenderer(); renderer.viewport.zooming$.next(true); await wait(); - expect(renderer.currentState).toBe('zooming'); + expect(renderer.state$.value).toBe('zooming'); renderer.viewport.zooming$.next(false); await wait(); - expect(renderer.currentState).not.toBe('zooming'); + expect(renderer.state$.value).not.toBe('zooming'); }); - test('state should become ready after rendering', async () => { - addSampleNotes(doc, 1); - await wait(100); + test('state transitions between pending and ready', async () => { const renderer = getRenderer(); + + // Initial state should be pending after adding content + addSampleNotes(doc, 1); + await wait(100); // Short wait for initial processing + expect(renderer.state$.value).toBe('pending'); + + // Ensure zooming is off and wait for debounce + buffer renderer.viewport.zooming$.next(false); await wait(renderer.options.debounceTime + 500); - expect(renderer.currentState).toBe('ready'); + expect(renderer.state$.value).toBe('ready'); }); - test('invalidation should reset state to pending', async () => { + test('initial layout cache data should be null', () => { + const renderer = getRenderer(); + expect(renderer.layoutCacheData).toBeNull(); + }); + + test('invalidation should reset layout cache data to null', async () => { const renderer = getRenderer(); addSampleNotes(doc, 1); - expect(renderer.currentState).toBe('pending'); - await wait(renderer.options.debounceTime + 500); - expect(renderer.currentState).toBe('ready'); + await wait(100); + + // Access getter to populate cache + const _cache = renderer.layoutCache; + noop(_cache); + expect(renderer.layoutCacheData).not.toBeNull(); + + // Invalidate addSampleNotes(doc, 1); await wait(100); - expect(renderer.currentState).toBe('pending'); + + expect(renderer.layoutCacheData).toBeNull(); + }); + + test('accessing layoutCache getter should populate cache data', async () => { + const renderer = getRenderer(); + addSampleNotes(doc, 1); + await wait(); + expect(renderer.layoutCacheData).toBeNull(); // Check internal state before access + + const _cache = renderer.layoutCache; // Access getter to populate cache + noop(_cache); + expect(renderer.layoutCacheData).not.toBeNull(); // Check internal state after access + expect(renderer.layoutCache?.roots.length).toBeGreaterThan(0); // Check public getter result }); });