-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathapp.testmanager.bmx
More file actions
359 lines (285 loc) · 9.38 KB
/
app.testmanager.bmx
File metadata and controls
359 lines (285 loc) · 9.38 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
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
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
Rem
===========================================================
APP SPECIFIC COMPILER TEST CLASS
===========================================================
Class to compile a given code with a compiler with options and
parameters compatible to "BlitzMax' bmk".
ENDREM
SuperStrict
Import "base.directorytree.bmx"
Import "app.testcompiler.bmx" 'containing the compiler specific test class
Import "app.testmodcompiler.bmx" 'containing the compiler specific test class
?Threaded
Import Brl.Threads
?
Global SCT_VERSION:String = "0.2.2"
Type TTestManager
Field tests:TList = CreateList()
?Threaded
Global testsDone:TList = CreateList()
'a lock for the done list
Global testsDoneMutex:TMutex = CreateMutex()
?
Field dirTree:TDirectoryTree = New TDirectoryTree
Field verbose:int
Field mods:TList = New TList
Method Init:TTestManager(args:String[])
ParseArgs(args)
Return Self
End Method
Method AddTest( test:TTestBase )
tests.addLast(test)
End Method
'generate a configuration which inherits from parental configurations
'in parental folders
Method GetInheritedConfig:TConfigMap( configFile:String, rootDirectory:String, currentDirectory:String )
'all found configs are stored within an array and get
'merged at the end - the top most array at last (to overwrite
'parental settings)
Local configs:TConfigMap[]
'add the test's individual config if possible
If FileSize(currentDirectory+"/"+configFile) > 0
configs :+ [ New TConfigMap.Init(currentDirectory+"/"+configFile) ]
EndIf
'loop through all parental directories
Local lastSlashPos:Int = -1
While currentDirectory.StartsWith(rootDirectory)
If FileSize(currentDirectory+"/base.conf") > 0
configs :+ [ New TConfigMap.Init(currentDirectory+"/base.conf") ]
EndIf
'strip of deepest directory
lastSlashPos = currentDirectory.FindLast("/")
If lastSlashPos >= 0
currentDirectory = currentDirectory[..lastSlashPos]
Else
'finished
currentDirectory = ""
EndIf
Wend
'add the basic configuration at last - so it is the base
'of all configs (stack wise as it gets reversed during the
'following merge)
configs :+ [TTestCompiler.baseconfig]
'return the merged config
Return TConfigMap.CreateMerged(configs, True)
End Method
Method AddTestsFromDirectory( directory:String )
'create a new directory tree containing all files/dirs of
'interest for us
dirTree = New TDirectoryTree.Init(directory, ["bmx", "conf", "res"], Null, ["*"], [".bmx"])
dirTree.relativePaths = False
dirTree.ScanDir()
'load *.bmx files
Local testFiles:String[] = dirTree.GetFiles("", "bmx")
Local test:TTestCompiler
For Local testFile:String = EachIn testFiles
test = New TTestCompiler.Init(testFile).SetCompileFile(testFile)
'try to load the expected result file
'prefer OS/arch specific result files
local testFileBase:string = StripExt(testFile)
local arch:string = TTestCompiler.baseConfig.GetString("app_arch", "x86")
?win32
local OS:string = "win32"
?linux
local OS:string = "linux"
?macos
local OS:string = "macos"
?
if FileType(testFileBase+"." + OS + "." + arch + ".res") = FILETYPE_FILE
test.SetExpectedOutput( test.LoadExpectedOutput(testFileBase + "." + OS + "." + arch + ".res") )
Elseif FileType(testFileBase + "." + OS + ".res") = FILETYPE_FILE
test.SetExpectedOutput( test.LoadExpectedOutput(testFileBase + "." + OS + ".res") )
Elseif FileType(testFileBase + ".res") = FILETYPE_FILE
test.SetExpectedOutput( test.LoadExpectedOutput(testFileBase + ".res") )
EndIf
'try to find a configuration for this test
test.config = GetInheritedConfig( StripAll(testFile) + ".conf", directory, ExtractDir(testFile) )
AddTest(test)
Next
End Method
Method BuildMods()
Print "=== STARTING MODULE BUILD ==="
If LoadMods()
Local config:TConfigMap = New TConfigMap.Init(TTestCompiler.baseConfig.GetString("test_base", "")+"/base.conf")
For Local m:String = EachIn mods
Local build:TTestModCompiler = New TTestModCompiler.Init(m).SetCompileFile(m)
build.config = config
print build.GetFormattedHeader(verbose)
build.Run()
print build.GetFormattedResult(verbose)
Next
Else
Print "* No modules to process *"
End If
Print "=== FINISHED MODULE BUILD ==="
End Method
Method LoadMods:Int()
Local file:TStream = ReadFile(TTestCompiler.baseConfig.GetString("test_base", "") + "/mods.conf")
If Not file Then Return False
While Not Eof(file)
Local line:String = ReadLine(file).Trim()
If line Then
mods.AddLast(line.ToLower())
End If
Wend
Return mods.Count() > 0
End Method
?Threaded
'helper function
Function RunTestThread:Object(testObject:object)
local test:TTestBase = TTestBase(testObject)
if not test then return null
test.Run()
LockMutex(testsDoneMutex)
testsDone.AddLast(test)
UnlockMutex(testsDoneMutex)
End Function
?
Method RunTests:Int()
Local start:Int = MilliSecs()
If TTestCompiler.baseConfig.GetInt("make_mods") Then
BuildMods()
End If
Print "=== STARTING TESTS ==="
Print "* AMOUNT OF TESTS: " + tests.count()
Local threadsEnabled:Int = TTestCompiler.baseConfig.GetInt("threadedTestRuns")
?Not threaded
threadsEnabled = False
?
If threadsEnabled
?Threaded
For Local test:TTestBase = EachIn tests
Print test.GetFormattedHeader(verbose)
test.Run()
Print test.GetFormattedResult(verbose)
Next
'run up to 4 concurrent Threads
Local maxThreads:Int = 4
Local threads:TThread[maxThreads]
Local testsToRun:TList = CreateList()
'remove results from last test run (if any)
testsDone.clear()
'copy existing tests into a new list which can get
'truncated later
For Local test:TTestBase = EachIn tests
testsToRun.AddLast(test)
Next
'repeat as long as there are tests to do
Local allDone:Int = (testsToRun.Count() = 0)
While Not allDone
If testsToRun.Count() > 0
Local useSlot:Int = -1
'find a free thread slot
For Local i:Int = 0 Until maxThreads
If Not threads[i] Then useSlot = i;Exit
If Not ThreadRunning(threads[i]) Then useSlot = i;Exit
Next
'wait a short time and try again
If useSlot = -1
Delay(1)
Continue
EndIf
'kick off a new thread
Local test:TTestBase = TTestBase(testsToRun.First())
testsToRun.RemoveFirst()
threads[useSlot] = CreateThread(RunTestThread, test)
EndIf
'adjust allDone to potentially finish the whileloop
allDone = (testsToRun.Count() = 0)
'check if there are no threads running anymore
For Local i:Int = 0 Until maxThreads
If Not threads[i] Then Continue
If ThreadRunning(threads[i]) Then allDone = False
Next
'check if there is some output available
If testsDone.count() > 0
LockMutex(testsDoneMutex)
For Local test:TTestBase = EachIn testsDone
Print test.GetFormattedHeader(verbose)
Print test.GetFormattedResult(verbose)
Next
testsDone.Clear()
UnlockMutex(testsDoneMutex)
EndIf
Delay(1)
Wend
Print "finished"
?
Else
For Local test:TTestBase = EachIn tests
Print test.GetFormattedHeader(verbose)
test.Run()
Print test.GetFormattedResult(verbose)
Next
EndIf
Print "=== FINISHED TESTS ==="
Print "* TIME: "+ (MilliSecs() - start)+"ms"
Print "* FAILED: "+ GetResultCount(TTestBase.RESULT_FAILED)
If verbose
For Local t:TTestBase = EachIn tests
If t.result = TTestBase.RESULT_FAILED
Print " " + t.name
EndIf
Next
EndIf
Print "* ERROR: "+ GetResultCount(TTestBase.RESULT_ERROR)
If verbose
For Local t:TTestBase = EachIn tests
If t.result = TTestBase.RESULT_ERROR
Print " " + t.name + t.GetErrorSummary()
EndIf
Next
EndIf
Print "* OK: "+ GetResultCount(TTestBase.RESULT_OK)
End Method
'returns how many tests got the specified resultType
Method GetResultCount:Int( resultType:Int = 0 )
Local count:Int = 0
For Local test:TTestbase = EachIn tests
If test.result = resultType Then count:+ 1
Next
Return count
End Method
Method ParseArgs(args:String[])
Local count:Int
If args.length = 0 Then
ShowUsage()
End If
While count < args.length
Local arg:String = args[count]
If arg[..1] <> "-" Then
Exit
End If
Select arg[1..]
Case "h"
ShowUsage()
Case "v"
ShowVersion()
Case "m"
TTestCompiler.baseConfig.Add("make_mods", "1")
Case "verbose"
verbose = True
End Select
count:+ 1
Wend
args = args[count..]
If args.length = 0
Print "Test directory missing from command line."
End
End If
' store real path of tests
TTestCompiler.baseConfig.Add("test_base", RealPath(args[0]))
AddTestsFromDirectory(args[0])
End Method
Method ShowUsage()
Print "usage : sct [-h] [-v] [-m] [-verbose] dir"
Print " -h Show this help"
Print " -v Version information"
Print " -m Build modules first. Reads mods.conf for a list of mods to build."
End
End Method
Method ShowVersion()
Print "SimpleCodeTester (sct) version " + SCT_VERSION
End
End Method
End Type