Commit 2b304724 authored by rginda@chromium.org's avatar rginda@chromium.org

Initial landing of Screen, Terminal, and VT100 classes.

Some of this code is based on Cory Maccarrone's html terminal, developed
internally in Google as part of a different project.  Thanks to Cory for
allowing us to repurpose it here!

This gets us to a point where we have the core classes largely fleshed out.  The code passes 45 tests, mostly related to correct handling of vt100 escape sequences.

Still to come: network connectivity, keyboard input, and text attributes!

BUG=chromium-os:23271
TEST=test_harness.html:45/45 tests passed.


Review URL: http://codereview.chromium.org/8680034

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@113066 0039d316-1c4b-4281-b951-d872f2087c98
parent 2ffbb484
<!DOCTYPE html>
<html>
<head>
<script src='../js/test_manager.js'></script>
<script src='../js/mock_row_provider.js'></script>
<!-- PubSub is currently unscoped (it's not in hterm.*) -->
<script src='../js/pubsub.js'></script>
<!-- hterm.* things -->
<script src='../js/hterm.js'></script>
<script src='../js/scrollport.js'></script>
<script src='../js/terminal.js'></script>
<script src='../js/options.js'></script>
<script src='../js/screen.js'></script>
<script src='../js/vt100.js'></script>
<script src='../js/pubsub_tests.js'></script>
<!-- Test specific things -->
<script src='../js/test_manager.js'></script>
<script src='../js/mock_row_provider.js'></script>
<!-- Tests -->
<script src='../js/scrollport_tests.js'></script>
<script src='../js/screen_tests.js'></script>
<script src='../js/terminal_tests.js'></script>
<script src='../js/vt100_tests.js'></script>
<script>
var testManager;
......@@ -17,10 +29,12 @@
function init() {
testManager = new TestManager();
testRun = testManager.createTestRun({window: window});
// Stop after the first failure to make it easier to debug in the
// JS console.
testRun.maxFailures = 1;
testRun.selectPattern(testRun.ALL_TESTS);
testRun.run();
}
</script>
......
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Declares the hterm.* namespace and some basic shared utilities
* that are too small to deserve dedicated files.
*/
var hterm = {};
/**
* Clamp a given integer to a specified range.
*
* @param {integer} v The value to be clamped.
* @param {integer} min The minimum acceptable value.
* @param {integer} max The maximum acceptable value.
*/
hterm.clamp = function(v, min, max) {
if (v < min)
return min;
if (v > max)
return max;
return v;
};
/**
* Return a string containing a given number of space characters.
*
* This method maintains a static cache of the largest amount of whitespace
* ever requested. It shouldn't be used to generate an insanely huge amount of
* whitespace.
*
* @param {integer} length The desired amount of whitespace.
* @param {string} A string of spaces of the requested length.
*/
hterm.getWhitespace = function(length) {
if (length == 0)
return '';
var f = this.getWhitespace;
if (!f.whitespace)
f.whitespace = ' ';
while (length > f.whitespace.length) {
f.whitespace += f.whitespace;
}
return f.whitespace.substr(0, length);
};
/**
* Constructor for a hterm.Size record.
*
* Instances of this class have public read/write members for width and height.
*
* @param {integer} width The width of this record.
* @param {integer} height The height of this record.
*/
hterm.Size = function(width, height) {
this.width = width;
this.height = height;
};
/**
* Adjust the width and height of this record.
*
* @param {integer} width The new width of this record.
* @param {integer} height The new height of this record.
*/
hterm.Size.prototype.resize = function(width, height) {
this.width = width;
this.height = height;
};
/**
* Return a copy of this record.
*
* @return {hterm.Size} A new hterm.Size instance with the same width and
* height.
*/
hterm.Size.prototype.clone = function() {
return new hterm.Size(this.width, this.height);
};
/**
* Test if another hterm.Size instance is equal to this one.
*
* @param {hterm.Size} that The other hterm.Size instance.
* @return {boolen} True if both instances have the same width/height, false
* otherwise.
*/
hterm.Size.prototype.equals = function(that) {
return this.width == that.width && this.height == that.height;
};
/**
* Return a string representation of this instance.
*
* @return {string} A string that identifies the width and height of this
* instance.
*/
hterm.Size.prototype.toString = function() {
return '[hterm.Size: ' + this.width + ', ' + this.height + ']';
};
/**
* Constructor for a hterm.RowCol record.
*
* Instances of this class have public read/write members for row and column.
*
* @param {integer} row The row of this record.
* @param {integer} column The column of this record.
*/
hterm.RowCol = function(row, column) {
this.row = row;
this.column = column;
};
/**
* Adjust the row and column of this record.
*
* @param {integer} row The new row of this record.
* @param {integer} column The new column of this record.
*/
hterm.RowCol.prototype.move = function(row, column) {
this.row = row;
this.column = column;
};
/**
* Return a copy of this record.
*
* @return {hterm.RowCol} A new hterm.RowCol instance with the same row and
* column.
*/
hterm.RowCol.prototype.clone = function() {
return new hterm.RowCol(this.row, this.column);
};
/**
* Test if another hterm.RowCol instance is equal to this one.
*
* @param {hterm.RowCol} that The other hterm.RowCol instance.
* @return {boolen} True if both instances have the same row/column, false
* otherwise.
*/
hterm.RowCol.prototype.equals = function(that) {
return this.row == that.row && this.column == that.column;
};
/**
* Return a string representation of this instance.
*
* @return {string} A string that identifies the row and column of this
* instance.
*/
hterm.RowCol.prototype.toString = function() {
return '[hterm.RowCol: ' + this.row + ', ' + this.column + ']';
};
......@@ -84,7 +84,7 @@ MockRowProvider.prototype.getRowNode = function(index) {
return this.rowNodeCache_[index];
var rec = this.getRowRecord_(index);
var rowNode = this.document_.createElement('div');
var rowNode = this.document_.createElement('x-row');
rowNode.rowIndex = index;
rowNode.innerHTML = rec.html;
......
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview This file implements the hterm.Options class,
* which stores current operating conditions for the terminal. This object is
* used instead of a series of parameters to allow saving/restoring of cursor
* conditions easily, and to provide an easy place for common configuration
* options.
*
* Original code by Cory Maccarrone.
*/
/**
* Constructor for the hterm.Options class, optionally acting as a copy
* constructor.
*
* @param {hterm.Options=} opt_copy Optional instance to copy.
* @constructor
*/
hterm.Options = function(opt_copy) {
// All attributes in this class are public to allow easy access by the
// terminal.
this.wraparound = opt_copy ? opt_copy.wraparound : true;
this.reverseWraparound = opt_copy ? opt_copy.reverseWraparound : false;
this.originMode = opt_copy ? opt_copy.originMode : false;
this.autoLinefeed = opt_copy ? opt_copy.autoLinefeed : true;
this.specialChars = opt_copy ? opt_copy.specialChars : false;
this.cursorVisible = opt_copy ? opt_copy.cursorVisible : true;
this.cursorBlink = opt_copy ? opt_copy.cursorBlink : true;
this.insertMode = opt_copy ? opt_copy.insertMode : false;
this.reverseVideo = opt_copy ? opt_copy.reverseVideo : false;
};
This diff is collapsed.
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Unit tests for the hterm.Screen class.
*/
hterm.Screen.Tests = new TestManager.Suite('hterm.Screen.Tests');
/**
* Clear out the current document and create a new hterm.Screen object for
* testing.
*
* Called before each test case in this suite.
*/
hterm.Screen.Tests.prototype.preamble = function(result, cx) {
cx.window.document.body.innerHTML = '';
cx.window.screen = this.screen = new hterm.Screen();
cx.window.screen.setColumnCount(80);
};
/**
* Test the push and pop functionality of the hterm.Screen.
*/
hterm.Screen.Tests.addTest('push-pop', function(result, cx) {
// Push one at a time.
var ary = [];
for (var i = 0; i < 10; i++) {
ary[i] = document.createElement('div');
ary[i].textContent = i;
this.screen.pushRow(ary[i]);
}
result.assertEQ(ary.length, this.screen.getHeight());
// Pop one at a time.
for (var i = ary.length - 1; i >= 0; i--) {
result.assertEQ(ary[i], this.screen.popRow(), 'i:' + i);
}
// Bulk push.
this.screen.pushRows(ary);
result.assertEQ(ary.length, this.screen.rowsArray.length);
// Bulk pop.
var popary = this.screen.popRows(ary.length);
result.assertEQ(ary.length, popary.length);
for (var i = ary.length - 1; i >= 0; i--) {
result.assertEQ(ary[i], popary[i], 'i:' + i);
}
// Reset, then partial bulk pop.
this.screen.pushRows(ary);
result.assertEQ(ary.length, this.screen.rowsArray.length);
var popary = this.screen.popRows(5);
for (var i = 0; i < 5; i++) {
result.assertEQ(ary[i + 5], popary[i], 'i:' + i);
}
result.pass();
});
/**
* Test the unshift and shift functionality of the hterm.Screen.
*/
hterm.Screen.Tests.addTest('unshift-shift', function(result, cx) {
// Unshift one at a time.
var ary = [];
for (var i = 0; i < 10; i++) {
ary[i] = document.createElement('div');
ary[i].textContent = i;
this.screen.unshiftRow(ary[i]);
}
result.assertEQ(ary.length, this.screen.rowsArray.length);
// Shift one at a time.
for (var i = ary.length - 1; i >= 0; i--) {
result.assertEQ(ary[i], this.screen.shiftRow(), 'i:' + i);
}
// Bulk unshift.
this.screen.unshiftRows(ary);
result.assertEQ(ary.length, this.screen.rowsArray.length);
// Bulk shift.
var shiftary = this.screen.shiftRows(ary.length);
result.assertEQ(ary.length, shiftary.length);
for (var i = ary.length - 1; i >= 0; i--) {
result.assertEQ(ary[i], shiftary[i], 'i:' + i);
}
// Reset, then partial bulk shift.
this.screen.unshiftRows(ary);
result.assertEQ(ary.length, this.screen.rowsArray.length);
var shiftary = this.screen.shiftRows(5);
for (var i = 0; i < 5; i++) {
result.assertEQ(ary[i], shiftary[i], 'i:' + i);
}
result.pass();
});
/**
* Test cursor positioning functionality.
*/
hterm.Screen.Tests.addTest('cursor-movement', function(result, cx) {
var ary = [];
for (var i = 0; i < 3; i++) {
ary[i] = document.createElement('div');
ary[i].textContent = i;
this.screen.pushRow(ary[i]);
}
this.screen.setCursorPosition(0, 0);
result.assertEQ(this.screen.cursorRowNode_, ary[0]);
result.assertEQ(this.screen.cursorNode_, ary[0].firstChild);
result.assertEQ(this.screen.cursorOffset_, 0);
this.screen.setCursorPosition(1, 0);
result.assertEQ(this.screen.cursorRowNode_, ary[1]);
result.assertEQ(this.screen.cursorNode_, ary[1].firstChild);
result.assertEQ(this.screen.cursorOffset_, 0);
this.screen.setCursorPosition(1, 10);
result.assertEQ(this.screen.cursorRowNode_, ary[1]);
result.assertEQ(this.screen.cursorNode_, ary[1].firstChild);
result.assertEQ(this.screen.cursorOffset_, 10);
this.screen.setCursorPosition(1, 5);
result.assertEQ(this.screen.cursorRowNode_, ary[1]);
result.assertEQ(this.screen.cursorNode_, ary[1].firstChild);
result.assertEQ(this.screen.cursorOffset_, 5);
this.screen.setCursorPosition(1, 10);
result.assertEQ(this.screen.cursorRowNode_, ary[1]);
result.assertEQ(this.screen.cursorNode_, ary[1].firstChild);
result.assertEQ(this.screen.cursorOffset_, 10);
ary[2].innerHTML = '01<div>23</div>45<div>67</div>89';
this.screen.setCursorPosition(2, 0);
result.assertEQ(this.screen.cursorRowNode_, ary[2]);
result.assertEQ(this.screen.cursorNode_, ary[2].firstChild);
result.assertEQ(this.screen.cursorOffset_, 0);
this.screen.setCursorPosition(2, 1);
result.assertEQ(this.screen.cursorRowNode_, ary[2]);
result.assertEQ(this.screen.cursorNode_, ary[2].firstChild);
result.assertEQ(this.screen.cursorOffset_, 1);
this.screen.setCursorPosition(2, 2);
result.assertEQ(this.screen.cursorRowNode_, ary[2]);
result.assertEQ(this.screen.cursorNode_, ary[2].childNodes[1]);
result.assertEQ(this.screen.cursorOffset_, 0);
this.screen.setCursorPosition(2, 3);
result.assertEQ(this.screen.cursorRowNode_, ary[2]);
result.assertEQ(this.screen.cursorNode_, ary[2].childNodes[1]);
result.assertEQ(this.screen.cursorOffset_, 1);
this.screen.setCursorPosition(2, 4);
result.assertEQ(this.screen.cursorRowNode_, ary[2]);
result.assertEQ(this.screen.cursorNode_, ary[2].childNodes[2]);
result.assertEQ(this.screen.cursorOffset_, 0);
this.screen.setCursorPosition(2, 5);
result.assertEQ(this.screen.cursorRowNode_, ary[2]);
result.assertEQ(this.screen.cursorNode_, ary[2].childNodes[2]);
result.assertEQ(this.screen.cursorOffset_, 1);
this.screen.setCursorPosition(2, 6);
result.assertEQ(this.screen.cursorRowNode_, ary[2]);
result.assertEQ(this.screen.cursorNode_, ary[2].childNodes[3]);
result.assertEQ(this.screen.cursorOffset_, 0);
this.screen.setCursorPosition(2, 7);
result.assertEQ(this.screen.cursorRowNode_, ary[2]);
result.assertEQ(this.screen.cursorNode_, ary[2].childNodes[3]);
result.assertEQ(this.screen.cursorOffset_, 1);
this.screen.setCursorPosition(2, 8);
result.assertEQ(this.screen.cursorRowNode_, ary[2]);
result.assertEQ(this.screen.cursorNode_, ary[2].childNodes[4]);
result.assertEQ(this.screen.cursorOffset_, 0);
this.screen.setCursorPosition(2, 9);
result.assertEQ(this.screen.cursorRowNode_, ary[2]);
result.assertEQ(this.screen.cursorNode_, ary[2].childNodes[4]);
result.assertEQ(this.screen.cursorOffset_, 1);
this.screen.setCursorPosition(2, 18);
result.assertEQ(this.screen.cursorRowNode_, ary[2]);
result.assertEQ(this.screen.cursorNode_, ary[2].childNodes[4]);
result.assertEQ(this.screen.cursorOffset_, 10);
result.pass();
});
/**
* Test character removal.
*/
hterm.Screen.Tests.addTest('delete-chars', function(result, cx) {
var row = document.createElement('div');
row.innerHTML = 'hello<div id="1"> </div><div id="2">world</div>';
this.screen.pushRow(row);
this.screen.setCursorPosition(0, 3);
this.screen.deleteChars(5);
result.assertEQ(row.innerHTML, 'hel<div id="2">rld</div>');
result.pass();
});
/**
* Test the ability to insert text in a line.
*/
hterm.Screen.Tests.addTest('insert', function(result, cx) {
// Sample rows. Row 0 is a simple, empty row. Row 1 simulates rows with
// mixed text attributes.
var ary = [document.createElement('div'), document.createElement('div')];
ary[1].innerHTML = 'hello<div id="1"> </div><div id="2">world</div>';
this.screen.pushRows(ary);
// Basic insert.
this.screen.setCursorPosition(0, 0);
this.screen.insertString('XXXXX');
result.assertEQ(ary[0].innerHTML, 'XXXXX');
// Test that positioning the cursor beyond the end of the current text does
// not cause spaces to be printed.
this.screen.clearCursorRow();
this.screen.setCursorPosition(0, 3);
result.assertEQ(ary[0].innerHTML, '');
// Print some text at this cursor position and make sure the spaces show up.
this.screen.insertString('XXXXX');
result.assertEQ(ary[0].innerHTML, ' XXXXX');
// Fetch enough whitespace to ensure that the row is full.
var ws = hterm.getWhitespace(this.screen.getWidth());
// Check simple overflow.
this.screen.clearCursorRow();
this.screen.insertString('XXXX');
this.screen.setCursorPosition(0, 0);
var overflow = this.screen.insertString(ws);
result.assertEQ(overflow, 'XXXX');
// Insert into a more complicated row.
this.screen.setCursorPosition(1, 3);
this.screen.insertString('XXXXX');
result.assertEQ(ary[1].innerHTML, 'helXXXXXlo<div id="1"> </div>' +
'<div id="2">world</div>');
// Check that multi-attribute is not implemented. We'll want a better test
// once its implemented.
this.screen.setCursorPosition(1, 0);
try {
this.screen.insertString(ws);
result.assert(false);
} catch (ex) {
result.assertEQ(ex, 'NOT IMPLEMENTED');
}
result.pass();
});
/**
* Test the ability to overwrite test.
*/
hterm.Screen.Tests.addTest('overwrite', function(result, cx) {
var ary = [];
ary[0] = document.createElement('div');
ary[0].innerHTML = 'hello<div id="1"> </div><div id="2">world</div>';
ary[1] = document.createElement('div');
this.screen.pushRows(ary);
this.screen.setCursorPosition(0, 3);
this.screen.overwriteString('XXXXX');
result.assertEQ(ary[0].innerHTML, 'helXXXXX<div id="2">rld</div>');
this.screen.setCursorPosition(1, 0);
this.screen.overwriteString('XXXXX');
result.assertEQ(ary[1].innerHTML, 'XXXXX');
result.pass();
});
......@@ -2,9 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
ScrollPort.Tests = new TestManager.Suite('ScrollPort.Tests');
hterm.ScrollPort.Tests = new TestManager.Suite('hterm.ScrollPort.Tests');
ScrollPort.Tests.prototype.setup = function(cx) {
hterm.ScrollPort.Tests.prototype.setup = function(cx) {
this.setDefaults(cx,
{ visibleColumnCount: 80,
visibleRowCount: 25,
......@@ -25,8 +25,8 @@ ScrollPort.Tests.prototype.setup = function(cx) {
div.style.width = '100%';
document.body.appendChild(div);
this.scrollPort = new ScrollPort(this.rowProvider,
this.fontSize, this.lineHeight);
this.scrollPort = new hterm.ScrollPort(this.rowProvider,
this.fontSize, this.lineHeight);
this.scrollPort.decorate(div);
};
......@@ -34,7 +34,7 @@ ScrollPort.Tests.prototype.setup = function(cx) {
* Ensure the selection is collapsed, row caching is on, and we're at the
* top of the scroll port.
*/
ScrollPort.Tests.prototype.preamble = function(result, cx) {
hterm.ScrollPort.Tests.prototype.preamble = function(result, cx) {
var selection = cx.window.getSelection();
if (!selection.isCollapsed)
selection.collapseToStart();
......@@ -49,7 +49,7 @@ ScrollPort.Tests.prototype.preamble = function(result, cx) {
* Basic test to make sure that the viewport contains the right number of
* rows at the right places after some scrolling.
*/
ScrollPort.Tests.addTest('basic-scroll', function(result, cx) {
hterm.ScrollPort.Tests.addTest('basic-scroll', function(result, cx) {
var topRow = this.scrollPort.getTopRowIndex();
result.assertEQ(topRow, 0);
result.assertEQ(this.scrollPort.getBottomRowIndex(topRow),
......@@ -66,9 +66,9 @@ ScrollPort.Tests.addTest('basic-scroll', function(result, cx) {
});
/**
* Make sure the ScrollPort is reusing the same row nodes when it can.
* Make sure the hterm.ScrollPort is reusing the same row nodes when it can.
*/
ScrollPort.Tests.addTest('node-recycler', function(result, cx) {
hterm.ScrollPort.Tests.addTest('node-recycler', function(result, cx) {
this.rowProvider.resetCallCount('getRowNode');
this.scrollPort.scrollRowToTop(1);
var count = this.rowProvider.getCallCount('getRowNode');
......@@ -82,7 +82,7 @@ ScrollPort.Tests.addTest('node-recycler', function(result, cx) {
/**
* Make sure the selection is maintained even after scrolling off screen.
*/
ScrollPort.Tests.addTest('scroll-selection', function(result, cx) {
hterm.ScrollPort.Tests.addTest('scroll-selection', function(result, cx) {
var doc = this.scrollPort.getDocument();
// Scroll into a part of the buffer that can be scrolled off the top
......@@ -122,7 +122,7 @@ ScrollPort.Tests.addTest('scroll-selection', function(result, cx) {
/**
* Test the select-all function.
*/
ScrollPort.Tests.addTest('select-all', function(result, cx) {
hterm.ScrollPort.Tests.addTest('select-all', function(result, cx) {
this.scrollPort.selectAll();
result.assertEQ(0, this.scrollPort.selection_.startRow.rowIndex);
result.assertEQ(this.totalRowCount - 1,
......@@ -137,7 +137,7 @@ ScrollPort.Tests.addTest('select-all', function(result, cx) {
* This should always be the last test of the suite, since it leaves the user
* with a full page scrollPort to poke at.
*/
ScrollPort.Tests.addTest('fullscreen', function(result, cx) {
hterm.ScrollPort.Tests.addTest('fullscreen', function(result, cx) {
var document = cx.window.document;
document.body.innerHTML = '';
......@@ -150,8 +150,8 @@ ScrollPort.Tests.addTest('fullscreen', function(result, cx) {
div.style.width = '100%';
document.body.appendChild(div);
this.scrollPort = new ScrollPort(this.rowProvider,
this.fontSize, this.lineHeight);
this.scrollPort = new hterm.ScrollPort(this.rowProvider,
this.fontSize, this.lineHeight);
this.scrollPort.decorate(div);
cx.window.scrollPort = this.scrollPort;
......
This diff is collapsed.
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview hterm.Terminal unit tests.
*/
hterm.Terminal.Tests = new TestManager.Suite('hterm.Terminal.Tests');
hterm.Terminal.Tests.prototype.setup = function(cx) {
this.setDefaults(cx,
{ visibleColumnCount: 80,
visibleRowCount: 25,
fontSize: 15,
lineHeight: 17,
charWidth: 9,
scrollbarWidth: 16,
});
};
/**
* Clear out the current document and create a new hterm.Terminal object for
* testing.
*
* Called before each test case in this suite.
*/
hterm.Terminal.Tests.prototype.preamble = function(result, cx) {
var document = cx.window.document;
document.body.innerHTML = '';
var div = document.createElement('div');
div.style.position = 'absolute';
div.style.height = this.lineHeight * this.visibleRowCount + 'px';
div.style.width = this.charWidth * this.visibleColumnCount +
this.scrollbarWidth + 'px';
document.body.appendChild(div);
cx.window.terminal = this.terminal = new hterm.Terminal(
this.fontSize, this.lineHeight);
this.terminal.decorate(div);
};
/**
* Overridden addTest method.
*
* Every test in this suite needs to wait for the terminal initialization to
* complete asynchronously. Rather than stick a bunch of biolerplate into each
* test case, we use this overridden addTest method to add a proxy around the
* actual test.
*/
hterm.Terminal.Tests.addTest = function(name, callback) {
function testProxy(result, cx) {
setTimeout(function() {
this.terminal.setCursorPosition(0, 0);
callback.apply(this, [result, cx]);
}, 0);
result.requestTime(200);
}
TestManager.Suite.addTest.apply(this, [name, testProxy]);
};
/**
* Fill the screen with 'X' characters one character at a time, in a way
* that should stress the cursor positioning code.
*/
hterm.Terminal.Tests.addTest('plaintext-stress-cursor-ltr',
function(result, cx) {
for (var col = 0; col < this.visibleColumnCount; col++) {
for (var row = 0; row < this.visibleRowCount; row++) {
console.log(row, col);
this.terminal.screen_.setCursorPosition(row, col);
this.terminal.screen_.insertString('X');
}
}
result.pass();
});
/**
* Fill the screen with 'X' characters one character at a time, in a way
* that should stress the cursor positioning code and the overwriteString()
* code.
*/
hterm.Terminal.Tests.addTest('plaintext-stress-cursor-rtl',
function(result, cx) {
for (var col = this.visibleColumnCount - 1; col >= 0; col--) {
for (var row = 0; row < this.visibleRowCount; row++) {
this.terminal.screen_.setCursorPosition(row, col);
this.terminal.screen_.overwriteString('X');
}
}
result.pass();
});
/**
* Fill the terminal with a lot of text as quickly as possible.
*
* This test doesn't actually assert anything, but the timing data in the test
* log is useful.
*/
hterm.Terminal.Tests.addTest('plaintext-stress-insert',
function(result, cx) {
var chunkSize = 1000;
var testCount = 10;
var self = this;
function test(count) {
for (var i = count * chunkSize; i < (count + 1) * chunkSize; i++) {
if (i != 0)
self.terminal.newLine();
self.terminal.screen_.insertString(
'line ' + i + ': All work and no play makes jack a dull boy.');
}
if (count + 1 >= testCount) {
result.pass();
} else {
result.requestTime(200);
setTimeout(test, 0, count + 1);
}
}
test(0);
});
......@@ -331,6 +331,22 @@ TestManager.Suite.prototype.setup = function(cx) {};
*/
TestManager.Suite.prototype.preamble = function(result, cx) {};
/**
* Subclassable method called to do post-test tear-down.
*
* The default implementation of this method is a no-op. If your test suite
* requires some kind of pre-test setup, this is the place to do it.
*
* This can be used to avoid a bunch of boilerplate setup/teardown code in
* this suite's testcases.
*
* Any exception here will abort the remainder of the test run.
*
* @param {TestManager.Result} result The result object for the upcoming test.
* @param {Object} cx The context object for a test run.
*/
TestManager.Suite.prototype.postamble = function(result, cx) {};
/**
* Object representing a single test in a test suite.
*
......@@ -474,9 +490,9 @@ TestManager.TestRun.prototype.selectTest = function(test) {
this.testQueue_.push(test);
};
TestManager.TestRun.prototype.selectSuite = function(suiteClass, pattern) {
TestManager.TestRun.prototype.selectSuite = function(suiteClass, opt_pattern) {
var pattern = opt_pattern || this.ALL_TESTS;
var selectCount = 0;
var testList = suiteClass.getTestList();
for (var j = 0; j < testList.length; j++) {
......@@ -487,7 +503,7 @@ TestManager.TestRun.prototype.selectSuite = function(suiteClass, pattern) {
if (pattern instanceof RegExp) {
if (!pattern.test(test.testName))
continue;
} else if (testName != pattern) {
} else if (test.testName != pattern) {
continue;
}
}
......@@ -537,7 +553,7 @@ TestManager.TestRun.prototype.onUncaughtException_ = function(
// This is a result.pass() or result.fail() call from a callback. We're
// already going to deal with it as part of the completeTest_() call
// that raised it. We can safely squelch this error message.
return false;
return true;
}
if (!this.currentResult)
......@@ -605,6 +621,14 @@ TestManager.TestRun.prototype.onTestRunComplete_ = function(opt_skipTimeout) {
* completed.
*/
TestManager.TestRun.prototype.onResultComplete = function(result) {
try {
result.suite.postamble();
} catch (ex) {
this.log.println('Unexpected exception in postamble: ' +
(ex.stack ? ex.stack : ex));
this.panic = true;
}
this.log.popPrefix();
this.log.print('} ' + result.status + ', ' +
this.msToSeconds_(result.duration));
......@@ -749,7 +773,7 @@ TestManager.TestRun.prototype.run = function() {
* Format milliseconds as fractional seconds.
*/
TestManager.TestRun.prototype.msToSeconds_ = function(ms) {
var secs = (ms / 100).toFixed(2);
var secs = (ms / 1000).toFixed(2);
return secs + 's';
};
......
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment