godot-xterm/test/test_terminal.gd
Leroy Hopson d00a31fb45
feat(term): implement clear() method
Clears all but the bottommost row of the terminal (including scrollback
buffer) and moves the bottommost row to the top.
2024-03-03 22:39:51 +13:00

242 lines
6.6 KiB
GDScript

# SPDX-FileCopyrightText: 2021-2024 Leroy Hopson <godot-xterm@leroy.nix.nz>
# SPDX-License-Identifier: MIT
class_name TerminalTest extends GodotXtermTest
func get_described_class():
return Terminal
func before_each():
super.before_each()
subject.size = Vector2(400, 200)
# Helper function to fill the screen with the given character.
func fill_screen(char: String = "A") -> String:
var result = char.repeat(subject.get_cols() * subject.get_rows())
subject.write(result)
return result
class TestInterface:
extends TerminalTest
## API V2.
# Properties.
func test_has_property_bell_muted():
assert_has_property_with_default_value("bell_muted", false)
func test_has_property_bell_cooldown():
assert_has_property_with_default_value("bell_cooldown", 0.1)
func test_has_property_blink_on_time():
assert_has_property_with_default_value("blink_on_time", 0.6)
func test_has_property_blink_off_time():
assert_has_property_with_default_value("blink_off_time", 0.3)
func test_has_property_copy_on_selection():
assert_has_property_with_default_value("copy_on_selection", false)
# TODO: Implement update_mode property.
func xtest_has_property_update_mode():
#assert_has_property_with_default_value("update_mode", UPDATE_MODE_AUTO)
pass
# cols and rows removed.
# Methods.
func test_has_method_clear():
assert_has_method_with_return_type("clear", TYPE_NIL)
func test_has_method_copy_all():
assert_has_method_with_return_type("copy_all", TYPE_STRING)
func test_has_method_copy_selection():
assert_has_method_with_return_type("copy_selection", TYPE_STRING)
func test_has_method_get_cols():
assert_has_method_with_return_type("get_cols", TYPE_INT)
func test_has_method_get_rows():
assert_has_method_with_return_type("get_rows", TYPE_INT)
func test_has_method_write():
assert_has_method(subject, "write")
# Signals.
func test_has_signal_data_sent():
assert_has_signal(subject, "data_sent")
func test_has_signal_key_pressed():
assert_has_signal(subject, "key_pressed")
func test_has_signal_size_changed():
assert_has_signal(subject, "size_changed")
func test_has_signal_bell():
assert_has_signal(subject, "bell")
# Enums.
# TODO: Implement UpdateMode enum.
func xtest_has_enum_update_mode():
assert_eq(described_class.UPDATE_MODE_DISABLED, 0)
assert_eq(described_class.AUTO, 1)
assert_eq(described_class.ALL, 2)
assert_eq(described_class.ALL_NEXT_FRAME, 3)
## API Next.
# Methods.
func test_has_method_get_cursor_pos():
assert_has_method_with_return_type("get_cursor_pos", TYPE_VECTOR2I)
func test_has_method_get_cell_size():
assert_has_method_with_return_type("get_cell_size", TYPE_VECTOR2)
func test_has_method_write_with_response():
assert_has_method_with_return_type("write", TYPE_STRING)
# Enums.
func test_has_enum_inverse_mode():
assert_eq(described_class.INVERSE_MODE_INVERT, 0)
assert_eq(described_class.INVERSE_MODE_SWAP, 1)
## Other tests.
func test_has_no_visible_children():
# We add children like the bell timer for private use that should not
# be visible outside of the node itself.
assert_eq(subject.get_child_count(), 0)
class TestBell:
extends TerminalTest
func test_bell() -> void:
watch_signals(subject)
subject.bell_cooldown = 0
subject.write(char(7))
subject.write(char(0x07))
subject.write("\a")
subject.write("\u0007")
subject.write("'Ask not for whom the \a tolls; it tolls for thee' - John Donne")
assert_signal_emit_count(subject, "bell", 5)
func test_bell_mute() -> void:
watch_signals(subject)
subject.bell_muted = true
subject.write("\a")
assert_signal_emit_count(subject, "bell", 0)
func test_bell_cooldown() -> void:
watch_signals(subject)
subject.bell_cooldown = 10000
subject.write("\a")
subject.write("\a")
assert_signal_emit_count(subject, "bell", 1)
func test_change_cooldown_while_active() -> void:
watch_signals(subject)
subject.bell_cooldown = 10000
subject.write("\a")
subject.bell_cooldown = 0
subject.write("\a")
assert_signal_emit_count(subject, "bell", 2)
class TestCursorPos:
extends TerminalTest
func test_get_cursor_pos_initial():
assert_eq(subject.get_cursor_pos(), Vector2i.ZERO)
func test_get_cursor_pos_x():
subject.write("_")
assert_eq(subject.get_cursor_pos().x, 1)
func test_get_cursor_pos_y():
subject.write("_".repeat(subject.cols + 1))
assert_eq(subject.get_cursor_pos().y, 1)
class TestWrite:
extends TerminalTest
func test_returns_response_when_input_contains_query():
var response = subject.write("\u001b[6n") # Query cursor position.
assert_eq(response, "\u001b[1;1R")
func test_returns_response_to_multiple_queries():
var response = subject.write("\u001b[6n\u001b[5n") # Query cursor position and status.
assert_eq(response, "\u001b[1;1R\u001b[0n")
func test_returns_response_to_multiple_queries_among_other_data():
var response = subject.write("hello\r\nworld\u001b[6nother\r\ndata\u001b[5ntest")
assert_eq(response, "\u001b[2;6R\u001b[0n")
func test_data_sent_emitted_on_query():
subject.write("\u001b[6n")
assert_signal_emitted(subject, "data_sent")
func test_data_sent_emitted_with_response():
subject.write("\u001b[6n")
assert_signal_emitted_with_parameters(
subject, "data_sent", ["\u001b[1;1R".to_utf8_buffer()]
)
func test_data_sent_not_emitted_when_empty_string_written():
subject.write("")
assert_signal_emit_count(subject, "data_sent", 0)
class TestCopy:
extends TerminalTest
func test_copy_all_copies_the_entire_screen():
var text = fill_screen()
# The text will be wrapped over multiple lines and copy_all() preserves
# these line wraps, therefore we need to strip them.
assert_eq(subject.copy_all().replace("\n", ""), text)
func test_copy_all_empty_screen():
assert_eq(subject.copy_all(), "\n".repeat(subject.get_rows()))
func test_copy_all_copies_the_scrollback_buffer():
var text = fill_screen()
text += fill_screen("B")
text += fill_screen("C")
assert_eq(subject.copy_all().replace("\n", ""), text)
func test_copy_all_copies_unicode_text():
var text = "アイウエオカキクケコサシスセソ"
subject.write(text)
assert_string_contains(subject.copy_all(), text)
class TestClear:
extends TerminalTest
func test_clear_an_empty_screen_changes_nothing():
var empty_screen = subject.copy_all()
subject.clear()
var screen_after = subject.copy_all()
assert_eq(screen_after, empty_screen)
func test_clear_when_screen_is_full_clears_all_but_the_bottommost_row():
fill_screen()
var final_line = "THIS SHOULDN'T BE CLEARED"
subject.write(final_line)
subject.clear()
var screen_after = subject.copy_all()
var expected = final_line + "\n".repeat(subject.get_rows())
assert_eq(screen_after, expected)