-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathtest_unitypackage_import.gd
More file actions
130 lines (106 loc) · 4.92 KB
/
Copy pathtest_unitypackage_import.gd
File metadata and controls
130 lines (106 loc) · 4.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
## End-to-end test of the .unitypackage importer: extracts the real vendor packages
## into user://test_unitypackage_import and checks the produced layout matches what
## the addon's scanners expect. Run headless:
## godot --headless -s tests/test_unitypackage_import.gd
extends SceneTree
const SKUnitypackageImporter := preload("res://addons/kickdot/editor/sk_unitypackage_importer.gd")
const PACKAGES: PackedStringArray = [
"res://vendor/SIDEKICK_Starter_Unity_2021_3_v1_0_4.unitypackage",
"res://vendor/Sidekicks.unitypackage",
"res://vendor/SidekicksDatabase.unitypackage",
]
const DEST := "user://test_unitypackage_import"
const TIMEOUT_MSEC := 300 * 1000
var _importer: SKUnitypackageImporter
var _started_msec := 0
var _failures := 0
func _init() -> void:
for package in PACKAGES:
if not FileAccess.file_exists(package):
print("SKIP: %s not present (vendor packages are not committed)" % package)
quit(0)
return
_remove_recursive(DEST)
_importer = SKUnitypackageImporter.new()
_importer.finished.connect(_on_finished)
_started_msec = Time.get_ticks_msec()
process_frame.connect(_on_frame)
if not _importer.start(PACKAGES, DEST):
print("FAIL: importer did not start")
quit(1)
func _on_frame() -> void:
if Time.get_ticks_msec() - _started_msec > TIMEOUT_MSEC:
print("FAIL: import timed out")
_importer.cancel()
_importer.wait()
quit(1)
func _on_finished(summary: Dictionary) -> void:
_importer.wait()
print("summary: %s" % str(summary))
_check((summary.errors as Array).is_empty(), "no errors reported: %s" % str(summary.errors))
_check(not summary.cancelled, "not cancelled")
_check(summary.db, "database imported")
_check(summary.base_model, "base model imported")
# Starter pack: 99 human species + 58 starter outfit part meshes; 8 .sk characters.
_check(_count_files(DEST + "/meshes/Species/Humans", "fbx") == 99,
"99 species meshes (got %d)" % _count_files(DEST + "/meshes/Species/Humans", "fbx"))
_check(_count_files(DEST + "/meshes/Outfits/Starter", "fbx") == 58,
"58 outfit meshes (got %d)" % _count_files(DEST + "/meshes/Outfits/Starter", "fbx"))
_check(_count_files(DEST + "/characters", "sk") == 8,
"8 .sk characters (got %d)" % _count_files(DEST + "/characters", "sk"))
# Sidekicks pack: base model, palette textures, UI art, user guide.
_check(FileAccess.file_exists(DEST + "/meshes/SK_BaseModel.fbx"), "SK_BaseModel.fbx present")
_check(FileAccess.file_exists(DEST + "/textures/T_ColorMap.png"), "T_ColorMap.png present")
_check(FileAccess.file_exists(DEST + "/textures/Masks/Mask_Cuts.png"), "mask textures present")
_check(FileAccess.file_exists(DEST + "/ui/T_SidekickTitle.png"), "UI title texture present")
_check(FileAccess.file_exists(DEST + "/docs/SidekickCharacters_UserGuide.pdf"), "user guide present")
# Database pack: exact filename the SKDatabase default expects.
_check(FileAccess.file_exists(DEST + "/db/side_kick_data.db"), "side_kick_data.db present")
# Editor thumbnails ride along for meshes, but characters/ stays .sk-only.
_check(FileAccess.file_exists(
DEST + "/meshes/Species/Humans/SK_HUMN_BASE_01_01HEAD_HU01.fbx.preview.png"),
"mesh previews written")
_check(_count_files(DEST + "/characters", "png") == 0, "no previews in characters/")
# Unity-only content must not leak through. .anim is the exception: the curated
# "A_" face pose/cycle clips are deliberately kept (SKFacePoses parses them into
# the baked face animation library) - but only under animations/.
for extension in ["cs", "prefab", "mat", "meta", "dll", "asset", "mask", "shader"]:
var leaked := _count_files_recursive(DEST, extension)
_check(leaked == 0, "no .%s files imported (got %d)" % [extension, leaked])
var anim_total := _count_files_recursive(DEST, "anim")
var anim_curated := _count_files_recursive(DEST + "/animations", "anim")
_check(anim_total == anim_curated,
"curated .anim clips only under animations/ (%d of %d)" % [anim_curated, anim_total])
_remove_recursive(DEST)
print("PASS" if _failures == 0 else "FAIL: %d checks failed" % _failures)
quit(0 if _failures == 0 else 1)
func _check(ok: bool, what: String) -> void:
if not ok:
_failures += 1
print(" %s %s" % ["ok " if ok else "FAIL", what])
func _count_files(path: String, extension: String) -> int:
var count := 0
var dir := DirAccess.open(path)
if dir == null:
return 0
for file in dir.get_files():
if file.get_extension().to_lower() == extension:
count += 1
return count
func _count_files_recursive(path: String, extension: String) -> int:
var count := _count_files(path, extension)
var dir := DirAccess.open(path)
if dir == null:
return count
for sub in dir.get_directories():
count += _count_files_recursive(path.path_join(sub), extension)
return count
func _remove_recursive(path: String) -> void:
var dir := DirAccess.open(path)
if dir == null:
return
for file in dir.get_files():
dir.remove(file)
for sub in dir.get_directories():
_remove_recursive(path.path_join(sub))
DirAccess.remove_absolute(path)