summaryrefslogtreecommitdiff
path: root/addons/gdUnit4/src/core/writers
diff options
context:
space:
mode:
Diffstat (limited to 'addons/gdUnit4/src/core/writers')
-rw-r--r--addons/gdUnit4/src/core/writers/GdUnitCSIMessageWriter.gd227
-rw-r--r--addons/gdUnit4/src/core/writers/GdUnitCSIMessageWriter.gd.uid1
-rw-r--r--addons/gdUnit4/src/core/writers/GdUnitMessageWriter.gd214
-rw-r--r--addons/gdUnit4/src/core/writers/GdUnitMessageWriter.gd.uid1
-rw-r--r--addons/gdUnit4/src/core/writers/GdUnitRichTextMessageWriter.gd115
-rw-r--r--addons/gdUnit4/src/core/writers/GdUnitRichTextMessageWriter.gd.uid1
6 files changed, 559 insertions, 0 deletions
diff --git a/addons/gdUnit4/src/core/writers/GdUnitCSIMessageWriter.gd b/addons/gdUnit4/src/core/writers/GdUnitCSIMessageWriter.gd
new file mode 100644
index 0000000..cc93ee2
--- /dev/null
+++ b/addons/gdUnit4/src/core/writers/GdUnitCSIMessageWriter.gd
@@ -0,0 +1,227 @@
+@tool
+class_name GdUnitCSIMessageWriter
+extends GdUnitMessageWriter
+## A message writer implementation using ANSI/CSI escape codes for console output.[br]
+## [br]
+## This writer provides formatted message output using CSI (Control Sequence Introducer) codes.[br]
+## It supports:[br]
+## - Color using RGB values[br]
+## - Text styles (bold, italic, underline)[br]
+## - Cursor positioning and text alignment[br]
+## [br]
+## Used primarily for console-based test execution and CI/CD environments.
+
+
+enum {
+ COLOR_TABLE,
+ COLOR_RGB
+}
+
+const CSI_BOLD = ""
+const CSI_ITALIC = ""
+const CSI_UNDERLINE = ""
+const CSI_RESET = ""
+
+# Control Sequence Introducer
+var _debug_show_color_codes := false
+var _color_mode := COLOR_TABLE
+
+## Current cursor position in the line
+var _current_pos := 0
+
+# Pre-compiled regex patterns for tag matching
+var _tag_regex: RegEx
+
+
+## Constructs CSI style codes based on flags.[br]
+## [br]
+## [param flags] The style flags to apply (BOLD, ITALIC, UNDERLINE).[br]
+## Returns the corresponding CSI codes.
+func _apply_style_flags(flags: int) -> String:
+ var _style := ""
+ if flags & BOLD:
+ _style += CSI_BOLD
+ if flags & ITALIC:
+ _style += CSI_ITALIC
+ if flags & UNDERLINE:
+ _style += CSI_UNDERLINE
+ return _style
+
+
+## Converts a color string (named or hex) to a Color object
+func _parse_color(color_str: String) -> Color:
+ return Color.from_string(color_str.strip_edges().to_lower(), Color.WHITE)
+
+
+## Generates CSI color code for foreground color
+func _color_to_csi_fg(c: Color) -> String:
+ return "[38;2;%d;%d;%dm" % [c.r8 * c.a, c.g8 * c.a, c.b8 * c.a]
+
+
+## Generates CSI color code for background color
+func _color_to_csi_bg(c: Color) -> String:
+ return "[48;2;%d;%d;%dm" % [c.r8 * c.a, c.g8 * c.a, c.b8 * c.a]
+
+
+func _init_regex_patterns() -> void:
+ if not _tag_regex:
+ _tag_regex = RegEx.new()
+ # Match all richtext tags: [tag], [tag=value], [/tag]
+ _tag_regex.compile(r"\[/?(?:color|bgcolor|b|i|u)(?:=[^\]]+)?\]")
+
+
+func _extract_color_from_tag(tag: String, tag_assign: String) -> Color:
+ var tag_assign_length := tag_assign.length()
+ var color_value := tag.substr(tag_assign_length, tag.length() - tag_assign_length - 1)
+ return _parse_color(color_value)
+
+
+## Optimized richtext to CSI conversion using regex and lookup processing
+func _bbcode_tags_to_csi_codes(message: String) -> String:
+ _init_regex_patterns()
+
+ var result := ""
+ var last_pos := 0
+ var color_stack: Array[Color] = []
+ var bgcolor_stack: Array[Color] = []
+
+ # Find all richtext tags
+ var matches := _tag_regex.search_all(message)
+
+ for match in matches:
+ var start_pos := match.get_start()
+ var end_pos := match.get_end()
+ var tag := match.get_string(0)
+
+ # Add text before this tag
+ result += message.substr(last_pos, start_pos - last_pos)
+
+ # Process the tag
+ if tag.begins_with("[color="):
+ var fg_color := _extract_color_from_tag(tag, "[color=")
+ color_stack.push_back(fg_color)
+ result += _color_to_csi_fg(fg_color)
+ elif tag.begins_with("[bgcolor="):
+ var bg_color := _extract_color_from_tag(tag, "[bgcolor=")
+ bgcolor_stack.push_back(bg_color)
+ result += _color_to_csi_bg(bg_color)
+ elif tag == "[b]":
+ result += CSI_BOLD
+ elif tag == "[i]":
+ result += CSI_ITALIC
+ elif tag == "[u]":
+ result += CSI_UNDERLINE
+ elif tag == "[/color]":
+ result += CSI_RESET
+ if color_stack.size() > 0:
+ color_stack.pop_back()
+ # Restore remaining styles and colors
+ if color_stack.size() > 0:
+ result += _color_to_csi_fg(color_stack[-1])
+ if bgcolor_stack.size() > 0:
+ result += _color_to_csi_bg(bgcolor_stack[-1])
+ elif tag == "[/bgcolor]":
+ result += CSI_RESET
+ if bgcolor_stack.size() > 0:
+ bgcolor_stack.pop_back()
+ # Restore remaining styles and colors
+ if color_stack.size() > 0:
+ result += _color_to_csi_fg(color_stack[-1])
+ if bgcolor_stack.size() > 0:
+ result += _color_to_csi_bg(bgcolor_stack[-1])
+ elif tag in ["[/b]", "[/i]", "[/u]"]:
+ result += CSI_RESET
+ # Restore remaining colors after style reset
+ if color_stack.size() > 0:
+ result += _color_to_csi_fg(color_stack[-1])
+ if bgcolor_stack.size() > 0:
+ result += _color_to_csi_bg(bgcolor_stack[-1])
+
+ last_pos = end_pos
+
+ # Add remaining text after last tag
+ result += message.substr(last_pos)
+
+ return result
+
+
+## Implementation of basic message output with formatting.
+func _print_message(_message: String, _color: Color, _indent: int, _flags: int) -> void:
+ var text := _bbcode_tags_to_csi_codes(_message)
+ var indent_text := "".lpad(_indent * 2)
+ var _style := _apply_style_flags(_flags)
+ printraw("%s[38;2;%d;%d;%dm%s%s" % [indent_text, _color.r8, _color.g8, _color.b8, _style, text])
+ _current_pos += _indent * 2 + text.length()
+
+
+## Implementation of line-ending message output with formatting.
+func _println_message(_message: String, _color: Color, _indent: int, _flags: int) -> void:
+ _print_message(_message, _color, _indent, _flags)
+ prints()
+ _current_pos = 0
+
+
+## Implementation of positioned message output with formatting.
+func _print_at(_message: String, cursor_pos: int, _color: Color, _effect: GdUnitMessageWriter.Effect, _align: Align, _flags: int) -> void:
+ if _align == Align.RIGHT:
+ cursor_pos = cursor_pos - _message.length()
+
+ if cursor_pos > _current_pos:
+ printraw("[%dG" % cursor_pos) # Move cursor to absolute position
+ else:
+ _message = " " + _message
+
+ var _style := _apply_style_flags(_flags)
+ printraw("[38;2;%d;%d;%dm%s%s" % [_color.r8, _color.g8, _color.b8, _style, _message])
+ _current_pos = cursor_pos + _message.length()
+
+
+## Writes a line break and returns self for chaining.
+func new_line() -> GdUnitCSIMessageWriter:
+ prints()
+ return self
+
+
+## Saves the current cursor position.[br]
+## Returns self for chaining.
+func save_cursor() -> GdUnitCSIMessageWriter:
+ printraw("")
+ return self
+
+
+## Restores previously saved cursor position.[br]
+## Returns self for chaining.
+func restore_cursor() -> GdUnitCSIMessageWriter:
+ printraw("")
+ return self
+
+
+## Clears screen content and resets cursor position.
+func clear() -> void:
+ printraw("") # Clear screen and move cursor to home
+ _current_pos = 0
+
+
+## Debug method to display the available color table.[br]
+## Shows both 6x6x6 color cube and RGB color modes.
+@warning_ignore("return_value_discarded")
+func _print_color_table() -> void:
+ color(Color.ANTIQUE_WHITE).println_message("Color Table 6x6x6")
+ _debug_show_color_codes = true
+ for green in range(0, 6):
+ for red in range(0, 6):
+ for blue in range(0, 6):
+ color(Color8(red * 42, green * 42, blue * 42)).println_message("████████ ")
+ new_line()
+ new_line()
+
+ color(Color.ANTIQUE_WHITE).println_message("Color Table RGB")
+ _color_mode = COLOR_RGB
+ for green in range(0, 6):
+ for red in range(0, 6):
+ for blue in range(0, 6):
+ color(Color8(red * 42, green * 42, blue * 42)).println_message("████████ ")
+ new_line()
+ new_line()
+ _color_mode = COLOR_TABLE
+ _debug_show_color_codes = false
diff --git a/addons/gdUnit4/src/core/writers/GdUnitCSIMessageWriter.gd.uid b/addons/gdUnit4/src/core/writers/GdUnitCSIMessageWriter.gd.uid
new file mode 100644
index 0000000..e87dda7
--- /dev/null
+++ b/addons/gdUnit4/src/core/writers/GdUnitCSIMessageWriter.gd.uid
@@ -0,0 +1 @@
+uid://bch3bjbfhr5lc
diff --git a/addons/gdUnit4/src/core/writers/GdUnitMessageWriter.gd b/addons/gdUnit4/src/core/writers/GdUnitMessageWriter.gd
new file mode 100644
index 0000000..5244efa
--- /dev/null
+++ b/addons/gdUnit4/src/core/writers/GdUnitMessageWriter.gd
@@ -0,0 +1,214 @@
+@tool
+class_name GdUnitMessageWriter
+extends RefCounted
+## Base interface class for writing formatted messages to different outputs.[br]
+## [br]
+## This class defines the interface and common functionality for writing formatted messages.[br]
+## It provides a fluent API for message formatting and supports different output targets.[br]
+## [br]
+## The class provides formatting options for:[br]
+## - Text colors[br]
+## - Text styles (bold, italic, underline)[br]
+## - Text effects (e.g., wave)[br]
+## - Text alignment[br]
+## - Indentation[br]
+## [br]
+## Two concrete implementations are available:[br]
+## - [GdUnitRichTextMessageWriter] writing to a [RichTextLabel][br]
+## - [GdUnitCSIMessageWriter] writing to console using CSI codes[br]
+## [br]
+## Example usage:[br]
+## [codeblock]
+## writer.color(Color.RED).style(BOLD).println_message("Test failed!")
+## writer.color(Color.GREEN).align(Align.RIGHT).print_at("Success", 80)
+## [/codeblock]
+
+
+## Text style flag for bold formatting
+const BOLD = 0x1
+## Text style flag for italic formatting
+const ITALIC = 0x2
+## Text style flag for underline formatting
+const UNDERLINE = 0x4
+
+
+## Represents special text effects that can be applied to the output
+enum Effect {
+ ## No special effect applied
+ NONE,
+ ## Applies a wave animation to the text
+ WAVE
+}
+
+
+## Controls text alignment at the specified cursor position
+enum Align {
+ ## Aligns text to the left of the cursor position
+ LEFT,
+ ## Aligns text to the right of the cursor position, accounting for text length
+ RIGHT
+}
+
+
+## The current text color to be used for the next output operation
+var _current_color := Color.WHITE
+
+## The current indentation level to be used for the next output operation.[br]
+## Each level represents two spaces of indentation.
+var _current_indent := 0
+
+## The current text style flags (BOLD, ITALIC, UNDERLINE) to be used for the next output operation
+var _current_flags := 0
+
+## The current text alignment to be used for the next output operation
+var _current_align := Align.LEFT
+
+## The current text effect to be used for the next output operation
+var _current_effect := GdUnitMessageWriter.Effect.NONE
+
+
+## Sets the text color for the next output operation.[br]
+## [br]
+## [param value] The color to be used for the text.
+## Returns self for method chaining.
+func color(value: Color) -> GdUnitMessageWriter:
+ _current_color = value
+ return self
+
+
+## Sets the indentation level for the next output operation.[br]
+## [br]
+## [param value] The number of indentation levels, where each level equals two spaces.
+## Returns self for method chaining.
+func indent(value: int) -> GdUnitMessageWriter:
+ _current_indent = value
+ return self
+
+
+## Sets text style flags for the next output operation.[br]
+## [br]
+## [param value] A combination of style flags (BOLD, ITALIC, UNDERLINE).
+## Returns self for method chaining.
+func style(value: int) -> GdUnitMessageWriter:
+ _current_flags = value
+ return self
+
+
+## Sets text effect for the next output operation.[br]
+## [br]
+## [param value] The effect to apply to the text (NONE, WAVE).
+## Returns self for method chaining.
+func effect(value: GdUnitMessageWriter.Effect) -> GdUnitMessageWriter:
+ _current_effect = value
+ return self
+
+
+## Sets text alignment for the next output operation.[br]
+## [br]
+## [param value] The alignment to use (LEFT, RIGHT).
+## Returns self for method chaining.
+func align(value: Align) -> GdUnitMessageWriter:
+ _current_align = value
+ return self
+
+
+## Resets all formatting options to their default values.[br]
+## [br]
+## Defaults:[br]
+## - color: Color.WHITE[br]
+## - indent: 0[br]
+## - flags: 0[br]
+## - align: LEFT[br]
+## - effect: NONE[br]
+## Returns self for method chaining.
+func reset() -> GdUnitMessageWriter:
+ _current_color = Color.WHITE
+ _current_indent = 0
+ _current_flags = 0
+ _current_align = Align.LEFT
+ _current_effect = Effect.NONE
+ return self
+
+
+## Prints a warning message in golden color.[br]
+## [br]
+## [param message] The warning message to print.
+func prints_warning(message: String) -> void:
+ color(Color.GOLDENROD).println_message(message)
+
+
+## Prints an error message in crimson color.[br]
+## [br]
+## [param message] The error message to print.
+func prints_error(message: String) -> void:
+ color(Color.CRIMSON).println_message(message)
+
+
+## Prints a message with current formatting settings.[br]
+## [br]
+## [param message] The text to print.
+func print_message(message: String) -> void:
+ _print_message(message, _current_color, _current_indent, _current_flags)
+ reset()
+
+
+## Prints a message with current formatting settings followed by a newline.[br]
+## [br]
+## [param message] The text to print.
+func println_message(message: String) -> void:
+ _println_message(message, _current_color, _current_indent, _current_flags)
+ reset()
+
+
+## Prints a message at a specific column position with current formatting settings.[br]
+## [br]
+## [param message] The text to print.[br]
+## [param cursor_pos] The column position where the text should start.
+func print_at(message: String, cursor_pos: int) -> void:
+ _print_at(message, cursor_pos, _current_color, _current_effect, _current_align, _current_flags)
+ reset()
+
+
+## Internal implementation of print_message.[br]
+## [br]
+## To be overridden by concrete formatters.[br]
+## [br]
+## [param message] The text to print.[br]
+## [param color] The color to use.[br]
+## [param indent] The indentation level.[br]
+## [param flags] The style flags to apply.
+func _print_message(_message: String, _color: Color, _indent: int, _flags: int) -> void:
+ pass
+
+
+## Internal implementation of println_message.[br]
+## [br]
+## To be overridden by concrete formatters.[br]
+## [br]
+## [param message] The text to print.[br]
+## [param color] The color to use.[br]
+## [param indent] The indentation level.[br]
+## [param flags] The style flags to apply.
+func _println_message(_message: String, _color: Color, _indent: int, _flags: int) -> void:
+ pass
+
+
+## Internal implementation of print_at.[br]
+## [br]
+## To be overridden by concrete formatters.[br]
+## [br]
+## [param message] The text to print.[br]
+## [param cursor_pos] The column position.[br]
+## [param color] The color to use.[br]
+## [param effect] The effect to apply.[br]
+## [param align] The text alignment.[br]
+## [param flags] The style flags to apply.
+func _print_at(_message: String, _cursor_pos: int, _color: Color, _effect: GdUnitMessageWriter.Effect, _align: Align, _flags: int) -> void:
+ pass
+
+
+## Clears all output content.[br]
+## [br]
+## To be overridden by concrete formatters.
+func clear() -> void:
+ pass
diff --git a/addons/gdUnit4/src/core/writers/GdUnitMessageWriter.gd.uid b/addons/gdUnit4/src/core/writers/GdUnitMessageWriter.gd.uid
new file mode 100644
index 0000000..0fe00d2
--- /dev/null
+++ b/addons/gdUnit4/src/core/writers/GdUnitMessageWriter.gd.uid
@@ -0,0 +1 @@
+uid://5etcu5juhye8
diff --git a/addons/gdUnit4/src/core/writers/GdUnitRichTextMessageWriter.gd b/addons/gdUnit4/src/core/writers/GdUnitRichTextMessageWriter.gd
new file mode 100644
index 0000000..b9f79e4
--- /dev/null
+++ b/addons/gdUnit4/src/core/writers/GdUnitRichTextMessageWriter.gd
@@ -0,0 +1,115 @@
+@tool
+class_name GdUnitRichTextMessageWriter
+extends GdUnitMessageWriter
+## A message writer implementation using [RichTextLabel] for the test report UI.[br]
+## [br]
+## This writer implementation writes formatted messages to a [RichTextLabel] using BBCode.[br]
+## It supports:[br]
+## - Text formatting using BBCode (bold, italic, underline)[br]
+## - Text coloring using push colors[br]
+## - Text indentation using push indent[br]
+## - Text effects like wave[br]
+## - Basic cursor positioning[br]
+## [br]
+## Used to format test reports in the editor UI.
+
+
+## The [RichTextLabel] instance to write formatted messages
+var _output: RichTextLabel
+
+## Tracks current position in characters from line start
+var _current_pos := 0
+
+
+## Creates a new message writer for the given [RichTextLabel].[br]
+## [br]
+## [param output] The [RichTextLabel] used for output.
+func _init(output: RichTextLabel) -> void:
+ _output = output
+
+
+## Applies text style flags by wrapping text in BBCode tags.[br]
+## [br]
+## Available styles:[br]
+## - BOLD: [b]text[/b][br]
+## - ITALIC: [i]text[/i][br]
+## - UNDERLINE: [u]text[/u][br]
+## [br]
+## [param message] The text to format.[br]
+## [param flags] The text style flags to apply.
+func _apply_flags(message: String, flags: int) -> String:
+ if flags & BOLD:
+ message = "[b]%s[/b]" % message
+ if flags & ITALIC:
+ message = "[i]%s[/i]" % message
+ if flags & UNDERLINE:
+ message = "[u]%s[/u]" % message
+ return message
+
+
+## Writes a message with formatting.[br]
+## [br]
+## [param message] The text to write.[br]
+## [param _color] The color to use.[br]
+## [param _indent] The indentation level.[br]
+## [param flags] The text style flags to apply.
+func _print_message(message: String, _color: Color, _indent: int, flags: int) -> void:
+ for i in _indent:
+ _output.push_indent(1)
+ _output.push_color(_color)
+ message = _apply_flags(message, flags)
+ _output.append_text(message)
+ _output.pop()
+ for i in _indent:
+ _output.pop()
+ _current_pos += _indent * 2 + message.length()
+
+
+## Writes a message with formatting followed by a line break.[br]
+## [br]
+## [param message] The text to write.[br]
+## [param _color] The color to use.[br]
+## [param _indent] The indentation level.[br]
+## [param flags] The text style flags to apply.
+func _println_message(message: String, _color: Color, _indent: int, flags: int) -> void:
+ _print_message(message, _color, _indent, flags)
+ _output.newline()
+ _current_pos = 0
+
+
+## Writes a message at a specific column position.[br]
+## [br]
+## [param message] The text to write.[br]
+## [param cursor_pos] The column position from line start.[br]
+## [param _color] The color to use.[br]
+## [param _effect] The text effect to apply (e.g. wave).[br]
+## [param _align] The text alignment (left or right).[br]
+## [param flags] The text style flags to apply.
+func _print_at(message: String, cursor_pos: int, _color: Color, _effect: GdUnitMessageWriter.Effect, _align: Align, flags: int) -> void:
+ if _align == Align.RIGHT:
+ cursor_pos = cursor_pos - message.length()
+
+ var spaces := cursor_pos - _current_pos
+ if spaces > 0:
+ _output.append_text("".lpad(spaces))
+ _current_pos += spaces
+ else:
+ _output.append_text(" ")
+ _current_pos += 1
+
+ _output.push_color(_color)
+ message = _apply_flags(message, flags)
+ match _effect:
+ Effect.NONE:
+ pass
+ Effect.WAVE:
+ message = "[wave]%s[/wave]" % message
+ _output.append_text(message)
+ _output.pop()
+ _current_pos += message.length()
+
+
+## Clears all written content from the [RichTextLabel].
+func clear() -> void:
+ _output.clear()
+ _current_pos = 0
diff --git a/addons/gdUnit4/src/core/writers/GdUnitRichTextMessageWriter.gd.uid b/addons/gdUnit4/src/core/writers/GdUnitRichTextMessageWriter.gd.uid
new file mode 100644
index 0000000..23fe6c0
--- /dev/null
+++ b/addons/gdUnit4/src/core/writers/GdUnitRichTextMessageWriter.gd.uid
@@ -0,0 +1 @@
+uid://cn0dawecdtlqx