Files
OpenMaidEngine/tools/test_init_table_profile.py
2026-07-23 23:17:47 -04:00

532 lines
20 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""Tests for INIT field profiling. Run: py -3.11 -X utf8 tools/test_init_table_profile.py"""
from __future__ import annotations
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
import init_table_profile as profile
def main() -> int:
fixture = {
"table": "TEST",
"field_semantics": {
"0x10": "test_parallel",
"0x30/3/0": "test_record.zero",
"0x40/5/4": "test_names.level_5",
},
"records": [
{"id": 1, "name": "one", "fields": {"0x10": 2, "0x20": 0},
"string_fields": {"0x40/5/4": "One5"},
"record_fields": {"0x30/3/0": 9},
"message": {"title": "One", "description": "First",
"furigana": [{"line": 0, "text": "One", "reading": "one"}]}},
{"id": 3, "name": "three", "fields": {"0x10": 2}},
{"id": 7, "name": "seven", "fields": {"0x10": 5},
"record_fields": {"0x30/3/2": 4}},
]
}
rows = {row["key"]: row for row in profile.profile_columns(fixture)}
assert rows["0x10"]["population"] == 3
assert rows["0x10"]["coverage"] == 1.0
assert rows["0x10"]["distinct_values"] == 2
assert rows["0x10"]["min"] == 2 and rows["0x10"]["max"] == 5
assert rows["0x10"]["common"][0] == {"value": "2", "count": 2}
assert rows["0x10"]["semantic_name"] == "test_parallel"
assert rows["0x10"]["examples"][0]["message_description"] == "First"
assert rows["0x20"]["population"] == 1
assert rows["0x20"]["examples"][0]["name"] == "one"
assert rows["0x30/3/0"]["kind"] == "record-column"
assert rows["0x30/3/0"]["base"] == "0x30"
assert rows["0x30/3/0"]["stride"] == 3
assert rows["0x30/3/0"]["semantic_name"] == "test_record.zero"
assert rows["0x30/3/2"]["column"] == 2
assert rows["0x40/5/4"]["kind"] == "string-record-column"
assert rows["0x40/5/4"]["stride"] == 5
assert rows["0x40/5/4"]["column"] == 4
assert rows["0x40/5/4"]["semantic_name"] == "test_names.level_5"
mixed_fixture = {
"table": "MIXED",
"mode": "mixed",
"array_layouts": {"0x100": {"length": 9, "stride": 3, "rows": 3}},
"field_semantics": {"0x40": "condition", "0x50": "scalar"},
"records": [
{
"id": 11,
"string_fields": {"0x40": "Win"},
"fields": {"0x50": 20},
"array_fields": {"0x100/4": 7},
"footer_arrays": {
"0x100/6": {"footer_off": "0x200", "values": [1, 2, 3]},
},
},
],
}
mixed = {row["key"]: row for row in profile.profile_columns(mixed_fixture)}
assert mixed["0x40"]["kind"] == "string-field"
assert mixed["0x40"]["semantic_name"] == "condition"
assert mixed["0x50"]["kind"] == "scalar-field"
assert mixed["0x100/4"]["kind"] == "array-cell"
assert mixed["0x100/4"]["stride"] == 3 and mixed["0x100/4"]["column"] == 1
assert mixed["0x100/6"]["kind"] == "footer-array"
assert mixed["0x100/6"]["examples"][0]["footer_off"] == "0x200"
assert mixed["0x100/6"]["common"][0]["value"] == "[1, 2, 3]"
assert mixed["0x40"]["examples"][0]["name"] == "Win"
rule_fixture = {
"table": "RULES",
"mode": "rules",
"records": [
{
"id": 1, "unit_id": 3, "title": "", "fields": {"0x10": -1},
"class_change_slot_index": 0, "skill_awards": [{"skill_id": 2}],
},
{
"id": 2, "unit_id": 5, "title": "Promoted",
"minimum_level": 50, "class_change_slot_index": 1,
"fields": {"0x10": 50},
},
],
}
rule_rows = {row["key"]: row for row in profile.profile_columns(rule_fixture)}
assert rule_rows["0x10"]["kind"] == "rule-output"
rule_summary = profile.profile_rules(rule_fixture)
assert rule_summary["unit_count"] == 2
assert rule_summary["titled_rule_count"] == 1
assert rule_summary["level_independent_rule_count"] == 1
assert rule_summary["minimum_levels"] == {"50": 1}
assert rule_summary["class_change_slot_indices"] == {"0": 1, "1": 1}
assert rule_summary["skill_award_count"] == 1
dispatch_fixture = {
"table": "DISPATCH",
"mode": "dispatch",
"assignment_count": 4,
"overwritten_record_count": 1,
"conflicting_chapter_record_count": 1,
"resolved_script_count": 2,
"scjump_joined_record_count": 2,
"scjump_chapter_match_count": 1,
"scjump_chapter_mismatches": [{"decision_id": 2}],
"records": [
{"id": 1, "name": "SC0000.BIN", "fields": {"0x100": 34, "0x200": 1}},
{"id": 2, "name": "SC0010.BIN", "fields": {"0x100": 286, "0x200": 2}},
],
}
dispatch_rows = {
row["key"]: row for row in profile.profile_columns(dispatch_fixture)
}
assert dispatch_rows["0x100"]["kind"] == "dispatch-field"
dispatch_summary = profile.profile_dispatch(dispatch_fixture)
assert dispatch_summary["assignment_count"] == 4
assert dispatch_summary["overwritten_record_count"] == 1
assert dispatch_summary["scjump_chapter_match_count"] == 1
assert dispatch_summary["scjump_chapter_mismatch_count"] == 1
banked_fixture = {
"table": "BANKED",
"mode": "banked",
"assignment_count": 6,
"populated_cell_count": 5,
"overwritten_cell_count": 1,
"conflicting_overwrite_count": 1,
"movement_step_count": 2,
"battle_step_count": 1,
"decoded_movement_provider_count": 1,
"decoded_movement_step_count": 1,
"decoded_movement_parameter_count": 2,
"decoded_movement_defaulted_parameter_count": 1,
"ignored_movement_parameter_count": 1,
"movement_provider_scripts": {"1": "RTN_M001.BIN"},
"battle_provider_scripts": {"1": "RTN_B001.BIN"},
"used_movement_provider_selectors": [1],
"used_battle_provider_selectors": [1],
"bank_layouts": {
"0x100": {"reserved_empty": False},
"0x200": {"reserved_empty": True},
},
"records": [
{
"id": 1,
"record_fields": {
"0x100/20/0": 1,
"0x100/20/1": 2,
},
},
],
}
banked_rows = {
row["key"]: row for row in profile.profile_columns(banked_fixture)
}
assert banked_rows["0x100/20/0"]["kind"] == "record-column"
banked_summary = profile.profile_banked(banked_fixture)
assert banked_summary["assignment_count"] == 6
assert banked_summary["populated_bank_count"] == 1
assert banked_summary["reserved_bank_count"] == 1
assert banked_summary["movement_step_count"] == 2
assert banked_summary["battle_provider_count"] == 1
assert banked_summary["decoded_movement_provider_count"] == 1
assert banked_summary["decoded_movement_step_count"] == 1
assert banked_summary["decoded_movement_parameter_count"] == 2
assert banked_summary["decoded_movement_defaulted_parameter_count"] == 1
assert banked_summary["ignored_movement_parameter_count"] == 1
map_fixture = {
"table": "MAP",
"mode": "footer",
"schema": "stage-terrain-atlas",
"record_count": 1472,
"row_stride": 53,
"authored_column_count": 50,
"tile_to_grid_scale": 2,
"authored_row_count": 1472,
"implicit_zero_row_count": 127,
"authored_grid_y_min": 2,
"authored_grid_y_max": 1600,
"nonzero_cell_count": 17126,
"stage_rectangle_nonzero_cell_count": 17079,
"outside_stage_rectangle_nonzero_cell_count": 47,
"terrain_ids_used": [0, 1, 2, 3],
"stage_map_count": 66,
"unique_atlas_rectangle_count": 53,
"shared_atlas_rectangles": [
{"stage_ids": [32, 33, 34]},
{"stage_ids": [35, 36]},
],
"records": [{}, {}, {}],
}
map_summary = profile.profile_map_atlas(map_fixture)
assert map_summary["row_stride"] == 53
assert map_summary["authored_row_count"] == 1472
assert map_summary["outside_stage_rectangle_nonzero_cell_count"] == 47
assert map_summary["stage_map_count"] == 66
assert map_summary["shared_rectangle_count"] == 2
assert map_summary["shared_stage_definition_count"] == 5
rendered_map = profile.render_markdown(map_fixture, [], 40)
assert "- geometry: 50 authored cells inside a 53-cell row pitch" in rendered_map
assert "- stage joins: 66 definitions over 53 unique rectangles" in rendered_map
terrain_fixture = {
"table": "LAND",
"mode": "name",
"schema": "terrain-definitions",
"record_count": 20,
"reserved_record_span": 30,
"authored_terrain_ids": list(range(17)),
"implicit_default_terrain_ids": [0, 5, 6],
"combat_stat_cell_count": 13,
"required_skill_count": 5,
"map_texture_slot_count": 20,
"default_texture_asset_count": 10,
"default_texture_asset_join_count": 10,
"records": [
{
"name": "通路" if index else None,
"effect_description": "▲命中" if index < 5 else None,
"required_skill_name": (
("飛行", "潜水", "探索", "耐熱")[index]
if index < 4 else None
),
}
for index in range(20)
],
}
terrain_summary = profile.profile_terrain_definitions(terrain_fixture)
assert terrain_summary["reserved_record_span"] == 30
assert terrain_summary["shipped_record_count"] == 20
assert terrain_summary["authored_record_count"] == 17
assert terrain_summary["effect_description_count"] == 5
assert terrain_summary["combat_stat_cell_count"] == 13
assert terrain_summary["required_skill_names"] == ["探索", "潜水", "耐熱", "飛行"]
rendered_terrain = profile.render_markdown(terrain_fixture, [], 40)
assert "- shipped terrain ids: 20 inside a 30-row table" in rendered_terrain
assert "- combat-stat cells: 13" in rendered_terrain
h_gallery_fixture = {
"table": "SP",
"mode": "numeric",
"schema": "h-scene-gallery-pages",
"record_count": 8,
"page_count": 8,
"slots_per_page": 15,
"registry_capacity": 120,
"populated_scene_count": 118,
"empty_cells": [{"page": 7, "slot": 13}, {"page": 7, "slot": 14}],
"resolved_scene_script_count": 118,
"resolved_thumbnail_sheet_count": 8,
"records": [],
}
h_gallery_summary = profile.profile_h_scene_gallery(h_gallery_fixture)
assert h_gallery_summary["page_count"] == 8
assert h_gallery_summary["populated_scene_count"] == 118
assert h_gallery_summary["resolved_thumbnail_sheet_count"] == 8
rendered_h_gallery = profile.render_markdown(h_gallery_fixture, [], 40)
assert "- geometry: 8 pages × 15 slots" in rendered_h_gallery
assert "- populated scenes: 118/120" in rendered_h_gallery
card_fixture = {
"table": "CARDS",
"mode": "numeric",
"schema": "card-generation-lists",
"record_count": 2,
"entry_count": 3,
"distinct_card_ids": [1, 14],
"resolved_card_reference_count": 3,
"used_selector_ids": [1],
"unreferenced_selector_ids": [55],
"stage_definition_reference_count": 1,
"stage_object_reference_count": 2,
"runtime_scan_capacity": 100,
"cleared_entry_prefix": 50,
"records": [
{
"id": 1,
"entry_count": 2,
"entries": [
{
"card_id": 1,
"required_story_flag_ids": [],
"forbidden_story_flag_ids": [],
},
{
"card_id": 14,
"required_story_flag_ids": [901],
"forbidden_story_flag_ids": [861],
},
],
},
{
"id": 55,
"entry_count": 1,
"entries": [
{
"card_id": 1,
"required_story_flag_ids": [],
"forbidden_story_flag_ids": [],
},
],
},
],
}
card_summary = profile.profile_card_generation_lists(card_fixture)
assert card_summary["list_count"] == 2
assert card_summary["entry_count"] == 3
assert card_summary["story_flag_gated_entry_count"] == 1
assert card_summary["list_entry_counts"] == {"1": 2, "55": 1}
rendered_cards = profile.render_markdown(card_fixture, [], 40)
assert "- card-generation lists: 2" in rendered_cards
assert "- runtime scan/clear prefix: 100/50 slots" in rendered_cards
definition_fixture = {
"table": "CARDDEFS",
"mode": "name",
"schema": "card-definitions",
"record_count": 3,
"reserved_record_count": 100,
"type_counts": {
"item_award": 1,
"resource_recovery": 1,
"trap": 1,
},
"awarded_item_join_count": 1,
"event_dispatch_join_count": 0,
"condition_join_count": 1,
"visual_asset_join_count": 3,
"ignored_required_story_flag_count": 1,
"records": [{}, {}, {}],
}
definition_summary = profile.profile_card_definitions(
definition_fixture
)
assert definition_summary["card_count"] == 3
assert definition_summary["reserved_record_count"] == 100
assert definition_summary["visual_asset_join_count"] == 3
rendered_definitions = profile.render_markdown(
definition_fixture, [], 40
)
assert "- card definitions: 3/100 rows" in rendered_definitions
assert "- engine-dead third required flags: 1" in rendered_definitions
effect_fixture = {
"table": "EFFECTS",
"mode": "numeric",
"schema": "battle-effect-definitions",
"record_count": 4,
"effect_definition_count": 4,
"runtime_work_slot_count": 6,
"visual_mode_counts": {"movie": 3, "opaque_sprite_sheet": 1},
"resolved_visual_asset_count": 4,
"sound_effect_count": 3,
"resolved_sound_asset_count": 3,
"sprite_sheet_effect_count": 1,
"hit_pulse_effect_count": 2,
"referenced_effect_definition_count": 3,
"unreferenced_effect_definition_ids": [999],
"engine_dead_atlas_row_count": 1,
"records": [{}, {}, {}, {}],
}
effect_summary = profile.profile_battle_effect_definitions(
effect_fixture
)
assert effect_summary["effect_definition_count"] == 4
assert effect_summary["visual_mode_counts"]["movie"] == 3
rendered_effects = profile.render_markdown(
effect_fixture, [], 40
)
assert "- effect definitions: 4 for 6 runtime slots" in rendered_effects
assert "- sound assets resolved: 3/3" in rendered_effects
animation_fixture = {
"table": "ANIMATIONS",
"mode": "numeric",
"schema": "battle-animation-timelines",
"record_count": 3,
"authored_record_count": 3,
"reserved_record_count": 1000,
"effect_reference_count": 12,
"distinct_effect_id_count": 8,
"resolved_effect_reference_count": 12,
"effect_slot_populations": {
"0": 2, "1": 2, "2": 2, "3": 2, "4": 0, "5": 4,
},
"delay_slot_populations": {
"0": 0, "1": 0, "2": 2, "3": 2, "4": 0, "5": 2,
},
"complete_timeline_count": 2,
"auxiliary_timeline_count": 1,
"skill_reference_count": 2,
"skill_animation_count": 2,
"weapon_class_animation_count": 21,
"unjoined_authored_animation_ids": [205],
"records": [{}, {}, {}],
}
animation_summary = profile.profile_battle_animations(
animation_fixture
)
assert animation_summary["authored_record_count"] == 3
assert animation_summary["auxiliary_timeline_count"] == 1
rendered_animations = profile.render_markdown(
animation_fixture, [], 40
)
assert "- battle animations: 3/1000 rows" in rendered_animations
assert "- full/auxiliary timelines: 2/1" in rendered_animations
training_fixture = {
"table": "TRAINING",
"mode": "name",
"schema": "training-action-definitions",
"record_count": 3,
"string_write_count": 7,
"authored_numeric_cell_counts": {
"required_story_flag_ids": 2,
"unit_stat_deltas": 4,
},
"event_cell_count": 6,
"distinct_event_story_flag_ids": [800, 801, 830],
"resolved_event_dispatch_count": 6,
"records": [
{
"description_lines": ["one", "cost"],
"locked_hint_lines": [],
"eligibility": {},
"effects": {"awarded_skill_id": 1},
"execution_limit": 3,
},
{
"description_lines": ["two", "cost"],
"locked_hint_lines": ["locked"],
"eligibility": {
"required_item_id": 35,
"minimum_alignment": 10,
"minimum_training_progress": 5,
},
"effects": {"awarded_item_id": 51},
"execution_limit": 2,
},
{
"description_lines": ["three"],
"locked_hint_lines": ["locked", "more"],
"eligibility": {"maximum_alignment": -20},
"effects": {},
"execution_limit": 1,
},
],
}
training_summary = profile.profile_training_actions(training_fixture)
assert training_summary["action_count"] == 3
assert training_summary["description_line_count"] == 5
assert training_summary["locked_hint_line_count"] == 3
assert training_summary["required_item_count"] == 1
assert training_summary["minimum_alignment_gate_count"] == 1
assert training_summary["maximum_alignment_gate_count"] == 1
assert training_summary["stat_delta_cell_count"] == 4
assert training_summary["execution_limits"] == {"1": 1, "2": 1, "3": 1}
rendered_training = profile.render_markdown(training_fixture, [], 40)
assert "- training actions: 3" in rendered_training
assert "- event slots: 6 across 3 distinct story flags" in rendered_training
messages = profile.profile_messages(fixture)
assert messages["population"] == 1
assert messages["coverage"] == 1 / 3
assert messages["furigana_records"] == 1
assert messages["examples"][0]["description"] == "First"
matches = profile.find_message_matches(fixture, "first|three")
assert [record["id"] for record in matches] == [1, 3]
rendered = profile.render_message_matches(fixture, "First")
assert "| 1 | one | First |" in rendered
assert "`test_record.zero` (`0x30/3/0`)=9" in rendered
enemy_fixture = {
"table": "ENEMY",
"records": [
{
"id": 101,
"name": "boss",
"message": {
"summary": "Powerful knight",
"strategy": "Avoid the first encounter",
},
}
],
}
enemy_messages = profile.profile_messages(enemy_fixture)
assert enemy_messages["examples"][0]["title"] == "Powerful knight"
assert enemy_messages["examples"][0]["description"] == "Avoid the first encounter"
assert enemy_messages["examples"][0]["message_fields"] == {
"summary": "Powerful knight",
"strategy": "Avoid the first encounter",
}
assert profile.find_message_matches(enemy_fixture, "first encounter")
character_fixture = {
"table": "CHARACTER",
"records": [
{
"id": 1,
"name": "Emilio",
"message": {
"biography": "Former demon king\nNow a familiar",
},
}
],
}
character_messages = profile.profile_messages(character_fixture)
assert character_messages["examples"][0]["title"] == ""
assert (
character_messages["examples"][0]["description"]
== "Former demon king\nNow a familiar"
)
assert character_messages["examples"][0]["message_fields"] == {
"biography": "Former demon king\nNow a familiar",
}
assert profile.find_message_matches(character_fixture, "familiar")
print("all init_table_profile checks passed")
return 0
if __name__ == "__main__":
raise SystemExit(main())