godot-xterm/test/integration/terminal.test.gd

157 lines
4.7 KiB
GDScript
Raw Normal View History

Fix unicode errors Changes `write()` method of native pipe and terminal to accept a PoolByteArray rather than String. This means that `get_string_from_utf8()` is no longer called on data coming from PTY and being sent to Terminal. The terminal state machine already has a UTF8 parser which maintains its state across calls to `write()`. This means that we can write half the bytes of a single unicode character in one call and the remaining half in the next call and the state machine will parse it correctly. On the other hand, the `get_string_from_utf8()` method of Godot's PoolByteArray requires that the array contains completely valid UTF8, otherwise we get errors such as "Unicode error: invalid skip". The data coming from PTY can be arbitrarily split in the middle of a unicode character meaning that we will sometimes get errors when calling `get_string_from_utf8()` on it. This is more likely to occur when there is a large amount of output (i.e. it's more likely to be split). In other cases, the data might intentionally contain invalid unicode such as when printing binary files or random data (e.g. `cat /bin/sh`, `cat /dev/random`). We avoid these errors by passing the PoolByteArray data directly to the terminal state machine. In addition to fixing unicode errors, this commit: - Prevents repeated calls to pipes `_read_cb()` method that would block Godot and result in a crash with the message "ERROR: All memory pool allocations are in use" that resulted from writing data to an ever-increasing number of PoolByteArrays before any of them could be freed. This could be triggered by running the `cat /dev/urandom` command after making the change to `write()` mentioned above. - Prevents memory leaks by freeing libuv buffers after they have been copied to PoolByteArrays. Fixes #55.
2022-08-14 09:45:17 +02:00
extends "res://addons/gut/test.gd"
const Terminal := preload("res://addons/godot_xterm/terminal.gd")
var terminal: Terminal
func before_each():
terminal = Terminal.new()
2022-11-09 21:57:46 +01:00
terminal.size = Vector2(400, 200)
add_child_autofree(terminal)
func test_bell() -> void:
terminal.bell_cooldown = 0
terminal.write(char(7))
terminal.write(char(0x07))
terminal.write("\a")
terminal.write("\u0007")
terminal.write("'Ask not for whom the \a tolls; it tolls for thee' - John Donne")
2022-11-09 21:57:46 +01:00
await yield_to(terminal, "bell", 5).YIELD
assert_signal_emit_count(terminal, "bell", 5)
func test_bell_cooldown() -> void:
watch_signals(terminal)
terminal.bell_cooldown = 0.5
terminal.write("\a")
terminal.write("\a")
2022-11-09 21:57:46 +01:00
await yield_for(1).YIELD
terminal.write("\a")
2022-11-09 21:57:46 +01:00
await yield_to(terminal, "bell", 5).YIELD
assert_signal_emit_count(terminal, "bell", 2)
Fix unicode errors Changes `write()` method of native pipe and terminal to accept a PoolByteArray rather than String. This means that `get_string_from_utf8()` is no longer called on data coming from PTY and being sent to Terminal. The terminal state machine already has a UTF8 parser which maintains its state across calls to `write()`. This means that we can write half the bytes of a single unicode character in one call and the remaining half in the next call and the state machine will parse it correctly. On the other hand, the `get_string_from_utf8()` method of Godot's PoolByteArray requires that the array contains completely valid UTF8, otherwise we get errors such as "Unicode error: invalid skip". The data coming from PTY can be arbitrarily split in the middle of a unicode character meaning that we will sometimes get errors when calling `get_string_from_utf8()` on it. This is more likely to occur when there is a large amount of output (i.e. it's more likely to be split). In other cases, the data might intentionally contain invalid unicode such as when printing binary files or random data (e.g. `cat /bin/sh`, `cat /dev/random`). We avoid these errors by passing the PoolByteArray data directly to the terminal state machine. In addition to fixing unicode errors, this commit: - Prevents repeated calls to pipes `_read_cb()` method that would block Godot and result in a crash with the message "ERROR: All memory pool allocations are in use" that resulted from writing data to an ever-increasing number of PoolByteArrays before any of them could be freed. This could be triggered by running the `cat /dev/urandom` command after making the change to `write()` mentioned above. - Prevents memory leaks by freeing libuv buffers after they have been copied to PoolByteArrays. Fixes #55.
2022-08-14 09:45:17 +02:00
func test_writing_random_data_to_terminal_does_not_crash_application():
2022-11-09 21:57:46 +01:00
add_child_autofree(preload("res://test/scenes/write_random.tscn").instantiate())
await yield_frames(5, "Writing random data to terminal").YIELD
Fix unicode errors Changes `write()` method of native pipe and terminal to accept a PoolByteArray rather than String. This means that `get_string_from_utf8()` is no longer called on data coming from PTY and being sent to Terminal. The terminal state machine already has a UTF8 parser which maintains its state across calls to `write()`. This means that we can write half the bytes of a single unicode character in one call and the remaining half in the next call and the state machine will parse it correctly. On the other hand, the `get_string_from_utf8()` method of Godot's PoolByteArray requires that the array contains completely valid UTF8, otherwise we get errors such as "Unicode error: invalid skip". The data coming from PTY can be arbitrarily split in the middle of a unicode character meaning that we will sometimes get errors when calling `get_string_from_utf8()` on it. This is more likely to occur when there is a large amount of output (i.e. it's more likely to be split). In other cases, the data might intentionally contain invalid unicode such as when printing binary files or random data (e.g. `cat /bin/sh`, `cat /dev/random`). We avoid these errors by passing the PoolByteArray data directly to the terminal state machine. In addition to fixing unicode errors, this commit: - Prevents repeated calls to pipes `_read_cb()` method that would block Godot and result in a crash with the message "ERROR: All memory pool allocations are in use" that resulted from writing data to an ever-increasing number of PoolByteArrays before any of them could be freed. This could be triggered by running the `cat /dev/urandom` command after making the change to `write()` mentioned above. - Prevents memory leaks by freeing libuv buffers after they have been copied to PoolByteArrays. Fixes #55.
2022-08-14 09:45:17 +02:00
assert_true(true, "Expected no crash when writing random data to terminal.")
class TestTheme:
extends "res://addons/gut/test.gd"
const Terminal := preload("res://addons/godot_xterm/terminal.gd")
const TestScene := preload("../scenes/theme.tscn")
const default_theme := preload("res://addons/godot_xterm/themes/default.tres")
const alt_theme := preload("res://addons/godot_xterm/themes/default_light.tres")
const COLORS := [
"black",
"red",
"green",
"yellow",
"blue",
"magenta",
"cyan",
"white",
"bright_black",
"bright_red",
"bright_green",
"bright_yellow",
"bright_blue",
"bright_magenta",
"bright_cyan",
"bright_white",
]
var terminal: Terminal
func _get_pixelv(src: Vector2) -> Color:
var screen := get_tree().root.get_texture().get_data()
2022-11-09 21:57:46 +01:00
false # screen.lock() # TODOConverter40, Image no longer requires locking, `false` helps to not break one line if/else, so it can freely be removed
screen.flip_y()
var pixel := screen.get_pixelv(src)
2022-11-09 21:57:46 +01:00
false # screen.unlock() # TODOConverter40, Image no longer requires locking, `false` helps to not break one line if/else, so it can freely be removed
return pixel
func _check_colors(theme: Theme):
var cell_size := Vector2(
2022-11-09 21:57:46 +01:00
int(terminal.size.x / terminal.get_cols()),
int(terminal.size.y / terminal.get_rows())
)
var src := cell_size / 2
for i in range(16):
var color_name = COLORS[i]
var expected_color = "#" + theme.get_color(color_name, "Terminal").to_html(false)
var actual_color = "#" + _get_pixelv(src).to_html(false)
assert_eq(
actual_color,
expected_color,
"Expected color '%s' to be displayed for '%s'." % [expected_color, color_name]
)
src += Vector2(cell_size.x, 0)
func before_each():
2022-11-09 21:57:46 +01:00
terminal = autofree(TestScene.instantiate())
await yield_frames(1).YIELD
func test_terminal_display_colors_from_default_theme():
terminal.theme = null
add_child(terminal)
2022-11-09 21:57:46 +01:00
await yield_to(terminal, "theme_changed", 5).YIELD
_check_colors(default_theme)
func test_terminal_displays_colors_from_theme():
terminal.theme = alt_theme
add_child(terminal)
2022-11-09 21:57:46 +01:00
await yield_to(terminal, "theme_changed", 5).YIELD
_check_colors(alt_theme)
func test_visible_characters_still_displayed_after_resize_with_default_theme():
terminal.theme = null
add_child(terminal)
2022-11-09 21:57:46 +01:00
await yield_frames(1).YIELD
OS.window_size += Vector2(1, 0)
2022-11-09 21:57:46 +01:00
await yield_to(terminal, "size_changed", 5).YIELD
_check_colors(default_theme)
func test_visible_characters_still_displayed_after_resize_with_custom_theme():
# Issue 57: https://github.com/lihop/godot-xterm/issues/57
terminal.theme = alt_theme
add_child(terminal)
2022-11-09 21:57:46 +01:00
await yield_to(terminal, "theme_changed", 5).YIELD
OS.window_size += Vector2(1, 0)
2022-11-09 21:57:46 +01:00
await yield_to(terminal, "size_changed", 5).YIELD
_check_colors(alt_theme)
func test_updates_colors_after_theme_set():
# Issue 58: https://github.com/lihop/godot-xterm/issues/58
terminal.theme = null
add_child(terminal)
2022-11-09 21:57:46 +01:00
await yield_frames(1).YIELD
terminal.theme = alt_theme
2022-11-09 21:57:46 +01:00
await yield_to(terminal, "theme_changed", 50).YIELD
_check_colors(alt_theme)
func test_updates_colors_after_theme_unset():
# Issue 58: https://github.com/lihop/godot-xterm/issues/58
terminal.theme = alt_theme
add_child(terminal)
2022-11-09 21:57:46 +01:00
await yield_to(terminal, "theme_changed", 5).YIELD
terminal.theme = null
2022-11-09 21:57:46 +01:00
await yield_to(terminal, "theme_changed", 5).YIELD
_check_colors(default_theme)
func test_updates_colors_after_theme_changed():
# Issue 58: https://github.com/lihop/godot-xterm/issues/58
terminal.theme = alt_theme
add_child(terminal)
2022-11-09 21:57:46 +01:00
await yield_to(terminal, "theme_changed", 5).YIELD
terminal.theme = default_theme
2022-11-09 21:57:46 +01:00
await yield_to(terminal, "theme_changed", 5).YIELD
_check_colors(default_theme)