mirror of
https://github.com/lihop/godot-xterm.git
synced 2025-05-03 20:14:22 +02:00
Add all the files
This commit is contained in:
parent
d7db117f8b
commit
96e9ddcf79
68 changed files with 9064 additions and 7 deletions
108
test/integration/test_terminal.gd
Normal file
108
test/integration/test_terminal.gd
Normal file
|
@ -0,0 +1,108 @@
|
|||
# Copyright (c) 2020 The GodotXterm authors. All rights reserved.
|
||||
# License MIT
|
||||
extends "res://addons/gut/test.gd"
|
||||
|
||||
const Parser = preload("res://addons/godot_xterm/parser/escape_sequence_parser.gd")
|
||||
const Terminal = preload("res://addons/godot_xterm/terminal.gd")
|
||||
const Decoder = preload("res://addons/godot_xterm/input/text_decoder.gd")
|
||||
const Constants = preload("res://addons/godot_xterm/parser/constants.gd")
|
||||
|
||||
const C0 = Constants.C0
|
||||
const C1 = Constants.C1
|
||||
|
||||
class TestBuffer:
|
||||
var calls = []
|
||||
var printed = ''
|
||||
|
||||
func handle_print(data, start, end):
|
||||
var string = Decoder.utf32_to_string(data.slice(start, end - 1))
|
||||
calls.append(['print', string])
|
||||
printed += string
|
||||
|
||||
|
||||
func handle_exec():
|
||||
calls.append(['exec'])
|
||||
|
||||
|
||||
func handle_csi(params):
|
||||
calls.append(['csi', params])
|
||||
|
||||
|
||||
func clear():
|
||||
printed = ''
|
||||
calls.resize(0)
|
||||
|
||||
|
||||
var parser
|
||||
var buffer
|
||||
var decoder
|
||||
|
||||
|
||||
func parse(parser, string):
|
||||
var container = []
|
||||
container.resize(string.length())
|
||||
var length = decoder.decode(string.to_utf8(), container)
|
||||
parser.parse(container, length)
|
||||
|
||||
|
||||
func before_all():
|
||||
buffer = TestBuffer.new()
|
||||
decoder = Decoder.Utf8ToUtf32.new()
|
||||
|
||||
|
||||
func before_each():
|
||||
parser = Parser.new()
|
||||
parser.set_print_handler(buffer, 'handle_print')
|
||||
buffer.clear()
|
||||
|
||||
|
||||
func test_prints_printables():
|
||||
var string = 'bash-4.4# '
|
||||
var data = string.to_utf8()
|
||||
var length = decoder.decode(data, data)
|
||||
parser.parse(data, length)
|
||||
assert_eq(buffer.calls, [['print', 'bash-4.4# ']])
|
||||
assert_eq(buffer.printed, 'bash-4.4# ')
|
||||
|
||||
|
||||
func skip_test_c0():
|
||||
for code in C0.values():
|
||||
parser.set_execute_handler(code, buffer, 'handle_exec')
|
||||
parse(parser, Decoder.string_from_codepoint(code))
|
||||
if code == 0x0 or code == 0x1b or code == 0x20 or code == 0x7f:
|
||||
assert_eq(buffer.calls, [])
|
||||
else:
|
||||
assert_eq(buffer.calls, [['exec']], 'code: 0x%x' % code)
|
||||
assert_eq(buffer.printed, '')
|
||||
parser.reset()
|
||||
buffer.clear()
|
||||
|
||||
|
||||
func skip_test_c1():
|
||||
for code in C1.values():
|
||||
parser.set_execute_handler(code, buffer, 'handle_exec')
|
||||
parse(parser, Decoder.string_from_codepoint(code))
|
||||
assert_eq(buffer.calls, [['exec']], 'code: 0x%x' % code)
|
||||
assert_eq(buffer.printed, '')
|
||||
parser.reset()
|
||||
buffer.clear()
|
||||
|
||||
|
||||
func test_print_csi_print():
|
||||
parser.set_csi_handler({'final': 'g'}, buffer, 'handle_csi')
|
||||
parse(parser, 'a\u001b[gb')
|
||||
assert_eq(buffer.calls, [['print', 'a'],['csi', [0]], ['print', 'b']])
|
||||
assert_eq(buffer.printed, 'ab')
|
||||
|
||||
|
||||
func test_csi_position_cursor():
|
||||
parser.set_csi_handler({'final': 'H'}, buffer, 'handle_csi')
|
||||
parse(parser, '\u001b[1;5H')
|
||||
assert_eq(buffer.calls, [['csi', [1, 5]]])
|
||||
assert_eq(buffer.printed, '')
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
17
test/test.tscn
Normal file
17
test/test.tscn
Normal file
|
@ -0,0 +1,17 @@
|
|||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://addons/gut/gut.gd" type="Script" id=1]
|
||||
|
||||
[node name="Gut" type="Control"]
|
||||
self_modulate = Color( 1, 1, 1, 0 )
|
||||
margin_right = 1031.0
|
||||
margin_bottom = 597.0
|
||||
rect_min_size = Vector2( 740, 250 )
|
||||
script = ExtResource( 1 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
_yield_between_tests = false
|
||||
_directory1 = "res://test/unit"
|
||||
_directory2 = "res://test/integration"
|
||||
_double_strategy = 1
|
100
test/unit/test_dcs_parser.gd
Normal file
100
test/unit/test_dcs_parser.gd
Normal file
|
@ -0,0 +1,100 @@
|
|||
# Copyright (c) 2020 The GodotXterm authors.
|
||||
# Copyright (c) 2019 The xterm.js authors. All rights reserved.
|
||||
# License MIT
|
||||
extends "res://addons/gut/test.gd"
|
||||
|
||||
const DcsParser = preload("res://addons/godot_xterm/parser/dcs_parser.gd")
|
||||
const Parser = preload("res://addons/godot_xterm/parser/escape_sequence_parser.gd")
|
||||
const Params = preload("res://addons/godot_xterm/parser/params.gd")
|
||||
const Decoder = preload("res://addons/godot_xterm/input/text_decoder.gd")
|
||||
|
||||
class Handler:
|
||||
extends Reference
|
||||
|
||||
|
||||
var _output
|
||||
var _msg
|
||||
var _return_false
|
||||
|
||||
|
||||
func _init(output: Array, msg: String, return_false: bool = false):
|
||||
_output = output
|
||||
_msg = msg
|
||||
_return_false = return_false
|
||||
|
||||
|
||||
func hook(params):
|
||||
_output.append([_msg, 'HOOK', params.to_array()])
|
||||
|
||||
|
||||
func put(data: Array, start: int, end: int):
|
||||
_output.append([_msg, 'PUT', Decoder.utf32_to_string(data, start, end)])
|
||||
|
||||
|
||||
func unhook(success: bool):
|
||||
_output.append([_msg, 'UNHOOK', success])
|
||||
if _return_false:
|
||||
return false
|
||||
|
||||
|
||||
var parser: DcsParser
|
||||
var reports: Array
|
||||
|
||||
|
||||
func to_utf32(s: String):
|
||||
var utf32 = []
|
||||
utf32.resize(s.length())
|
||||
var decoder = Decoder.Utf8ToUtf32.new()
|
||||
var length = decoder.decode(s.to_utf8(), utf32)
|
||||
assert_eq(length, s.length())
|
||||
return utf32.slice(0, length - 1)
|
||||
|
||||
|
||||
func handler_fallback(id, action, data):
|
||||
if action == 'HOOK':
|
||||
data = data.to_array()
|
||||
reports.append([id, action, data])
|
||||
|
||||
|
||||
func before_each():
|
||||
parser = DcsParser.new()
|
||||
parser.set_handler_fallback(self, 'handler_fallback')
|
||||
reports = []
|
||||
|
||||
|
||||
func test_set_dcs_handler():
|
||||
parser.set_handler(Parser.identifier({'intermediates': '+', 'final': 'p'}),
|
||||
Handler.new(reports, 'th'))
|
||||
parser.hook(Parser.identifier({'intermediates': '+', 'final': 'p'}),
|
||||
Params.from_array([1, 2, 3]))
|
||||
var data = to_utf32('Here comes')
|
||||
parser.put(data, 0, data.size())
|
||||
data = to_utf32('the mouse!')
|
||||
parser.put(data, 0, data.size())
|
||||
parser.unhook(true)
|
||||
assert_eq(reports, [
|
||||
# messages from Handler
|
||||
['th', 'HOOK', [1, 2, 3]],
|
||||
['th', 'PUT', 'Here comes'],
|
||||
['th', 'PUT', 'the mouse!'],
|
||||
['th', 'UNHOOK', true],
|
||||
])
|
||||
|
||||
|
||||
func test_clear_dcs_handler():
|
||||
var ident = Parser.identifier({'intermediates': '+', 'final': 'p'})
|
||||
parser.set_handler(ident, Handler.new(reports, 'th'))
|
||||
parser.clear_handler(ident)
|
||||
parser.hook(ident, Params.from_array([1, 2, 3]))
|
||||
var data = to_utf32('Here comes')
|
||||
parser.put(data, 0, data.size())
|
||||
data = to_utf32('the mouse!')
|
||||
parser.put(data, 0, data.size())
|
||||
parser.unhook(true)
|
||||
assert_eq(reports, [
|
||||
# messages from fallback handler
|
||||
[ident, 'HOOK', [1, 2, 3]],
|
||||
[ident, 'PUT', 'Here comes'],
|
||||
[ident, 'PUT', 'the mouse!'],
|
||||
[ident, 'UNHOOK', true],
|
||||
])
|
411
test/unit/test_escape_sequence_parser.gd
Normal file
411
test/unit/test_escape_sequence_parser.gd
Normal file
|
@ -0,0 +1,411 @@
|
|||
# Copyright (c) 2020 The GodotXterm authors.
|
||||
# Copyright (c) 2018 The xterm.js authors. All rights reserved.
|
||||
# License MIT
|
||||
extends 'res://addons/gut/test.gd'
|
||||
|
||||
const Parser = preload("res://addons/godot_xterm/parser/escape_sequence_parser.gd")
|
||||
const Params = preload("res://addons/godot_xterm/parser/params.gd")
|
||||
const Decoder = preload("res://addons/godot_xterm/input/text_decoder.gd")
|
||||
const Constants = preload("res://addons/godot_xterm/parser/constants.gd")
|
||||
const ParserState = Constants.ParserState
|
||||
|
||||
class TestTerminal:
|
||||
var calls = []
|
||||
|
||||
func clear():
|
||||
calls = []
|
||||
|
||||
|
||||
func handle_print(data, start, end):
|
||||
var string = Decoder.utf32_to_string(data, start, end)
|
||||
calls.append(['print', string])
|
||||
|
||||
|
||||
func handle_csi(ident, params):
|
||||
var id = Parser.ident_to_string(ident)
|
||||
var collect = id.substr(0, id.length() - 1)
|
||||
var flag = id.substr(id.length() - 1, 1)
|
||||
calls.append(['csi', collect, params, flag])
|
||||
|
||||
|
||||
func handle_esc(ident: int):
|
||||
var id = Parser.ident_to_string(ident)
|
||||
var collect = id.substr(0, id.length() - 1)
|
||||
var flag = id.substr(id.length() - 1, 1)
|
||||
calls.append(['esc', collect, flag])
|
||||
|
||||
|
||||
func handle_execute(code: int):
|
||||
var flag = Decoder.string_from_codepoint(code)
|
||||
calls.append(['exe', flag])
|
||||
|
||||
|
||||
func handle_dcs(collect_and_flag, action, payload):
|
||||
match action:
|
||||
'HOOK':
|
||||
calls.append(['dcs hook', payload.to_array()])
|
||||
'PUT':
|
||||
calls.append(['dcs put', payload])
|
||||
'UNHOOK':
|
||||
calls.append(['dcs unhook', payload])
|
||||
|
||||
|
||||
# derived parser with access to internal states
|
||||
class TestParser:
|
||||
extends Parser
|
||||
|
||||
var params setget _set_params,_get_params
|
||||
var collect setget _set_collect,_get_collect
|
||||
|
||||
|
||||
func _init():
|
||||
pass
|
||||
|
||||
|
||||
func _set_params(value):
|
||||
_params = Params.from_array(value)
|
||||
|
||||
|
||||
func _get_params():
|
||||
return _params.to_array()
|
||||
|
||||
|
||||
func _set_collect(value: String):
|
||||
_collect = 0
|
||||
for c in value.to_ascii():
|
||||
_collect <<= 8
|
||||
_collect |= c
|
||||
|
||||
|
||||
func _get_collect() -> String:
|
||||
return ident_to_string(_collect)
|
||||
|
||||
|
||||
func real_params():
|
||||
return _params
|
||||
|
||||
# translate string based parse calls into typed array based
|
||||
func parse(parser: TestParser, data):
|
||||
if data == '': # handle the 0x00 codepoint
|
||||
data = PoolByteArray([0])
|
||||
else:
|
||||
data = data.to_utf8()
|
||||
var container = []
|
||||
var decoder = Decoder.Utf8ToUtf32.new()
|
||||
decoder.clear()
|
||||
var length = decoder.decode(data, container)
|
||||
parser.parse(container, length)
|
||||
|
||||
|
||||
var parser
|
||||
var test_terminal
|
||||
|
||||
|
||||
func before_all():
|
||||
parser = TestParser.new()
|
||||
test_terminal = TestTerminal.new()
|
||||
|
||||
parser.set_print_handler(test_terminal, 'handle_print')
|
||||
parser.set_csi_handler_fallback(test_terminal, 'handle_csi')
|
||||
parser.set_esc_handler_fallback(test_terminal, 'handle_esc')
|
||||
parser.set_execute_handler_fallback(test_terminal, "handle_execute")
|
||||
parser.set_dcs_handler_fallback(test_terminal, "handle_dcs")
|
||||
|
||||
|
||||
func before_each():
|
||||
parser.reset()
|
||||
test_terminal.clear()
|
||||
|
||||
|
||||
func test_initial_states():
|
||||
assert_eq(parser.initial_state, ParserState.GROUND)
|
||||
assert_eq(parser.current_state, ParserState.GROUND)
|
||||
assert_eq(parser._params.to_array(), [0])
|
||||
|
||||
func test_reset_states():
|
||||
var params = Params.new()
|
||||
params.add_param(123)
|
||||
parser.current_state = 124
|
||||
parser._params = params
|
||||
parser.reset()
|
||||
assert_eq(parser.current_state, ParserState.GROUND)
|
||||
assert_eq(parser._params.to_array(), [0])
|
||||
|
||||
# state transitions and actions
|
||||
|
||||
func test_state_GROUND_execute_action():
|
||||
var exes = range(0x00, 0x18) + [0x19] + range(0x1c, 0x20)
|
||||
for exe in exes:
|
||||
parser.current_state = ParserState.GROUND
|
||||
parse(parser, Decoder.string_from_codepoint(exe))
|
||||
assert_eq(parser.current_state, ParserState.GROUND)
|
||||
parser.reset()
|
||||
|
||||
func test_state_GROUND_print_action():
|
||||
var printables = range(0x20, 0x7f) # NOTE: DEL excluded
|
||||
for printable in printables:
|
||||
var string = Decoder.string_from_codepoint(printable)
|
||||
parser.current_state = ParserState.GROUND
|
||||
parse(parser, string)
|
||||
assert_eq(parser.current_state, ParserState.GROUND)
|
||||
assert_eq(test_terminal.calls, [['print', string]])
|
||||
parser.reset()
|
||||
test_terminal.clear()
|
||||
|
||||
func test_trans_ANYWHERE_to_GROUND_with_actions():
|
||||
var exes = [
|
||||
'\u0018', '\u001a',
|
||||
'\u0080', '\u0081', '\u0082', '\u0083', '\u0084', '\u0085', '\u0086', '\u0087', '\u0088',
|
||||
'\u0089', '\u008a', '\u008b', '\u008c', '\u008d', '\u008e', '\u008f',
|
||||
'\u0091', '\u0092', '\u0093', '\u0094', '\u0095', '\u0096', '\u0097', '\u0099', '\u009a'
|
||||
]
|
||||
var exceptions = {
|
||||
8: { '\u0018': [], '\u001a': [] }, # abort OSC_STRING
|
||||
13: { '\u0018': [['dcs unhook', false]], '\u001a': [['dcs unhook', false]] } # abort DCS_PASSTHROUGH
|
||||
}
|
||||
for state in ParserState.values():
|
||||
for exe in exes:
|
||||
if exe != '\u0018' and exe != '\u001a':
|
||||
continue
|
||||
parser.current_state = state
|
||||
parse(parser, exe)
|
||||
assert_eq(parser.current_state, ParserState.GROUND)
|
||||
assert_eq(
|
||||
test_terminal.calls,
|
||||
exceptions[state][exe] if exceptions.has(state) and exceptions[state].has(exe) else [['exe', exe]],
|
||||
'state: %s exe: %x' % [ParserState.keys()[state], exe.to_utf8()[0]]
|
||||
)
|
||||
parser.reset()
|
||||
test_terminal.clear()
|
||||
parse(parser, '\u009c')
|
||||
assert_eq(parser.current_state, ParserState.GROUND)
|
||||
assert_eq(test_terminal.calls, [])
|
||||
parser.reset()
|
||||
test_terminal.clear()
|
||||
|
||||
|
||||
func skip_test_trans_ANYWHERE_to_ESCAPE_with_clear():
|
||||
for state in ParserState.values():
|
||||
var state_name = ParserState.keys()[state]
|
||||
parser.current_state = state
|
||||
parser.params = [23]
|
||||
parser.collect = '#'
|
||||
parse(parser, '\u001b')
|
||||
assert_eq(parser.current_state, ParserState.ESCAPE,
|
||||
'wrong current_state. start state: %s' % state_name)
|
||||
assert_eq(parser.params, [0],
|
||||
'wrong params. start state: %s' % state_name)
|
||||
assert_eq(parser.collect, '',
|
||||
'wrong collect. start state: %s' % state_name)
|
||||
parser.reset()
|
||||
|
||||
|
||||
func test_state_ESCAPE_execute_rules():
|
||||
var exes = range(0x00, 0x18) + [0x19] + range(0x1c, 0x20)
|
||||
for exe in exes:
|
||||
parser.current_state = ParserState.ESCAPE
|
||||
var data = Decoder.string_from_codepoint(exe)
|
||||
parse(parser, data)
|
||||
assert_eq(parser.current_state, ParserState.ESCAPE, 'exe: %x' % exe)
|
||||
assert_eq(test_terminal.calls, [['exe', data]], 'exe: %x' % exe)
|
||||
parser.reset()
|
||||
test_terminal.clear()
|
||||
|
||||
|
||||
func test_state_ESCAPE_ignore():
|
||||
parser.current_state = ParserState.ESCAPE
|
||||
parse(parser, '\u007f')
|
||||
assert_eq(parser.current_state, ParserState.ESCAPE)
|
||||
assert_eq(test_terminal.calls, [])
|
||||
|
||||
func test_trans_ESCAPE_to_GROUND_with_esc_dispatch_action():
|
||||
var dispatches = range(0x30, 0x50) + range(0x51, 0x58) + [0x59, 0x5a] + range(0x60, 0x7f)
|
||||
for dispatch in dispatches:
|
||||
parser.current_state = ParserState.ESCAPE
|
||||
var data = Decoder.string_from_codepoint(dispatch)
|
||||
parse(parser, data)
|
||||
assert_eq(parser.current_state, ParserState.GROUND,
|
||||
'wrong state: %s, dispatch: %x' % [ParserState.keys()[parser.current_state], dispatch])
|
||||
assert_eq(test_terminal.calls, [['esc', '', data]],
|
||||
'wrong call. dispatch: %x' % dispatch)
|
||||
parser.reset()
|
||||
test_terminal.clear()
|
||||
|
||||
|
||||
func test_trans_ESCAPE_to_ESCAPE_INTERMEDIATE_with_collect_action():
|
||||
var collect = range(0x20, 0x30)
|
||||
for c in collect:
|
||||
parser.current_state = ParserState.ESCAPE
|
||||
var data = Decoder.string_from_codepoint(c)
|
||||
parse(parser, data)
|
||||
assert_eq(parser.current_state, ParserState.ESCAPE_INTERMEDIATE)
|
||||
assert_eq(parser.collect, data)
|
||||
parser.reset()
|
||||
|
||||
|
||||
func test_state_ESCAPE_INTERMEDIATE_execute_rules():
|
||||
var exes = range(0x00, 0x18) + [0x19] + range(0x1c, 0x20)
|
||||
for exe in exes:
|
||||
var data = Decoder.string_from_codepoint(exe)
|
||||
parser.current_state = ParserState.ESCAPE_INTERMEDIATE
|
||||
parse(parser, data)
|
||||
assert_eq(parser.current_state, ParserState.ESCAPE_INTERMEDIATE)
|
||||
assert_eq(test_terminal.calls, [['exe', data]])
|
||||
parser.reset()
|
||||
test_terminal.clear()
|
||||
|
||||
|
||||
func test_state_ESCAPE_INTERMEDIATE_ignore():
|
||||
parser.current_state = ParserState.ESCAPE_INTERMEDIATE
|
||||
parse(parser, '\u007f')
|
||||
assert_eq(parser.current_state, ParserState.ESCAPE_INTERMEDIATE)
|
||||
assert_eq(test_terminal.calls, [])
|
||||
|
||||
|
||||
func test_state_ESCAPE_INTERMEDIATE_collect_action():
|
||||
var collect = range(0x20, 0x30)
|
||||
for c in collect:
|
||||
var data = Decoder.string_from_codepoint(c)
|
||||
parser.current_state = ParserState.ESCAPE_INTERMEDIATE
|
||||
parse(parser, data)
|
||||
assert_eq(parser.current_state, ParserState.ESCAPE_INTERMEDIATE)
|
||||
assert_eq(parser.collect, data)
|
||||
parser.reset()
|
||||
|
||||
|
||||
func test_trans_ESCAPE_INTERMEDIATE_to_GROUND_with_esc_dispatch_action():
|
||||
var collect = range(0x30, 0x7f)
|
||||
for c in collect:
|
||||
var data = Decoder.string_from_codepoint(c)
|
||||
parser.current_state = ParserState.ESCAPE_INTERMEDIATE
|
||||
parse(parser, data)
|
||||
assert_eq(parser.current_state, ParserState.GROUND)
|
||||
# '\u005c' --> ESC + \ (7bit ST) parser does not expose this as it already got handled
|
||||
assert_eq(test_terminal.calls, [] if c == 0x5c else [['esc', '', data]], 'c: 0x%x' % c)
|
||||
parser.reset()
|
||||
test_terminal.clear()
|
||||
|
||||
func test_ANYWHERE_or_ESCAPE_to_CSI_ENTRY_with_clear():
|
||||
# C0
|
||||
parser.current_state = ParserState.ESCAPE
|
||||
parser.params = [123]
|
||||
parser.collect = '#'
|
||||
parse(parser, '[')
|
||||
assert_eq(parser.current_state, ParserState.CSI_ENTRY)
|
||||
assert_eq(parser.params, [0])
|
||||
assert_eq(parser.collect, '')
|
||||
parser.reset()
|
||||
# C1
|
||||
for state in ParserState.values():
|
||||
parser.current_state = state
|
||||
parser.params = [123]
|
||||
parser.collect = '#'
|
||||
parse(parser, '\u009b')
|
||||
assert_eq(parser.current_state, ParserState.CSI_ENTRY)
|
||||
assert_eq(parser.collect, '')
|
||||
parser.reset()
|
||||
|
||||
|
||||
func test_CSI_ENTRY_execute_rules():
|
||||
var exes = range(0x00, 0x18) + [0x19] + range(0x1c, 0x20)
|
||||
for exe in exes:
|
||||
var data = Decoder.string_from_codepoint(exe)
|
||||
parser.current_state = ParserState.CSI_ENTRY
|
||||
parse(parser, data)
|
||||
assert_eq(parser.current_state, ParserState.CSI_ENTRY)
|
||||
assert_eq(test_terminal.calls, [['exe', data]])
|
||||
parser.reset()
|
||||
test_terminal.clear()
|
||||
|
||||
|
||||
func test_state_CSI_ENTRY_ignore():
|
||||
parser.current_state = ParserState.CSI_ENTRY
|
||||
parse(parser, '\u007f')
|
||||
assert_eq(parser.current_state, ParserState.CSI_ENTRY)
|
||||
assert_eq(test_terminal.calls, [])
|
||||
|
||||
|
||||
func test_trans_CSI_ENTRY_to_GROUND_with_csi_dispatch_action():
|
||||
var dispatches = range(0x40, 0x7f)
|
||||
for dispatch in dispatches:
|
||||
var data = Decoder.string_from_codepoint(dispatch)
|
||||
parser.current_state = ParserState.CSI_ENTRY
|
||||
parse(parser, data)
|
||||
assert_eq(parser.current_state, ParserState.GROUND)
|
||||
assert_eq(test_terminal.calls, [['csi', '', [0], data]])
|
||||
parser.reset()
|
||||
test_terminal.clear()
|
||||
|
||||
|
||||
func test_trans_CSI_ENTRY_to_CSI_PARAMS_with_param_or_collect_action():
|
||||
var params = range(0x30, 0x3a)
|
||||
var collect = ['\u003c', '\u003d', '\u003e', '\u003f']
|
||||
for param in params:
|
||||
parser.current_state = ParserState.CSI_ENTRY
|
||||
parse(parser, Decoder.string_from_codepoint(param))
|
||||
assert_eq(parser.current_state, ParserState.CSI_PARAM)
|
||||
assert_eq(parser.params, [param - 48], 'param: 0x%x' % param)
|
||||
parser.reset()
|
||||
parser.current_state = ParserState.CSI_ENTRY
|
||||
parse(parser, '\u003b')
|
||||
assert_eq(parser.current_state, ParserState.CSI_PARAM)
|
||||
assert_eq(parser.params, [0, 0])
|
||||
parser.reset()
|
||||
for c in collect:
|
||||
parser.current_state = ParserState.CSI_ENTRY
|
||||
parse(parser, c)
|
||||
assert_eq(parser.current_state, ParserState.CSI_PARAM)
|
||||
assert_eq(parser.collect, c)
|
||||
parser.reset()
|
||||
|
||||
|
||||
func test_state_CSI_PARAM_execute_rules():
|
||||
var exes = range(0x00, 0x018) + [0x19] + range(0x1c, 0x20)
|
||||
for exe in exes:
|
||||
var data = Decoder.string_from_codepoint(exe)
|
||||
parser.current_state = ParserState.CSI_PARAM
|
||||
parse(parser, data)
|
||||
assert_eq(parser.current_state, ParserState.CSI_PARAM)
|
||||
assert_eq(test_terminal.calls, [['exe', data]])
|
||||
parser.reset()
|
||||
test_terminal.clear()
|
||||
|
||||
|
||||
func test_state_CSI_PARAM_param_action():
|
||||
var params = range(0x30, 0x3a)
|
||||
for param in params:
|
||||
parser.current_state = ParserState.CSI_PARAM
|
||||
parse(parser, Decoder.string_from_codepoint(param))
|
||||
assert_eq(parser.current_state, ParserState.CSI_PARAM)
|
||||
assert_eq(parser.params, [param - 48], 'param: 0x%x' % param)
|
||||
parser.reset()
|
||||
|
||||
|
||||
func test_state_CSI_PARAM_ignore():
|
||||
parser.current_state = ParserState.CSI_PARAM
|
||||
parse(parser, '\u007f')
|
||||
assert_eq(parser.current_state, ParserState.CSI_PARAM)
|
||||
assert_eq(test_terminal.calls, [])
|
||||
|
||||
|
||||
func test_trans_CSI_PARAM_to_GROUND_with_csi_dispatch_action():
|
||||
var dispatches = range(0x40, 0x7f)
|
||||
for dispatch in dispatches:
|
||||
var data = Decoder.string_from_codepoint(dispatch)
|
||||
parser.current_state = ParserState.CSI_PARAM
|
||||
parser.params = [0, 1]
|
||||
parse(parser, data)
|
||||
assert_eq(parser.current_state, ParserState.GROUND)
|
||||
assert_eq(test_terminal.calls, [['csi', '', [0, 1], data]])
|
||||
parser.reset()
|
||||
test_terminal.clear()
|
||||
|
||||
|
||||
func test_trans_CSI_ENTRY_to_CSI_INTERMEDIATE_with_collect_action():
|
||||
for collect in range(0x20, 0x30):
|
||||
var data = Decoder.string_from_codepoint(collect)
|
||||
parser.current_state = ParserState.CSI_ENTRY
|
||||
parse(parser, data)
|
||||
assert_eq(parser.current_state, ParserState.CSI_INTERMEDIATE)
|
||||
assert_eq(parser.collect, data)
|
||||
parser.reset()
|
199
test/unit/test_parser.gd
Normal file
199
test/unit/test_parser.gd
Normal file
|
@ -0,0 +1,199 @@
|
|||
extends 'res://addons/gut/test.gd'
|
||||
|
||||
const Params = preload("res://addons/godot_xterm/parser/params.gd")
|
||||
|
||||
class TestParams:
|
||||
extends 'res://addons/gut/test.gd'
|
||||
|
||||
var params
|
||||
|
||||
func before_each():
|
||||
params = Params.new()
|
||||
|
||||
func test_respects_ctor_args():
|
||||
params = Params.new(12, 23)
|
||||
assert_eq(params.params.size(), 12)
|
||||
assert_eq(params.sub_params.size(), 23)
|
||||
assert_eq(params.to_array(), [])
|
||||
|
||||
func test_add_param():
|
||||
params.add_param(1)
|
||||
assert_eq(params.length, 1)
|
||||
assert_eq(params.params.slice(0, params.length - 1), [1])
|
||||
assert_eq(params.to_array(), [1])
|
||||
params.add_param(23)
|
||||
assert_eq(params.length, 2)
|
||||
assert_eq(params.params.slice(0, params.length - 1), [1, 23])
|
||||
assert_eq(params.to_array(), [1, 23])
|
||||
assert_eq(params.sub_params_length, 0)
|
||||
|
||||
func test_add_sub_param():
|
||||
params.add_param(1)
|
||||
params.add_sub_param(2)
|
||||
params.add_sub_param(3)
|
||||
assert_eq(params.length, 1)
|
||||
assert_eq(params.sub_params_length, 2)
|
||||
assert_eq(params.to_array(), [1, [2, 3]])
|
||||
params.add_param(12345)
|
||||
params.add_sub_param(-1)
|
||||
assert_eq(params.length, 2)
|
||||
assert_eq(params.sub_params_length, 3)
|
||||
assert_eq(params.to_array(), [1, [2,3], 12345, [-1]])
|
||||
|
||||
func test_should_not_add_sub_params_without_previous_param():
|
||||
params.add_sub_param(2)
|
||||
params.add_sub_param(3)
|
||||
assert_eq(params.length, 0)
|
||||
assert_eq(params.sub_params_length, 0)
|
||||
assert_eq(params.to_array(), [])
|
||||
params.add_param(1)
|
||||
params.add_sub_param(2)
|
||||
params.add_sub_param(3)
|
||||
assert_eq(params.length, 1)
|
||||
assert_eq(params.sub_params_length, 2)
|
||||
assert_eq(params.to_array(), [1, [2, 3]])
|
||||
|
||||
func test_reset():
|
||||
params.add_param(1)
|
||||
params.add_sub_param(2)
|
||||
params.add_sub_param(3)
|
||||
params.add_param(12345)
|
||||
params.reset()
|
||||
assert_eq(params.length, 0)
|
||||
assert_eq(params.sub_params_length, 0)
|
||||
assert_eq(params.to_array(), [])
|
||||
params.add_param(1)
|
||||
params.add_sub_param(2)
|
||||
params.add_sub_param(3)
|
||||
params.add_param(12345)
|
||||
params.add_sub_param(-1)
|
||||
assert_eq(params.length, 2)
|
||||
assert_eq(params.sub_params_length, 3)
|
||||
assert_eq(params.to_array(), [1, [2, 3], 12345, [-1]])
|
||||
|
||||
|
||||
func test_from_array_to_array():
|
||||
var data = []
|
||||
assert_eq(params.from_array(data).to_array(), data)
|
||||
data = [1, [2, 3], 12345, [-1]]
|
||||
assert_eq(params.from_array(data).to_array(), data)
|
||||
data = [38, 2, 50, 100, 150]
|
||||
assert_eq(params.from_array(data).to_array(), data)
|
||||
data = [38, 2, 50, 100, [150]]
|
||||
assert_eq(params.from_array(data).to_array(), data)
|
||||
data = [38, [2, 50, 100, 150]]
|
||||
assert_eq(params.from_array(data).to_array(), data)
|
||||
# strip empty sub params
|
||||
data = [38, [2, 50, 100, 150], 5, [], 6]
|
||||
assert_eq(Params.from_array(data).to_array(), [38, [2, 50, 100, 150], 5, 6])
|
||||
# ignore leading sub params
|
||||
data = [[1,2], 12345, [-1]]
|
||||
assert_eq(Params.from_array(data).to_array(), [12345, [-1]])
|
||||
|
||||
|
||||
class TestParse:
|
||||
extends 'res://addons/gut/test.gd'
|
||||
|
||||
var params
|
||||
|
||||
func parse(params, s):
|
||||
params.reset()
|
||||
params.add_param(0)
|
||||
if typeof(s) == TYPE_STRING:
|
||||
s = [s]
|
||||
for chunk in s:
|
||||
var i = 0
|
||||
while i < chunk.length():
|
||||
# Start for
|
||||
var code = chunk.to_ascii()[i]
|
||||
var do = true
|
||||
while do:
|
||||
match code:
|
||||
0x3b:
|
||||
params.add_param(0)
|
||||
0x3a:
|
||||
params.add_sub_param(-1)
|
||||
_:
|
||||
params.add_digit(code - 48)
|
||||
code = chunk.to_ascii()[i] if i < chunk.length() else 0
|
||||
i+=1
|
||||
do = i < s.size() and code > 0x2f and code < 0x3c
|
||||
i-=1
|
||||
# End for
|
||||
i+=1
|
||||
|
||||
func before_each():
|
||||
params = Params.new()
|
||||
|
||||
func test_param_defaults_to_0(): # ZDM (Zero Default Mode)
|
||||
parse(params, '')
|
||||
assert_eq(params.to_array(), [0])
|
||||
|
||||
func test_sub_param_defaults_to_neg_1():
|
||||
parse(params, ':')
|
||||
assert_eq(params.to_array(), [0, [-1]])
|
||||
|
||||
func test_reset_on_new_sequence():
|
||||
parse(params, '1;2;3')
|
||||
assert_eq(params.to_array(), [1, 2, 3])
|
||||
parse(params, '4')
|
||||
assert_eq(params.to_array(), [4])
|
||||
parse(params, '4::123:5;6;7')
|
||||
assert_eq(params.to_array(), [4, [-1, 123, 5], 6, 7])
|
||||
parse(params, '')
|
||||
assert_eq(params.to_array(), [0])
|
||||
|
||||
func test_should_handle_length_restrictions_correctly():
|
||||
params = Params.new(3, 3)
|
||||
parse(params, '1;2;3')
|
||||
assert_eq(params.to_array(), [1, 2, 3])
|
||||
parse(params, '4')
|
||||
assert_eq(params.to_array(), [4])
|
||||
parse(params, '4::123:5;6;7')
|
||||
assert_eq(params.to_array(), [4, [-1, 123, 5], 6, 7])
|
||||
parse(params, '')
|
||||
assert_eq(params.to_array(), [0])
|
||||
# overlong params
|
||||
parse(params, '4;38:2::50:100:150;48:5:22')
|
||||
assert_eq(params.to_array(), [4, 38, [2, -1, 50], 48])
|
||||
# overlong sub params
|
||||
parse(params, '4;38:2::50:100:150;48:5:22')
|
||||
assert_eq(params.to_array(), [4, 38, [2, -1, 50], 48])
|
||||
|
||||
func test_typical_sequences():
|
||||
# SGR with semicolon syntax
|
||||
parse(params, '0;4;38;2;50;100;150;48;5;22')
|
||||
assert_eq(params.to_array(), [0, 4, 38, 2, 50, 100, 150, 48, 5, 22])
|
||||
# SGR mixed style (partly wrong)
|
||||
parse(params, '0;4;38;2;50:100:150;48;5:22')
|
||||
assert_eq(params.to_array(), [0, 4, 38, 2, 50, [100, 150], 48, 5, [22]])
|
||||
# SGR colon style
|
||||
parse(params, '0;4;38:2::50:100:150;48:5:22')
|
||||
assert_eq(params.to_array(), [0, 4, 38, [2, -1, 50, 100, 150], 48, [5, 22]])
|
||||
|
||||
func test_clamp_parsed_params():
|
||||
parse(params, '2147483648')
|
||||
assert_eq(params.to_array(), [0x7FFFFFFF])
|
||||
|
||||
func test_clamp_parsed_sub_params():
|
||||
parse(params, ':2147483648')
|
||||
assert_eq(params.to_array(), [0, [0x7FFFFFFF]])
|
||||
|
||||
func test_should_cancel_subdigits_if_beyond_params_limit():
|
||||
parse(params, ';;;;;;;;;10;;;;;;;;;;20;;;;;;;;;;30;31;32;33;34;35::::::::')
|
||||
assert_eq(params.to_array(), [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 10,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 20,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 32
|
||||
])
|
||||
|
||||
# func test_should_carry_forward_is_sub_state():
|
||||
# parse(params, ['1:22:33', '44'])
|
||||
# assert_eq(params.to_array(), [1, [22, 3344]])
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
97
test/unit/test_text_decoder.gd
Normal file
97
test/unit/test_text_decoder.gd
Normal file
|
@ -0,0 +1,97 @@
|
|||
# Copyright (c) 2020 The GodotXterm authors.
|
||||
# Copyright (c) 2019 The xterm.js authors. All rights reserved.
|
||||
# License MIT
|
||||
extends 'res://addons/gut/test.gd'
|
||||
|
||||
const Decoder = preload("res://addons/godot_xterm/input/text_decoder.gd")
|
||||
|
||||
# Note: There might be some invisible characters (such as emoji) depending
|
||||
# on your editor and font settings.
|
||||
const TEST_STRINGS = [
|
||||
"Лорем ипсум долор сит амет, ех сеа аццусам диссентиет. Ан еос стет еирмод витуперата. Иус дицерет урбанитас ет. Ан при алтера долорес сплендиде, цу яуо интегре денияуе, игнота волуптариа инструцтиор цу вим.",
|
||||
"ლორემ იფსუმ დოლორ სით ამეთ, ფაცერ მუციუს ცონსეთეთურ ყუო იდ, ფერ ვივენდუმ ყუაერენდუმ ეა, ესთ ამეთ მოვეთ სუავითათე ცუ. ვითაე სენსიბუს ან ვიხ. ეხერცი დეთერრუისსეთ უთ ყუი. ვოცენთ დებითის ადიფისცი ეთ ფერ. ნეც ან ფეუგაით ფორენსიბუს ინთერესსეთ. იდ დიცო რიდენს იუს. დისსენთიეთ ცონსეყუუნთურ სედ ნე, ნოვუმ მუნერე ეუმ ათ, ნე ეუმ ნიჰილ ირაცუნდია ურბანითას.",
|
||||
"अधिकांश अमितकुमार प्रोत्साहित मुख्य जाने प्रसारन विश्लेषण विश्व दारी अनुवादक अधिकांश नवंबर विषय गटकउसि गोपनीयता विकास जनित परस्पर गटकउसि अन्तरराष्ट्रीयकरन होसके मानव पुर्णता कम्प्युटर यन्त्रालय प्रति साधन",
|
||||
"覧六子当聞社計文護行情投身斗来。増落世的況上席備界先関権能万。本物挙歯乳全事携供板栃果以。頭月患端撤競見界記引去法条公泊候。決海備駆取品目芸方用朝示上用報。講申務紙約週堂出応理田流団幸稿。起保帯吉対阜庭支肯豪彰属本躍。量抑熊事府募動極都掲仮読岸。自続工就断庫指北速配鳴約事新住米信中験。婚浜袋著金市生交保他取情距。",
|
||||
"八メル務問へふらく博辞説いわょ読全タヨムケ東校どっ知壁テケ禁去フミ人過を装5階がねぜ法逆はじ端40落ミ予竹マヘナセ任1悪た。省ぜりせ製暇ょへそけ風井イ劣手はぼまず郵富法く作断タオイ取座ゅょが出作ホシ月給26島ツチ皇面ユトクイ暮犯リワナヤ断連こうでつ蔭柔薄とレにの。演めけふぱ損田転10得観びトげぎ王物鉄夜がまけ理惜くち牡提づ車惑参ヘカユモ長臓超漫ぼドかわ。",
|
||||
"모든 국민은 행위시의 법률에 의하여 범죄를 구성하지 아니하는 행위로 소추되지 아니하며. 전직대통령의 신분과 예우에 관하여는 법률로 정한다, 국회는 헌법 또는 법률에 특별한 규정이 없는 한 재적의원 과반수의 출석과 출석의원 과반수의 찬성으로 의결한다. 군인·군무원·경찰공무원 기타 법률이 정하는 자가 전투·훈련등 직무집행과 관련하여 받은 손해에 대하여는 법률이 정하는 보상외에 국가 또는 공공단체에 공무원의 직무상 불법행위로 인한 배상은 청구할 수 없다.",
|
||||
"كان فشكّل الشرقي مع, واحدة للمجهود تزامناً بعض بل. وتم جنوب للصين غينيا لم, ان وبدون وكسبت الأمور ذلك, أسر الخاسر الانجليزية هو. نفس لغزو مواقعها هو. الجو علاقة الصعداء انه أي, كما مع بمباركة للإتحاد الوزراء. ترتيب الأولى أن حدى, الشتوية باستحداث مدن بل, كان قد أوسع عملية. الأوضاع بالمطالبة كل قام, دون إذ شمال الربيع،. هُزم الخاصّة ٣٠ أما, مايو الصينية مع قبل.",
|
||||
"או סדר החול מיזמי קרימינולוגיה. קהילה בגרסה לויקיפדים אל היא, של צעד ציור ואלקטרוניקה. מדע מה ברית המזנון ארכיאולוגיה, אל טבלאות מבוקשים כלל. מאמרשיחהצפה העריכהגירסאות שכל אל, כתב עיצוב מושגי של. קבלו קלאסיים ב מתן. נבחרים אווירונאוטיקה אם מלא, לוח למנוע ארכיאולוגיה מה. ארץ לערוך בקרבת מונחונים או, עזרה רקטות לויקיפדים אחר גם.",
|
||||
"Лорем ლორემ अधिकांश 覧六子 八メル 모든 בקרבת 💮 😂 äggg 123€ 𝄞.",
|
||||
]
|
||||
|
||||
func test_utf32_to_utf8():
|
||||
# 1 byte utf8 character
|
||||
assert_eq(
|
||||
Decoder.utf32_to_utf8(0x00000061),
|
||||
PoolByteArray([0x61])
|
||||
)
|
||||
# 2 byte utf8 character
|
||||
assert_eq(
|
||||
Decoder.utf32_to_utf8(0x00000761),
|
||||
PoolByteArray([0xdd, 0xa1])
|
||||
)
|
||||
# 3 byte utf8 character
|
||||
assert_eq(
|
||||
Decoder.utf32_to_utf8(0x00002621),
|
||||
PoolByteArray([0xe2, 0x98, 0xa1])
|
||||
)
|
||||
# 4 byte utf8 character
|
||||
assert_eq(
|
||||
Decoder.utf32_to_utf8(0x00010144),
|
||||
PoolByteArray([0xf0, 0x90, 0x85, 0x84])
|
||||
)
|
||||
assert_eq(
|
||||
Decoder.utf32_to_utf8(0x0001f427) as Array,
|
||||
PoolByteArray([0xf0, 0x9f, 0x90, 0xa7]) as Array
|
||||
)
|
||||
|
||||
func test_string_from_codepoint():
|
||||
assert_eq(Decoder.string_from_codepoint(49), '1')
|
||||
assert_eq(Decoder.string_from_codepoint(0x1f427), '🐧')
|
||||
assert_eq(Decoder.string_from_codepoint(0x1d11e), '𝄞')
|
||||
|
||||
func test_utf32_to_string():
|
||||
assert_eq(
|
||||
Decoder.utf32_to_string([49, 50, 51, 0x1d11e, 49, 50, 51]),
|
||||
'123𝄞123'
|
||||
)
|
||||
|
||||
class TestUtf8ToUtf32Decoder:
|
||||
extends 'res://addons/gut/test.gd'
|
||||
|
||||
var decoder = Decoder.Utf8ToUtf32.new()
|
||||
var target = []
|
||||
|
||||
func before_each():
|
||||
decoder.clear()
|
||||
target.clear()
|
||||
target.resize(5)
|
||||
|
||||
func skip_test_full_code_point_0_to_65535(): # 1/2/3 byte sequences
|
||||
for i in range(65536):
|
||||
# skip surrogate pairs
|
||||
if i >= 0xD800 and i <= 0xDFFF:
|
||||
continue
|
||||
var utf8_data = Decoder.utf32_to_utf8(i)
|
||||
var length = decoder.decode(utf8_data, target)
|
||||
assert_eq(length, 1)
|
||||
assert_eq(
|
||||
Decoder.string_from_codepoint(target[0]),
|
||||
utf8_data.get_string_from_utf8()
|
||||
)
|
||||
decoder.clear()
|
||||
|
||||
func skip_test_full_codepoint_65536_to_0x10FFFF(): # 4 byte sequences
|
||||
for i in range(65536, 0x10FFFF):
|
||||
var utf8_data = Decoder.utf32_to_utf8(i)
|
||||
var length = decoder.decode(utf8_data, target)
|
||||
assert_eq(length, 1)
|
||||
assert_eq(target[0], i)
|
||||
|
||||
func test_test_strings():
|
||||
target.resize(500)
|
||||
for string in TEST_STRINGS:
|
||||
var utf8_data = string.to_utf8()
|
||||
var length = decoder.decode(utf8_data, target)
|
||||
assert_eq(Decoder.utf32_to_string(target, 0, length), string)
|
||||
decoder.clear()
|
Loading…
Add table
Add a link
Reference in a new issue