/** * Copyright (c) 2023 The xterm.js authors. All rights reserved. * @license MIT */ import * as assert from 'assert'; import { IWidthCacheFontVariantCanvas, WidthCache, WidthCacheSettings } from './WidthCache'; class MockWidthCacheFontVariantCanvas implements IWidthCacheFontVariantCanvas { public widths: { [key: string]: number } = {}; public setFont(_fontFamily: string, _fontSize: number, _fontWeight: unknown, _italic: boolean): void { } public measure(c: string): number { return this.widths[c] ?? 5; } } export class TestWidthCache extends WidthCache { public get flat(): Float32Array { return (this as any)._flat; } public get holey(): Map | undefined { return (this as any)._holey; } public get canvasElements(): MockWidthCacheFontVariantCanvas[] { return (this as any)._canvasElements; } constructor() { super(() => new MockWidthCacheFontVariantCanvas()); } public setWidths(widths: { [key: string]: number }): void { for (const canvas of this.canvasElements) { canvas.widths = widths; } } } function castf32(v: number): number { const buffer = new Float32Array(1); buffer[0] = v; return buffer[0]; } describe('WidthCache', () => { let wc: TestWidthCache; beforeEach(() => { wc = new TestWidthCache(); wc.setFont('monospace', 15, 'normal', 'bold'); }); describe('cache invalidation', () => { beforeEach(() => { wc.flat.fill(1.23); wc.holey?.set('a', 2.34); }); it('can cache values', () => { assert.deepStrictEqual(wc.flat[0], castf32(1.23)); assert.deepStrictEqual(wc.holey?.get('a'), 2.34); assert.deepStrictEqual(wc.holey?.size, 1); }); it('clear resets cache entries', () => { wc.clear(); assert.deepStrictEqual(wc.flat[0], castf32(WidthCacheSettings.FLAT_UNSET)); assert.deepStrictEqual(wc.holey?.get('a'), undefined); assert.deepStrictEqual(wc.holey?.size, 0); }); it('setFont with changed font name', () => { wc.setFont('Arial', 15, 'normal', 'bold'); assert.deepStrictEqual(wc.flat[0], castf32(WidthCacheSettings.FLAT_UNSET)); assert.deepStrictEqual(wc.holey?.get('a'), undefined); assert.deepStrictEqual(wc.holey?.size, 0); }); it('setFont with changed font size', () => { wc.setFont('monospace', 14, 'normal', 'bold'); assert.deepStrictEqual(wc.flat[0], castf32(WidthCacheSettings.FLAT_UNSET)); assert.deepStrictEqual(wc.holey?.get('a'), undefined); assert.deepStrictEqual(wc.holey?.size, 0); }); it('setFont with changed weight', () => { wc.setFont('monospace', 15, '100', 'bold'); assert.deepStrictEqual(wc.flat[0], castf32(WidthCacheSettings.FLAT_UNSET)); assert.deepStrictEqual(wc.holey?.get('a'), undefined); assert.deepStrictEqual(wc.holey?.size, 0); }); it('setFont with changed weightBold', () => { wc.setFont('monospace', 15, 'normal', '900'); assert.deepStrictEqual(wc.flat[0], castf32(WidthCacheSettings.FLAT_UNSET)); assert.deepStrictEqual(wc.holey?.get('a'), undefined); assert.deepStrictEqual(wc.holey?.size, 0); }); it('setFont with unchanged settings does not cache entries', () => { wc.setFont('monospace', 15, 'normal', 'bold'); assert.deepStrictEqual(wc.flat[0], castf32(1.23)); assert.deepStrictEqual(wc.holey?.get('a'), 2.34); assert.deepStrictEqual(wc.holey?.size, 1); }); }); describe('get', () => { it('store regular < WidthCacheSettings.FLAT_SIZE in flat', () => { for (let i = 0; i < WidthCacheSettings.FLAT_SIZE + 10; ++i) { const width = wc.get(String.fromCharCode(i), false, false); assert.deepStrictEqual(width, 5); if (i < WidthCacheSettings.FLAT_SIZE) { assert.deepStrictEqual(wc.flat[i], 5); assert.deepStrictEqual(wc.holey?.get(String.fromCharCode(i)), undefined); } else { assert.deepStrictEqual(wc.holey?.get(String.fromCharCode(i)), 5); } } }); it('stores bold & italic in holey', () => { // bold let width = wc.get('b', true, false); assert.deepStrictEqual(width, 5); assert.deepStrictEqual(wc.holey?.get('bB'), 5); // italic width = wc.get('i', false, true); assert.deepStrictEqual(width, 5); assert.deepStrictEqual(wc.holey?.get('iI'), 5); // bold&italic width = wc.get('x', true, true); assert.deepStrictEqual(width, 5); assert.deepStrictEqual(wc.holey?.get('xBI'), 5); }); it('can store any string', () => { // regular let width = wc.get('foo', false, false); assert.deepStrictEqual(width, 5); assert.deepStrictEqual(wc.holey?.get('foo'), 5); // bold&italic width = wc.get('bar&baz', true, true); assert.deepStrictEqual(width, 5); assert.deepStrictEqual(wc.holey?.get('bar&bazBI'), 5); }); }); });