extends GdUnitTestSuite ## The whole look comes from the project theme, themes/main_theme.tres: scenes and ## scripts name its styles instead of setting colours, sizes or style boxes themselves. const THEME_PATH: String = "res://themes/main_theme.tres" ## Folders with the game's scenes and scripts. const GAME_FOLDERS: Array[String] = [ "res://main", "res://quiz", "res://player", "res://popups", "res://common", "res://data", "res://server", "res://statistics", ] ## The named styles the game uses, and the type each one is based on. const STYLES: Dictionary = { "WindowBackground": "Panel", "PopupBackground": "Panel", "DarkSpacer": "Panel", "CharacterCard": "Panel", "MusicList": "PanelContainer", "SongRow": "PanelContainer", "ShortcutRowDark": "PanelContainer", "ListRowEven": "PanelContainer", "ListRowOdd": "PanelContainer", "TitleLarge": "Label", "TitleMedium": "Label", "Title": "Label", "TextLarge": "Label", "TextMedium": "Label", "HighlightLarge": "Label", "HighlightMedium": "Label", "SongRowLabel": "Label", "SongRowLabelPlaying": "Label", "StatisticsCard": "PanelContainer", "StatisticsCardHover": "PanelContainer", "StatisticsGrid": "GridContainer", "TabButton": "Button", } var _theme: Theme = load(THEME_PATH) ## Every file in [param folder] and its subfolders whose name ends with [param extension]. func _files(folder: String, extension: String) -> Array[String]: var found: Array[String] = [] for file_name: String in DirAccess.get_files_at(folder): if file_name.ends_with(extension): found.append(folder.path_join(file_name)) for subfolder: String in DirAccess.get_directories_at(folder): found.append_array(_files(folder.path_join(subfolder), extension)) return found func _game_files(extension: String) -> Array[String]: var found: Array[String] = [] for folder: String in GAME_FOLDERS: found.append_array(_files(folder, extension)) return found func _lines_matching(files: Array[String], pattern: String) -> Array[String]: var regex: RegEx = RegEx.create_from_string(pattern) var hits: Array[String] = [] for path: String in files: var lines: PackedStringArray = FileAccess.get_file_as_string(path).split("\n") for i: int in lines.size(): if regex.search(lines[i]) != null: hits.append("%s:%d: %s" % [path, i + 1, lines[i].strip_edges()]) return hits func test_project_uses_the_main_theme() -> void: assert_str(ProjectSettings.get_setting("gui/theme/custom")).is_equal(THEME_PATH) func test_theme_has_every_named_style() -> void: for style: String in STYLES: assert_str(String(_theme.get_type_variation_base(style))) \ .override_failure_message(style + " is missing or has the wrong base") \ .is_equal(STYLES[style]) func test_theme_has_the_palette() -> void: assert_bool(_theme.has_color("backdrop", "Palette")).is_true() func test_every_style_named_in_scenes_and_scripts_exists() -> void: var regex: RegEx = RegEx.create_from_string('&"([A-Z][A-Za-z]+)"') var missing: Array[String] = [] for path: String in _game_files(".tscn") + _game_files(".gd"): for found: RegExMatch in regex.search_all(FileAccess.get_file_as_string(path)): var style: String = found.get_string(1) if !(style in STYLES) && style != "Palette": missing.append("%s: %s" % [path, style]) assert_array(missing).override_failure_message( "Styles not in the theme:\n" + "\n".join(missing)).is_empty() func test_scenes_take_their_look_from_the_theme() -> void: var hits: Array[String] = _lines_matching(_game_files(".tscn"), "^(theme = |label_settings = |theme_override_(styles|colors|font_sizes|fonts)/)" \ + "|^\\[sub_resource type=\"(StyleBoxFlat|LabelSettings|Theme)\"") assert_array(hits).override_failure_message( "Look set in a scene instead of the theme:\n" + "\n".join(hits)).is_empty() func test_scripts_take_their_look_from_the_theme() -> void: var hits: Array[String] = _lines_matching(_game_files(".gd"), "add_theme_[a-z_]+_override|StyleBoxFlat\\.new|Color\\(") assert_array(hits).override_failure_message( "Look set in a script instead of the theme:\n" + "\n".join(hits)).is_empty() func test_main_window_background_comes_from_the_theme_and_lets_clicks_through() -> void: var main: Control = auto_free(load("res://main/MainWindow.tscn").instantiate()) var background: Panel = main.get_child(0) assert_str(background.name).is_equal("Background") assert_str(String(background.theme_type_variation)).is_equal("WindowBackground") assert_int(background.mouse_filter).is_equal(Control.MOUSE_FILTER_IGNORE) assert_float(background.anchor_right).is_equal(1.0) assert_float(background.anchor_bottom).is_equal(1.0) func test_default_font_shows_japanese() -> void: var font: Font = _theme.default_font for character: String in "幕開けミッション": var found: bool = font.fallbacks.any( func(fallback: Font) -> bool: return fallback.has_char(character.unicode_at(0))) assert_bool(found).override_failure_message("No font has " + character).is_true() func test_japanese_fallback_keeps_the_line_height_of_godots_font() -> void: var builtin: Font = ThemeDB.get_default_theme().default_font for size: int in [16, 18, 20, 25, 35, 50]: assert_float(_theme.default_font.get_height(size)) \ .override_failure_message("Line height at size %d" % size) \ .is_equal(builtin.get_height(size))