gdharness 1.3.51 → 1.3.53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cli.js +11900 -10947
- package/build/godot/addons/gdharness_editor/plugin.gd +9 -0
- package/build/godot/addons/gdharness_editor/release_export.gd +66 -0
- package/build/godot/addons/gdharness_editor/release_export.gd.uid +1 -0
- package/build/godot/loader/loader.gd +3 -2
- package/build/godot/tests/listed.gd +191 -17
- package/build/keeper.js +658 -282
- package/package.json +1 -1
|
@@ -6,6 +6,7 @@ extends EditorPlugin
|
|
|
6
6
|
|
|
7
7
|
const GdhBridgeClient = preload("bridge_client.gd")
|
|
8
8
|
const GdhToolExecutor = preload("tool_executor.gd")
|
|
9
|
+
const GdhReleaseExport = preload("release_export.gd")
|
|
9
10
|
## The tools answered while the editor scans or imports, because none of them saves a file: the
|
|
10
11
|
## ones a caller waits on a scan with, and the ones that only read or stop a played game.
|
|
11
12
|
const ANSWERED_WHILE_SCANNING: Array[String] = [
|
|
@@ -20,9 +21,13 @@ const ANSWERED_WHILE_SCANNING: Array[String] = [
|
|
|
20
21
|
var _client: GdhBridgeClient
|
|
21
22
|
var _tool_executor: GdhToolExecutor
|
|
22
23
|
var _status_label: Label
|
|
24
|
+
var _release_export: GdhReleaseExport
|
|
23
25
|
|
|
24
26
|
|
|
25
27
|
func _enter_tree() -> void:
|
|
28
|
+
_release_export = GdhReleaseExport.new()
|
|
29
|
+
add_export_plugin(_release_export)
|
|
30
|
+
|
|
26
31
|
_client = GdhBridgeClient.new()
|
|
27
32
|
_client.name = "GdharnessBridgeClient"
|
|
28
33
|
add_child(_client)
|
|
@@ -41,6 +46,10 @@ func _enter_tree() -> void:
|
|
|
41
46
|
|
|
42
47
|
|
|
43
48
|
func _exit_tree() -> void:
|
|
49
|
+
if _release_export:
|
|
50
|
+
remove_export_plugin(_release_export)
|
|
51
|
+
_release_export = null
|
|
52
|
+
|
|
44
53
|
if _client:
|
|
45
54
|
if _client.connected.is_connected(_on_connected):
|
|
46
55
|
_client.connected.disconnect(_on_connected)
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
@tool
|
|
2
|
+
extends EditorExportPlugin
|
|
3
|
+
## Leaves gdharness out of a release export: the runtime autoload's entry, its loader and the
|
|
4
|
+
## addons, none of which does anything in a release build, so no player's build carries them. A
|
|
5
|
+
## debug export keeps them, since the runtime serves a debug build.
|
|
6
|
+
|
|
7
|
+
## The loader the runtime autoload's entry names, as `gdharness setup` writes it.
|
|
8
|
+
const LOADER: String = "res://gdharness/loader.gd"
|
|
9
|
+
## The folders a release export leaves out: the loader's and the addons that never run there.
|
|
10
|
+
const LEFT_OUT: Array[String] = [
|
|
11
|
+
"res://gdharness/",
|
|
12
|
+
"res://addons/gdharness_runtime/",
|
|
13
|
+
"res://addons/gdharness_editor/",
|
|
14
|
+
"res://addons/auto_reload/",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
## Each autoload entry taken out for the export, by setting name, with its value and its order.
|
|
18
|
+
var _values: Dictionary[String, Variant] = {}
|
|
19
|
+
var _orders: Dictionary[String, int] = {}
|
|
20
|
+
var _release: bool = false
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
## Before "GDScript", by which the export orders its plugins: a file stops at the first that skips
|
|
24
|
+
## or replaces it, and that one turns every script into tokens, so a later skip leaves them in.
|
|
25
|
+
func _get_name() -> String:
|
|
26
|
+
return "GDHarness"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
func _export_begin(
|
|
30
|
+
_features: PackedStringArray, is_debug: bool, _path: String, _flags: int
|
|
31
|
+
) -> void:
|
|
32
|
+
_release = not is_debug
|
|
33
|
+
_values.clear()
|
|
34
|
+
_orders.clear()
|
|
35
|
+
if not _release:
|
|
36
|
+
return
|
|
37
|
+
# The export writes project.binary from the settings as they stand once this returns, and the
|
|
38
|
+
# editor saves project.godot only for an edit made in its settings dialog.
|
|
39
|
+
for property: Dictionary in ProjectSettings.get_property_list():
|
|
40
|
+
var setting: String = property["name"]
|
|
41
|
+
if not setting.begins_with("autoload/"):
|
|
42
|
+
continue
|
|
43
|
+
var value: Variant = ProjectSettings.get_setting(setting)
|
|
44
|
+
if value is String and str(value).trim_prefix("*") == LOADER:
|
|
45
|
+
_values[setting] = value
|
|
46
|
+
_orders[setting] = ProjectSettings.get_order(setting)
|
|
47
|
+
for setting: String in _values:
|
|
48
|
+
ProjectSettings.set_setting(setting, null)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
func _export_file(path: String, _type: String, _features: PackedStringArray) -> void:
|
|
52
|
+
if not _release:
|
|
53
|
+
return
|
|
54
|
+
for folder: String in LEFT_OUT:
|
|
55
|
+
if path.begins_with(folder):
|
|
56
|
+
skip()
|
|
57
|
+
return
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
func _export_end() -> void:
|
|
61
|
+
for setting: String in _values:
|
|
62
|
+
ProjectSettings.set_setting(setting, _values[setting])
|
|
63
|
+
ProjectSettings.set_order(setting, _orders[setting])
|
|
64
|
+
_values.clear()
|
|
65
|
+
_orders.clear()
|
|
66
|
+
_release = false
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
uid://dlypkbduqc14w
|
|
@@ -31,8 +31,9 @@ func _ready() -> void:
|
|
|
31
31
|
_runtime = runtime
|
|
32
32
|
|
|
33
33
|
|
|
34
|
-
## Whether gdharness's runtime is listening in this engine: false in a
|
|
35
|
-
## and
|
|
34
|
+
## Whether gdharness's runtime is listening in this engine: false in a script run, an engine without
|
|
35
|
+
## the addon and a release build that still carries this node, though it is in every one of them. A
|
|
36
|
+
## release export made with the editor addon enabled leaves this node out.
|
|
36
37
|
func serving() -> bool:
|
|
37
38
|
if not is_instance_valid(_runtime) or not _runtime.has_method("serving"):
|
|
38
39
|
return false
|
|
@@ -7,10 +7,10 @@ extends SceneTree
|
|
|
7
7
|
##
|
|
8
8
|
## Started from outside the project, `-s` with this file's absolute path, so it declares no class of
|
|
9
9
|
## its own and reaches gdUnit4 through the project's global classes. Besides the file of suites it
|
|
10
|
-
## finds each case by its id in a dictionary ([GdhIndexedSession]), can
|
|
11
|
-
##
|
|
12
|
-
##
|
|
13
|
-
##
|
|
10
|
+
## finds each case by its id in a dictionary ([GdhIndexedSession]), can turn off the reports the
|
|
11
|
+
## caller does not read ([constant REPORTS_OFF]), writes the errors and warnings each suite declares
|
|
12
|
+
## it prints ([constant EXPECTED_OUT]) and what the run loads ([constant REACHED_OUT]), and in an
|
|
13
|
+
## engine of its own compiles every script together ([constant COMPILE_ONLY]).
|
|
14
14
|
|
|
15
15
|
const LISTED: String = "--suites-in"
|
|
16
16
|
const SKIPPING: String = "--skipping-in"
|
|
@@ -42,11 +42,6 @@ const REPORTS_OFF: Dictionary[String, String] = {
|
|
|
42
42
|
"--html-report-off": "res://addons/gdUnit4/src/core/hooks/GdUnitHtmlReporterTestSessionHook.gd",
|
|
43
43
|
"--junit-report-off": "res://addons/gdUnit4/src/core/hooks/GdUnitXMLReporterTestSessionHook.gd",
|
|
44
44
|
}
|
|
45
|
-
## Compiles the suites together beside every class of the game ([method compiled_together]) before
|
|
46
|
-
## gdUnit4 loads them. A gate asks for it, since it runs every suite and wants every script that
|
|
47
|
-
## does not compile named. project_test does not: its answer is about the scripts its run reached,
|
|
48
|
-
## and the compile names every broken script of the game, then once more as the one script it made.
|
|
49
|
-
const TOGETHER: String = "--compile-together"
|
|
50
45
|
## The file to write what every suite declares it prints into, as JSON: each key of
|
|
51
46
|
## [constant EXPECTED] holding the declarations keyed by suite path and then case name.
|
|
52
47
|
const EXPECTED_OUT: String = "--expected-out"
|
|
@@ -56,6 +51,20 @@ const EXPECTED: Dictionary[String, String] = {
|
|
|
56
51
|
"errors": "GDHARNESS_EXPECTED_ERRORS",
|
|
57
52
|
"warnings": "GDHARNESS_EXPECTED_WARNINGS",
|
|
58
53
|
}
|
|
54
|
+
## Compiles the engine's own suites together ([method compiled_together]) before gdUnit4 loads them,
|
|
55
|
+
## so they share one reading of what they reach, and no class of the game they do not: what the
|
|
56
|
+
## engine holds is then what its suites reach, which is what [constant REACHED_OUT] records.
|
|
57
|
+
const TOGETHER: String = "--compile-together"
|
|
58
|
+
## The file to write what the run loads into, a line as each suite ends ([GdhReached]).
|
|
59
|
+
const REACHED_OUT: String = "--reached-out"
|
|
60
|
+
## Compiles the suites together beside every class of the game ([method compiled_together]), prints
|
|
61
|
+
## [constant COMPILED] and quits, running no suite: a run's check of every script, made in an engine
|
|
62
|
+
## of its own so the engines running its suites load only what their suites do. `gdharness test`
|
|
63
|
+
## asks for it, since it wants every script that does not compile named. project_test does not: its
|
|
64
|
+
## answer is about the scripts its run reached, and the compile names every broken script of the
|
|
65
|
+
## game, then once more as the one script it made.
|
|
66
|
+
const COMPILE_ONLY: String = "--compile-only"
|
|
67
|
+
const COMPILED: String = "gdharness compiled every script together"
|
|
59
68
|
## The tool name gdUnit4 reads its options after.
|
|
60
69
|
const TOOL: String = "GdUnitCmdTool.gd"
|
|
61
70
|
## Ends the runner's own arguments: everything after it is gdUnit4's options, for a run with no
|
|
@@ -76,8 +85,23 @@ const WALK_HOLD: StringName = &"gdharness_scripts_held_past_the_shutdown_walk"
|
|
|
76
85
|
var _runner: GdhRunner = null
|
|
77
86
|
|
|
78
87
|
|
|
88
|
+
## Here rather than in [method _initialize]: Godot loads the script given with `-s` and makes its
|
|
89
|
+
## instance before it loads the autoloads, so a load an autoload makes as it is made is recorded
|
|
90
|
+
## too.
|
|
91
|
+
func _init() -> void:
|
|
92
|
+
var into: String = argument_after(OS.get_cmdline_user_args(), REACHED_OUT)
|
|
93
|
+
if not into.is_empty():
|
|
94
|
+
GdhReached.start(into)
|
|
95
|
+
|
|
96
|
+
|
|
79
97
|
func _initialize() -> void:
|
|
80
|
-
|
|
98
|
+
var given: PackedStringArray = OS.get_cmdline_user_args()
|
|
99
|
+
if given.has(COMPILE_ONLY):
|
|
100
|
+
compile_only(given)
|
|
101
|
+
return
|
|
102
|
+
if GdhReached.current != null:
|
|
103
|
+
GdhReached.current.autoloaded()
|
|
104
|
+
_runner = started(given)
|
|
81
105
|
_runner.own_path = get_script().resource_path
|
|
82
106
|
root.add_child(_runner)
|
|
83
107
|
|
|
@@ -86,11 +110,32 @@ func _initialize() -> void:
|
|
|
86
110
|
## The runner is not queued for deletion here, as gdUnit4's does: the tree has freed root and the
|
|
87
111
|
## runner with it before this is called, and queueing a freed node writes into freed memory.
|
|
88
112
|
func _finalize() -> void:
|
|
113
|
+
GdhReached.stop()
|
|
89
114
|
if OS.is_stdout_verbose():
|
|
90
115
|
print("Orphan nodes left as the run ends:")
|
|
91
116
|
Window.print_orphan_nodes()
|
|
92
117
|
|
|
93
118
|
|
|
119
|
+
## Compiles the suites [param given] lists together and quits, holding the scripts past the
|
|
120
|
+
## engine's shutdown walk as a run of suites does ([GdhShutdownWalk]).
|
|
121
|
+
func compile_only(given: PackedStringArray) -> void:
|
|
122
|
+
var oldest: Array[GDScript] = []
|
|
123
|
+
if GdhShutdownWalk.guarded():
|
|
124
|
+
oldest = GdhShutdownWalk.marker(WALK_ENDED)
|
|
125
|
+
var together: GDScript = compiled_together(listed(given, LISTED), held_classes())
|
|
126
|
+
print(COMPILED)
|
|
127
|
+
if GdhShutdownWalk.guarded():
|
|
128
|
+
var also: Array[GDScript] = oldest.duplicate()
|
|
129
|
+
also.append_array(GdhShutdownWalk.marker(WALK_BEGAN))
|
|
130
|
+
also.append(together)
|
|
131
|
+
var own_path: String = get_script().resource_path
|
|
132
|
+
var own: GDScript = ResourceLoader.get_cached_ref(own_path) as GDScript
|
|
133
|
+
if own != null:
|
|
134
|
+
also.append(own)
|
|
135
|
+
GdhShutdownWalk.hold(also)
|
|
136
|
+
quit()
|
|
137
|
+
|
|
138
|
+
|
|
94
139
|
## The runner over the suites [param given] lists, to be added to the tree, which starts it. Shared
|
|
95
140
|
## by this script and the suites scene of the tests addon, which loads this file from where the
|
|
96
141
|
## server ships it and calls this, since the addon itself names no gdUnit4 class.
|
|
@@ -125,7 +170,7 @@ static func started(given: PackedStringArray) -> GdhRunner:
|
|
|
125
170
|
if at >= 0:
|
|
126
171
|
runner.given = given.slice(at + 1)
|
|
127
172
|
if asked.has(TOGETHER):
|
|
128
|
-
runner.together = compiled_together(suites)
|
|
173
|
+
runner.together = compiled_together(suites, [])
|
|
129
174
|
var expected_out: String = argument_after(asked, EXPECTED_OUT)
|
|
130
175
|
if not expected_out.is_empty():
|
|
131
176
|
write_expected(expected_out, suites)
|
|
@@ -162,16 +207,16 @@ static func unread_off(
|
|
|
162
207
|
## [param suites] compiled inside one script, answered for the caller to hold so gdUnit4's own loads
|
|
163
208
|
## find them compiled. The engine keeps what it read of a class only while the compile that asked
|
|
164
209
|
## for it runs (`GDScriptCache`), so a suite compiled on its own reads again every class it reaches
|
|
165
|
-
## a member of, some seventy milliseconds a suite. As the preloads of one script
|
|
166
|
-
##
|
|
167
|
-
## hundred suites compiled in a third of the time they took one at a time. The names are public,
|
|
210
|
+
## a member of, some seventy milliseconds a suite. As the preloads of one script, after the
|
|
211
|
+
## [param classes] it names first, held from that script's compile, they share one reading of each:
|
|
212
|
+
## two hundred suites compiled in a third of the time they took one at a time. The names are public,
|
|
168
213
|
## since a private one unread is a warning and every warning an error, and each a variable, since a
|
|
169
214
|
## class is no constant expression. A suite that does not compile fails here and again when gdUnit4
|
|
170
215
|
## loads it, which reports it as it would have. A directory listed is left to gdUnit4 to walk.
|
|
171
|
-
static func compiled_together(suites: PackedStringArray) -> GDScript:
|
|
216
|
+
static func compiled_together(suites: PackedStringArray, classes: PackedStringArray) -> GDScript:
|
|
172
217
|
var lines: PackedStringArray = ["extends RefCounted"]
|
|
173
218
|
var at: int = 0
|
|
174
|
-
for named: String in
|
|
219
|
+
for named: String in classes:
|
|
175
220
|
var _held: bool = lines.append("var held_%d: Variant = %s" % [at, named])
|
|
176
221
|
at += 1
|
|
177
222
|
at = 0
|
|
@@ -186,7 +231,7 @@ static func compiled_together(suites: PackedStringArray) -> GDScript:
|
|
|
186
231
|
return together
|
|
187
232
|
|
|
188
233
|
|
|
189
|
-
## The classes [
|
|
234
|
+
## The classes [constant COMPILE_ONLY] compiles before the suites: the game's own GDScript classes,
|
|
190
235
|
## none of an addon's or a suite's, then gdUnit4's suite. Only GDScript shares the engine's reading,
|
|
191
236
|
## and a sandboxed mod's SafeGDScript class would load the sandbox's compiler, some 290 MB and up to
|
|
192
237
|
## 2.5 s, for nothing.
|
|
@@ -455,6 +500,8 @@ class GdhRunner:
|
|
|
455
500
|
var _watched: int = tree.process_frame.connect(drawn_window)
|
|
456
501
|
GdUnitEvent.TESTSUITE_BEFORE:
|
|
457
502
|
drawn_window()
|
|
503
|
+
if GdhReached.current != null:
|
|
504
|
+
GdhReached.current.began(event.resource_path())
|
|
458
505
|
suite_running = event.resource_path()
|
|
459
506
|
orphans_at_suite = orphan_ids()
|
|
460
507
|
orphans_named.clear()
|
|
@@ -472,6 +519,8 @@ class GdhRunner:
|
|
|
472
519
|
# gdUnit4 minimises the window once the suite's end is reported.
|
|
473
520
|
if event.type() == GdUnitEvent.TESTSUITE_AFTER:
|
|
474
521
|
minimise_pending = true
|
|
522
|
+
if GdhReached.current != null:
|
|
523
|
+
GdhReached.current.ended(event.resource_path())
|
|
475
524
|
# After gdUnit4's own report of the case, so the line falls under it.
|
|
476
525
|
if event.orphan_nodes() == 0:
|
|
477
526
|
return
|
|
@@ -743,3 +792,128 @@ class GdhShutdownWalk:
|
|
|
743
792
|
if not inner.begins_with("."):
|
|
744
793
|
var _inner: bool = pending.append(directory.path_join(inner))
|
|
745
794
|
ThemeDB.set_meta(WALK_HOLD, held)
|
|
795
|
+
|
|
796
|
+
|
|
797
|
+
## What the run loads, written to the file named after [constant REACHED_OUT] a line of JSON at a
|
|
798
|
+
## time, each with every path asked for since the line before: one once the autoloads are loaded,
|
|
799
|
+
## one as each suite begins, which also names the project's files the cache holds then, and one as
|
|
800
|
+
## it ends. The engine answers a load of a resource it holds from its cache without asking any
|
|
801
|
+
## loader, and it holds one only while something refers to it, so what a suite can load unasked is
|
|
802
|
+
## what the cache held as it began. Placed first among the loaders, it is asked about every load and
|
|
803
|
+
## every [method ResourceLoader.exists] and declines each. A script compiled for a preload, a base
|
|
804
|
+
## or a class name is read without a loader, so a suite's last line also has the project's files
|
|
805
|
+
## the cache came to hold that no loader was asked for.
|
|
806
|
+
class GdhReached:
|
|
807
|
+
extends ResourceFormatLoader
|
|
808
|
+
|
|
809
|
+
static var current: GdhReached = null
|
|
810
|
+
|
|
811
|
+
var _file: FileAccess = null
|
|
812
|
+
## Loads on other threads ask too, as a threaded load runs its own.
|
|
813
|
+
var _mutex: Mutex = Mutex.new()
|
|
814
|
+
## What the current line holds, since a path freed and loaded again is asked for again.
|
|
815
|
+
var _seen: Dictionary[String, bool] = {}
|
|
816
|
+
var _fresh: PackedStringArray = []
|
|
817
|
+
var _files: PackedStringArray = []
|
|
818
|
+
var _resident: Dictionary[String, bool] = {}
|
|
819
|
+
|
|
820
|
+
static func start(into: String) -> void:
|
|
821
|
+
var file: FileAccess = FileAccess.open(into, FileAccess.WRITE)
|
|
822
|
+
if file == null:
|
|
823
|
+
push_error(
|
|
824
|
+
(
|
|
825
|
+
"gdharness test could not write what the run loads to %s: %s"
|
|
826
|
+
% [into, error_string(FileAccess.get_open_error())]
|
|
827
|
+
)
|
|
828
|
+
)
|
|
829
|
+
return
|
|
830
|
+
current = GdhReached.new()
|
|
831
|
+
current._file = file
|
|
832
|
+
current._files = project_files()
|
|
833
|
+
ResourceLoader.add_resource_format_loader(current, true)
|
|
834
|
+
|
|
835
|
+
static func stop() -> void:
|
|
836
|
+
if current == null:
|
|
837
|
+
return
|
|
838
|
+
ResourceLoader.remove_resource_format_loader(current)
|
|
839
|
+
current._file.close()
|
|
840
|
+
current = null
|
|
841
|
+
|
|
842
|
+
## Every file under res:// the engine sees, as its file system does: no hidden folder, and no
|
|
843
|
+
## folder holding a `.gdignore`.
|
|
844
|
+
static func project_files() -> PackedStringArray:
|
|
845
|
+
var found: PackedStringArray = []
|
|
846
|
+
var pending: PackedStringArray = ["res://"]
|
|
847
|
+
while not pending.is_empty():
|
|
848
|
+
var directory: String = pending[pending.size() - 1]
|
|
849
|
+
pending.remove_at(pending.size() - 1)
|
|
850
|
+
if FileAccess.file_exists(directory.path_join(".gdignore")):
|
|
851
|
+
continue
|
|
852
|
+
for file: String in DirAccess.get_files_at(directory):
|
|
853
|
+
if not file.ends_with(".import") and not file.ends_with(".uid"):
|
|
854
|
+
var _file_found: bool = found.append(directory.path_join(file))
|
|
855
|
+
for inner: String in DirAccess.get_directories_at(directory):
|
|
856
|
+
if not inner.begins_with("."):
|
|
857
|
+
var _inner: bool = pending.append(directory.path_join(inner))
|
|
858
|
+
return found
|
|
859
|
+
|
|
860
|
+
func _recognize_path(path: String, _type: StringName) -> bool:
|
|
861
|
+
_mutex.lock()
|
|
862
|
+
if not _seen.has(path):
|
|
863
|
+
_seen[path] = true
|
|
864
|
+
var _asked: bool = _fresh.append(path)
|
|
865
|
+
_mutex.unlock()
|
|
866
|
+
return false
|
|
867
|
+
|
|
868
|
+
func _get_recognized_extensions() -> PackedStringArray:
|
|
869
|
+
return []
|
|
870
|
+
|
|
871
|
+
func _handles_type(_type: StringName) -> bool:
|
|
872
|
+
return false
|
|
873
|
+
|
|
874
|
+
## Writes the line for the autoloads, which have all been loaded by now.
|
|
875
|
+
func autoloaded() -> void:
|
|
876
|
+
written({ "autoloads": true })
|
|
877
|
+
|
|
878
|
+
## Writes the line of the suite at [param suite], which is about to begin, with what the cache
|
|
879
|
+
## holds. The cache is asked outside the lock, since a load on another thread holds the cache's
|
|
880
|
+
## own lock while it asks this.
|
|
881
|
+
func began(suite: String) -> void:
|
|
882
|
+
var resident: PackedStringArray = []
|
|
883
|
+
_resident.clear()
|
|
884
|
+
for path: String in _files:
|
|
885
|
+
if ResourceLoader.has_cached(path):
|
|
886
|
+
var _held: bool = resident.append(path)
|
|
887
|
+
_resident[path] = true
|
|
888
|
+
written({ "began": suite, "resident": resident })
|
|
889
|
+
|
|
890
|
+
## Writes the line of the suite at [param suite], which has just ended, with the project's files
|
|
891
|
+
## the cache came to hold while it ran that no loader was asked for.
|
|
892
|
+
func ended(suite: String) -> void:
|
|
893
|
+
_mutex.lock()
|
|
894
|
+
var unseen: PackedStringArray = []
|
|
895
|
+
for path: String in _files:
|
|
896
|
+
if not _seen.has(path) and not _resident.has(path):
|
|
897
|
+
var _unseen: bool = unseen.append(path)
|
|
898
|
+
_mutex.unlock()
|
|
899
|
+
var cached: PackedStringArray = []
|
|
900
|
+
for path: String in unseen:
|
|
901
|
+
if ResourceLoader.has_cached(path):
|
|
902
|
+
var _cached: bool = cached.append(path)
|
|
903
|
+
_mutex.lock()
|
|
904
|
+
for path: String in cached:
|
|
905
|
+
if not _seen.has(path):
|
|
906
|
+
_seen[path] = true
|
|
907
|
+
var _held: bool = _fresh.append(path)
|
|
908
|
+
_mutex.unlock()
|
|
909
|
+
written({ "suite": suite })
|
|
910
|
+
|
|
911
|
+
## Writes [param fields] as a line with every path asked for since the line before.
|
|
912
|
+
func written(fields: Dictionary) -> void:
|
|
913
|
+
_mutex.lock()
|
|
914
|
+
fields["reached"] = _fresh
|
|
915
|
+
_fresh = []
|
|
916
|
+
_seen.clear()
|
|
917
|
+
_mutex.unlock()
|
|
918
|
+
var _stored: bool = _file.store_line(JSON.stringify(fields))
|
|
919
|
+
_file.flush()
|