-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathtest_collector.gd
286 lines (246 loc) · 8.3 KB
/
test_collector.gd
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
# ------------------------------------------------------------------------------
# Used to keep track of info about each test ran.
# ------------------------------------------------------------------------------
class Test:
# indicator if it passed or not. defaults to true since it takes only
# one failure to make it not pass. _fail in gut will set this.
var passed = true
# the name of the function
var name = ""
# flag to know if the name has been printed yet.
var has_printed_name = false
# the number of arguments the method has
var arg_count = 0
# The number of asserts in the test
var assert_count = 0
# if the test has been marked pending at anypont during
# execution.
var pending = false
# ------------------------------------------------------------------------------
# This holds all the meta information for a test script. It contains the
# name of the inner class and an array of Test "structs".
#
# This class also facilitates all the exporting and importing of tests.
# ------------------------------------------------------------------------------
class TestScript:
var inner_class_name = null
var tests = []
var path = null
var _utils = null
var _lgr = null
func _init(utils=null, logger=null):
_utils = utils
_lgr = logger
func to_s():
var to_return = path
if(inner_class_name != null):
to_return += str('.', inner_class_name)
to_return += "\n"
for i in range(tests.size()):
to_return += str(' ', tests[i].name, "\n")
return to_return
func get_new():
return load_script().new()
func load_script():
#print('loading: ', get_full_name())
var to_return = load(path)
if(inner_class_name != null):
# If we wanted to do inner classes in inner classses
# then this would have to become some kind of loop or recursive
# call to go all the way down the chain or this class would
# have to change to hold onto the loaded class instead of
# just path information.
to_return = to_return.get(inner_class_name)
return to_return
func get_filename_and_inner():
var to_return = get_filename()
if(inner_class_name != null):
to_return += '.' + inner_class_name
return to_return
func get_full_name():
var to_return = path
if(inner_class_name != null):
to_return += '.' + inner_class_name
return to_return
func get_filename():
return path.get_file()
func has_inner_class():
return inner_class_name != null
# Note: although this no longer needs to export the inner_class names since
# they are pulled from metadata now, it is easier to leave that in
# so we don't have to cut the export down to unique script names.
func export_to(config_file, section):
config_file.set_value(section, 'path', path)
config_file.set_value(section, 'inner_class', inner_class_name)
var names = []
for i in range(tests.size()):
names.append(tests[i].name)
config_file.set_value(section, 'tests', names)
func _remap_path(source_path):
var to_return = source_path
if(!_utils.file_exists(source_path)):
_lgr.debug('Checking for remap for: ' + source_path)
var remap_path = source_path.get_basename() + '.gd.remap'
if(_utils.file_exists(remap_path)):
var cf = ConfigFile.new()
cf.load(remap_path)
to_return = cf.get_value('remap', 'path')
else:
_lgr.warn('Could not find remap file ' + remap_path)
return to_return
func import_from(config_file, section):
path = config_file.get_value(section, 'path')
path = _remap_path(path)
# Null is an acceptable value, but you can't pass null as a default to
# get_value since it thinks you didn't send a default...then it spits
# out red text. This works around that.
var inner_name = config_file.get_value(section, 'inner_class', 'Placeholder')
if(inner_name != 'Placeholder'):
inner_class_name = inner_name
else: # just being explicit
inner_class_name = null
func get_test_named(name):
return _utils.search_array(tests, 'name', name)
# ------------------------------------------------------------------------------
# start test_collector, I don't think I like the name.
# ------------------------------------------------------------------------------
var scripts = []
var _test_prefix = 'test_'
var _test_class_prefix = 'Test'
var _utils = load('res://addons/gut/utils.gd').get_instance()
var _lgr = _utils.get_logger()
func _does_inherit_from_test(thing):
var base_script = thing.get_base_script()
var to_return = false
if(base_script != null):
var base_path = base_script.get_path()
if(base_path == 'res://addons/gut/test.gd'):
to_return = true
else:
to_return = _does_inherit_from_test(base_script)
return to_return
func _populate_tests(test_script):
var methods = test_script.load_script().get_script_method_list()
for i in range(methods.size()):
var name = methods[i]['name']
if(name.begins_with(_test_prefix)):
var t = Test.new()
t.name = name
t.arg_count = methods[i]['args'].size()
test_script.tests.append(t)
func _get_inner_test_class_names(loaded):
var inner_classes = []
var const_map = loaded.get_script_constant_map()
for key in const_map:
var thing = const_map[key]
if(typeof(thing) == TYPE_OBJECT):
if(key.begins_with(_test_class_prefix)):
if(_does_inherit_from_test(thing)):
inner_classes.append(key)
else:
_lgr.warn(str('Ignoring Inner Class ', key,
' because it does not extend res://addons/gut/test.gd'))
# This could go deeper and find inner classes within inner classes
# but requires more experimentation. Right now I'm keeping it at
# one level since that is what the previous version did and there
# has been no demand for deeper nesting.
# _populate_inner_test_classes(thing)
return inner_classes
func _parse_script(test_script):
var inner_classes = []
var scripts_found = []
var loaded = load(test_script.path)
if(_does_inherit_from_test(loaded)):
_populate_tests(test_script)
scripts_found.append(test_script.path)
inner_classes = _get_inner_test_class_names(loaded)
for i in range(inner_classes.size()):
var loaded_inner = loaded.get(inner_classes[i])
if(_does_inherit_from_test(loaded_inner)):
var ts = TestScript.new(_utils, _lgr)
ts.path = test_script.path
ts.inner_class_name = inner_classes[i]
_populate_tests(ts)
scripts.append(ts)
scripts_found.append(test_script.path + '[' + inner_classes[i] +']')
return scripts_found
# -----------------
# Public
# -----------------
func add_script(path):
# SHORTCIRCUIT
if(has_script(path)):
return []
var f = File.new()
# SHORTCIRCUIT
if(!f.file_exists(path)):
_lgr.error('Could not find script: ' + path)
return
var ts = TestScript.new(_utils, _lgr)
ts.path = path
scripts.append(ts)
return _parse_script(ts)
func clear():
scripts.clear()
func has_script(path):
var found = false
var idx = 0
while(idx < scripts.size() and !found):
if(scripts[idx].get_full_name() == path):
found = true
else:
idx += 1
return found
func export_tests(path):
var success = true
var f = ConfigFile.new()
for i in range(scripts.size()):
scripts[i].export_to(f, str('TestScript-', i))
var result = f.save(path)
if(result != OK):
_lgr.error(str('Could not save exported tests to [', path, ']. Error code: ', result))
success = false
return success
func import_tests(path):
var success = false
var f = ConfigFile.new()
var result = f.load(path)
if(result != OK):
_lgr.error(str('Could not load exported tests from [', path, ']. Error code: ', result))
else:
var sections = f.get_sections()
for key in sections:
var ts = TestScript.new(_utils, _lgr)
ts.import_from(f, key)
_populate_tests(ts)
scripts.append(ts)
success = true
return success
func get_script_named(name):
return _utils.search_array(scripts, 'get_filename_and_inner', name)
func get_test_named(script_name, test_name):
var s = get_script_named(script_name)
if(s != null):
return s.get_test_named(test_name)
else:
return null
func to_s():
var to_return = ''
for i in range(scripts.size()):
to_return += scripts[i].to_s() + "\n"
return to_return
# ---------------------
# Accessors
# ---------------------
func get_logger():
return _lgr
func set_logger(logger):
_lgr = logger
func get_test_prefix():
return _test_prefix
func set_test_prefix(test_prefix):
_test_prefix = test_prefix
func get_test_class_prefix():
return _test_class_prefix
func set_test_class_prefix(test_class_prefix):
_test_class_prefix = test_class_prefix