Coverage for captest.py : 89%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3#
4# This module contains the regression test driver for the CAPASM software
5# (c) 2020 Joachim Siebold
6#
7# This program is free software; you can redistribute it and/or
8# modify it under the terms of the GNU General Public License
9# as published by the Free Software Foundation; either version 2
10# of the License, or (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program; if not, write to the Free Software
19# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20#
21#--------------------------------------------------------------------------
22#
23# 28.05.2020 jsi
24# - start change log
25#
26import os
27from pathlib import Path
28from capasm.assembler import clsAssembler
29from capasm.ncas import clsNcas
30from capasm.captools import clsLifCreator,fileDiff, clsSymClassGenerator, \
31 silentRemove, clsRomCreator,clsAsmSourceFileConverter, \
32 clsSymbolFileConverter
33from capasm.capcommon import capasmError
35ASSEMBLER=clsAssembler()
36LIFCREATOR=clsLifCreator()
37GENERATOR=clsSymClassGenerator()
38ROMCREATOR=clsRomCreator()
39ASMCONVERTER=clsAsmSourceFileConverter()
40SYMCONVERTER=clsSymbolFileConverter()
41NCAS=clsNcas()
43#
44# Test case specifications ------------------------------------------------
45#
46# dictionary CAPASM_TEST { testKey: testSpec ... }
47# list testSpec [ testDescription, command1, command2, ...]
48# list command [ method, expected return value, positional params,
49# keyword params, exceptMsg]
50# list posParams [ list of positional parameters ]
51# dict keyParams { dictionary of key parameters }
52# string exceptMsg expected message to check certain capasmException
53#
54CAPASM_TESTS={
55 "rom000": \
56 ["Assemble ROM000",
57 [ ASSEMBLER.assemble,False,["rom85/rom000.asm"],{ "symNamLen":7, \
58 "globalSymbolFile": "85", \
59 "listFileName": "rom000.lst", "referenceOpt":2}],
60 [fileDiff.testBinFile,False,["rom000.bin","rom85/rom000.bin"]],
61 [silentRemove, False, ["rom000.bin","rom000.lst"]]
62 ],
63 "romsys1": \
64 ["Assemble ROMSYS1",
65 [ ASSEMBLER.assemble,False,["rom85/romsys1.asm"],{ "symNamLen":7, \
66 "globalSymbolFile": "85", \
67 "listFileName": "romsys1.lst", "referenceOpt":2}],
68 [fileDiff.testBinFile,False,["romsys1.bin","rom85/romsys1.bin"]],
69 [silentRemove,False, ["romsys1.bin","romsys1.lst"]]
70 ],
71 "romsys2": \
72 ["Assemble ROMSYS2",
73 [ ASSEMBLER.assemble,False,["rom85/romsys2.asm"],{ "symNamLen":7, \
74 "globalSymbolFile": "85", \
75 "listFileName": "romsys2.lst", "referenceOpt":2}],
76 [fileDiff.testBinFile,False,["romsys2.bin","rom85/romsys2.bin"]],
77 [silentRemove,False, ["romsys2.bin","romsys2.lst"]]
78 ],
79 "romsys3": \
80 ["Assemble ROMSYS3",
81 [ ASSEMBLER.assemble,False,["rom85/romsys3.asm"],{ "symNamLen":7, \
82 "globalSymbolFile": "85", \
83 "listFileName": "romsys3.lst", "referenceOpt":2}],
84 [fileDiff.testBinFile,False,["romsys3.bin","rom85/romsys3.bin"]],
85 [silentRemove,False, ["romsys3.bin","romsys3.lst"]]
86 ],
87 "rom050": \
88 ["Assemble ROM050",
89 [ ASSEMBLER.assemble,False,["rom85/rom050.asm"],{ "symNamLen":7, \
90 "globalSymbolFile": "85", \
91 "listFileName": "rom050.lst", "referenceOpt":2}],
92 [ROMCREATOR.create,False,["rom050.bin"],{"romSize":8}],
93 [fileDiff.testBinFile,False,["rom050.rom","test/rom050.rom"]],
94 [silentRemove,False, ["rom050.bin","rom050.lst"]]
95 ],
96 "rom320b": \
97 ["Assemble ROM320B",
98 [ ASSEMBLER.assemble,False,["rom85/rom320b.asm"],{ "symNamLen":7, \
99 "globalSymbolFile": "85", \
100 "listFileName": "rom320b.lst", "referenceOpt":2}],
101 [ROMCREATOR.create,False,["rom320b.bin"],{"romSize":8}],
102 [fileDiff.testBinFile,False,["rom320b.rom","rom85/rom320b.rom"]],
103 [silentRemove,False, ["rom320b.bin","rom320b.lst"]]
104 ],
105 "plotrom75": \
106 ["Assemble HP-75 plotter rom",
107 [ ASSEMBLER.assemble,False,["rom75/plotrom.asm"],{ "symNamLen":7, \
108 "globalSymbolFile": "75", \
109 "listFileName": "plotrom.lst", "referenceOpt":2}],
110 [ROMCREATOR.create,False,["plotrom.bin"],{"romSize":8}],
111 [fileDiff.testBinFile,False,["plotrom.rom","rom75/plotrom.bin"]],
112 [silentRemove,False, ["plotrom.bin","plotrom.lst"]]
113 ],
114 "missing": \
115 ["Assemble missing statements",
116 [ ASSEMBLER.assemble,False,["test/missing.asm"],{ \
117 "globalSymbolFile": "85", "extendedChecks":True, \
118 "listFileName": "missing.lst"}],
119 [fileDiff.testBinFile,False,["missing.bin","test/missing.bin"]],
120 [fileDiff.testAscFile,False,["missing.lst","test/missing.lst"]],
121 [silentRemove,False, ["missing.bin","missing.lst"]]
122 ],
123 "ftoc":\
124 ["Assemble HP-85 lex file ftoc.asm",
125 [ ASSEMBLER.assemble,False,["lex85/ftoc.asm"]],
126 [LIFCREATOR.create,False,["ftoc.bin"],{"machine":"85",\
127 "lifFileName":"FTOCB","lexOnly":True}],
128 [LIFCREATOR.create,False,["ftoc.bin"],{"machine":"85",\
129 "lifFileName":"FTOCB","lexOnly":False}],
130 [fileDiff.testBinFile,False,["ftoc.bin","test/ftoc.bin"]],
131 [fileDiff.testBinFile,False,["ftoc.dat","test/ftoc.dat"]],
132 [fileDiff.testBinFile,False,["ftoc.lex","test/ftoc.lex"]],
133 [silentRemove,False, ["ftoc.bin","ftoc.lex","ftoc.dat"]]
134 ],
135 "gcurs": \
136 ["Assemble HP-85 lex file gcurs.asm",
137 [ ASSEMBLER.assemble,False,["lex85/gcurs.asm"],{ "symNamLen":6, \
138 "listFileName": "gcurs.lst", "referenceOpt":2}],
139 [fileDiff.testBinFile,False,["gcurs.bin","lex85/gcurs.bin"]],
140 [silentRemove,False, ["gcurs.bin","gcurs.lst"]]
141 ],
142 "rp": \
143 ["Assemble HP-85 lex file rp.asm",
144 [ ASSEMBLER.assemble,False,["lex85/rp.asm"],{ "symNamLen":6, \
145 "listFileName": "rp.lst", "referenceOpt":2}],
146 [fileDiff.testBinFile,False,["rp.bin","lex85/rp.bin"]],
147 [silentRemove,False, ["rp.bin","rp.lst"]]
148 ],
149 "softky": \
150 ["Assemble HP-85 lex file softky.asm",
151 [ ASSEMBLER.assemble,False,["lex85/softky.asm"],{ "symNamLen":6, \
152 "listFileName": "softky.lst", "referenceOpt":2}],
153 [fileDiff.testBinFile,False,["softky.bin","lex85/softky.bin"]],
154 [silentRemove,False, ["softky.bin","softky.lst"]]
155 ],
156 "udlbin": \
157 ["Assemble HP-85 lex file udlbin.asm",
158 [ ASSEMBLER.assemble,False,["lex85/udlbin.asm"],{ "symNamLen":6, \
159 "listFileName": "udlbin.lst", "referenceOpt":2}],
160 [fileDiff.testBinFile,False,["udlbin.bin","lex85/udlbin.bin"]],
161 [silentRemove,False, ["udlbin.bin","udlbin.lst"]]
162 ],
163 "alpha": \
164 ["Assemble HP-87 lex file alpha.asm",
165 [ ASSEMBLER.assemble,False,["lex87/alpha.asm"],{ "symNamLen":8, \
166 "listFileName": "alpha.lst", "referenceOpt":2}],
167 [fileDiff.testBinFile,False,["alpha.bin","lex87/alpha.bin"]],
168 [silentRemove,False, ["alpha.bin","alpha.lst"]]
169 ],
170 "hglbin": \
171 ["Assemble HP-87 lex file hglbin.asm",
172 [ ASSEMBLER.assemble,False,["lex87/hglbin.asm"],{ "symNamLen":8, \
173 "listFileName": "hglbin.lst", "referenceOpt":2}],
174 [fileDiff.testBinFile,False,["hglbin.bin","lex87/hglbin.bin"]],
175 [silentRemove,False, ["hglbin.bin","hglbin.lst"]]
176 ],
177 "keys": \
178 ["Assemble HP-87 lex file keys.asm",
179 [ ASSEMBLER.assemble,False,["lex87/keys.asm"],{ "symNamLen":8, \
180 "listFileName": "keys.lst", "referenceOpt":2}],
181 [fileDiff.testBinFile,False,["keys.bin","lex87/keys.bin"]],
182 [silentRemove,False, ["keys.bin","keys.lst"]]
183 ],
184 "linput": \
185 ["Assemble HP-87 lex file linput.asm",
186 [ ASSEMBLER.assemble,False,["lex87/linput.asm"],{ "symNamLen":8, \
187 "listFileName": "linput.lst", "referenceOpt":2}],
188 [fileDiff.testBinFile,False,["linput.bin","lex87/linput.bin"]],
189 [silentRemove,False, ["linput.bin","linput.lst"]]
190 ],
191 "savg": \
192 ["Assemble HP-87 lex file savg.asm",
193 [ ASSEMBLER.assemble,False,["lex87/savg.asm"],{ "symNamLen":8, \
194 "listFileName": "savg.lst", "referenceOpt":2}],
195 [fileDiff.testBinFile,False,["savg.bin","lex87/savg.bin"]],
196 [silentRemove,False, ["savg.bin","savg.lst"]]
197 ],
198 "bad": \
199 ["Assemble faulty statements",
200 [ ASSEMBLER.assemble,True,["test/bad.asm"],{ \
201 "listFileName": "bad.lst", "extendedChecks":True}],
202 [fileDiff.testAscFile,False,["bad.lst","test/bad.lst"]],
203 [silentRemove,False, ["bad.lst"]]
204 ],
205 "bad2": \
206 ["Assemble faulty statements part 2",
207 [ ASSEMBLER.assemble,True,["test/bad2.asm"],{ \
208 "listFileName": "bad2.lst", "extendedChecks":True}],
209 [fileDiff.testAscFile,False,["bad2.lst","test/bad2.lst"]],
210 [silentRemove,False, ["bad2.lst"]]
211 ],
212 "bad3": \
213 ["Assemble faulty statements part 3",
214 [ ASSEMBLER.assemble,True,["test/bad3.asm"],{ \
215 "listFileName": "bad3.lst", "extendedChecks":True}],
216 [fileDiff.testAscFile,False,["bad3.lst","test/bad3.lst"]],
217 [silentRemove,False, ["bad3.lst"]]
218 ],
219 "bad4": \
220 ["Assemble faulty statements part 4",
221 [ ASSEMBLER.assemble,True,["test/bad4.asm"],{ \
222 "listFileName": "bad4.lst", "extendedChecks":True}],
223 [fileDiff.testAscFile,False,["bad4.lst","test/bad4.lst"]],
224 [silentRemove,False, ["bad4.lst"]]
225 ],
226 "jrel": \
227 ["Assemble faulty jrel statements",
228 [ ASSEMBLER.assemble,True,["test/jrel.asm"],{ \
229 "listFileName": "jrel.lst", "extendedChecks":True}],
230 [fileDiff.testAscFile,False,["jrel.lst","test/jrel.lst"]],
231 [silentRemove,False, ["jrel.lst"]]
232 ],
233 "phyconst":\
234 ["HP-75 lex file pyhconst.asm",
235 [ ASSEMBLER.assemble,False,["lex75/phyconst.asm"],{"symNamLen":7, \
236 "globalSymbolFile":"75"}],
237 [LIFCREATOR.create,False,["phyconst.bin"],{ "machine":"75",\
238 "lexOnly":True}],
239 [LIFCREATOR.create,False,["phyconst.bin"],{ "machine":"75",\
240 "lexOnly":False}],
241 [fileDiff.testBinFile,False,["phyconst.bin","test/phyconst.bin"]],
242 [fileDiff.testBinFile,False,["phyconst.lex","test/phyconst.lex"]],
243 [fileDiff.testBinFile,False,["phyconst.dat","test/phyconst.dat"]],
244 [silentRemove,False, ["phyconst.bin","phyconst.lex","phyconst.dat"]]
245 ],
246 "nonexistent":\
247 ["Assemble non existing file",
248 [ASSEMBLER.assemble,False,["nonexistend"],{},\
249 "Error opening source file"],
250 ],
251 "writeonly1":\
252 ["Assemble write only file",
253 [ASSEMBLER.assemble,False,["writeonly"],{},\
254 "Error opening source file"],
255 ],
256 "writeonly2":\
257 ["Assemble to read only binary file",
258 [ASSEMBLER.assemble,False,["rom85/rom000.asm"],{"binFileName":\
259 "readonly"},"Error opening object file"],
260 ],
261 "writeonly3":\
262 ["Assemble and write list to read only file",
263 [ASSEMBLER.assemble,False,["rom85/rom000.asm"],{"listFileName":\
264 "readonly"},"Error opening list file"],
265 [silentRemove,False, ["rom000.bin"]]
266 ],
267 "glonopyextension":\
268 ["Assemble with global symbol file that has no .py suffix",
269 [ASSEMBLER.assemble,False,["test/missing.asm"],\
270 {"globalSymbolFile":"nonexistent"},\
271 "global symbol file does not have a .py suffix"],
272 ],
273 "glononexistent":\
274 ["Assemble wth non existing global symbol file",
275 [ASSEMBLER.assemble,False,["test/missing.asm"],\
276 {"globalSymbolFile":"nonexistent.py"},\
277 "cannot open or read global symbol file"],
278 ],
279 "gloinvalid":\
280 ["Assemble wth invalid global symbol file",
281 [ASSEMBLER.assemble,False,["test/missing.asm"],\
282 {"globalSymbolFile":"test/illglo.py"},\
283 "Invalid global symbol file"],
284 ],
285 "symbols":\
286 ["Generate global symbol class file",
287 [GENERATOR.generate,False,["symbols/globals85.txt","globals85.py"]],
288 [fileDiff.testAscFile,False,["globals85.py","capasm/globals85.py"]],
289 [silentRemove,False,["globals85.py"]],
290 ],
291 "illsym":\
292 ["Faulty global symbol file",
293 [GENERATOR.generate,True,["test/illsym.txt","illsym.py"]],
294 [silentRemove,False,["illsym.py"]],
295 ],
296 "symnoex":\
297 ["Non existing global symbol file",
298 [GENERATOR.generate,False,["nonexisting.glo","illsym.py"], {}, \
299 "cannot open input file"],
300 ],
301 "empty":\
302 ["Assemble empty file",
303 [ASSEMBLER.assemble,False,["empty"],{},\
304 "Empty source file"],
305 ],
306 "cvglo":\
307 ["Convert global data file",
308 [SYMCONVERTER.convert,False,["test/GLOBAL8.DTA8x","temp.glo"]],
309 [fileDiff.testAscFile,False,["temp.glo","test/GLOBAL8.glo"]],
310 [silentRemove,False,["temp.glo"]],
311 ],
312 "cvglonoex":\
313 ["Convert non existing global data file",
314 [SYMCONVERTER.convert,False,["nonexisting","temp.glo"],{},\
315 "cannot open/read input file"],
316 ],
317 "cvasmnoex":\
318 ["Convert non existing tokenized assembler source file",
319 [ASMCONVERTER.convert,False,["nonexisting","temp.glo"],{},\
320 "cannot open/read input file"],
321 ],
322 "cvasm85":\
323 ["Convert tokenized HP-85 asm file",
324 [ASMCONVERTER.convert,False,["test/REDZER2.ASCII","temp.asm"]],
325 [fileDiff.testAscFile,False,["temp.asm","test/redzers85.asm"]],
326 [silentRemove,False,["temp.asm"]],
327 ],
328 "cvasm87":\
329 ["Convert tokenized HP-87 asm file",
330 [ASMCONVERTER.convert,False,["test/REDZERS.e014","temp.asm"]],
331 [fileDiff.testAscFile,False,["temp.asm","test/redzers87.asm"]],
332 [silentRemove,False,["temp.asm"]],
333 ],
334 "forthrom87":\
335 ["Assembling forth for HP-87",
336 [ASSEMBLER.assemble,False,["forth/GEFRT1"],{"listFileName":\
337 "GEFRT1.lis", "referenceOpt":"2","useHex":True},],
338 [ROMCREATOR.create,False,["GEFRT1.bin"],{"romSize":8}],
339 [fileDiff.testBinFile,False,["GEFRT1.rom","forth/FORTHROM87"]],
340 [silentRemove,False,["GEFRT1.bin","GEFRT1.rom","GEFRT1.lis"]],
341 ],
342#
343# ncas tests
344#
345 "ncas SYSROM":\
346 ["Assembling SYSROM for HP-75 with ncas",
347 [ NCAS.assemble,False,["rom75/SYSROM.asm"],{ "globalSymbolFile": "75", \
348 "listFileName": "SYSROM.lst", "referenceOpt":2}],
349 [fileDiff.testBinFile,False,["SYSROM.bin","rom75/SYSROM.bin"]],
350 [silentRemove,False,["SYSROM.bin","SYSROM.lis"]],
351 ],
352#
353 "ncas BASROM":\
354 ["Assembling BASROM for HP-75 with ncas",
355 [ NCAS.assemble,False,["rom75/BASROM.asm"],{ "globalSymbolFile": "75", \
356 "listFileName": "BASROM.lst", "referenceOpt":2}],
357 [fileDiff.testBinFile,False,["BASROM.bin","rom75/BASROM.bin"]],
358 [silentRemove,False,["BASROM.bin","BASROM.lis"]],
359 ],
361#
362 "ncas MELROM":\
363 ["Assembling MELROM for HP-75 with ncas",
364 [ NCAS.assemble,False,["rom75/MELROM.asm"],{ "globalSymbolFile": "75", \
365 "listFileName": "MELROM.lst", "referenceOpt":2}],
366 [fileDiff.testBinFile,False,["MELROM.bin","rom75/MELROM.bin"]],
367 [silentRemove,False,["MELROM.bin","MELROM.lis"]],
368 ],
370#
371 "ncas ALTROM":\
372 ["Assembling ALTROM for HP-75 with ncas",
373 [ NCAS.assemble,False,["rom75/ALTROM.asm"],{ "globalSymbolFile": "75", \
374 "listFileName": "ALTROM.lst", "referenceOpt":2}],
375 [fileDiff.testBinFile,False,["ALTROM.bin","rom75/ALTROM.bin"]],
376 [silentRemove,False,["ALTROM.bin","ALTROM.lis"]],
377 ],
379 "ncas riowio":\
380 ["Assembling riowio for HP-75 with ncas",
381 [ NCAS.assemble,False,["ncas/riowio.asm"],{ "globalSymbolFile": "75", \
382 "listFileName": "riowio.lst", "referenceOpt":2}],
383 [fileDiff.testBinFile,False,["riowio.bin","test/riowio.bin"]],
384 [silentRemove,False,["riowio.bin","riowio.lis"]],
385 ],
387 "ncas conditions":\
388 ["Assembling conditional assembly tests with ncas",
389 [ NCAS.assemble,False,["test/cond.asm"],{ "globalSymbolFile": "75", \
390 "listFileName": "cond.lst","referenceOpt":2, \
391 "definedFlags":["TEST2"]}],
392 [fileDiff.testBinFile,False,["cond.bin","test/cond.bin"]],
393 [silentRemove,False,["cond.bin","cond.lis"]],
394 ],
396 "ncas expressions":\
397 ["Assembling expressions with ncas",
398 [ NCAS.assemble,True,["test/expression.asm"],{ \
399 "globalSymbolFile": "75", "extendedChecks":True, \
400 "listFileName": "expression.lst"}],
401 [fileDiff.testAscFile,False,["expression.lst","test/expression.lst"]],
402 [silentRemove,False,["expression.lst"]],
403 ],
404}
405#
406# execute a regression test command
407#
408# Test conditions are:
409# - the command returns the expected return value
410# or
411# - the command raises the expected capasmEror condition
412#
413# All other error conditions are not handeled!
414#
415# Returns:
416# False: command passed
417# True: command failed
418#
419def doTestCommand(command):
421 ret=False
422#
423# Method or function pointer
424#
425 method=command[0]
426#
427# expected return value
428#
429 expectedRetval=command[1]
430#
431# List of positional parameters
432#
433 if len(command)>2:
434 posParams=command[2]
435 else:
436 posParams=[]
437#
438# List of keyword parameters
439#
440 if len(command)>3:
441 varParams=command[3]
442 else:
443 varParams={}
444#
445# exception message we check against
446#
447 if len(command)>4:
448 exceptionMessage=command[4]
449 else:
450 exceptionMessage=""
451 try:
452 r=method(*posParams, **varParams)
453 ret|= not (r==expectedRetval)
454 except capasmError as e:
455 if e.msg!=exceptionMessage:
456 print("Unexpected capasm exception message: ")
457 if not exceptionMessage:
458 print(e.msg)
459 else:
460 print(e.msg+"!="+exceptionMessage)
461 ret|=True
462# except Exception as e:
463# print("Unexpected exception ")
464 return ret
465#
466# run a complete test spec
467#
468def doTest(testSpec):
469 ret=False
470#
471# get test spec and run all commands
472#
473 print(testSpec[0])
474 for command in testSpec[1:]:
475 ret|= doTestCommand(command)
476 return ret
478#
479# regression test main program
480#
481def main():
482 os.environ["CAPASMREGRESSIONTEST"]="1"
483 testCount=0
484 failedCount=0
485 testResults=[]
486 Path("writeonly").touch(mode=0o222,exist_ok=True)
487 Path("readonly").touch(mode=0o444,exist_ok=True)
488 Path("empty").touch(mode=0o666,exist_ok=True)
490 for t in CAPASM_TESTS:
491 testSpec=CAPASM_TESTS[t]
492 testDescription=testSpec[0]
493 ret=doTest(testSpec)
494 testCount+=1
495 if ret:
496 failedCount+=1
497 testResults.append([testDescription,ret])
498 print("")
499 print("Results of regression tests:")
500 for result in testResults:
501 s="Passed"
502 if result[1]:
503 s="Failed!"
504 print("{:50s} {:s}".format(result[0],s))
505 print("{:d} of {:d} tests failed".format(failedCount,testCount))
506 Path("writeonly").chmod(0o666)
507 Path("readonly").chmod(0o666)
508 silentRemove("writeonly","readonly","empty")
510if __name__ == "__main__":
511 main()