Back to Repositories

Testing Unicode Character Handling in Terminal Row Components in Termux-app

A comprehensive unit test suite for the TerminalRow class in the Termux terminal emulator, focusing on Unicode character handling, display width calculations, and text manipulation. The tests verify proper rendering and positioning of various character types including surrogate pairs, combining characters, and double-width characters.

Test Coverage Overview

The test suite provides extensive coverage of terminal text rendering scenarios:
  • Basic ASCII character handling and positioning
  • Unicode character display width calculations
  • Surrogate pair character handling
  • Combining character sequences
  • Double-width character edge cases
  • Text buffer manipulation and boundary conditions

Implementation Analysis

The testing approach uses JUnit framework with systematic verification of character handling:
  • Direct character buffer manipulation tests
  • Column position calculation validation
  • Unicode normalization verification
  • Edge case handling for display width conflicts

Technical Details

Testing infrastructure includes:
  • JUnit test framework
  • Custom assertion helpers for character validation
  • Unicode constants for various character types
  • Mock TerminalRow implementation
  • Character width calculation utilities

Best Practices Demonstrated

The test suite exemplifies quality testing practices:
  • Comprehensive edge case coverage
  • Systematic test organization
  • Clear test naming conventions
  • Thorough setup and teardown procedures
  • Robust assertion patterns

termux/termux-app

terminal-emulator/src/test/java/com/termux/terminal/TerminalRowTest.java

            
package com.termux.terminal;

import junit.framework.TestCase;

import java.util.Arrays;
import java.util.Random;

public class TerminalRowTest extends TestCase {

	/** The properties of these code points are validated in {@link #testStaticConstants()}. */
	private static final int ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_1 = 0x679C;
	private static final int ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_2 = 0x679D;
	private static final int TWO_JAVA_CHARS_DISPLAY_WIDTH_TWO_1 = 0x2070E;
	private static final int TWO_JAVA_CHARS_DISPLAY_WIDTH_TWO_2 = 0x20731;

	/** Unicode Character 'MUSICAL SYMBOL G CLEF' (U+1D11E). Two java chars required for this. */
	static final int TWO_JAVA_CHARS_DISPLAY_WIDTH_ONE_1 = 0x1D11E;
	/** Unicode Character 'MUSICAL SYMBOL G CLEF OTTAVA ALTA' (U+1D11F). Two java chars required for this. */
	private static final int TWO_JAVA_CHARS_DISPLAY_WIDTH_ONE_2 = 0x1D11F;

	private final int COLUMNS = 80;

	/** A combining character. */
	private static final int DIARESIS_CODEPOINT = 0x0308;

	private TerminalRow row;

	@Override
	protected void setUp() throws Exception {
		super.setUp();
		row = new TerminalRow(COLUMNS, TextStyle.NORMAL);
	}

	private void assertLineStartsWith(int... codePoints) {
		char[] chars = row.mText;
		int charIndex = 0;
		for (int i = 0; i < codePoints.length; i++) {
			int lineCodePoint = chars[charIndex++];
			if (Character.isHighSurrogate((char) lineCodePoint)) {
				lineCodePoint = Character.toCodePoint((char) lineCodePoint, chars[charIndex++]);
			}
			assertEquals("Differing a code point index=" + i, codePoints[i], lineCodePoint);
		}
	}

	private void assertColumnCharIndicesStartsWith(int... indices) {
		for (int i = 0; i < indices.length; i++) {
			int expected = indices[i];
			int actual = row.findStartOfColumn(i);
			assertEquals("At index=" + i, expected, actual);
		}
	}

	public void testSimpleDiaresis() {
		row.setChar(0, DIARESIS_CODEPOINT, 0);
		assertEquals(81, row.getSpaceUsed());
		row.setChar(0, DIARESIS_CODEPOINT, 0);
		assertEquals(82, row.getSpaceUsed());
		assertLineStartsWith(' ', DIARESIS_CODEPOINT, DIARESIS_CODEPOINT, ' ');
	}

	public void testStaticConstants() {
		assertEquals(1, Character.charCount(ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_1));
		assertEquals(1, Character.charCount(ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_2));
		assertEquals(2, WcWidth.width(ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_1));
		assertEquals(2, WcWidth.width(ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_2));

		assertEquals(2, Character.charCount(TWO_JAVA_CHARS_DISPLAY_WIDTH_ONE_1));
		assertEquals(2, Character.charCount(TWO_JAVA_CHARS_DISPLAY_WIDTH_ONE_2));
		assertEquals(1, WcWidth.width(TWO_JAVA_CHARS_DISPLAY_WIDTH_ONE_1));
		assertEquals(1, WcWidth.width(TWO_JAVA_CHARS_DISPLAY_WIDTH_ONE_2));

		assertEquals(2, Character.charCount(TWO_JAVA_CHARS_DISPLAY_WIDTH_TWO_1));
		assertEquals(2, Character.charCount(TWO_JAVA_CHARS_DISPLAY_WIDTH_TWO_2));
		assertEquals(2, WcWidth.width(TWO_JAVA_CHARS_DISPLAY_WIDTH_TWO_1));
		assertEquals(2, WcWidth.width(TWO_JAVA_CHARS_DISPLAY_WIDTH_TWO_2));

		assertEquals(1, Character.charCount(DIARESIS_CODEPOINT));
		assertEquals(0, WcWidth.width(DIARESIS_CODEPOINT));
	}

	public void testOneColumn() {
		assertEquals(0, row.findStartOfColumn(0));
		row.setChar(0, 'a', 0);
		assertEquals(0, row.findStartOfColumn(0));
	}

	public void testAscii() {
		assertEquals(0, row.findStartOfColumn(0));
		row.setChar(0, 'a', 0);
		assertLineStartsWith('a', ' ', ' ');
		assertEquals(1, row.findStartOfColumn(1));
		assertEquals(80, row.getSpaceUsed());
		row.setChar(0, 'b', 0);
		assertEquals(1, row.findStartOfColumn(1));
		assertEquals(2, row.findStartOfColumn(2));
		assertEquals(80, row.getSpaceUsed());
		assertColumnCharIndicesStartsWith(0, 1, 2, 3);

		char[] someChars = new char[]{'a', 'c', 'e', '4', '5', '6', '7', '8'};

		char[] rawLine = new char[80];
		Arrays.fill(rawLine, ' ');
		Random random = new Random();
		for (int i = 0; i < 1000; i++) {
			int lineIndex = random.nextInt(rawLine.length);
			int charIndex = random.nextInt(someChars.length);
			rawLine[lineIndex] = someChars[charIndex];
			row.setChar(lineIndex, someChars[charIndex], 0);
		}
		char[] lineChars = row.mText;
		for (int i = 0; i < rawLine.length; i++) {
			assertEquals(rawLine[i], lineChars[i]);
		}
	}

	public void testUnicode() {
		assertEquals(0, row.findStartOfColumn(0));
		assertEquals(80, row.getSpaceUsed());

		row.setChar(0, TWO_JAVA_CHARS_DISPLAY_WIDTH_ONE_1, 0);
		assertEquals(81, row.getSpaceUsed());
		assertEquals(0, row.findStartOfColumn(0));
		assertEquals(2, row.findStartOfColumn(1));
		assertLineStartsWith(TWO_JAVA_CHARS_DISPLAY_WIDTH_ONE_1, ' ', ' ');
		assertColumnCharIndicesStartsWith(0, 2, 3, 4);

		row.setChar(0, 'a', 0);
		assertEquals(80, row.getSpaceUsed());
		assertEquals(0, row.findStartOfColumn(0));
		assertEquals(1, row.findStartOfColumn(1));
		assertLineStartsWith('a', ' ', ' ');
		assertColumnCharIndicesStartsWith(0, 1, 2, 3);

		row.setChar(0, TWO_JAVA_CHARS_DISPLAY_WIDTH_ONE_1, 0);
		row.setChar(1, 'a', 0);
		assertLineStartsWith(TWO_JAVA_CHARS_DISPLAY_WIDTH_ONE_1, 'a', ' ');

		row.setChar(0, TWO_JAVA_CHARS_DISPLAY_WIDTH_ONE_1, 0);
		row.setChar(1, TWO_JAVA_CHARS_DISPLAY_WIDTH_ONE_2, 0);
		assertLineStartsWith(TWO_JAVA_CHARS_DISPLAY_WIDTH_ONE_1, TWO_JAVA_CHARS_DISPLAY_WIDTH_ONE_2, ' ');
		assertColumnCharIndicesStartsWith(0, 2, 4, 5);
		assertEquals(82, row.getSpaceUsed());
	}

	public void testDoubleWidth() {
		row.setChar(0, 'a', 0);
		row.setChar(1, ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_2, 0);
		assertLineStartsWith('a', ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_2, ' ');
		assertColumnCharIndicesStartsWith(0, 1, 1, 2);
		row.setChar(0, ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_1, 0);
		assertLineStartsWith(ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_1, ' ', ' ');
		assertColumnCharIndicesStartsWith(0, 0, 1, 2);

		row.setChar(0, ' ', 0);
		assertLineStartsWith(' ', ' ', ' ', ' ');
		assertColumnCharIndicesStartsWith(0, 1, 2, 3, 4);
		row.setChar(0, ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_1, 0);
		row.setChar(2, ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_2, 0);
		assertLineStartsWith(ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_1, ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_2);
		assertColumnCharIndicesStartsWith(0, 0, 1, 1, 2);
		row.setChar(0, 'a', 0);
		assertLineStartsWith('a', ' ', ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_2, ' ');
	}

	/** Just as {@link #testDoubleWidth()} but requires a surrogate pair. */
	public void testDoubleWidthSurrogage() {
		row.setChar(0, 'a', 0);
		assertColumnCharIndicesStartsWith(0, 1, 2, 3, 4);

		row.setChar(1, TWO_JAVA_CHARS_DISPLAY_WIDTH_TWO_2, 0);
		assertColumnCharIndicesStartsWith(0, 1, 1, 3, 4);
		assertLineStartsWith('a', TWO_JAVA_CHARS_DISPLAY_WIDTH_TWO_2, ' ');
		row.setChar(0, TWO_JAVA_CHARS_DISPLAY_WIDTH_TWO_1, 0);
		assertColumnCharIndicesStartsWith(0, 0, 2, 3, 4);
		assertLineStartsWith(TWO_JAVA_CHARS_DISPLAY_WIDTH_TWO_1, ' ', ' ', ' ');

		row.setChar(0, ' ', 0);
		assertLineStartsWith(' ', ' ', ' ', ' ');
		row.setChar(0, TWO_JAVA_CHARS_DISPLAY_WIDTH_TWO_1, 0);
		row.setChar(1, TWO_JAVA_CHARS_DISPLAY_WIDTH_TWO_2, 0);
		assertLineStartsWith(' ', TWO_JAVA_CHARS_DISPLAY_WIDTH_TWO_2, ' ');
		row.setChar(0, 'a', 0);
		assertLineStartsWith('a', TWO_JAVA_CHARS_DISPLAY_WIDTH_TWO_2, ' ');
	}

	public void testReplacementChar() {
		row.setChar(0, TerminalEmulator.UNICODE_REPLACEMENT_CHAR, 0);
		row.setChar(1, 'Y', 0);
		assertLineStartsWith(TerminalEmulator.UNICODE_REPLACEMENT_CHAR, 'Y', ' ', ' ');
	}

	public void testSurrogateCharsWithNormalDisplayWidth() {
		// These requires a UTF-16 surrogate pair, and has a display width of one.
		int first = 0x1D306;
		int second = 0x1D307;
		// Assert the above statement:
		assertEquals(2, Character.toChars(first).length);
		assertEquals(2, Character.toChars(second).length);

		row.setChar(0, second, 0);
		assertEquals(second, Character.toCodePoint(row.mText[0], row.mText[1]));
		assertEquals(' ', row.mText[2]);
		assertEquals(2, row.findStartOfColumn(1));

		row.setChar(0, first, 0);
		assertEquals(first, Character.toCodePoint(row.mText[0], row.mText[1]));
		assertEquals(' ', row.mText[2]);
		assertEquals(2, row.findStartOfColumn(1));

		row.setChar(1, second, 0);
		row.setChar(2, 'a', 0);
		assertEquals(first, Character.toCodePoint(row.mText[0], row.mText[1]));
		assertEquals(second, Character.toCodePoint(row.mText[2], row.mText[3]));
		assertEquals('a', row.mText[4]);
		assertEquals(' ', row.mText[5]);
		assertEquals(0, row.findStartOfColumn(0));
		assertEquals(2, row.findStartOfColumn(1));
		assertEquals(4, row.findStartOfColumn(2));
		assertEquals(5, row.findStartOfColumn(3));
		assertEquals(6, row.findStartOfColumn(4));

		row.setChar(0, ' ', 0);
		assertEquals(' ', row.mText[0]);
		assertEquals(second, Character.toCodePoint(row.mText[1], row.mText[2]));
		assertEquals('a', row.mText[3]);
		assertEquals(' ', row.mText[4]);
		assertEquals(0, row.findStartOfColumn(0));
		assertEquals(1, row.findStartOfColumn(1));
		assertEquals(3, row.findStartOfColumn(2));
		assertEquals(4, row.findStartOfColumn(3));
		assertEquals(5, row.findStartOfColumn(4));

		for (int i = 0; i < 80; i++) {
			row.setChar(i, i % 2 == 0 ? first : second, 0);
		}
		for (int i = 0; i < 80; i++) {
			int idx = row.findStartOfColumn(i);
			assertEquals(i % 2 == 0 ? first : second, Character.toCodePoint(row.mText[idx], row.mText[idx + 1]));
		}
		for (int i = 0; i < 80; i++) {
			row.setChar(i, i % 2 == 0 ? 'a' : 'b', 0);
		}
		for (int i = 0; i < 80; i++) {
			int idx = row.findStartOfColumn(i);
			assertEquals(i, idx);
			assertEquals(i % 2 == 0 ? 'a' : 'b', row.mText[i]);
		}
	}

	public void testOverwritingDoubleDisplayWidthWithNormalDisplayWidth() {
		// Initial "OO "
		row.setChar(0, ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_1, 0);
		assertEquals(ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_1, row.mText[0]);
		assertEquals(' ', row.mText[1]);
		assertEquals(0, row.findStartOfColumn(0));
		assertEquals(0, row.findStartOfColumn(1));
		assertEquals(1, row.findStartOfColumn(2));

		// Setting first column to a clears second: "a  "
		row.setChar(0, 'a', 0);
		assertEquals('a', row.mText[0]);
		assertEquals(' ', row.mText[1]);
		assertEquals(0, row.findStartOfColumn(0));
		assertEquals(1, row.findStartOfColumn(1));
		assertEquals(2, row.findStartOfColumn(2));

		// Back to initial "OO "
		row.setChar(0, ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_1, 0);
		assertEquals(ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_1, row.mText[0]);
		assertEquals(' ', row.mText[1]);
		assertEquals(0, row.findStartOfColumn(0));
		assertEquals(0, row.findStartOfColumn(1));
		assertEquals(1, row.findStartOfColumn(2));

		// Setting first column to a clears first: " a "
		row.setChar(1, 'a', 0);
		assertEquals(' ', row.mText[0]);
		assertEquals('a', row.mText[1]);
		assertEquals(' ', row.mText[2]);
		assertEquals(0, row.findStartOfColumn(0));
		assertEquals(1, row.findStartOfColumn(1));
		assertEquals(2, row.findStartOfColumn(2));
	}

	public void testOverwritingDoubleDisplayWidthWithSelf() {
		row.setChar(0, ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_1, 0);
		row.setChar(0, ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_1, 0);
		assertEquals(ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_1, row.mText[0]);
		assertEquals(' ', row.mText[1]);
		assertEquals(0, row.findStartOfColumn(0));
		assertEquals(0, row.findStartOfColumn(1));
		assertEquals(1, row.findStartOfColumn(2));
	}

	public void testNormalCharsWithDoubleDisplayWidth() {
		// These fit in one java char, and has a display width of two.
		assertTrue(ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_1 != ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_2);
		assertEquals(1, Character.charCount(ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_1));
		assertEquals(1, Character.charCount(ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_2));
		assertEquals(2, WcWidth.width(ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_1));
		assertEquals(2, WcWidth.width(ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_2));

		row.setChar(0, ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_1, 0);
		assertEquals(ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_1, row.mText[0]);
		assertEquals(0, row.findStartOfColumn(1));
		assertEquals(' ', row.mText[1]);

		row.setChar(0, 'a', 0);
		assertEquals('a', row.mText[0]);
		assertEquals(' ', row.mText[1]);
		assertEquals(1, row.findStartOfColumn(1));

		row.setChar(0, ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_1, 0);
		assertEquals(ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_1, row.mText[0]);
		// The first character fills both first columns.
		assertEquals(0, row.findStartOfColumn(1));
		row.setChar(2, 'a', 0);
		assertEquals(ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_1, row.mText[0]);
		assertEquals('a', row.mText[1]);
		assertEquals(1, row.findStartOfColumn(2));

		row.setChar(0, 'c', 0);
		assertEquals('c', row.mText[0]);
		assertEquals(' ', row.mText[1]);
		assertEquals('a', row.mText[2]);
		assertEquals(' ', row.mText[3]);
		assertEquals(0, row.findStartOfColumn(0));
		assertEquals(1, row.findStartOfColumn(1));
		assertEquals(2, row.findStartOfColumn(2));
	}

	public void testNormalCharsWithDoubleDisplayWidthOverlapping() {
		// These fit in one java char, and has a display width of two.
		row.setChar(0, ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_1, 0);
		row.setChar(2, ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_2, 0);
		row.setChar(4, 'a', 0);
		// O = ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO
		// A = ANOTHER_JAVA_CHAR_DISPLAY_WIDTH_TWO
		// "OOAAa    "
		assertEquals(0, row.findStartOfColumn(0));
		assertEquals(0, row.findStartOfColumn(1));
		assertEquals(1, row.findStartOfColumn(2));
		assertEquals(1, row.findStartOfColumn(3));
		assertEquals(2, row.findStartOfColumn(4));
		assertEquals(ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_1, row.mText[0]);
		assertEquals(ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_2, row.mText[1]);
		assertEquals('a', row.mText[2]);
		assertEquals(' ', row.mText[3]);

		row.setChar(1, ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_2, 0);
		// " AA a    "
		assertEquals(' ', row.mText[0]);
		assertEquals(ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_2, row.mText[1]);
		assertEquals(' ', row.mText[2]);
		assertEquals('a', row.mText[3]);
		assertEquals(' ', row.mText[4]);
		assertEquals(0, row.findStartOfColumn(0));
		assertEquals(1, row.findStartOfColumn(1));
		assertEquals(1, row.findStartOfColumn(2));
		assertEquals(2, row.findStartOfColumn(3));
		assertEquals(3, row.findStartOfColumn(4));
	}

	// https://github.com/jackpal/Android-Terminal-Emulator/issues/145
	public void testCrashATE145() {
		// 0xC2541 is unassigned, use display width 1 for UNICODE_REPLACEMENT_CHAR.
		// assertEquals(1, WcWidth.width(0xC2541));
		assertEquals(2, Character.charCount(0xC2541));

		assertEquals(2, WcWidth.width(0x73EE));
		assertEquals(1, Character.charCount(0x73EE));

		assertEquals(0, WcWidth.width(0x009F));
		assertEquals(1, Character.charCount(0x009F));

		int[] points = new int[]{0xC2541, 'a', '8', 0x73EE, 0x009F, 0x881F, 0x8324, 0xD4C9, 0xFFFD, 'B', 0x009B, 0x61C9, 'Z'};
		// int[] expected = new int[] { TerminalEmulator.UNICODE_REPLACEMENT_CHAR, 'a', '8', 0x73EE, 0x009F, 0x881F, 0x8324, 0xD4C9, 0xFFFD,
		// 'B', 0x009B, 0x61C9, 'Z' };
		int currentColumn = 0;
		for (int point : points) {
			row.setChar(currentColumn, point, 0);
			currentColumn += WcWidth.width(point);
		}
		// assertLineStartsWith(points);
		// assertEquals(Character.highSurrogate(0xC2541), line.mText[0]);
		// assertEquals(Character.lowSurrogate(0xC2541), line.mText[1]);
		// assertEquals('a', line.mText[2]);
		// assertEquals('8', line.mText[3]);
		// assertEquals(Character.highSurrogate(0x73EE), line.mText[4]);
		// assertEquals(Character.lowSurrogate(0x73EE), line.mText[5]);
		//
		// char[] chars = line.mText;
		// int charIndex = 0;
		// for (int i = 0; i < points.length; i++) {
		// char c = chars[charIndex];
		// charIndex++;
		// int thisPoint = (int) c;
		// if (Character.isHighSurrogate(c)) {
		// thisPoint = Character.toCodePoint(c, chars[charIndex]);
		// charIndex++;
		// }
		// assertEquals("At index=" + i + ", charIndex=" + charIndex + ", char=" + (char) thisPoint, points[i], thisPoint);
		// }
	}

	public void testNormalization() {
		// int lowerCaseN = 0x006E;
		// int combiningTilde = 0x0303;
		// int combined = 0x00F1;
		row.setChar(0, 0x006E, 0);
		assertEquals(80, row.getSpaceUsed());
		row.setChar(0, 0x0303, 0);
		assertEquals(81, row.getSpaceUsed());
		// assertEquals("\u00F1  ", new String(term.getScreen().getLine(0)));
		assertLineStartsWith(0x006E, 0x0303, ' ');
	}

	public void testInsertWideAtLastColumn() {
		row.setChar(COLUMNS - 2, 'Z', 0);
		row.setChar(COLUMNS - 1, 'a', 0);
		assertEquals('Z', row.mText[row.findStartOfColumn(COLUMNS - 2)]);
		assertEquals('a', row.mText[row.findStartOfColumn(COLUMNS - 1)]);
		row.setChar(COLUMNS - 1, 'ö', 0);
		assertEquals('Z', row.mText[row.findStartOfColumn(COLUMNS - 2)]);
		assertEquals('ö', row.mText[row.findStartOfColumn(COLUMNS - 1)]);
		// line.setChar(COLUMNS - 1, ONE_JAVA_CHAR_DISPLAY_WIDTH_TWO_1);
		// assertEquals('Z', line.mText[line.findStartOfColumn(COLUMNS - 2)]);
		// assertEquals(' ', line.mText[line.findStartOfColumn(COLUMNS - 1)]);
	}

}