From a5df5e7333f946bcbaecb6a1a564be78ae3a98f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Thu, 27 Jul 2023 11:25:57 +0200 Subject: [PATCH] first unit tests --- src/browser/renderer/dom/DomRenderer.ts | 2 +- .../dom/DomRendererRowFactory.test.ts | 106 +++++++++++++++++- 2 files changed, 106 insertions(+), 2 deletions(-) diff --git a/src/browser/renderer/dom/DomRenderer.ts b/src/browser/renderer/dom/DomRenderer.ts index da4740ee..a6eeae65 100644 --- a/src/browser/renderer/dom/DomRenderer.ts +++ b/src/browser/renderer/dom/DomRenderer.ts @@ -25,7 +25,7 @@ const SELECTION_CLASS = 'xterm-selection'; let nextTerminalId = 1; // font metrics calc settings -const enum FontMetrics { +export const enum FontMetrics { START = 32, // start codepoint MAX = 256, // only calc up to this codepoint (256 means only Basic Latin + Latin-1 Supplement) BATCH_SIZE = 30, // amount of codepoints to calc in a single batch (sync & blocking) diff --git a/src/browser/renderer/dom/DomRendererRowFactory.test.ts b/src/browser/renderer/dom/DomRendererRowFactory.test.ts index ef83722e..6a454d37 100644 --- a/src/browser/renderer/dom/DomRendererRowFactory.test.ts +++ b/src/browser/renderer/dom/DomRendererRowFactory.test.ts @@ -13,8 +13,9 @@ import { CellData } from 'common/buffer/CellData'; import { MockCoreService, MockDecorationService, MockOptionsService } from 'common/TestUtils.test'; import { css } from 'common/Color'; import { MockCharacterJoinerService, MockCoreBrowserService, MockThemeService } from 'browser/TestUtils.test'; +import { FontMetrics } from 'browser/renderer/dom/DomRenderer'; -const EMPTY_METRICS = new Uint8Array(1024); +const EMPTY_METRICS = new Uint8Array(FontMetrics.MAX); EMPTY_METRICS.fill(0xFF); const EMPTY_LINKSTATE = new Uint8Array(3); @@ -298,6 +299,109 @@ describe('DomRendererRowFactory', () => { }); }); + describe.only('createRow with merged spans', () => { + // for test purpose assume all in codepoints 0..255 are merging + const ALL_MERGING = new Uint8Array(FontMetrics.MAX); + + beforeEach(() => { + lineData = createEmptyLineData(10); + }); + + it('should not create anything for an empty row', () => { + const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, ALL_MERGING, EMPTY_LINKSTATE); + assert.equal(getFragmentHtml(fragment), + '' + ); + }); + + it('can merge codepoints in FontMetrics range', () => { + lineData.setCell(0, CellData.fromCharData([DEFAULT_ATTR, 'a', 1, 'a'.charCodeAt(0)])); + lineData.setCell(1, CellData.fromCharData([DEFAULT_ATTR, 'b', 1, 'b'.charCodeAt(0)])); + lineData.setCell(2, CellData.fromCharData([DEFAULT_ATTR, 'c', 1, 'c'.charCodeAt(0)])); + const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, ALL_MERGING, EMPTY_LINKSTATE); + assert.equal(getFragmentHtml(fragment), + 'abc' + ); + }); + + it('should not merge codepoints outside of FontMetrics range', () => { + lineData.setCell(0, CellData.fromCharData([DEFAULT_ATTR, 'a', 1, 'a'.charCodeAt(0)])); + lineData.setCell(1, CellData.fromCharData([DEFAULT_ATTR, '€', 1, '€'.charCodeAt(0)])); + lineData.setCell(2, CellData.fromCharData([DEFAULT_ATTR, 'c', 1, 'c'.charCodeAt(0)])); + const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, ALL_MERGING, EMPTY_LINKSTATE); + assert.equal(getFragmentHtml(fragment), + 'ac' + ); + }); + + it('should not merge on FG change', () => { + const a_color1 = CellData.fromCharData([DEFAULT_ATTR, 'a', 1, 'a'.charCodeAt(0)]); + a_color1.fg |= Attributes.CM_P16 | 1; + const b_color2 = CellData.fromCharData([DEFAULT_ATTR, 'b', 1, 'b'.charCodeAt(0)]); + b_color2.fg |= Attributes.CM_P16 | 2; + lineData.setCell(0, a_color1); + lineData.setCell(1, a_color1); + lineData.setCell(2, b_color2); + lineData.setCell(3, b_color2); + const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, ALL_MERGING, EMPTY_LINKSTATE); + assert.equal(getFragmentHtml(fragment), + 'aabb' + ); + }); + + it('should not merge cursor cell', () => { + lineData.setCell(0, CellData.fromCharData([DEFAULT_ATTR, 'a', 1, 'a'.charCodeAt(0)])); + lineData.setCell(1, CellData.fromCharData([DEFAULT_ATTR, 'a', 1, 'a'.charCodeAt(0)])); + lineData.setCell(2, CellData.fromCharData([DEFAULT_ATTR, 'X', 1, 'X'.charCodeAt(0)])); + lineData.setCell(3, CellData.fromCharData([DEFAULT_ATTR, 'b', 1, 'b'.charCodeAt(0)])); + lineData.setCell(4, CellData.fromCharData([DEFAULT_ATTR, 'b', 1, 'b'.charCodeAt(0)])); + const fragment = rowFactory.createRow(lineData, 0, true, undefined, 2, false, 5, ALL_MERGING, EMPTY_LINKSTATE); + assert.equal(getFragmentHtml(fragment), + 'aaXbb' + ); + }); + + it('should handle BCE correctly', () => { + const nullCell = lineData.loadCell(0, new CellData()); + nullCell.bg = Attributes.CM_P16 | 1; + lineData.setCell(2, nullCell); + nullCell.bg = Attributes.CM_P16 | 2; + lineData.setCell(3, nullCell); + lineData.setCell(4, nullCell); + const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, ALL_MERGING, EMPTY_LINKSTATE); + assert.equal(getFragmentHtml(fragment), + ' ' + ); + }); + + it('should contain px value in BCE for multiple cells', () => { + const nullCell = lineData.loadCell(0, new CellData()); + nullCell.bg = Attributes.CM_P16 | 1; + lineData.setCell(0, nullCell); + let fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, ALL_MERGING, EMPTY_LINKSTATE); + assert.equal(getFragmentHtml(fragment), + ' ' + ); + lineData.setCell(1, nullCell); + fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, ALL_MERGING, EMPTY_LINKSTATE); + assert.equal(getFragmentHtml(fragment), + ' ' + ); + lineData.setCell(2, nullCell); + lineData.setCell(3, nullCell); + fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, ALL_MERGING, EMPTY_LINKSTATE); + assert.equal(getFragmentHtml(fragment), + ' ' + ); + lineData.setCell(4, CellData.fromCharData([DEFAULT_ATTR, 'a', 1, 'a'.charCodeAt(0)])); + fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, ALL_MERGING, EMPTY_LINKSTATE); + assert.equal(getFragmentHtml(fragment), + ' a' + ); + }); + + }); + function getFragmentHtml(fragment: DocumentFragment): string { const element = dom.window.document.createElement('div'); element.appendChild(fragment);