# SPDX-FileCopyrightText: 2021-2024 Leroy Hopson # 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) func test_copy_selection_when_nothing_selected(): assert_eq(subject.copy_selection(), "") 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) class TestSelect: extends TerminalTest # Use the behavior of TextEdit's select() method as a reference. var text_edit: TextEdit func assert_select_eq(argv, expected): text_edit.callv("select", argv) subject.callv("select", argv) assert_eq( expected, text_edit.get_selected_text(), "expected does not match reference implementation" ) assert_eq(subject.copy_selection(), expected) func before_each(): super.before_each() text_edit = TextEdit.new() text_edit.text = "0123456789\nABCDEFGHIJ\n)!@#$%^&*(\n\n\n\n\n\n" add_child_autofree(text_edit) subject.write("0123456789\r\nABCDEFGHIJ\r\n)!@#$%^&*(") func test_select_nothing(): assert_select_eq([0, 0, 0, 0], "") func test_select_first_character(): assert_select_eq([0, 0, 0, 1], "0") func test_select_last_character(): assert_select_eq([2, 9, 2, 10], "(") func test_select_reverse_column(): assert_select_eq([0, 6, 0, 1], "12345") func test_select_preceeds_column_bounds(): assert_select_eq([0, -2, 0, -1], "") assert_select_eq([0, -2, 0, 0], "") assert_select_eq([0, -2, 0, 1], "0") func test_select_exceeds_column_bounds(): assert_select_eq([0, 5, 0, 999], "56789") func test_select_first_row(): assert_select_eq([0, 0, 0, 10], "0123456789") func test_select_second_row(): assert_select_eq([1, 0, 1, 10], "ABCDEFGHIJ") func test_select_multiple_rows(): assert_select_eq([0, 0, 1, 10], "0123456789\nABCDEFGHIJ") func test_select_rows_reverse(): assert_select_eq([1, 5, 0, 0], "0123456789\nABCDE") func test_select_preceeds_row_bounds(): assert_select_eq([-2, 0, -1, 10], "0123456789") assert_select_eq([-2, 0, 0, 10], "0123456789") assert_select_eq([-2, 0, 1, 10], "0123456789\nABCDEFGHIJ") func test_select_exceeds_row_bounds(): assert_select_eq([1, 5, 999, 999], "FGHIJ\n)!@#$%^&*(\n\n\n\n\n\n") func test_wide_bounds(): assert_select_eq([-999, -999, 999, 999], "0123456789\nABCDEFGHIJ\n)!@#$%^&*(\n\n\n\n\n\n")