264 lines
13 KiB
GDScript
264 lines
13 KiB
GDScript
extends SceneTree
|
|
|
|
const EntryShell = preload("res://entry/entry_shell.gd")
|
|
const SettingsStore = preload("res://entry/settings_store.gd")
|
|
const DraftService = preload("res://entry/initial_company_draft_service.gd")
|
|
const ContractService = preload("res://entry/guild_contract_service.gd")
|
|
const FormationService = preload("res://entry/formation_risk_service.gd")
|
|
const BattleService = preload("res://entry/battle_service.gd")
|
|
|
|
const DRAFT_PATH := "user://dev5_result_draft.json"
|
|
const COMPANY_PATH := "user://dev5_result_company.json"
|
|
const SEED := "00112233445566778899aabbccddeeff"
|
|
const CONTRACT_OP := "abcdefabcdefabcdefabcdefabcdefab"
|
|
const HIRE_OP_1 := "11112222333344445555666677778888"
|
|
const HIRE_OP_2 := "88887777666655554444333322221111"
|
|
const ORDER_OP := "fedcbafedcbafedcbafedcbafedcbafe"
|
|
const READY_OP := "1234567890abcdef1234567890abcdef"
|
|
const BATTLE_OP := "0123456789abcdef0123456789abcdef"
|
|
|
|
var failures: Array[String] = []
|
|
var parsed_touches := 0
|
|
|
|
|
|
func _initialize() -> void:
|
|
call_deferred("run_test")
|
|
|
|
|
|
func run_test() -> void:
|
|
_cleanup()
|
|
root.content_scale_mode = Window.CONTENT_SCALE_MODE_CANVAS_ITEMS
|
|
root.content_scale_aspect = Window.CONTENT_SCALE_ASPECT_EXPAND
|
|
root.size = Vector2i(844, 390)
|
|
for locale in ["zh_CN", "en"]:
|
|
_cleanup()
|
|
var prepared := _create_prepared_company()
|
|
if not prepared.is_empty():
|
|
var result_company := _settle_result()
|
|
if not result_company.is_empty():
|
|
if locale == "zh_CN":
|
|
verify_result_domain(result_company)
|
|
await verify_result_ui(result_company, locale)
|
|
_cleanup()
|
|
if failures.is_empty():
|
|
print("ABG_DEV5_RESULT_OK result=true objective=true decisive_event=damage diagnosis_chain=true reward=27 coin=24,51 xp_total=48 xp_per_survivor=16 knockout_identity=true battle_local=true duplicate_settlement=true locales=zh_CN,en text_130=true")
|
|
print("ABG_DEV5_RECOVERY_OK result=true objective=true decisive_event=damage diagnosis_chain=true reward=27 coin=24,51 xp_total=48 xp_per_survivor=16 knockout_identity=true battle_local=true continue=knockout_review,recovery,market restart=market duplicate_settlement=true recovery_receipt=one same_identity=true duplicate_recovery=true locales=zh_CN,en text_130=true parsed_touch=%d" % parsed_touches)
|
|
quit(0)
|
|
else:
|
|
for failure in failures:
|
|
push_error(failure)
|
|
quit(1)
|
|
|
|
|
|
func verify_result_domain(company: Dictionary) -> void:
|
|
var battle: Dictionary = company.active_contract.battle_state
|
|
var result: Dictionary = battle.result
|
|
expect(battle.phase_events.size() == 9, "result keeps exactly nine authored timeline events")
|
|
expect(String(result.outcome) == "victory" and int(result.reward_coin) == 27, "result reports victory and the pressured reward")
|
|
expect(int(result.coin_before) == 24 and int(result.coin_after) == 51, "result carries the exact Coin before and after")
|
|
expect(int(result.xp_award) == 48 and int(result.xp_per_recruit) == 16, "result carries total and per-survivor XP")
|
|
var decisive := _event_by_hash(battle, String(result.decisive_event_hash))
|
|
expect(String(decisive.get("kind", "")) == "damage", "decisive event points to the first decisive damage")
|
|
var knocked: Array = result.knocked_out_ids
|
|
expect(knocked.size() == 1, "result carries one battle-local knockout")
|
|
if knocked.size() == 1:
|
|
var knocked_recruit := _recruit_by_id(company, String(knocked[0]))
|
|
expect(int(knocked_recruit.get("xp", -1)) == 0 and int(knocked_recruit.get("level", -1)) == 1, "knocked-out Recruit receives no XP")
|
|
for recruit in company.owned_recruits:
|
|
if String(recruit.recruit_id) not in knocked:
|
|
expect(int(recruit.xp) == 16 and int(recruit.level) == 1, "surviving Recruit receives 16 XP without an invented level-up")
|
|
var retry: Dictionary = _battle_service().advance_battle(BATTLE_OP)
|
|
expect(retry.ok and retry.code == "battle_already_settled" and int(retry.company.active_contract.coin) == 51 and retry.company.active_contract.battle_settlement_receipts.size() == 1, "duplicate result settlement keeps one reward receipt")
|
|
|
|
|
|
func verify_result_ui(company: Dictionary, locale: String) -> void:
|
|
var settings := SettingsStore.new().defaults()
|
|
settings.setup_completed = true
|
|
settings.locale = locale
|
|
settings.text_scale = 1.3
|
|
var shell := EntryShell.new()
|
|
shell.auto_start = false
|
|
shell.initial_company_service.path = DRAFT_PATH
|
|
shell.initial_company_service.company_path = COMPANY_PATH
|
|
root.add_child(shell)
|
|
await _frames(3)
|
|
shell.settings = settings
|
|
shell.setup_draft = settings.duplicate(true)
|
|
shell.show_battle_result(company)
|
|
await _frames(3)
|
|
expect(shell.current_screen == "battle_result", "%s result screen opens from settled receipt" % locale)
|
|
expect(shell.find_child("BattleResultObjective", true, false) != null and shell.find_child("BattleResultDecisive", true, false) != null, "%s result names objective and first decisive event" % locale)
|
|
expect(shell.find_child("BattleResultReward", true, false) != null and shell.find_child("BattleResultXP", true, false) != null, "%s result exposes Coin and XP totals" % locale)
|
|
expect(shell.find_child("BattleResultKnockouts", true, false) != null and shell.find_child("BattleResultChain", true, false) != null, "%s result names knockout and causal chain" % locale)
|
|
var diagnosis := shell.find_child("BattleResultDiagnosis", true, false) as Button
|
|
var continue_button := shell.find_child("BattleResultContinue", true, false) as Button
|
|
expect(diagnosis != null and continue_button != null and not continue_button.disabled, "%s result exposes usable diagnosis and Continue" % locale)
|
|
await _touch(diagnosis)
|
|
expect(shell.find_child("BattleResultDiagnosisDetail", true, false) != null, "%s diagnosis expands a reviewable causal detail" % locale)
|
|
var continue_target := shell.find_child("BattleResultContinue", true, false) as Button
|
|
await _touch(continue_target)
|
|
expect(shell.current_screen == "battle_knockout_review" and shell.find_child("BattleKnockoutCurrentGap", true, false) != null, "%s Continue reaches knockout review with the current battle gap" % locale)
|
|
var acknowledge := shell.find_child("BattleKnockoutAcknowledge", true, false) as Button
|
|
expect(acknowledge != null and not acknowledge.disabled, "%s knockout review exposes an acknowledge action" % locale)
|
|
await _touch(acknowledge)
|
|
expect(shell.current_screen == "battle_recovery" and shell.find_child("BattleRecoveryRule", true, false) != null, "%s acknowledgement reaches recovery commitment" % locale)
|
|
var commit := shell.find_child("BattleRecoveryCommit", true, false) as Button
|
|
expect(commit != null and not commit.disabled, "%s recovery exposes a real commit action" % locale)
|
|
await _touch(commit)
|
|
expect(shell.current_screen == "market" and shell.find_child("MarketLayout", true, false) != null, "%s recovery returns to next-stage Market" % locale)
|
|
var recovered: Dictionary = _draft_service().load_company()
|
|
expect(recovered.ok and recovered.company.active_contract.status == "market" and recovered.company.active_contract.recovery_receipts.size() == 1 and not recovered.company.active_contract.has("formation"), "%s recovery atomically clears stale formation and writes one receipt" % locale)
|
|
if recovered.ok:
|
|
expect(_recruit_ids(recovered.company) == _recruit_ids(company), "%s recovery keeps the same Recruit IDs" % locale)
|
|
for recruit_id in _recruit_ids(company):
|
|
var before_recruit := _recruit_by_id(company, recruit_id)
|
|
var after_recruit := _recruit_by_id(recovered.company, recruit_id)
|
|
expect(String(before_recruit.profession_id) == String(after_recruit.profession_id) and String(before_recruit.trait_id) == String(after_recruit.trait_id) and String(before_recruit.equipped_item_id) == String(after_recruit.equipped_item_id), "%s recovery preserves Profession, Trait and equipment for %s" % [locale, recruit_id])
|
|
var recovery_battle: Dictionary = recovered.company.active_contract.battle_state
|
|
var recovery_operation := ("%s|recovery|%s" % [String(recovery_battle.operation_id), String(recovered.company.company_id)]).sha256_text().substr(0, 32)
|
|
var duplicate_recovery: Dictionary = _battle_service().recover_next_stage(recovery_operation)
|
|
expect(duplicate_recovery.ok and duplicate_recovery.code == "recovery_already_applied" and duplicate_recovery.company.active_contract.recovery_receipts.size() == 1, "%s duplicate recovery returns the original receipt" % locale)
|
|
shell.queue_free()
|
|
await _timer_seconds(0.2)
|
|
var resumed := EntryShell.new()
|
|
resumed.auto_start = false
|
|
resumed.initial_company_service.path = DRAFT_PATH
|
|
resumed.initial_company_service.company_path = COMPANY_PATH
|
|
root.add_child(resumed)
|
|
await _frames(2)
|
|
resumed.settings = settings
|
|
resumed.setup_draft = settings.duplicate(true)
|
|
resumed._open_company_destination(recovered.company if recovered.ok else company)
|
|
await _frames(3)
|
|
expect(resumed.current_screen == "market" and resumed.find_child("MarketLayout", true, false) != null, "%s restart returns to the next-stage Market" % locale)
|
|
resumed.queue_free()
|
|
await _timer_seconds(0.2)
|
|
|
|
|
|
func _settle_result() -> Dictionary:
|
|
var company: Dictionary = _draft_service().load_company()
|
|
if not company.ok:
|
|
return {}
|
|
var option_id := String(company.company.active_contract.formation.risk_options[1].risk_option_id)
|
|
var started: Dictionary = _battle_service().begin_battle(BATTLE_OP, option_id)
|
|
if not started.ok:
|
|
expect(false, "result fixture starts battle")
|
|
return {}
|
|
var latest: Dictionary = started
|
|
for _phase in range(9):
|
|
latest = _battle_service().advance_battle(BATTLE_OP)
|
|
if not latest.ok:
|
|
expect(false, "result fixture settles every phase")
|
|
return {}
|
|
return latest.company
|
|
|
|
|
|
func _create_prepared_company() -> Dictionary:
|
|
var draft: Dictionary = _draft_service().begin_or_load(SEED)
|
|
if not draft.ok:
|
|
return {}
|
|
_draft_service().set_selection([String(draft.draft.offers[0].offer_id), String(draft.draft.offers[1].offer_id)])
|
|
var committed: Dictionary = _draft_service().commit_company()
|
|
if not committed.ok:
|
|
return {}
|
|
var created: Dictionary = _contract_service().create_contract(CONTRACT_OP, "CTR-R1-01", "standard")
|
|
if not created.ok:
|
|
return {}
|
|
var offers: Array = created.company.active_contract.market_board.recruit_offers
|
|
_contract_service().hire_recruit(HIRE_OP_1, String(offers[0].source_offer_id), 14)
|
|
var hired: Dictionary = _contract_service().hire_recruit(HIRE_OP_2, String(offers[1].source_offer_id), 14)
|
|
if not hired.ok:
|
|
return {}
|
|
var begun: Dictionary = _formation_service().begin_formation()
|
|
var saved: Dictionary = _formation_service().save_order(ORDER_OP, begun.formation.base_front_to_rear_ids)
|
|
var ready: Dictionary = _formation_service().accept_readiness(READY_OP, String(saved.receipt.order_hash))
|
|
if not ready.ok:
|
|
return {}
|
|
return ready.company
|
|
|
|
|
|
func _draft_service() -> RefCounted:
|
|
var service := DraftService.new()
|
|
service.path = DRAFT_PATH
|
|
service.company_path = COMPANY_PATH
|
|
return service
|
|
|
|
|
|
func _contract_service() -> RefCounted:
|
|
var service := ContractService.new()
|
|
service.company_path = COMPANY_PATH
|
|
return service
|
|
|
|
|
|
func _formation_service() -> RefCounted:
|
|
var service := FormationService.new()
|
|
service.company_path = COMPANY_PATH
|
|
return service
|
|
|
|
|
|
func _battle_service() -> RefCounted:
|
|
var service := BattleService.new()
|
|
service.company_path = COMPANY_PATH
|
|
return service
|
|
|
|
|
|
func _event_by_hash(battle: Dictionary, event_hash: String) -> Dictionary:
|
|
for event in battle.get("phase_events", []):
|
|
if String(event.get("event_hash", "")) == event_hash:
|
|
return Dictionary(event)
|
|
return {}
|
|
|
|
|
|
func _recruit_by_id(company: Dictionary, recruit_id: String) -> Dictionary:
|
|
for recruit in company.get("owned_recruits", []):
|
|
if String(recruit.recruit_id) == recruit_id:
|
|
return Dictionary(recruit)
|
|
return {}
|
|
|
|
|
|
func _recruit_ids(company: Dictionary) -> Array[String]:
|
|
var ids: Array[String] = []
|
|
for recruit in company.get("owned_recruits", []):
|
|
ids.append(String(recruit.recruit_id))
|
|
return ids
|
|
|
|
|
|
func _touch(control: Control) -> void:
|
|
if control == null:
|
|
expect(false, "touch target exists")
|
|
return
|
|
var center := control.get_global_rect().get_center()
|
|
var press := InputEventScreenTouch.new()
|
|
press.position = center
|
|
press.pressed = true
|
|
press.index = 0
|
|
Input.parse_input_event(press)
|
|
await _frames(2)
|
|
var release := InputEventScreenTouch.new()
|
|
release.position = center
|
|
release.pressed = false
|
|
release.index = 0
|
|
Input.parse_input_event(release)
|
|
parsed_touches += 1
|
|
await _frames(5)
|
|
|
|
|
|
func _frames(count: int) -> void:
|
|
for _index in range(count):
|
|
await process_frame
|
|
|
|
|
|
func _timer_seconds(seconds: float) -> void:
|
|
await create_timer(seconds).timeout
|
|
|
|
|
|
func _cleanup() -> void:
|
|
for path in [DRAFT_PATH, "%s.bak" % DRAFT_PATH, "%s.candidate" % DRAFT_PATH, "%s.bak.candidate" % DRAFT_PATH, COMPANY_PATH, "%s.bak" % COMPANY_PATH, "%s.candidate" % COMPANY_PATH, "%s.bak.candidate" % COMPANY_PATH, "%s.corrupt" % COMPANY_PATH]:
|
|
if FileAccess.file_exists(path):
|
|
DirAccess.remove_absolute(path)
|
|
|
|
|
|
func expect(condition: bool, message: String) -> void:
|
|
if not condition:
|
|
failures.append("DEV-5 result: %s" % message)
|