extends SceneTree ## Asset-free contract, dependency and timing regression for M2 batch planning. const PLANNER_SCRIPT := preload("res://src/render/m2/m2_build_batch_planner.gd") const PLANNER_PATH := "res://src/render/m2/m2_build_batch_planner.gd" const LOADER_PATH := "res://src/scenes/streaming/streaming_world_loader.gd" func _initialize() -> void: var failures: Array[String] = [] _verify_static_batch_limit(failures) _verify_animated_batch_limit(failures) _verify_non_positive_limit_clamp(failures) _verify_remaining_count_cap(failures) _verify_exact_group_completion(failures) _verify_empty_and_past_end(failures) _verify_historical_negative_offset(failures) _verify_source_boundaries(failures) var elapsed_milliseconds := _verify_bounded_timing(failures) if not failures.is_empty(): for failure in failures: push_error("M2_BUILD_BATCH_PLANNER: %s" % failure) quit(1) return print( "M2_BUILD_BATCH_PLANNER PASS cases=8 iterations=20000 elapsed_ms=%.3f" % elapsed_milliseconds ) quit(0) func _verify_static_batch_limit(failures: Array[String]) -> void: var plan := _plan(300, 0, false, 8, 128) _expect_plan(plan, 128, 128, 128, false, "static batch", failures) func _verify_animated_batch_limit(failures: Array[String]) -> void: var plan := _plan(300, 0, true, 8, 128) _expect_plan(plan, 8, 8, 8, false, "animated batch", failures) func _verify_non_positive_limit_clamp(failures: Array[String]) -> void: var animated_plan := _plan(4, 0, true, 0, 128) _expect_plan(animated_plan, 1, 1, 1, false, "zero animated limit", failures) var static_plan := _plan(4, 0, false, 8, -10) _expect_plan(static_plan, 1, 1, 1, false, "negative static limit", failures) func _verify_remaining_count_cap(failures: Array[String]) -> void: var plan := _plan(140, 128, false, 8, 128) _expect_plan(plan, 128, 12, 0, true, "remaining cap", failures) func _verify_exact_group_completion(failures: Array[String]) -> void: var plan := _plan(16, 8, true, 8, 128) _expect_plan(plan, 8, 8, 0, true, "exact completion", failures) func _verify_empty_and_past_end(failures: Array[String]) -> void: var empty_plan := _plan(0, 0, false, 8, 128) _expect_plan(empty_plan, 128, 0, 0, true, "empty group", failures) var past_end_plan := _plan(10, 11, false, 8, 128) _expect_plan(past_end_plan, 128, 0, 0, true, "past end", failures) func _verify_historical_negative_offset(failures: Array[String]) -> void: var plan := _plan(10, -2, false, 8, 4) _expect_plan(plan, 4, 4, 2, false, "negative offset", failures) func _verify_source_boundaries(failures: Array[String]) -> void: var loader_source := _read_text(LOADER_PATH, failures) var planner_source := _read_text(PLANNER_PATH, failures) var build_loop_source := _source_between( loader_source, "func _process_m2_build_jobs()", "func _materialize_m2_group_batch(", failures ) _expect_equal( build_loop_source.count("_m2_build_batch_planner.plan_batch("), 1, "one loader planner adapter", failures ) _expect_true( not build_loop_source.contains("var batch_size:"), "loader omits batch-size formula", failures ) _expect_true( not build_loop_source.contains("mini(batch_size"), "loader omits batch-count formula", failures ) for forbidden_dependency in [ "WorkerThreadPool", "RenderingServer", "ResourceLoader", "MultiMesh", "Node", "Mutex", ]: _expect_true( not planner_source.contains(forbidden_dependency), "planner omits %s" % forbidden_dependency, failures ) func _verify_bounded_timing(failures: Array[String]) -> float: var planner := PLANNER_SCRIPT.new() var started_microseconds := Time.get_ticks_usec() for iteration in range(20000): var plan: Dictionary = planner.plan_batch( 512, iteration % 512, iteration % 3 == 0, 8, 128 ) if int(plan.get("batch_count", -1)) < 0: failures.append("timing iteration returned negative batch count") break var elapsed_milliseconds := float(Time.get_ticks_usec() - started_microseconds) / 1000.0 _expect_true(elapsed_milliseconds < 1000.0, "20000 batch plans bounded", failures) return elapsed_milliseconds func _plan( transform_count: int, current_offset: int, has_animated_prototype: bool, animated_batch_limit: int, static_batch_limit: int ) -> Dictionary: var planner := PLANNER_SCRIPT.new() return planner.plan_batch( transform_count, current_offset, has_animated_prototype, animated_batch_limit, static_batch_limit ) func _expect_plan( plan: Dictionary, expected_batch_size: int, expected_batch_count: int, expected_next_offset: int, expected_group_complete: bool, label: String, failures: Array[String] ) -> void: _expect_equal( int(plan.get("effective_batch_size", -1)), expected_batch_size, "%s effective batch size" % label, failures ) _expect_equal( int(plan.get("batch_count", -1)), expected_batch_count, "%s batch count" % label, failures ) _expect_equal( int(plan.get("next_offset", -1)), expected_next_offset, "%s next offset" % label, failures ) _expect_true( bool(plan.get("group_complete", not expected_group_complete)) == expected_group_complete, "%s group complete" % label, failures ) func _read_text(path: String, failures: Array[String]) -> String: var file := FileAccess.open(path, FileAccess.READ) if file == null: failures.append("cannot open %s" % path) return "" return file.get_as_text() func _source_between( source: String, start_marker: String, end_marker: String, failures: Array[String] ) -> String: var start_offset := source.find(start_marker) var end_offset := source.find(end_marker, start_offset + start_marker.length()) if start_offset < 0 or end_offset <= start_offset: failures.append("cannot isolate source between %s and %s" % [start_marker, end_marker]) return "" return source.substr(start_offset, end_offset - start_offset) func _expect_equal( actual_value: int, expected_value: int, label: String, failures: Array[String] ) -> void: if actual_value != expected_value: failures.append("%s expected %d, got %d" % [label, expected_value, actual_value]) func _expect_true(actual_value: bool, label: String, failures: Array[String]) -> void: if not actual_value: failures.append("%s expected true" % label)