Skip to content
Snippets Groups Projects
Commit 6560904b authored by George Nachman's avatar George Nachman Committed by George Nachman
Browse files

Refactor the CSI parser and add a suite of automatic unit tests to measure xterm compatibility.

parent 66065314
No related branches found
No related tags found
No related merge requests found
Showing
with 2630 additions and 42 deletions
Loading
Loading
@@ -45,91 +45,91 @@
}
 
- (void)testNoModeYet {
VT100Token *token = [self tokenForDataWithFormat:@"%c]", ESC];
VT100Token *token = [self tokenForDataWithFormat:@"%c]", VT100CC_ESC];
assert(token->type == VT100_WAIT);
 
// In case saved state gets used, verify it can continue from there.
token = [self tokenForDataWithFormat:@"%c]0;title%c", ESC, VT100CC_BEL];
token = [self tokenForDataWithFormat:@"%c]0;title%c", VT100CC_ESC, VT100CC_BEL];
assert(token->type == XTERMCC_WINICON_TITLE);
assert([token.string isEqualToString:@"title"]);
}
 
- (void)testWellFormedSetWindowTitleTerminatedByBell {
VT100Token *token = [self tokenForDataWithFormat:@"%c]0;title%c", ESC, VT100CC_BEL];
VT100Token *token = [self tokenForDataWithFormat:@"%c]0;title%c", VT100CC_ESC, VT100CC_BEL];
assert(token->type == XTERMCC_WINICON_TITLE);
assert([token.string isEqualToString:@"title"]);
}
 
- (void)testWellFormedSetWindowTitleTerminatedByST {
VT100Token *token = [self tokenForDataWithFormat:@"%c]0;title%c\\", ESC, ESC];
VT100Token *token = [self tokenForDataWithFormat:@"%c]0;title%c\\", VT100CC_ESC, VT100CC_ESC];
assert(token->type == XTERMCC_WINICON_TITLE);
assert([token.string isEqualToString:@"title"]);
}
 
- (void)testIgnoreEmbeddedOSC {
VT100Token *token = [self tokenForDataWithFormat:@"%c]0;ti%c]tle%c", ESC, ESC, VT100CC_BEL];
VT100Token *token = [self tokenForDataWithFormat:@"%c]0;ti%c]tle%c", VT100CC_ESC, VT100CC_ESC, VT100CC_BEL];
assert(token->type == XTERMCC_WINICON_TITLE);
assert([token.string isEqualToString:@"title"]);
}
 
- (void)testIgnoreEmbeddedOSCTwoPart_OutOfDataAfterBracket {
// Running out of data just after an embedded ESC ] hits a special path.
VT100Token *token = [self tokenForDataWithFormat:@"%c]0;ti%c]", ESC, ESC];
VT100Token *token = [self tokenForDataWithFormat:@"%c]0;ti%c]", VT100CC_ESC, VT100CC_ESC];
assert(token->type == VT100_WAIT);
 
token = [self tokenForDataWithFormat:@"%c]0;ti%c]tle%c", ESC, ESC, VT100CC_BEL];
token = [self tokenForDataWithFormat:@"%c]0;ti%c]tle%c", VT100CC_ESC, VT100CC_ESC, VT100CC_BEL];
assert(token->type == XTERMCC_WINICON_TITLE);
assert([token.string isEqualToString:@"title"]);
}
 
- (void)testIgnoreEmbeddedOSCTwoPart_OutOfDataAfterEsc {
// Running out of data just after an embedded ESC hits a special path.
VT100Token *token = [self tokenForDataWithFormat:@"%c]0;ti%c", ESC, ESC];
VT100Token *token = [self tokenForDataWithFormat:@"%c]0;ti%c", VT100CC_ESC, VT100CC_ESC];
assert(token->type == VT100_WAIT);
 
token = [self tokenForDataWithFormat:@"%c]0;ti%c]tle%c", ESC, ESC, VT100CC_BEL];
token = [self tokenForDataWithFormat:@"%c]0;ti%c]tle%c", VT100CC_ESC, VT100CC_ESC, VT100CC_BEL];
assert(token->type == XTERMCC_WINICON_TITLE);
assert([token.string isEqualToString:@"title"]);
}
 
- (void)testFailOnEmbddedEscapePlusCharacter {
VT100Token *token = [self tokenForDataWithFormat:@"%c]0;ti%cc", ESC, ESC];
VT100Token *token = [self tokenForDataWithFormat:@"%c]0;ti%cc", VT100CC_ESC, VT100CC_ESC];
assert(token->type == VT100_NOTSUPPORT);
}
 
- (void)testNonstandardLinuxSetPalette {
VT100Token *token = [self tokenForDataWithFormat:@"%c]Pa123456", ESC];
VT100Token *token = [self tokenForDataWithFormat:@"%c]Pa123456", VT100CC_ESC];
assert(token->type == XTERMCC_SET_PALETTE);
assert([token.string isEqualToString:@"a123456"]);
}
 
- (void)testUnsupportedFirstParameterNoTerminator {
VT100Token *token = [self tokenForDataWithFormat:@"%c]x", ESC];
VT100Token *token = [self tokenForDataWithFormat:@"%c]x", VT100CC_ESC];
assert(token->type == VT100_WAIT);
}
 
- (void)testUnsupportedFirstParameter {
VT100Token *token = [self tokenForDataWithFormat:@"%c]x%c", ESC, VT100CC_BEL];
VT100Token *token = [self tokenForDataWithFormat:@"%c]x%c", VT100CC_ESC, VT100CC_BEL];
assert(token->type == VT100_NOTSUPPORT);
}
 
- (void)testPartialNonstandardLinuxSetPalette {
VT100Token *token = [self tokenForDataWithFormat:@"%c]Pa12345", ESC];
VT100Token *token = [self tokenForDataWithFormat:@"%c]Pa12345", VT100CC_ESC];
assert(token->type == VT100_WAIT);
}
 
- (void)testCancelAbortsOSC {
VT100Token *token = [self tokenForDataWithFormat:@"%c]0%c", ESC, VT100CC_CAN];
VT100Token *token = [self tokenForDataWithFormat:@"%c]0%c", VT100CC_ESC, VT100CC_CAN];
assert(token->type == VT100_NOTSUPPORT);
}
 
- (void)testSubstituteAbortsOSC {
VT100Token *token = [self tokenForDataWithFormat:@"%c]0%c", ESC, VT100CC_SUB];
VT100Token *token = [self tokenForDataWithFormat:@"%c]0%c", VT100CC_ESC, VT100CC_SUB];
assert(token->type == VT100_NOTSUPPORT);
}
 
- (void)testUnfinishedMultitoken {
VT100Token *token = [self tokenForDataWithFormat:@"%c]1337;File=blah;foo=bar:abc", ESC];
VT100Token *token = [self tokenForDataWithFormat:@"%c]1337;File=blah;foo=bar:abc", VT100CC_ESC];
assert(token->type == VT100_WAIT);
assert(CVectorCount(&_incidentals) == 2);
 
Loading
Loading
@@ -145,7 +145,7 @@
 
- (void)testCompleteMultitoken {
VT100Token *token = [self tokenForDataWithFormat:@"%c]1337;File=blah;foo=bar:abc%c",
ESC, VT100CC_BEL];
VT100CC_ESC, VT100CC_BEL];
assert(token->type == XTERMCC_MULTITOKEN_END);
assert(CVectorCount(&_incidentals) == 2);
 
Loading
Loading
@@ -160,17 +160,17 @@
}
 
- (void)testCompleteMultitokenInMultiplePasses {
VT100Token *token = [self tokenForDataWithFormat:@"%c]1337;File=blah;", ESC];
VT100Token *token = [self tokenForDataWithFormat:@"%c]1337;File=blah;", VT100CC_ESC];
assert(token->type == VT100_WAIT);
assert(CVectorCount(&_incidentals) == 0);
 
// Give it some more header
token = [self tokenForDataWithFormat:@"%c]1337;File=blah;foo=bar", ESC];
token = [self tokenForDataWithFormat:@"%c]1337;File=blah;foo=bar", VT100CC_ESC];
assert(token->type == VT100_WAIT);
assert(CVectorCount(&_incidentals) == 0);
 
// Give it the final colon so the header can be parsed
token = [self tokenForDataWithFormat:@"%c]1337;File=blah;foo=bar:", ESC];
token = [self tokenForDataWithFormat:@"%c]1337;File=blah;foo=bar:", VT100CC_ESC];
assert(token->type == VT100_WAIT);
assert(CVectorCount(&_incidentals) == 1);
 
Loading
Loading
@@ -180,7 +180,7 @@
assert([header.kvpValue isEqualToString:@"blah;foo=bar"]);
 
// Give it some body.
token = [self tokenForDataWithFormat:@"%c]1337;File=blah;foo=bar:a", ESC];
token = [self tokenForDataWithFormat:@"%c]1337;File=blah;foo=bar:a", VT100CC_ESC];
assert(token->type == VT100_WAIT);
assert(CVectorCount(&_incidentals) == 2);
 
Loading
Loading
@@ -189,7 +189,7 @@
assert([body.string isEqualToString:@"a"]);
 
// More body
token = [self tokenForDataWithFormat:@"%c]1337;File=blah;foo=bar:abc", ESC];
token = [self tokenForDataWithFormat:@"%c]1337;File=blah;foo=bar:abc", VT100CC_ESC];
assert(token->type == VT100_WAIT);
assert(CVectorCount(&_incidentals) == 3);
 
Loading
Loading
@@ -198,28 +198,28 @@
assert([body.string isEqualToString:@"bc"]);
 
// Start finishing up
token = [self tokenForDataWithFormat:@"%c]1337;File=blah;foo=bar:abc%c", ESC, ESC];
token = [self tokenForDataWithFormat:@"%c]1337;File=blah;foo=bar:abc%c", VT100CC_ESC, VT100CC_ESC];
assert(token->type == VT100_WAIT);
assert(CVectorCount(&_incidentals) == 3);
 
// And, done.
token = [self tokenForDataWithFormat:@"%c]1337;File=blah;foo=bar:abc%c\\", ESC, ESC];
token = [self tokenForDataWithFormat:@"%c]1337;File=blah;foo=bar:abc%c\\", VT100CC_ESC, VT100CC_ESC];
assert(token->type == XTERMCC_MULTITOKEN_END);
assert(CVectorCount(&_incidentals) == 3);
}
 
- (void)testLateFailureMultitokenInMultiplePasses {
VT100Token *token = [self tokenForDataWithFormat:@"%c]1337;File=blah;", ESC];
VT100Token *token = [self tokenForDataWithFormat:@"%c]1337;File=blah;", VT100CC_ESC];
assert(token->type == VT100_WAIT);
assert(CVectorCount(&_incidentals) == 0);
 
// Give it some more header
token = [self tokenForDataWithFormat:@"%c]1337;File=blah;foo=bar", ESC];
token = [self tokenForDataWithFormat:@"%c]1337;File=blah;foo=bar", VT100CC_ESC];
assert(token->type == VT100_WAIT);
assert(CVectorCount(&_incidentals) == 0);
 
// Give it the final colon so the header can be parsed
token = [self tokenForDataWithFormat:@"%c]1337;File=blah;foo=bar:", ESC];
token = [self tokenForDataWithFormat:@"%c]1337;File=blah;foo=bar:", VT100CC_ESC];
assert(token->type == VT100_WAIT);
assert(CVectorCount(&_incidentals) == 1);
 
Loading
Loading
@@ -229,7 +229,7 @@
assert([header.kvpValue isEqualToString:@"blah;foo=bar"]);
 
// Give it some body.
token = [self tokenForDataWithFormat:@"%c]1337;File=blah;foo=bar:a", ESC];
token = [self tokenForDataWithFormat:@"%c]1337;File=blah;foo=bar:a", VT100CC_ESC];
assert(token->type == VT100_WAIT);
assert(CVectorCount(&_incidentals) == 2);
 
Loading
Loading
@@ -238,7 +238,7 @@
assert([body.string isEqualToString:@"a"]);
 
// More body
token = [self tokenForDataWithFormat:@"%c]1337;File=blah;foo=bar:abc", ESC];
token = [self tokenForDataWithFormat:@"%c]1337;File=blah;foo=bar:abc", VT100CC_ESC];
assert(token->type == VT100_WAIT);
assert(CVectorCount(&_incidentals) == 3);
 
Loading
Loading
@@ -247,12 +247,12 @@
assert([body.string isEqualToString:@"bc"]);
 
// Now a bogus character.
token = [self tokenForDataWithFormat:@"%c]1337;File=blah;foo=bar:abc%c", ESC, VT100CC_SUB];
token = [self tokenForDataWithFormat:@"%c]1337;File=blah;foo=bar:abc%c", VT100CC_ESC, VT100CC_SUB];
assert(token->type == VT100_NOTSUPPORT);
}
 
- (void)testUnfinishedMultitokenWithDeprecatedMode {
VT100Token *token = [self tokenForDataWithFormat:@"%c]50;File=blah;foo=bar:abc", ESC];
VT100Token *token = [self tokenForDataWithFormat:@"%c]50;File=blah;foo=bar:abc", VT100CC_ESC];
assert(token->type == VT100_WAIT);
assert(CVectorCount(&_incidentals) == 2);
 
Loading
Loading
@@ -267,53 +267,53 @@
}
 
- (void)testUnterminatedOSCWaits {
VT100Token *token = [self tokenForDataWithFormat:@"%c]0;foo", ESC];
VT100Token *token = [self tokenForDataWithFormat:@"%c]0;foo", VT100CC_ESC];
assert(token->type == VT100_WAIT);
}
 
- (void)testUnterminateOSCWaits_2 {
VT100Token *token = [self tokenForDataWithFormat:@"%c]0", ESC];
VT100Token *token = [self tokenForDataWithFormat:@"%c]0", VT100CC_ESC];
assert(token->type == VT100_WAIT);
}
 
- (void)testMultiPartOSC {
// Pass in a partial escape code. The already-parsed data should be saved in the saved-state
// dictionary.
VT100Token *token = [self tokenForDataWithFormat:@"%c]0;foo", ESC];
VT100Token *token = [self tokenForDataWithFormat:@"%c]0;foo", VT100CC_ESC];
assert(token->type == VT100_WAIT);
assert(_savedState.allKeys.count > 0);
 
// Give it a more-formed code. The first three characters have changed. Normally they would be
// the same, but it's done here to ensure that they are ignored.
token = [self tokenForDataWithFormat:@"%c]0;XXXbar", ESC];
token = [self tokenForDataWithFormat:@"%c]0;XXXbar", VT100CC_ESC];
assert(token->type == VT100_WAIT);
assert(_savedState.allKeys.count > 0);
 
// Now a fully-formed code. The entire string value must come from saved state.
token = [self tokenForDataWithFormat:@"%c]0;XXXXXX%c", ESC, VT100CC_BEL];
token = [self tokenForDataWithFormat:@"%c]0;XXXXXX%c", VT100CC_ESC, VT100CC_BEL];
assert(token->type == XTERMCC_WINICON_TITLE);
assert([token.string isEqualToString:@"foobar"]);
}
 
- (void)testEmbeddedColon {
VT100Token *token = [self tokenForDataWithFormat:@"%c]1;foo:bar%c", ESC, VT100CC_BEL];
VT100Token *token = [self tokenForDataWithFormat:@"%c]1;foo:bar%c", VT100CC_ESC, VT100CC_BEL];
assert(token->type == XTERMCC_ICON_TITLE);
assert([token.string isEqualToString:@"foo:bar"]);
}
 
- (void)testUnsupportedMode {
VT100Token *token = [self tokenForDataWithFormat:@"%c]999;foo%c", ESC, VT100CC_BEL];
VT100Token *token = [self tokenForDataWithFormat:@"%c]999;foo%c", VT100CC_ESC, VT100CC_BEL];
assert(token->type == VT100_NOTSUPPORT);
}
 
- (void)testBelAfterEmbeddedOSC {
VT100Token *token = [self tokenForDataWithFormat:@"%c]1;%c]%c", ESC, ESC, VT100CC_BEL];
VT100Token *token = [self tokenForDataWithFormat:@"%c]1;%c]%c", VT100CC_ESC, VT100CC_ESC, VT100CC_BEL];
assert(token->type == XTERMCC_ICON_TITLE);
assert([token.string isEqualToString:@""]);
}
 
- (void)testIgnoreEmbeddedOSCWhenFailing {
VT100Token *token = [self tokenForDataWithFormat:@"%c]x%c]%c", ESC, ESC, VT100CC_BEL];
VT100Token *token = [self tokenForDataWithFormat:@"%c]x%c]%c", VT100CC_ESC, VT100CC_ESC, VT100CC_BEL];
assert(token->type == VT100_NOTSUPPORT);
assert(iTermParserNumberOfBytesConsumed(&_context) == 6);
}
Loading
Loading
Loading
Loading
@@ -32,6 +32,15 @@ NS_INLINE unsigned char iTermParserPeek(iTermParserContext *context) {
return context->datap[0];
}
 
NS_INLINE BOOL iTermParserTryPeek(iTermParserContext *context, unsigned char *c) {
if (iTermParserCanAdvance(context)) {
*c = iTermParserPeek(context);
return YES;
} else {
return NO;
}
}
NS_INLINE void iTermParserAdvance(iTermParserContext *context) {
context->datap++;
context->datalen--;
Loading
Loading
@@ -107,3 +116,36 @@ NS_INLINE BOOL iTermParserConsumeInteger(iTermParserContext *context, int *n) {
 
return numDigits > 0;
}
#pragma mark - CSI
#define VT100CSIPARAM_MAX 16 // Maximum number of CSI parameters in VT100Token.csi->p.
#define VT100CSISUBPARAM_MAX 16 // Maximum number of CSI sub-parameters in VT100Token.csi->p.
typedef struct {
// Integer parameters. The first |count| elements are valid. -1 means the value is unset; set
// values are always nonnegative.
int p[VT100CSIPARAM_MAX];
// Number of defined values in |p|.
int count;
// An integer that holds a packed representation of the prefix byte, intermediate byte, and
// final byte.
int32_t cmd;
// Sub-parameters.
int sub[VT100CSIPARAM_MAX][VT100CSISUBPARAM_MAX];
// Number of subparameters for each parameter.
int subCount[VT100CSIPARAM_MAX];
} CSIParam;
// If the n'th parameter has a negative (default) value, replace it with |value|.
// CSI parameter values are all initialized to -1 before parsing, so this has the effect of setting
// a value iff it hasn't already been set.
// If there aren't yet n+1 parameters, increase the count to n+1.
NS_INLINE void iTermParserSetCSIParameterIfDefault(CSIParam *csiParam, int n, int value) {
csiParam->p[n] = csiParam->p[n] < 0 ? value : csiParam->p[n];
csiParam->count = MAX(csiParam->count, n + 1);
}
Loading
Loading
@@ -458,6 +458,16 @@
 
}
 
- (void)sendStringToTerminalWithFormat:(NSString *)formatString, ... {
va_list args;
va_start(args, formatString);
NSString *string = [[[NSString alloc] initWithFormat:formatString arguments:args] autorelease];
va_end(args);
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
[self sendDataToTerminal:data];
}
#pragma mark - VT100ScreenDelegate
 
- (void)screenSetColor:(NSColor *)color forKey:(int)key {
Loading
Loading
@@ -4143,4 +4153,190 @@
assert(buffer[2].backgroundColor == 5);
}
 
#pragma mark - CSI Tests
- (void)testCSI_CUD {
// Cursor Down Ps Times (default = 1) (CUD)
// This control function moves the cursor down a specified number of lines in the same column. The
// cursor stops at the bottom margin. If the cursor is already below the bottom margin, then the
// cursor stops at the bottom line.
// Test basic usage, default parameter.
VT100Screen *screen = [self screenWithWidth:3 height:5];
[screen.currentGrid setCursorX:1];
[screen.currentGrid setCursorY:1];
[self sendStringToTerminalWithFormat:@"\033[B"];
assert(screen.currentGrid.cursorX == 1);
assert(screen.currentGrid.cursorY == 2);
// Basic usage, explicit parameter.
screen = [self screenWithWidth:3 height:5];
[screen.currentGrid setCursorX:1];
[screen.currentGrid setCursorY:1];
[self sendStringToTerminalWithFormat:@"\033[2B"];
assert(screen.currentGrid.cursorX == 1);
assert(screen.currentGrid.cursorY == 3);
// Start inside scroll region - should stop at bottom margin
screen = [self screenWithWidth:3 height:5];
[screen terminalSetScrollRegionTop:2 bottom:4];
[screen.currentGrid setCursorX:1];
[screen.currentGrid setCursorY:2];
[self sendStringToTerminalWithFormat:@"\033[99B"];
assert(screen.currentGrid.cursorX == 1);
assert(screen.currentGrid.cursorY == 4);
// Start above scroll region - should stop at bottom margin
screen = [self screenWithWidth:3 height:5];
[screen terminalSetScrollRegionTop:2 bottom:3];
[screen.currentGrid setCursorX:1];
[screen.currentGrid setCursorY:0];
[self sendStringToTerminalWithFormat:@"\033[99B"];
assert(screen.currentGrid.cursorX == 1);
assert(screen.currentGrid.cursorY == 3);
// Start below bottom margin - should stop at bottom of screen.
screen = [self screenWithWidth:3 height:5];
[screen terminalSetScrollRegionTop:1 bottom:2];
[screen.currentGrid setCursorX:1];
[screen.currentGrid setCursorY:3];
[self sendStringToTerminalWithFormat:@"\033[99B"];
assert(screen.currentGrid.cursorX == 1);
assert(screen.currentGrid.cursorY == 4);
}
- (void)testCSI_CUF {
// Cursor Forward Ps Times (default = 1) (CUF)
// This control function moves the cursor to the right by a specified number of columns. The
// cursor stops at the right border of the page.
// Test basic usage, default parameter.
VT100Screen *screen = [self screenWithWidth:5 height:5];
[screen.currentGrid setCursorX:1];
[screen.currentGrid setCursorY:1];
[self sendStringToTerminalWithFormat:@"\033[C"];
assert(screen.currentGrid.cursorX == 2);
assert(screen.currentGrid.cursorY == 1);
// Test basic usage, explicit parameter.
screen = [self screenWithWidth:5 height:5];
[screen.currentGrid setCursorX:1];
[screen.currentGrid setCursorY:1];
[self sendStringToTerminalWithFormat:@"\033[2C"];
assert(screen.currentGrid.cursorX == 3);
assert(screen.currentGrid.cursorY == 1);
// Test stops on right border.
screen = [self screenWithWidth:5 height:5];
[screen.currentGrid setCursorX:1];
[screen.currentGrid setCursorY:1];
[self sendStringToTerminalWithFormat:@"\033[99C"];
assert(screen.currentGrid.cursorX == 4);
assert(screen.currentGrid.cursorY == 1);
// Test respects region when starting inside it
screen = [self screenWithWidth:5 height:5];
[screen terminalSetUseColumnScrollRegion:YES];
[screen terminalSetLeftMargin:1 rightMargin:3];
[screen.currentGrid setCursorX:2];
[screen.currentGrid setCursorY:1];
[self sendStringToTerminalWithFormat:@"\033[99C"];
assert(screen.currentGrid.cursorX == 3);
assert(screen.currentGrid.cursorY == 1);
// Test does not respect region when starting outside it
screen = [self screenWithWidth:5 height:5];
[screen terminalSetUseColumnScrollRegion:YES];
[screen terminalSetLeftMargin:1 rightMargin:2];
[screen.currentGrid setCursorX:3];
[screen.currentGrid setCursorY:1];
[self sendStringToTerminalWithFormat:@"\033[99C"];
assert(screen.currentGrid.cursorX == 4);
assert(screen.currentGrid.cursorY == 1);
}
/*
{ 0, 0, 'C', VT100CSI_CUF, 1, -1 },
{ 0, 0, 'D', VT100CSI_CUB, 1, -1 },
{ 0, 0, 'E', VT100CSI_CNL, 1, -1 },
{ 0, 0, 'F', VT100CSI_CPL, 1, -1 },
{ 0, 0, 'G', ANSICSI_CHA, 1, -1 },
{ 0, 0, 'H', VT100CSI_CUP, 1, 1 },
// I not supported (Cursor Forward Tabulation P s tab stops (default = 1) (CHT))
{ 0, 0, 'J', VT100CSI_ED, 0, -1 },
// ?J not supported (Erase in Display (DECSED))
{ 0, 0, 'K', VT100CSI_EL, 0, -1 },
// ?K not supported ((Erase in Line (DECSEL))
{ 0, 0, 'L', XTERMCC_INSLN, 1, -1 },
{ 0, 0, 'M', XTERMCC_DELLN, 1, -1 },
{ 0, 0, 'P', XTERMCC_DELCH, 1, -1 },
{ 0, 0, 'S', XTERMCC_SU, 1, -1 },
// ?Pi;Pa;PvS not supported (Sixel/ReGIS)
{ 0, 0, 'T', XTERMCC_SD, 1, -1 },
// Ps;Ps;Ps;Ps;PsT not supported (Initiate highlight mouse tracking)
{ 0, 0, 'X', ANSICSI_ECH, 1, -1 },
{ 0, 0, 'Z', ANSICSI_CBT, 1, -1 },
// ` not supported (Character Position Absolute [column] (default = [row,1]) (HPA))
// a not supported (Character Position Relative [columns] (default = [row,col+1]) (HPR))
// b not supported (Repeat the preceding graphic character P s times (REP))
{ 0, 0, 'c', VT100CSI_DA, 0, -1 },
{ '>', 0, 'c', VT100CSI_DA2, 0, -1 },
{ 0, 0, 'd', ANSICSI_VPA, 1, -1 },
{ 0, 0, 'e', ANSICSI_VPR, 1, -1 },
{ 0, 0, 'f', VT100CSI_HVP, 1, 1 },
{ 0, 0, 'g', VT100CSI_TBC, 0, -1 },
{ 0, 0, 'h', VT100CSI_SM, -1, -1 },
{ '?', 0, 'h', VT100CSI_DECSET, -1, -1 },
{ 0, 0, 'i', ANSICSI_PRINT, 0, -1 },
// ?i not supported (Media Copy (MC, DEC-specific))
{ 0, 0, 'l', VT100CSI_RM, -1, -1 },
{ '?', 0, 'l', VT100CSI_DECRST, -1, -1 },
{ 0, 0, 'm', VT100CSI_SGR, 0, -1 },
{ '>', 0, 'm', VT100CSI_SET_MODIFIERS, -1, -1 },
{ 0, 0, 'n', VT100CSI_DSR, 0, -1 },
{ '>', 0, 'n', VT100CSI_RESET_MODIFIERS, -1, -1 },
{ '?', 0, 'n', VT100CSI_DECDSR, 0, -1 },
// >p not supported (Set resource value pointerMode. This is used by xterm to decide whether
// to hide the pointer cursor as the user types.)
{ '!', 0, 'p', VT100CSI_DECSTR, -1, -1 },
// $p not supported (Request ANSI mode (DECRQM))
// ?$p not supported (Request DEC private mode (DECRQM))
// "p not supported (Set conformance level (DECSCL))
// q not supported (Load LEDs (DECLL))
{ 0, ' ', 'q', VT100CSI_DECSCUSR, 0, -1 },
// "q not supported (Select character protection attribute (DECSCA))
{ 0, 0, 'r', VT100CSI_DECSTBM, -1, -1 },
// $r not supported (Change Attributes in Rectangular Area (DECCARA))
{ 0, 0, 's', VT100CSI_DECSLRM_OR_ANSICSI_SCP, -1, -1 },
// ?s not supported (Save DEC Private Mode Values)
// t tested in -testWindowManipulationCodes
// $t not supported (Reverse Attributes in Rectangular Area (DECRARA))
// >t not supported (Set one or more features of the title modes)
// SP t not supported (Set warning-bell volume (DECSWBV, VT520))
{ 0, 0, 'u', ANSICSI_RCP, -1, -1 },
{ 1, XTERMCC_DEICONIFY },
{ 2, XTERMCC_ICONIFY },
{ 3, XTERMCC_WINDOWPOS },
{ 4, XTERMCC_WINDOWSIZE_PIXEL },
{ 5, XTERMCC_RAISE },
{ 6, XTERMCC_LOWER },
// 7 is not supported (Refresh the window)
{ 8, XTERMCC_WINDOWSIZE },
// 9 is not supported (Various maximize window actions)
// 10 is not supported (Various full-screen actions)
{ 11, XTERMCC_REPORT_WIN_STATE },
// 12 is not defined
{ 13, XTERMCC_REPORT_WIN_POS },
{ 14, XTERMCC_REPORT_WIN_PIX_SIZE },
// 15, 16, and 17 are not defined
{ 18, XTERMCC_REPORT_WIN_SIZE },
{ 19, XTERMCC_REPORT_SCREEN_SIZE },
{ 20, XTERMCC_REPORT_ICON_TITLE },
{ 21, XTERMCC_REPORT_WIN_TITLE },
{ 22, XTERMCC_PUSH_TITLE },
{ 23, XTERMCC_POP_TITLE },
*/
@end
Loading
Loading
@@ -27,6 +27,7 @@ DECLARE_TEST(PTYSessionTest)
DECLARE_TEST(iTermPasteHelperTest)
DECLARE_TEST(SemanticHistoryTest)
DECLARE_TEST(VT100XtermParserTest);
DECLARE_TEST(VT100CSIParserTest);
 
static void RunTestsInObject(iTermTest *test) {
NSLog(@"-- Begin tests in %@ --", [test class]);
Loading
Loading
@@ -61,7 +62,8 @@ NSArray *AllTestClasses() {
[PTYSessionTest class],
[iTermPasteHelperTest class],
[SemanticHistoryTest class],
[VT100XtermParserTest class] ];
[VT100XtermParserTest class],
[VT100CSIParserTest class] ];
}
 
NSArray *TestClassesToRun(NSArray *include, NSArray *exclude) {
Loading
Loading
@@ -92,6 +94,7 @@ int main(int argc, const char * argv[]) {
// Up to one of |include| or |exclude| may be non-nil. Set it to an array of test Class objects.
// If include is set, exactly the listed tests will be run. If exclude is set, all but the
// listed tests will run.
NSArray *include = nil;
// This is what I often use because applescript tests are slow.
// NSArray *exclude = @[ [AppleScriptTest class] ];
Loading
Loading
Loading
Loading
@@ -24,6 +24,7 @@ typedef enum {
// Tests can use this to prevent warning popups.
+ (void)setWarningHandler:(id<iTermWarningHandler>)handler;
+ (id<iTermWarningHandler>)warningHandler;
+ (BOOL)showingWarning;
 
// Show a warning, optionally with a suppression checkbox. It may not be shown
// if it was previously suppressed.
Loading
Loading
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too.
When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.
Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and modification follow.
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.
You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.
c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.
In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.
7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.
10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
esctest
Automatic unit tests for terminal emulation
George Nachman
georgen@google.com
esctest is a suite of unit tests that test a terminal emulator's similarity to a
theoretical ideal. That ideal is defined as "xterm, but without bugs in George's
opinion."
The tested set of control sequences are documented somewhat tersely at this URL:
http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
The official documentation for DEC-supported escape sequences is here:
http://www.vt100.net/docs/vt510-rm/.
All tests are automatic; no user interaction is required. As a consequence, some
control sequences cannot be tested. For example, it is impossible to examine the
color of a particular cell, so color-setting control sequences are not testable.
The character value of a cell is testable, as are various window attributes, and
cursor position; these form the bulk of the tests.
Usage
-----
The most basic usage is:
esctest.py --expected-terminal={iTerm2,xterm}
Flags are as follows:
--disable-xterm-checksum-bug
xterm's implementation of DECRQCRA (as of patch 314) contains a bug. DECRQCRA is
essential to these tests. By default, a workaround for the bug is used. If it is
fixed in the future, this flag should be given until the workaround is dropped.
--include=regex
Only tests whose name matches "regex" will be run.
--expected-terminal=terminal
This has two effects:
1. Each terminal has a different set of known bugs. It should be set to the name
of the terminal the test is running in. This allows any novel bug to be properly
identified, and any bugs that have been fixed can also be found.
2. There are minor implementation differences between terminals (for example the
character used for "blanks", such as when a character is erased). The assertions
and expectations of tests are changed to be appropriate for the current terminal
to avoid false or uninformative failures.
The legal values of "terminal" are "xterm" and "iTerm2".
--no-print-logs
Normally, the test logs are printed when the test finishes. Use this argument to
see what the screen looked like at the time the last-run test finished.
--test-case-dir=path
If set text files are created in "path" for each test run. They contain the data
that were sent to the terminal. This can be helpful to debug a failing test.
--stop-on-failure
If set, tests stop running after the first failure encountered.
--force
If set, tests will run to completion even though an assertion may fail along the
way. Failing tests will appear to pass. This can be useful for debugging.
--options option1 option2 ...
Defines which optional features are enabled in the terminal being tested.
The following options are supported:
* xtermWinopsEnabled
This option indicates that xterm is configured to allow all window operations,
some of which are off by default. The following X resources must be set before
this option is used:
xterm*disallowedWindowOps:
xterm*allowWindowOps: true
* disableWideChars
This option indicates that wide character (that is, UTF-8) support is disabled.
8-bit controls are tested when this option is enabled.
--max-vt-level=level
Tests are tagged with the VT level required for their execution. No test needing
features from a higher VT level will be run. The default value is 5. In order to
support VT level 5 in xterm, set the following resource:
xterm*decTerminalID: 520
--logfile=file
The logs are written to "file", which defaults to "/tmp/esctest.log".
--v=verbosity
Verbosity level for logging. The following levels are defined:
* 1: Errors only.
* 2: Errors and informational messages (the default).
* 3: Errors, informational messages, and debug messages.
Examples
--------
To test a vanilla xterm:
esctest.py --expected-terminal=xterm --max-vt-level=4
To test xterm with winops enabled and emulating a VT 520:
esctest.py --expected-terminal=xterm --options xtermWinopsEnabled
To test iTerm2:
esctest.py --expected-terminal=iTerm2
To debug a failing test:
esctest.py --test-case-dir=/tmp --stop-on-failure --no-print-logs
Writing Tests
-------------
Tests are divided into classes. There's one class per file. Each class has tests
for one escape sequence, which may have multiple functions (for example, xterm's
winops).
Test methods must be of the form "test_" + escape sequence name + "_" + details.
Methods not beginning with test_ will not be run.
Every method should use one of the built-in assertion methods, or it will always
fail. The assertion methods are defined in escutil and are:
AssertGE(actual, minimum)
Asserts that the first value is at least as large as the second value.
AssertEQ(actual, expected)
Asserts that both values are equal.
AssertTrue(value, details)
Asserts the value is true. The optional detail will be logged on failure.
AssertScreenCharsInRectEqual(rect, strings)
Asserts that the characters on the screen within a given rectangle equal those
passed in the second argument, which is a list of strings (one per row).
Test methods may be decorated with the following decorators, defined in escutil:
@vtLevel(minimum)
The test will be run only in the --max-vt-level is at least "minimum".
@intentionalDeviationFromSpec(terminal, reason)
This is for documentation purposes only. The given terminal has some quirk but
it is intentional, as described in "reason".
@optionRequired(terminal, option, allowPassWithoutOption)
If the given option is not provided for the given terminal, the test should be
expected to fail. If it passes anyway, that is an error. The optional argument
allowPassWithoutOption may be set to true to tolerate passage, which is useful
for "flaky" tests which might pass by accident. Its use should be avoided.
@knownBug(terminal, reason, noop, shouldTry)
Indicates that a test is known to fail for a given terminal. The nature of the
problem should be described in reason. If the test passes when a terminal does
nothing at all, then noop should be set to True. If the test should be skipped
for this terminal then the optional shouldTry should be False (e.g., for crash
bugs).
All test classes are in the "tests" directory. Each is explicitly linked to from
__init__.py.
import escargs
NUL = chr(0)
BS = chr(8)
TAB = chr(9)
LF = chr(10)
VT = chr(11)
CR = chr(13)
FF = chr(12)
ESC = chr(27)
S7C1T = ESC + " F"
S8C1T = ESC + " G"
ST = ESC + "\\"
# VT x00 level. vtLevel may be 1, 2, 3, 4, or 5.
vtLevel = 1
def blank():
if escargs.args.expected_terminal == "xterm":
return ' '
else:
return NUL
import argparse
# To enable this option, add the following line to ~/.Xresources:
# xterm*disallowedWindowOps:
# Then run "xrdb -merge ~/.Xresources" and restart xterm.
XTERM_WINOPS_ENABLED = "xtermWinopsEnabled"
DISABLE_WIDE_CHARS = "disableWideChars"
parser = argparse.ArgumentParser()
parser.add_argument("--disable-xterm-checksum-bug",
help="Don't use buggy parameter order for DECRQCRA",
action="store_true")
parser.add_argument("--include",
help="Regex for names of tests to run.",
default=".*")
parser.add_argument("--expected-terminal",
help="Terminal in use. Modifies tests for known differences.",
choices=("iTerm2", "xterm"),
default="iTerm2")
parser.add_argument("--no-print-logs",
help="Print logs after finishing?",
action="store_true")
parser.add_argument("--test-case-dir",
help="Create files with test cases in the specified directory",
default=None)
parser.add_argument("--stop-on-failure",
help="Stop running tests after a failure.",
action="store_true")
parser.add_argument("--force",
help="If set, assertions won't stop execution.",
action="store_true")
parser.add_argument("--options",
help="Space-separated options that are enabled.",
nargs="+",
choices=[ XTERM_WINOPS_ENABLED, DISABLE_WIDE_CHARS ])
parser.add_argument("--max-vt-level",
help="Do not run tests requiring a higher VT level than this.",
type=int,
default=5)
parser.add_argument("--logfile",
help="Log file to write output to",
default="/tmp/esctest.log")
parser.add_argument("--v",
help="Verbosity level. 1=errors, 2=errors and info, 3=debug, errors, and info",
default=2,
type=int)
from esc import ESC
import escargs
import escio
# DECSET/DECRESET
Allow80To132 = 40 # Allow 80->132 Mode
ALTBUF = 47 # Switch to alt buf
DECAAM = 100
DECANM = 2
DECARM = 8
DECARSM = 98
DECAWM = 7 # Autowrap
DECBKM = 67
DECCANSM = 101
DECCKM = 1
DECCOLM = 3 # 132-column mode
DECCRTSM = 97
DECESKM = 104
DECHCCM = 60
DECHDPXM = 103
DECHEBM = 35
DECHEM = 36
DECKBUM = 68
DECKPM = 81
DECLRMM = 69 # Left/right margin enabled
DECMCM = 99
DECNAKB = 57
DECNCSM = 95
DECNCSM = 95 # Clear screen on enabling column mode
DECNKM = 66
DECNRCM = 42
DECNULM = 102
DECOM = 6 # Origin mode
DECOSCNM = 106
DECPCCM = 64
DECPEX = 19
DECPFF = 18
DECRLCM = 96
DECRLM = 34 # Right-to-left mode
DECSCLM = 4
DECSCNM = 5
DECTCEM = 25
DECVCCM = 61
DECVSSM = 69
DECXRLM = 73
MoreFix = 41 # Work around bug in more(1) (see details in test_DECSET_MoreFix)
OPT_ALTBUF = 1047 # Switch to alt buf. DECRESET first clears the alt buf.
OPT_ALTBUF_CURSOR = 1049 # Like 1047 but saves/restores main screen's cursor position.
ReverseWraparound = 45 # Reverse-wraparound mode (only works on conjunction with DECAWM)
SaveRestoreCursor = 1048 # Save cursor as in DECSC.
# Xterm Winops (CSI Ps t)
WINOP_DEICONIFY = 1
WINOP_ICONIFY = 2
WINOP_MOVE = 3
WINOP_RESIZE_PIXELS = 4
WINOP_RESIZE_CHARS = 8
WINOP_MAXIMIZE = 9
WINOP_FULLSCREEN = 10
WINOP_REPORT_WINDOW_STATE = 11
WINOP_REPORT_WINDOW_POSITION = 13
WINOP_REPORT_WINDOW_SIZE_PIXELS = 14
WINOP_REPORT_TEXT_AREA_CHARS = 18
WINOP_REPORT_SCREEN_SIZE_CHARS = 19
WINOP_REPORT_ICON_LABEL = 20
WINOP_REPORT_WINDOW_TITLE = 21
WINOP_PUSH_TITLE = 22
WINOP_POP_TITLE = 23
# WINOP_MAXIMIZE sub-commands
WINOP_MAXIMIZE_HV = 1
WINOP_MAXIMIZE_V = 2
WINOP_MAXIMIZE_H = 3
# WINOP_FULLSCREEN sub-commands
WINOP_FULLSCREEN_EXIT = 0
WINOP_FULLSCREEN_ENTER = 1
WINOP_FULLSCREEN_TOGGLE = 2
# WINOP_PUSH_TITLE sub-commands
WINOP_PUSH_TITLE_ICON_AND_WINDOW = 0
WINOP_PUSH_TITLE_ICON = 1
WINOP_PUSH_TITLE_WINDOW = 2
# WINOP_POP_TITLE sub-commands
WINOP_POP_TITLE_ICON_AND_WINDOW = 0
WINOP_POP_TITLE_ICON = 1
WINOP_POP_TITLE_WINDOW = 2
# DECDSR
DECCKSR = 63
DECMSR = 62
DECXCPR = 6
DSRCPR = 6
DSRDECLocatorStatus = 55
DSRIntegrityReport = 75
DSRKeyboard = 26
DSRLocatorId = 56
DSRMultipleSessionStatus = 85
DSRPrinterPort = 15
DSRUDKLocked = 25
DSRXtermLocatorStatus = 55
# SM/RM
EBM = 19
FEAM = 13
FETM = 14
GATM = 1
HEM = 10
IRM = 4
KAM = 2
LNM = 20
MATM = 15
PUM = 11
SATM = 17
SRM = 12
SRTM = 5
TSM = 18
TTM = 16
VEM = 7
# SMTitle
SET_HEX = 0
QUERY_HEX = 1
SET_UTF8 = 2
QUERY_UTF8 = 3
def ANSIRC():
"""Restore cursor."""
escio.WriteCSI(final="u")
def ANSISC():
"""Save cursor."""
escio.WriteCSI(final="s")
def APC():
"""Application Program Command."""
if escio.use8BitControls:
escio.Write(chr(0x9f))
else:
escio.Write(ESC + "_")
def CBT(Pn=None):
"""Move cursor back by Pn tab stops or to left margin. Default is 1."""
if Pn is None:
params = []
else:
params = [ Pn ]
escio.WriteCSI(params=params, final="Z")
def ChangeColor(*args):
params = [ 4 ]
isQuery = True
try:
params.index("?")
except:
isQuery = False
params.extend(args)
escio.WriteOSC(params, requestsReport=isQuery)
def ChangeDynamicColor(*args):
params = [ ]
isQuery = True
try:
params.index("?")
except:
isQuery = False
params.extend(args)
escio.WriteOSC(params, requestsReport=isQuery)
def ChangeSpecialColor(*args):
if len(args) > 0 and int(args[0]) >= 10:
params = []
else:
params = [ 5 ]
isQuery = True
try:
params.index("?")
except:
isQuery = False
params.extend(args)
escio.WriteOSC(params, requestsReport=isQuery)
def ChangeWindowTitle(title, bel=False, suppressSideChannel=False):
"""Change the window title."""
escio.WriteOSC(params=[ "2", title ], bel=bel, requestsReport=suppressSideChannel)
def ChangeIconTitle(title, bel=False, suppressSideChannel=False):
"""Change the icon (tab) title."""
escio.WriteOSC(params=[ "1", title ], bel=bel, requestsReport=suppressSideChannel)
def ChangeWindowAndIconTitle(title, bel=False, suppressSideChannel=False):
"""Change the window and icon (tab) title."""
escio.WriteOSC(params=[ "0", title ],
bel=bel,
requestsReport=suppressSideChannel)
def CHA(Pn=None):
"""Move cursor to Pn column, or first column by default."""
if Pn is None:
params = []
else:
params = [ Pn ]
escio.WriteCSI(params=params, final="G")
def CHT(Ps=None):
"""Move cursor forward by Ps tab stops (default is 1)."""
if Ps is None:
params = []
else:
params = [ Ps ]
escio.WriteCSI(params=params, final="I")
def CNL(Ps=None):
"""Cursor down Ps times and to the left margin."""
if Ps is None:
params = []
else:
params = [ Ps ]
escio.WriteCSI(params=params, final="E")
def CPL(Ps=None):
"""Cursor up Ps times and to the left margin."""
if Ps is None:
params = []
else:
params = [ Ps ]
escio.WriteCSI(params=params, final="F")
def CUB(Ps=None):
"""Cursor left Ps times."""
if Ps is None:
params = []
else:
params = [ Ps ]
escio.WriteCSI(params=params, final="D")
def CUD(Ps=None):
"""Cursor down Ps times."""
if Ps is None:
params = []
else:
params = [ Ps ]
escio.WriteCSI(params=params, final="B")
def CUF(Ps=None):
"""Cursor right Ps times."""
if Ps is None:
params = []
else:
params = [ Ps ]
escio.WriteCSI(params=params, final="C")
def CUP(point=None, row=None, col=None):
""" Move cursor to |point| """
if point is None and row is None and col is None:
escio.WriteCSI(params=[ ], final="H")
elif point is None:
if row is None:
row = ""
if col is None:
escio.WriteCSI(params=[ row ], final="H")
else:
escio.WriteCSI(params=[ row, col ], final="H")
else:
escio.WriteCSI(params=[ point.y(), point.x() ], final="H")
def CUU(Ps=None):
"""Cursor up Ps times."""
if Ps is None:
params = []
else:
params = [ Ps ]
escio.WriteCSI(params=params, final="A")
def DA(Ps=None):
"""Request primary device attributes."""
if Ps is None:
params = []
else:
params = [ Ps ]
escio.WriteCSI(params=params, final="c")
def DA2(Ps=None):
"""Request secondary device attributes."""
if Ps is None:
params = []
else:
params = [ Ps ]
escio.WriteCSI(params=params, prefix='>', final="c")
def DCH(Ps=None):
"""Delete Ps characters at cursor."""
if Ps is None:
params = []
else:
params = [ Ps ]
escio.WriteCSI(params=params, final="P")
def DCS():
"""Device control string. Prefixes various commands."""
if escio.use8BitControls:
escio.Write(chr(0x90))
else:
escio.Write(ESC + "P")
def DECALN():
"""Write test pattern."""
escio.Write(ESC + "#8")
def DECBI():
"""Index left, scrolling region right if cursor at margin."""
escio.Write(ESC + "6")
def DECCRA(source_top=None, source_left=None, source_bottom=None,
source_right=None, source_page=None, dest_top=None,
dest_left=None, dest_page=None):
"""Copy a region."""
params = [ source_top,
source_left,
source_bottom,
source_right,
source_page,
dest_top,
dest_left,
dest_page ]
escio.WriteCSI(params=params, intermediate="$", final="v")
def DECDC(Pn=None):
"""Delete column at cursor."""
if Pn is not None:
params = [ Pn ]
else:
params = []
escio.WriteCSI(params=params, intermediate="'", final="~")
def DECDHL(x):
"""Double-width, double-height line.
x = 3: top half
x = 4: bottom half"""
escio.Write(ESC + "#" + str(x))
def DECDSR(Ps, Pid=None, suppressSideChannel=False):
"""Send device status request. Does not read response."""
if Ps is None:
params = []
else:
if Pid is None:
params = [ Ps ]
else:
params = [ Ps, Pid ]
escio.WriteCSI(params=params, prefix='?', requestsReport=suppressSideChannel, final='n')
def DECERA(Pt, Pl, Pb, Pr):
"""Erase rectangle."""
escio.WriteCSI(params=[ Pt, Pl, Pb, Pr ], intermediate="$", final="z")
def DECFI():
"""Forward index."""
escio.Write(ESC + "9")
def DECFRA(Pch, Pt, Pl, Pb, Pr):
"""Fill rectangle with Pch"""
escio.WriteCSI(params=[ Pch, Pt, Pl, Pb, Pr ], intermediate="$", final="x")
def DECIC(Pn=None):
"""Insert column at cursor."""
if Pn is not None:
params = [ Pn ]
else:
params = []
escio.WriteCSI(params=params, intermediate="'", final="}")
def DECID():
"""Obsolete form of DA."""
if escio.use8BitControls:
escio.Write(chr(0x9a))
else:
escio.Write(ESC + "Z")
def DECRC():
"""Restore the cursor and resets various attributes."""
escio.Write(ESC + "8")
def DECRQCRA(Pid, Pp=None, rect=None):
"""Compute the checksum (16-bit sum of ordinals) in a rectangle."""
# xterm versions 314 and earlier incorrectly expect the Pid in the second
# argument and ignore Pp.
# For the time being, iTerm2 is compatible with the bug.
if not escargs.args.disable_xterm_checksum_bug:
Pid, Pp = Pp, Pid
params = [ Pid ]
if Pp is not None:
params += [ Pp ]
elif Pp is None and rect is not None:
params += [ "" ]
if rect is not None:
params.extend(rect.params())
escio.WriteCSI(params=params, intermediate='*', final='y', requestsReport=True)
def DECRQM(mode, DEC):
"""Requests if a mode is set or not."""
if DEC:
escio.WriteCSI(params=[ mode ], intermediate='$', prefix='?', final='p')
else:
escio.WriteCSI(params=[ mode ], intermediate='$', final='p')
def DECRQSS(Pt):
escio.WriteDCS("$q", Pt)
def DECRESET(Pm):
"""Reset the parameter |Pm|."""
escio.WriteCSI(params=[ Pm ], prefix='?', final='l')
def DECSASD(Ps=None):
"""Direct output to status line if Ps is 1, to main display if 0."""
if Ps is None:
params = []
else:
params = [ Ps ]
escio.WriteCSI(params=params, intermediate='$', final="}")
def DECSC():
"""Saves the cursor."""
escio.Write(ESC + "7")
def DECSCA(Ps=None):
"""Turn on character protection if Ps is 1, off if 0."""
if Ps is None:
params = []
else:
params = [ Ps ]
escio.WriteCSI(params=params, intermediate='"', final="q")
def DECSCL(level, sevenBit=None):
"""Level should be one of 61, 62, 63, or 64. sevenBit can be 0 or 1, or not
specified."""
if sevenBit is None:
params = [ level ]
else:
params = [ level, sevenBit ]
escio.WriteCSI(params=params, intermediate='"', final="p")
def DECSCUSR(Ps=None):
"""Set cursor style 0 through 6, or default of 1."""
if Ps is None:
params = [ ]
else:
params = [ Ps ]
escio.WriteCSI(params=params, intermediate=' ', final="q")
def DECSED(Ps=None):
"""Like ED but respects character protection."""
if Ps is None:
params = []
else:
params = [ Ps ]
escio.WriteCSI(params=params, prefix='?', final="J")
def DECSEL(Ps=None):
"""Like EL but respects character protection."""
if Ps is None:
params = []
else:
params = [ Ps ]
escio.WriteCSI(params=params, prefix='?', final="K")
def DECSERA(Pt, Pl, Pb, Pr):
"""Selective erase rectangle."""
escio.WriteCSI(params=[ Pt, Pl, Pb, Pr ], intermediate="$", final="{")
def DECSET(Pm):
"""Set the parameter |Pm|."""
escio.WriteCSI(params=[ Pm ], prefix='?', final='h')
def DECSLRM(Pl, Pr):
"""Set the left and right margins."""
escio.WriteCSI(params=[ Pl, Pr ], final='s')
def DECSTBM(top=None, bottom=None):
"""Set Scrolling Region [top;bottom] (default = full size of window)."""
params = []
if top is not None:
params.append(top)
if bottom is not None:
params.append(bottom)
escio.WriteCSI(params=params, final="r")
def DECSTR():
"""Soft reset."""
escio.WriteCSI(prefix='!', final='p')
def DL(Pn=None):
"""Delete |Pn| lines at the cursor. Default value is 1."""
if Pn is None:
params = []
else:
params = [ Pn ]
escio.WriteCSI(params=params, final="M")
def DSR(Ps, suppressSideChannel=False):
"""Send device status request. Does not read response."""
if Ps is None:
params = []
else:
params = [ Ps ]
escio.WriteCSI(params=params, requestsReport=suppressSideChannel, final='n')
def ECH(Pn=None):
"""Erase |Pn| characters starting at the cursor. Default value is 1."""
if Pn is None:
params = []
else:
params = [ Pn ]
escio.WriteCSI(params=params, final="X")
def ED(Ps=None):
"""Erase characters, clearing display attributes. Works in or out of scrolling regions.
Ps = 0 (default): From the cursor through the end of the display
Ps = 1: From the beginning of the display through the cursor
Ps = 2: The complete display
"""
if Ps is None:
params = []
else:
params = [ Ps ]
escio.WriteCSI(params=params, final="J")
def EL(Ps=None):
"""Erase in the active line.
Ps = 0 (default): Erase to right of cursor
Ps = 1: Erase to left of cursor
Ps = 2: Erase whole line
"""
if Ps is None:
params = []
else:
params = [ Ps ]
escio.WriteCSI(params=params, final="K")
def EPA():
"""End protected area."""
if escio.use8BitControls:
escio.Write(chr(0x97))
else:
escio.Write(ESC + "W")
def HPA(Pn=None):
"""Position the cursor at the Pn'th column. Default value is 1."""
if Pn is None:
params = []
else:
params = [ Pn ]
escio.WriteCSI(params=params, final="`")
def HPR(Pn=None):
"""Position the cursor at the Pn'th column relative to its current position.
Default value is 1."""
if Pn is None:
params = []
else:
params = [ Pn ]
escio.WriteCSI(params=params, final="a")
def HTS():
"""Set a horizontal tab stop."""
if escio.use8BitControls:
escio.Write(chr(0x88))
else:
escio.Write(ESC + "H")
def HVP(point=None, row=None, col=None):
""" Move cursor to |point| """
if point is None and row is None and col is None:
escio.WriteCSI(params=[ ], final="H")
elif point is None:
if row is None:
row = ""
if col is None:
escio.WriteCSI(params=[ row ], final="H")
else:
escio.WriteCSI(params=[ row, col ], final="H")
else:
escio.WriteCSI(params=[ point.y(), point.x() ], final="f")
def ICH(Pn=None):
""" Insert |Pn| blanks at cursor. Cursor does not move. """
if Pn is None:
params = []
else:
params = [ Pn ]
escio.WriteCSI(params=params, final="@")
def IL(Pn=None):
"""Insert |Pn| blank lines after the cursor. Default value is 1."""
if Pn is None:
params = []
else:
params = [ Pn ]
escio.WriteCSI(params=params, final="L")
def IND():
"""Move cursor down one line."""
if escio.use8BitControls:
escio.Write(chr(0x84))
else:
escio.Write(ESC + "D")
def ManipulateSelectionData(Pc="", Pd=None):
params = [ "52", Pc ]
if Pd is not None:
params.append(Pd)
escio.WriteOSC(params)
def NEL():
"""Index plus carriage return."""
if escio.use8BitControls:
escio.Write(chr(0x85))
else:
escio.Write(ESC + "E")
def PM():
"""Privacy message."""
if escio.use8BitControls:
escio.Write(chr(0x9e))
else:
escio.Write(ESC + "^")
def REP(Ps=None):
"""Repeat the preceding character |Ps| times. Undocumented default is 1."""
if Ps is None:
params = []
else:
params = [ Ps ]
escio.WriteCSI(params=params, final="b")
def ResetSpecialColor(*args):
params = [ "105" ]
params.extend(args)
escio.WriteOSC(params)
def ResetColor(c=""):
escio.WriteOSC([ "104", c ])
def ResetDynamicColor(c):
escio.WriteOSC([ str(c) ])
def RI():
"""Move cursor up one line."""
if escio.use8BitControls:
escio.Write(chr(0x8d))
else:
escio.Write(ESC + "M")
def RIS():
"""Reset."""
escio.Write(ESC + "c")
def RM(Pm=None):
"""Reset mode."""
if Pm is None:
params = []
else:
params = [ Pm ]
escio.WriteCSI(params=params, final="l")
def RM_Title(Ps1, Ps2=None):
"""Reset title mode."""
params = [ Ps1 ]
if Ps2 is not None:
params.append(Ps2)
escio.WriteCSI(params=params, prefix=">", final="T")
def SD(Ps=None):
"""Scroll down by |Ps| lines. Default value is 1."""
if Ps is None:
params = []
else:
params = [ Ps ]
escio.WriteCSI(params=params, final="T")
def SM(Pm=None):
"""Set mode."""
if Pm is None:
params = []
else:
params = [ Pm ]
escio.WriteCSI(params=params, final="h")
def SM_Title(Ps1, Ps2=None):
"""Set title mode."""
params = [ Ps1 ]
if Ps2 is not None:
params.append(Ps2)
escio.WriteCSI(params=params, prefix=">", final="t")
def SOS():
"""Start of string."""
if escio.use8BitControls:
escio.Write(chr(0x98))
else:
escio.Write(ESC + "X")
def SPA():
"""Start protected area."""
if escio.use8BitControls:
escio.Write(chr(0x96))
else:
escio.Write(ESC + "V")
def SGR(*args):
"""Select graphic rendition. Params is an array of numbers."""
escio.WriteCSI(params=args, final="m")
def SU(Ps=None):
"""Scroll up by |Ps| lines. Default value is 1."""
if Ps is None:
params = []
else:
params = [ Ps ]
escio.WriteCSI(params=params, final="S")
def TBC(Ps=None):
"""Clear tab stop. Default arg is 0 (clear tabstop at cursor)."""
if Ps is None:
params = []
else:
params = [ Ps ]
escio.WriteCSI(params=params, final="g")
def VPA(Ps=None):
"""Move to line |Ps|. Default value is 1."""
if Ps is None:
params = []
else:
params = [ Ps ]
escio.WriteCSI(params=params, final="d")
def VPR(Ps=None):
"""Move down by |Ps| rows. Default value is 1."""
if Ps is None:
params = []
else:
params = [ Ps ]
escio.WriteCSI(params=params, final="e")
def XTERM_RESTORE(Ps=None):
"""Restore given DEC private mode parameters."""
if Ps is None:
params = []
else:
params = [ Ps ]
escio.WriteCSI(params=params, prefix="?", final="r")
def XTERM_SAVE(Ps=None):
"""Save given DEC private mode parameters."""
if Ps is None:
params = []
else:
params = [ Ps ]
escio.WriteCSI(params=params, prefix="?", final="s")
def XTERM_WINOPS(Ps1=None, Ps2=None, Ps3=None):
if Ps3 is not None:
params = [ Ps1, Ps2, Ps3 ]
elif Ps2 is not None:
params = [ Ps1, Ps2 ]
elif Ps1 is not None:
params = [ Ps1 ]
else:
params = []
requestsReport = Ps1 in [ WINOP_REPORT_WINDOW_STATE,
WINOP_REPORT_WINDOW_POSITION,
WINOP_REPORT_WINDOW_SIZE_PIXELS,
WINOP_REPORT_TEXT_AREA_CHARS,
WINOP_REPORT_SCREEN_SIZE_CHARS,
WINOP_REPORT_ICON_LABEL,
WINOP_REPORT_WINDOW_TITLE ]
escio.WriteCSI(params=params, final="t", requestsReport=requestsReport)
from esc import ESC, ST, NUL
from esclog import LogDebug, LogInfo, LogError, Print
import esctypes
import os
import select
import sys
import tty
stdin_fd = None
stdout_fd = None
gSideChannel = None
use8BitControls = False
def Init():
global stdout_fd
global stdin_fd
stdout_fd = os.fdopen(sys.stdout.fileno(), 'w', 0)
stdin_fd = os.fdopen(sys.stdin.fileno(), 'r', 0)
tty.setraw(stdin_fd)
def Shutdown():
tty.setcbreak(stdin_fd)
def Write(s, sideChannelOk=True):
global gSideChannel
if sideChannelOk and gSideChannel is not None:
gSideChannel.write(s)
stdout_fd.write(s)
def SetSideChannel(filename):
global gSideChannel
if filename is None:
if gSideChannel:
gSideChannel.close()
gSideChannel = None
else:
gSideChannel = open(filename, "w")
def OSC():
if use8BitControls:
return chr(0x9d)
else:
return ESC + "]"
def CSI():
if use8BitControls:
return chr(0x9b)
else:
return ESC + "["
def DCS():
if use8BitControls:
return chr(0x90)
else:
return ESC + "P"
def WriteOSC(params, bel=False, requestsReport=False):
str_params = map(str, params)
joined_params = ";".join(str_params)
ST = ESC + "\\"
BEL = chr(7)
if bel:
terminator = BEL
else:
terminator = ST
sequence = OSC() + joined_params + terminator
LogDebug("Send sequence: " + sequence.replace(ESC, "<ESC>"))
Write(sequence, sideChannelOk=not requestsReport)
def WriteDCS(introducer, params):
Write(DCS() + introducer + params + ST)
def WriteCSI(prefix="", params=[], intermediate="", final="", requestsReport=False):
if len(final) == 0:
raise esctypes.InternalError("final must not be empty")
def StringifyCSIParam(p):
if p is None:
return ""
else:
return str(p)
str_params = map(StringifyCSIParam, params)
# Remove trailing empty args
while len(str_params) > 0 and str_params[-1] == "":
str_params = str_params[:-1]
joined_params = ";".join(str_params)
sequence = CSI() + prefix + joined_params + intermediate + final
LogDebug("Send sequence: " + sequence.replace(ESC, "<ESC>"))
Write(sequence, sideChannelOk=not requestsReport)
def ReadOrDie(e):
c = read(1)
AssertCharsEqual(c, e)
def AssertCharsEqual(c, e):
if c != e:
raise esctypes.InternalError("Read %c (0x%02x), expected %c (0x%02x)" % (c, ord(c), e, ord(e)))
def ReadOSC(expected_prefix):
"""Read an OSC code starting with |expected_prefix|."""
ReadOrDie(ESC)
ReadOrDie(']')
for c in expected_prefix:
ReadOrDie(c)
s = ""
while not s.endswith(ST):
c = read(1)
s += c
return s[:-2]
def ReadCSI(expected_final, expected_prefix=None):
"""Read a CSI code ending with |expected_final| and returns an array of parameters. """
c = read(1)
if c == ESC:
ReadOrDie('[')
elif ord(c) != 0x9b:
raise esctypes.InternalError("Read %c (0x%02x), expected CSI" % (c, ord(c)))
params = []
current_param = ""
c = read(1)
if not c.isdigit() and c != ';':
if c == expected_prefix:
c = read(1)
else:
raise esctypes.InternalError("Unexpected character 0x%02x" % ord(c))
while True:
if c == ";":
params.append(int(current_param))
current_param = ""
elif c >= '0' and c <= '9':
current_param += c
else:
# Read all the final characters, asserting they match.
while True:
AssertCharsEqual(c, expected_final[0])
expected_final = expected_final[1:]
if len(expected_final) > 0:
c = read(1)
else:
break
if current_param == "":
params.append(None)
else:
params.append(int(current_param))
break
c = read(1)
return params
def ReadDCS():
""" Read a DCS code. Returns the characters between DCS and ST. """
c = read(1)
if c == ESC:
ReadOrDie("P")
elif ord(c) != 0x90:
raise esctypes.InternalError("Read %c (0x%02x), expected DCS" % (c, ord(c)))
result = ""
while not result.endswith(ST) and not result.endswith(chr(0x9c)):
c = read(1)
result += c
if result.endswith(ST):
return result[:-2]
else:
return result[:-1]
def read(n):
"""Try to read n bytes. Times out if it takes more than 1
second to read any given byte."""
s = ""
f = sys.stdin.fileno()
for i in xrange(n):
r, w, e = select.select([ f ], [], [], 1)
if f not in r:
raise esctypes.InternalError("Timeout waiting to read.")
s += os.read(f, 1)
return s
#!/usr/bin/python2.7
import argparse
import escargs
LOG_ERROR = 1
LOG_INFO = 2
LOG_DEBUG = 3
gLogFile = None
log = ""
def LogInfo(fmt):
Log(LOG_INFO, fmt)
def LogDebug(fmt):
Log(LOG_DEBUG, fmt)
def LogError(fmt):
Log(LOG_ERROR, fmt)
def Log(level, fmt):
global log
global gLogFile
if escargs.args.v >= level:
if gLogFile is None:
gLogFile = open(escargs.args.logfile, "w")
s = fmt + "\n"
gLogFile.write(s)
gLogFile.flush()
log += s
def Print():
print log.replace("\n", "\r\n")
#!/usr/bin/python2.7
import esc
import escargs
import esccmd
import escio
import esclog
import esctypes
import escutil
import inspect
import os
import re
import tests
import traceback
def init():
global newline
global logfile
global log
newline = "\r\n"
parser = escargs.parser
escargs.args = parser.parse_args()
logfile = open(escargs.args.logfile, "w")
log = ""
esc.vtLevel = escargs.args.max_vt_level
escio.Init()
def shutdown():
escio.Shutdown()
def reset():
esccmd.DECSCL(60 + esc.vtLevel, 1)
escio.use8BitControls = False
esccmd.DECSTR()
esccmd.XTERM_WINOPS(esccmd.WINOP_RESIZE_CHARS, 25, 80)
esccmd.DECRESET(esccmd.OPT_ALTBUF) # Is this needed?
esccmd.DECRESET(esccmd.OPT_ALTBUF_CURSOR) # Is this needed?
esccmd.DECRESET(esccmd.ALTBUF) # Is this needed?
esccmd.DECRESET(esccmd.DECLRMM) # This can be removed when the bug revealed by test_DECSET_DECLRMM_ResetByDECSTR is fixed.
esccmd.RM(esccmd.IRM)
esccmd.RM(esccmd.LNM)
# Technically, autowrap should be off by default (this is what the spec calls for).
# However, xterm and iTerm2 turn it on by default. xterm has a comment that says:
# There are a couple of differences from real DEC VTxxx terminals (to avoid
# breaking applications which have come to rely on xterm doing
# this)...autowrap mode should be reset (instead it's reset to the resource
# default).
esccmd.DECSET(esccmd.DECAWM)
esccmd.DECRESET(esccmd.MoreFix)
# Set and query title with utf-8
esccmd.RM_Title(0, 1)
esccmd.SM_Title(2, 3)
esccmd.ED(2)
# Pop the title stack just in case something got left on there
for i in xrange(5):
esccmd.XTERM_WINOPS(esccmd.WINOP_POP_TITLE,
esccmd.WINOP_PUSH_TITLE_ICON_AND_WINDOW)
# Clear tab stops and reset them at 1, 9, ...
esccmd.TBC(3)
width = escutil.GetScreenSize().width()
x = 1
while x <= width:
esccmd.CUP(esctypes.Point(x, 1))
esccmd.HTS()
x += 8
esccmd.CUP(esctypes.Point(1, 1))
esccmd.XTERM_WINOPS(esccmd.WINOP_DEICONIFY)
# Reset all colors.
esccmd.ResetColor()
# Work around a bug in reset colors where dynamic colors do not get reset.
esccmd.ChangeDynamicColor("10", "#000")
esccmd.ChangeDynamicColor("11", "#ffffff")
def AttachSideChannel(name):
if escargs.args.test_case_dir:
path = os.path.join(escargs.args.test_case_dir, name + ".txt")
escio.SetSideChannel(path)
def RemoveSideChannel():
escio.SetSideChannel(None)
def RunTest(class_name, name, method):
ok = True
esclog.LogInfo("Run test: " + class_name + "." + name)
try:
reset()
AttachSideChannel(name)
method()
RemoveSideChannel()
escutil.AssertAssertionAsserted()
esclog.LogInfo("Passed.")
except esctypes.KnownBug, e:
RemoveSideChannel()
esclog.LogInfo("Fails as expected: " + str(e))
ok = None
except esctypes.InsufficientVTLevel, e:
RemoveSideChannel()
esclog.LogInfo("Skipped because terminal lacks requisite capability: " +
str(e))
ok = None
except Exception, e:
RemoveSideChannel()
tb = traceback.format_exc()
ok = False
esclog.LogError("*** TEST %s FAILED:" % name)
esclog.LogError(tb)
esclog.LogInfo("")
return ok
def RunTests():
failed = 0
passed = 0
knownBugs = 0
failures = []
classes = []
for category in [ tests.tests ]:
classes.extend(category)
for testClass in classes:
try:
testObject = testClass()
except:
esclog.LogError("Failed to create test class " + testClass.__name__)
raise
members = inspect.getmembers(testObject, predicate=inspect.ismethod)
for name, method in members:
if name.startswith("test_") and (re.search(escargs.args.include, name) or
re.search(escargs.args.include, testClass.__name__)):
status = RunTest(testClass.__name__, name, method)
if status is None:
knownBugs += 1
elif status:
passed += 1
else:
failures.append(testClass.__name__ + "." + name)
failed += 1
else:
esclog.LogDebug("Skipping test %s in class %s" % (
name, testClass.__name__))
if escargs.args.stop_on_failure and failed > 0:
break
if escargs.args.stop_on_failure and failed > 0:
break
if failed > 0:
esclog.LogInfo(
"*** %s passed, %s, %s FAILED ***" % (
plural("test", passed),
plural("known bug", knownBugs),
plural("TEST", failed, caps=True)))
esclog.LogInfo("Failing tests:\n" + "\n".join(failures))
else:
esclog.LogInfo(
"*** %s passed, %s, %s failed ***" % (
plural("test", passed),
plural("known bug", knownBugs),
plural("test", failed)))
def plural(word, count, caps=False):
if count == 1:
suffix = ""
elif caps:
suffix = "S"
else:
suffix = "s"
return str(count) + " " + word + suffix
def main():
init()
try:
RunTests()
except Exception, e:
tb = traceback.format_exc()
try:
reset()
except:
print "reset() failed with traceback:"
print traceback.format_exc().replace("\n", "\r\n")
print "RunTests failed:\r\n"
print tb.replace("\n", "\r\n")
esclog.LogError("Failed with traceback:")
esclog.LogError(tb)
finally:
if escargs.args.no_print_logs:
# Hackily move the cursor to the bottom of the screen.
esccmd.CUP(esctypes.Point(1, 1))
esccmd.CUD(999)
else:
try:
reset()
except:
print "reset() failed with traceback:"
print traceback.format_exc().replace("\n", "\r\n")
print "\r\nLogs:\r\n"
esclog.Print()
shutdown()
main()
class ChecksumException(Exception):
def __init__(self, points, actual, expected):
message = "Checksum failed at the following locations:\n%s\nActual:\n%s\n\nExpected:\n%s" % (
"\n".join(map(str, points)),
"\n".join(actual),
"\n".join(expected))
super(ChecksumException, self).__init__(message)
class BadResponse(Exception):
def __init__(self, actual, expected):
message = "Bad response from server. Expected '%s' but got '%s'" % (expected, actual)
super(BadResponse, self).__init__(message)
class TestFailure(Exception):
def __init__(self, actual, expected, details=None):
message = "Test failed: expected '%s' but got '%s'" % (str(expected), str(actual))
if details is not None:
message += ". " + details
super(TestFailure, self).__init__(message)
class InternalError(Exception):
def __init__(self, message):
super(InternalError, self).__init__(message)
class KnownBug(Exception):
def __init__(self, reason):
super(KnownBug, self).__init__(reason)
class BrokenTest(Exception):
def __init__(self, reason):
super(BrokenTest, self).__init__(reason)
class InsufficientVTLevel(Exception):
def __init(self, actualLevel, minimumLevel):
reason = "Terminal implements VT level %d but %d is needed." % (
actualLevel, minimumLevel)
super(InsufficientVTLevel, self).__init__(reason)
class Point(object):
def __init__(self, x, y):
self._x = x
self._y = y
def __str__(self):
return "Point(x=%d, y=%d)" % (self._x, self._y)
def x(self):
return self._x
def y(self):
return self._y
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.__dict__ == other.__dict__
else:
return False
def __ne__(self, other):
return not self.__eq__(other)
class Size(object):
def __init__(self, width, height):
self._width = width
self._height = height
def __str__(self):
return "Size(width=%d, height=%d)" % (self._width, self._height)
def width(self):
return self._width
def height(self):
return self._height
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.__dict__ == other.__dict__
else:
return False
def __ne__(self, other):
return not self.__eq__(other)
class Rect(object):
def __init__(self, left, top, right, bottom):
self._left = left
self._top = top
self._right = right
self._bottom = bottom
def __str__(self):
return "Rect(left=%d, top=%d, right=%d, bottom=%d)" % (
self._left, self._top, self._right, self._bottom)
def left(self):
return self._left
def top(self):
return self._top
def right(self):
return self._right
def bottom(self):
return self._bottom
def width(self):
return self._right - self._left + 1
def height(self):
return self._bottom - self._top + 1
def params(self):
return [ self._top, self._left, self._bottom, self._right ]
def points(self):
y = self._top
while y <= self._bottom:
x = self._left
while x <= self._right:
yield Point(x, y)
x += 1
y += 1
import esc
import escargs
import esccmd
import escio
from esclog import LogDebug, LogInfo, LogError, Print
import esctypes
from esctypes import Point, Size, Rect
import functools
import traceback
gNextId = 1
gHaveAsserted = False
def Raise(e):
if not escargs.args.force:
raise e
def AssertGE(actual, minimum):
global gHaveAsserted
gHaveAsserted = True
if actual < minimum:
Raise(esctypes.TestFailure(actual, expected))
def AssertEQ(actual, expected):
global gHaveAsserted
gHaveAsserted = True
if actual != expected:
Raise(esctypes.TestFailure(actual, expected))
def AssertTrue(value, details=None):
if escargs.args.force:
return
global gHaveAsserted
gHaveAsserted = True
if value != True:
Raise(esctypes.TestFailure(value, True, details))
def GetIconTitle():
esccmd.XTERM_WINOPS(esccmd.WINOP_REPORT_ICON_LABEL)
return escio.ReadOSC("L")
def GetWindowTitle():
if escargs.args.expected_terminal == "iTerm2":
raise esctypes.InternalError(
"iTerm2 uses L instead of l as initial char for window title reports.")
esccmd.XTERM_WINOPS(esccmd.WINOP_REPORT_WINDOW_TITLE)
return escio.ReadOSC("l")
def GetWindowSizePixels():
"""Returns a Size giving the window's size in pixels."""
esccmd.XTERM_WINOPS(esccmd.WINOP_REPORT_WINDOW_SIZE_PIXELS)
params = escio.ReadCSI("t")
AssertTrue(params[0] == 4)
AssertTrue(len(params) >= 3)
return Size(params[2], params[1])
def GetWindowPosition():
"""Returns a Point giving the window's origin in screen pixels."""
esccmd.XTERM_WINOPS(esccmd.WINOP_REPORT_WINDOW_POSITION)
params = escio.ReadCSI("t")
AssertTrue(params[0] == 3)
AssertTrue(len(params) >= 3)
return Point(params[1], params[2])
def GetIsIconified():
esccmd.XTERM_WINOPS(esccmd.WINOP_REPORT_WINDOW_STATE)
params = escio.ReadCSI("t")
AssertTrue(params[0] in [ 1, 2 ], "Params are " + str(params))
return params[0] == 2
def GetCursorPosition():
esccmd.DSR(esccmd.DSRCPR, suppressSideChannel=True)
params = escio.ReadCSI("R")
return Point(int(params[1]), int(params[0]))
def GetScreenSize():
esccmd.XTERM_WINOPS(esccmd.WINOP_REPORT_TEXT_AREA_CHARS)
params = escio.ReadCSI("t")
return Size(params[2], params[1])
def GetDisplaySize():
esccmd.XTERM_WINOPS(esccmd.WINOP_REPORT_SCREEN_SIZE_CHARS)
params = escio.ReadCSI("t")
return Size(params[2], params[1])
def AssertScreenCharsInRectEqual(rect, expected_lines):
global gHaveAsserted
gHaveAsserted = True
if rect.height() != len(expected_lines):
raise esctypes.InternalError(
"Height of rect (%d) does not match number of expected lines (%d)" % (
rect.height(),
len(expected_lines)))
# Check each point individually. The dumb checksum algorithm can't distinguish
# "ab" from "ba", so equivalence of two multiple-character rects means nothing.
# |actual| and |expected| will form human-readable arrays of lines
actual = []
expected = []
# Additional information about mismatches.
errorLocations = []
for point in rect.points():
y = point.y() - rect.top()
x = point.x() - rect.left()
expected_line = expected_lines[y]
if rect.width() != len(expected_line):
fmt = ("Width of rect (%d) does not match number of characters in expected line " +
"index %d, coordinate %d (its length is %d)")
raise esctypes.InternalError(
fmt % (rect.width(),
y,
point.y(),
len(expected_lines[y])))
expected_checksum = ord(expected_line[x])
actual_checksum = GetChecksumOfRect(Rect(left=point.x(),
top=point.y(),
right=point.x(),
bottom=point.y()))
if len(actual) <= y:
actual.append("")
if actual_checksum == 0:
actual[y] += '.'
else:
actual[y] += chr(actual_checksum)
if len(expected) <= y:
expected.append("")
if expected_checksum == 0:
expected[y] += '.'
else:
expected[y] += chr(expected_checksum)
if expected_checksum != actual_checksum:
errorLocations.append("At %s expected '%c' (0x%02x) but got '%c' (0x%02x)" % (
str(point),
chr(expected_checksum),
expected_checksum,
chr(actual_checksum),
actual_checksum))
if len(errorLocations) > 0:
Raise(esctypes.ChecksumException(errorLocations, actual, expected))
def GetChecksumOfRect(rect):
global gNextId
Pid = gNextId
gNextId += 1
esccmd.DECRQCRA(Pid, 0, rect)
params = escio.ReadDCS()
str_pid = str(Pid)
if not params.startswith(str_pid):
Raise(esctypes.BadResponse(params, "Prefix of " + str_pid))
i = len(str_pid)
AssertTrue(params[i:].startswith("!~"))
i += 2
hex_checksum = params[i:]
return int(hex_checksum, 16)
def vtLevel(minimum):
"""Defines the minimum VT level the terminal must be capable of to succeed."""
def decorator(func):
@functools.wraps(func)
def func_wrapper(self, *args, **kwargs):
if esc.vtLevel >= minimum:
func(self, *args, **kwargs)
else:
raise esctypes.InsufficientVTLevel(esc.vtLevel, minimum)
return func_wrapper
return decorator
def intentionalDeviationFromSpec(terminal, reason):
"""Decorator for a method indicating that what it tests deviates from the
sepc and why."""
def decorator(func):
@functools.wraps(func)
def func_wrapper(self, *args, **kwargs):
func(self, *args, **kwargs)
return func_wrapper
return decorator
def optionRejects(terminal, option):
"""Decorator for a method indicating that it will fail if an option is present."""
reason = "Terminal \"" + terminal + "\" is known to fail this test with option \"" + option + "\" set."
def decorator(func):
@functools.wraps(func)
def func_wrapper(self, *args, **kwargs):
hasOption = (escargs.args.options is not None and
option in escargs.args.options)
if escargs.args.expected_terminal == terminal:
try:
func(self, *args, **kwargs)
except Exception, e:
if not hasOption:
# Failed despite option being unset. Re-raise.
raise
tb = traceback.format_exc()
lines = tb.split("\n")
lines = map(lambda x: "EXPECTED FAILURE (MISSING OPTION): " + x, lines)
raise esctypes.KnownBug(reason + "\n\n" + "\n".join(lines))
# Got here because test passed. If the option is set, that's
# unexpected so we raise an error.
if not escargs.args.force and hasOption:
raise esctypes.InternalError("Should have failed: " + reason)
else:
func(self, *args, **kwargs)
return func_wrapper
return decorator
def optionRequired(terminal, option, allowPassWithoutOption=False):
"""Decorator for a method indicating that it should fail unless an option is
present."""
reason = "Terminal \"" + terminal + "\" requires option \"" + option + "\" for this test to pass."
def decorator(func):
@functools.wraps(func)
def func_wrapper(self, *args, **kwargs):
hasOption = (escargs.args.options is not None and
option in escargs.args.options)
if escargs.args.expected_terminal == terminal:
try:
func(self, *args, **kwargs)
except Exception, e:
if hasOption:
# Failed despite option being set. Re-raise.
raise
tb = traceback.format_exc()
lines = tb.split("\n")
lines = map(lambda x: "EXPECTED FAILURE (MISSING OPTION): " + x, lines)
raise esctypes.KnownBug(reason + "\n\n" + "\n".join(lines))
# Got here because test passed. If the option isn't set, that's
# unexpected so we raise an error.
if not escargs.args.force and not hasOption and not allowPassWithoutOption:
raise esctypes.InternalError("Should have failed: " + reason)
else:
func(self, *args, **kwargs)
return func_wrapper
return decorator
def knownBug(terminal, reason, noop=False, shouldTry=True):
"""Decorator for a method indicating that it should fail and explaining why.
If the method is intended to succeed when nothing happens (that is, the
sequence being tested is a no-op) then the caller should set noop=True.
Otherwise, successes will raise an InternalError exception. If shouldTry is
true then the test will be run to make sure it really does fail."""
def decorator(func):
@functools.wraps(func)
def func_wrapper(self, *args, **kwargs):
if escargs.args.expected_terminal == terminal:
if not shouldTry:
raise esctypes.KnownBug(reason + " (not trying)")
try:
func(self, *args, **kwargs)
except Exception, e:
tb = traceback.format_exc()
lines = tb.split("\n")
lines = map(lambda x: "KNOWN BUG: " + x, lines)
raise esctypes.KnownBug(reason + "\n" + "\n".join(lines))
# Shouldn't get here because the test should have failed. If 'force' is on then
# tests always pass, though.
if not escargs.args.force and not noop:
raise esctypes.InternalError("Should have failed")
elif noop:
raise esctypes.KnownBug(reason + " (test ran and passed, but is documented as a 'no-op'; the nature of the bug makes it untestable)")
else:
func(self, *args, **kwargs)
return func_wrapper
return decorator
def AssertAssertionAsserted():
if escargs.args.force:
return
global gHaveAsserted
ok = gHaveAsserted
gHaveAsserted = False
if not ok and not escargs.args.force:
raise esctypes.BrokenTest("No assertion attempted.")
# The following CSI codes supported by xcode are not tested.
# Query ReGIS/Sixel attributes: CSI ? Pi ; Pa ; P vS
# Initiate highlight mouse tracking: CSI Ps ; Ps ; Ps ; Ps ; Ps T
# Media Copy (MC): CSI Pm i
# Media Copy (MC, DEC-specific): CSI ? Pm i
# Character Attributes (SGR): CSI Pm m
# Disable modifiers: CSI > Ps n
# Set pointer mode: CSI > Ps p
# Load LEDs (DECLL): CSI Ps q
# Set cursor style (DECSCUSR): CIS Ps SP q
# Select character protection attribute (DECSCA): CSI Ps " q [This is already tested by DECSED and DECSEL]
# Window manipulation: CSI Ps; Ps; Ps t
# Reverse Attributes in Rectangular Area (DECRARA): CSI Pt ; Pl ; Pb ; Pr ; Ps $ t
# Set warning bell volume (DECSWBV): CSI Ps SP t
# Set margin-bell volume (DECSMBV): CSI Ps SP u
# Enable Filter Rectangle (DECEFR): CSI Pt ; Pl ; Pb ; Pr ' w
# Request Terminal Parameters (DECREQTPARM): CSI Ps x
# Select Attribute Change Extent (DECSACE): CSI Ps * x
# Request Checksum of Rectangular Area (DECRQCRA): CSI Pi ; Pg ; Pt ; Pl ; Pb ; Pr * y
# Select Locator Events (DECSLE): CSI Pm ' {
# Request Locator Position (DECRQLP): CSI PS ' |
# ESC SP L Set ANSI conformance level 1 (dpANS X3.134.1).
# ESC SP M Set ANSI conformance level 2 (dpANS X3.134.1).
# ESC SP N Set ANSI conformance level 3 (dpANS X3.134.1).
# In xterm, all these do is fiddle with character sets, which are not testable.
# ESC # 3 DEC double-height line, top half (DECDHL).
# ESC # 4 DEC double-height line, bottom half (DECDHL).
# ESC # 5 DEC single-width line (DECSWL).
# ESC # 6 DEC double-width line (DECDWL).
# Double-width affects display only and is generally not introspectable. Wrap
# doesn't work so there's no way to tell where the cursor is visually.
# ESC % @ Select default character set. That is ISO 8859-1 (ISO 2022).
# ESC % G Select UTF-8 character set (ISO 2022).
# ESC ( C Designate G0 Character Set (ISO 2022, VT100).
# ESC ) C Designate G1 Character Set (ISO 2022, VT100).
# ESC * C Designate G2 Character Set (ISO 2022, VT220).
# ESC + C Designate G3 Character Set (ISO 2022, VT220).
# ESC - C Designate G1 Character Set (VT300).
# ESC . C Designate G2 Character Set (VT300).
# ESC / C Designate G3 Character Set (VT300).
# Character set stuff is not introspectable.
# Shift in (SI): ^O
# Shift out (SO): ^N
# Space (SP): 0x20
# Tab (TAB): 0x09 [tested in HTS]
# ESC = Application Keypad (DECKPAM).
# ESC > Normal Keypad (DECKPNM).
# ESC F Cursor to lower left corner of screen. This is enabled by the
# hpLowerleftBugCompat resource. (Not worth testing as it's off by
# default, and silly regardless)
# ESC l Memory Lock (per HP terminals). Locks memory above the cursor.
# ESC m Memory Unlock (per HP terminals).
# ESC n Invoke the G2 Character Set as GL (LS2).
# ESC o Invoke the G3 Character Set as GL (LS3).
# ESC | Invoke the G3 Character Set as GR (LS3R).
# ESC } Invoke the G2 Character Set as GR (LS2R).
# ESC ~ Invoke the G1 Character Set as GR (LS1R).
# DCS + p Pt ST Set Termcap/Terminfo Data
# DCS + q Pt ST Request Termcap/Terminfo String
# The following OSC commands are tested in xterm_winops and don't have their own test:
# Ps = 0 -> Change Icon Name and Window Title to Pt.
# Ps = 1 -> Change Icon Name to Pt.
# Ps = 2 -> Change Window Title to Pt.
# This test is too ill-defined and X-specific, and is not tested:
# Ps = 3 -> Set X property on top-level window. Pt should be
# in the form "prop=value", or just "prop" to delete the prop-
# erty
# No introspection for whether special color are enabled/disabled:
# Ps = 6 ; c; f -> Enable/disable Special Color Number c. The
# second parameter tells xterm to enable the corresponding color
# mode if nonzero, disable it if zero.
# Off by default, obvious security issues:
# Ps = 4 6 -> Change Log File to Pt. (This is normally dis-
# abled by a compile-time option).
# No introspection for fonts:
# Ps = 5 0 -> Set Font to Pt.
# No-op:
# Ps = 5 1 -> reserved for Emacs shell.
import ansirc
import apc
import bs
import cbt
import cha
import change_color
import change_special_color
import change_dynamic_color
import cht
import cnl
import cpl
import cr
import cub
import cud
import cuf
import cup
import cuu
import da
import da2
import dch
import dcs
import decaln
import decbi
import deccra
import decdc
import decdsr
import decera
import decfra
import decfi
import decic
import decid
import decrc
import decrqm
import decrqss
import decscl
import decsed
import decsel
import decsera
import decset
import decset_tite_inhibit
import decstbm
import decstr
import dl
import ech
import ed
import el
import ff
import hpa
import hpr
import hts
import hvp
import ich
import il
import ind
import lf
import manipulate_selection_data
import nel
import pm
import rep
import reset_color
import reset_special_color
import ri
import ris
import rm
import s8c1t
import sd
import sm
import sm_title
import sos
import su
import tbc
import vpa
import vpr
import vt
import xterm_save
import xterm_winops
tests = [
ansirc.ANSIRCTests,
apc.APCTests,
bs.BSTests,
cbt.CBTTests,
cha.CHATests,
change_color.ChangeColorTests,
change_special_color.ChangeSpecialColorTests,
change_dynamic_color.ChangeDynamicColorTests,
cht.CHTTests,
cnl.CNLTests,
cpl.CPLTests,
cr.CRTests,
cub.CUBTests,
cud.CUDTests,
cuf.CUFTests,
cup.CUPTests,
cuu.CUUTests,
da.DATests,
da2.DA2Tests,
dch.DCHTests,
dcs.DCSTests,
decaln.DECALNTests,
decbi.DECBITests,
deccra.DECCRATests,
decdc.DECDCTests,
decdsr.DECDSRTests,
decera.DECERATests,
decfra.DECFRATests,
decfi.DECFITests,
decic.DECICTests,
decid.DECIDTests,
decrc.DECRCTests,
decrqm.DECRQMTests,
decrqss.DECRQSSTests,
decscl.DECSCLTests,
decsed.DECSEDTests,
decsel.DECSELTests,
decsera.DECSERATests,
decset.DECSETTests,
decset_tite_inhibit.DECSETTiteInhibitTests,
decstbm.DECSTBMTests,
decstr.DECSTRTests,
dl.DLTests,
ech.ECHTests,
ed.EDTests,
el.ELTests,
ff.FFTests,
hpa.HPATests,
hpr.HPRTests,
hts.HTSTests,
hvp.HVPTests,
ich.ICHTests,
il.ILTests,
ind.INDTests,
lf.LFTests,
manipulate_selection_data.ManipulateSelectionDataTests,
nel.NELTests,
pm.PMTests,
rep.REPTests,
reset_color.ResetColorTests,
reset_special_color.ResetSpecialColorTests,
ri.RITests,
ris.RISTests,
rm.RMTests,
s8c1t.S8C1TTests,
sd.SDTests,
sm.SMTests,
sm_title.SMTitleTests,
sos.SOSTests,
su.SUTests,
tbc.TBCTests,
vpa.VPATests,
vpr.VPRTests,
vt.VTTests,
xterm_save.XtermSaveTests,
xterm_winops.XtermWinopsTests,
]
import esccmd
from escutil import knownBug
from tests.save_restore_cursor import SaveRestoreCursorTests
class ANSIRCTests(SaveRestoreCursorTests):
def __init__(self):
SaveRestoreCursorTests.__init__(self)
def saveCursor(self):
esccmd.ANSISC()
def restoreCursor(self):
esccmd.ANSIRC()
@knownBug(terminal="iTerm2", reason="Does not reset origin mode.")
def test_SaveRestoreCursor_ResetsOriginMode(self):
SaveRestoreCursorTests.test_SaveRestoreCursor_ResetsOriginMode(self)
def test_SaveRestoreCursor_WorksInLRM(self, shouldWork=True):
SaveRestoreCursorTests.test_SaveRestoreCursor_WorksInLRM(self, False)
from esc import NUL, ST, S7C1T, S8C1T
import escargs
import esccmd
import escio
from escutil import AssertScreenCharsInRectEqual, knownBug, optionRequired
from esctypes import Rect
class APCTests(object):
@knownBug(terminal="iTerm2", reason="Not implemented.")
def test_APC_Basic(self):
esccmd.APC()
escio.Write("xyz")
escio.Write(ST)
escio.Write("A")
AssertScreenCharsInRectEqual(Rect(1, 1, 3, 1),
[ "A" + NUL * 2 ])
@knownBug(terminal="iTerm2", reason="8-bit controls not implemented.")
@optionRequired(terminal="xterm", option=escargs.DISABLE_WIDE_CHARS)
def test_APC_8bit(self):
escio.use8BitControls = True
escio.Write(S8C1T)
esccmd.APC()
escio.Write("xyz")
escio.Write(ST)
escio.Write("A")
escio.Write(S7C1T)
escio.use8BitControls = False
AssertScreenCharsInRectEqual(Rect(1, 1, 3, 1),
[ "A" + NUL * 2 ])
import esc
import esccmd
import escio
from escutil import AssertEQ, GetCursorPosition, GetScreenSize, knownBug
from esctypes import Point, Rect
class BSTests(object):
def test_BS_Basic(self):
esccmd.CUP(Point(3, 3))
escio.Write(esc.BS)
AssertEQ(GetCursorPosition(), Point(2, 3))
def test_BS_NoWrapByDefault(self):
esccmd.CUP(Point(1, 3))
escio.Write(esc.BS)
AssertEQ(GetCursorPosition(), Point(1, 3))
@knownBug(terminal="iTerm2",
reason="Implementation differs from xterm, but maybe should match it. iTerm2 reverse wraps only if there is a soft EOL at the end of the preceding line.")
def test_BS_WrapsInWraparoundMode(self):
esccmd.DECSET(esccmd.DECAWM)
esccmd.DECSET(esccmd.ReverseWraparound)
esccmd.CUP(Point(1, 3))
escio.Write(esc.BS)
size = GetScreenSize()
AssertEQ(GetCursorPosition(), Point(size.width(), 2))
@knownBug(terminal="iTerm2",
reason="Implementation differs from xterm, but maybe should match it. iTerm2 never reverse-wraps with a left margin.")
def test_BS_ReverseWrapWithLeftRight(self):
esccmd.DECSET(esccmd.DECAWM)
esccmd.DECSET(esccmd.ReverseWraparound)
esccmd.DECSET(esccmd.DECLRMM)
esccmd.DECSLRM(5, 10)
esccmd.CUP(Point(5, 3))
escio.Write(esc.BS)
AssertEQ(GetCursorPosition(), Point(10, 2))
def test_BS_StopsAtLeftMargin(self):
esccmd.DECSET(esccmd.DECLRMM)
esccmd.DECSLRM(5, 10)
esccmd.CUP(Point(5, 1))
escio.Write(esc.BS)
esccmd.DECRESET(esccmd.DECLRMM)
AssertEQ(GetCursorPosition(), Point(5, 1))
@knownBug(terminal="iTerm2", reason="Doesn't move left.")
def test_BS_MovesLeftWhenLeftOfLeftMargin(self):
esccmd.DECSET(esccmd.DECLRMM)
esccmd.DECSLRM(5, 10)
esccmd.CUP(Point(4, 1))
escio.Write(esc.BS)
esccmd.DECRESET(esccmd.DECLRMM)
AssertEQ(GetCursorPosition(), Point(3, 1))
def test_BS_StopsAtOrigin(self):
esccmd.CUP(Point(1, 1))
escio.Write(esc.BS)
AssertEQ(GetCursorPosition(), Point(1, 1))
@knownBug(terminal="xterm",
reason="BS wraps past top margin. Bad idea in my opinion, but there is no standard for reverse wrap.")
def test_BS_WillNotReverseWrapPastTopMargin(self):
esccmd.DECSET(esccmd.DECAWM)
esccmd.DECSET(esccmd.ReverseWraparound)
esccmd.DECSTBM(2, 5)
esccmd.CUP(Point(1, 2))
escio.Write(esc.BS)
AssertEQ(GetCursorPosition(), Point(1, 2))
import esccmd
from esctypes import Point
from escutil import AssertEQ, GetCursorPosition, knownBug
class CBTTests(object):
def test_CBT_OneTabStopByDefault(self):
esccmd.CUP(Point(17, 1))
esccmd.CBT()
position = GetCursorPosition()
AssertEQ(position.x(), 9)
def test_CBT_ExplicitParameter(self):
esccmd.CUP(Point(25, 1))
esccmd.CBT(2)
position = GetCursorPosition()
AssertEQ(position.x(), 9)
def test_CBT_StopsAtLeftEdge(self):
esccmd.CUP(Point(25, 2))
esccmd.CBT(5)
position = GetCursorPosition()
AssertEQ(position.x(), 1)
AssertEQ(position.y(), 2)
def test_CBT_IgnoresRegion(self):
# Set a scroll region.
esccmd.DECSET(esccmd.DECLRMM)
esccmd.DECSLRM(5, 30)
# Move to center of region
esccmd.CUP(Point(7, 9))
# Tab backwards out of the region.
esccmd.CBT(2)
position = GetCursorPosition()
AssertEQ(position.x(), 1)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment