8
8
"""
9
9
10
10
11
- import os , subprocess , struct , enum , sys
11
+ import os , subprocess , struct , enum , sys , functools , inspect
12
12
13
13
14
14
BUILD_DIR = "build"
21
21
TESTS_ERRORED = 0
22
22
23
23
24
+ def infile (name ):
25
+ global DATA_DIR
26
+ return DATA_DIR + "/" + name + ".sq"
27
+
28
+
29
+ def outfile (name ):
30
+ global BUILD_DIR
31
+ return BUILD_DIR + "/" + name + ".x"
32
+
33
+
24
34
def build (input_file , output_file ):
25
35
""" Takes in a SUBLEQ filename as input and invokes the assembler on it,
26
36
producing an output binary of similar name in the build directory. The
@@ -29,6 +39,8 @@ def build(input_file, output_file):
29
39
binary. If an error occurred, then the output value is the message output
30
40
by the assembler.
31
41
"""
42
+ global ASSEMBLER
43
+
32
44
result = subprocess .run ([ ASSEMBLER ,
33
45
input_file ,
34
46
output_file
@@ -39,73 +51,182 @@ def build(input_file, output_file):
39
51
return result .stdout , result .returncode
40
52
41
53
42
- ##
54
+ def unpack (file ):
55
+ data = None
56
+
57
+ with open (file , "rb" ) as f :
58
+ data = [ i [0 ] for i in struct .iter_unpack ("i" , f .read ()) ]
59
+
60
+ return data
61
+
62
+
63
+ class TestResult ():
64
+
65
+ def __init__ (self , name , success , messages = []):
66
+ self .test = name
67
+ self .success = success
68
+ self .messages = messages
69
+
70
+ def report (self ):
71
+ status = "PASSED" if self .success else "FAILED"
72
+
73
+ message = f"==> Test \" { self .test } \" { status } "
74
+
75
+ if self .success is not True :
76
+ def prepend_tab (string ):
77
+ return "\t " + string
78
+
79
+ message = message + "\n " + "\n " .join (map (prepend_tab , self .messages ))
80
+
81
+ print (message )
82
+
83
+
84
+ class TestAssertion ():
85
+
86
+ def __init__ (self , expected , actual , success , message ):
87
+ self .expected = expected
88
+ self .actual = actual
89
+ self .success = success
90
+ self .message = message
91
+
92
+ def report (self ):
93
+ status = "Failed to assert" if not self .success else "Asserted"
94
+ return f"{ status } that { self .message } "
95
+
96
+
97
+ class Test ():
98
+
99
+ def __init__ (self , name , test ):
100
+ self .name = name
101
+ self ._test = test
102
+ self ._asserts = []
103
+ self ._errored = False
104
+
105
+ def run (self ):
106
+ self ._test (self )
107
+
108
+ # Get all the messages from the assertions that failed.
109
+ messages = [ a .report () for a in self ._asserts if not a .success ]
110
+
111
+ passing = len (messages ) == 0
112
+
113
+ return TestResult (self .name , passing and not self ._errored , messages )
114
+
115
+ def error (self , message ):
116
+ print (f"==> TEST ERROR: \t { message } " )
117
+
118
+ def is_equal (self , expected , actual ):
119
+ self ._asserts .append (TestAssertion (
120
+ expected ,
121
+ actual ,
122
+ expected == actual ,
123
+ f"{ expected } == { actual } "
124
+ ))
125
+
126
+
127
+ class Tester ():
128
+
129
+ def __init__ (self ):
130
+ self ._tests = []
131
+
132
+ def add_test (self , test ):
133
+ self ._tests .append (Test (test .__name__ , test ))
134
+
135
+ def run (self ):
136
+ results = []
137
+
138
+ for test in self ._tests :
139
+ result = test .run ()
140
+ result .report ()
141
+
142
+ results .append (result )
143
+
144
+ failing_tests = len ([ r for r in results if r .success is False ])
145
+
146
+ if failing_tests > 0 :
147
+ passing_tests = len (self ._tests ) - failing_tests
148
+ print (f"\n \t { passing_tests } / { len (self ._tests )} tests passing." )
149
+
150
+ else :
151
+ print ("\n \t All tests passing." )
152
+
153
+
154
+
155
+ tester = Tester ()
156
+
157
+
43
158
## ASSEMBLER TESTS
44
- ##
45
159
46
- # TODO[joe] Create tests that confirm assembler error messages?
47
- # These would include usage errors and syntax errors. This functionality would
48
- # be desirable at some point, when we get around to implementing syntax error
49
- # checking. However, this is sufficient for now.
50
- assembler_tests = {
51
- 'basic' : [ 0 , 0 , - 1 ],
52
- 'next_address' : [ 1 , 2 , 3 ],
53
- 'complex' : [ 0 , 1 , 3 , 0 , 1 , 6 , 0 , 0 , - 1 ],
54
- 'two_address' : [ 0 , 1 , 3 ],
55
- 'one_address' : [ 1 , 1 , 3 ],
56
- }
160
+ @tester .add_test
161
+ def basic (test ):
162
+ _ , returncode = build (infile (test .name ), outfile (test .name ))
57
163
164
+ if returncode :
165
+ test .error (f"Build for { outfile (self )} exited with code { returncode } " )
58
166
59
- for test , expected_result in assembler_tests .items ():
60
- test_file = DATA_DIR + "\\ " + test + ".sq"
61
- output_file = test_file .replace (".sq" , ".x" ).replace (DATA_DIR , BUILD_DIR )
167
+ test .is_equal ([ 0 , 0 , - 1 ], unpack (outfile (test .name )))
62
168
63
- output , returncode = build (test_file , output_file )
169
+ @tester .add_test
170
+ def next_address (test ):
171
+ _ , returncode = build (infile (test .name ), outfile (test .name ))
64
172
65
173
if returncode :
66
- if output :
67
- print (output .decode ("utf-8" ))
174
+ test .error (f"Build for { outfile (self )} exited with code { returncode } " )
68
175
69
- print ("==> ERROR: Build for \" {}\" exited with code {}" .format (test_file ,
70
- returncode ))
176
+ test .is_equal ([ 1 , 2 , 3 ], unpack (outfile (test .name )))
71
177
72
- TESTS_ERRORED = TESTS_ERRORED + 1
73
-
74
- continue
178
+ @ tester . add_test
179
+ def two_address ( test ):
180
+ _ , returncode = build ( infile ( test . name ), outfile ( test . name ))
75
181
76
- program = []
182
+ if returncode :
183
+ test .error (f"Build for { outfile (self )} exited with code { returncode } " )
77
184
78
- with open (output_file , "rb" ) as program_file :
79
- program = [ i [0 ] for i in struct .iter_unpack ("i" , program_file .read ()) ]
185
+ test .is_equal ([ 0 , 1 , 3 ], unpack (outfile (test .name )))
80
186
81
- if not program == expected_result :
82
- print ( " \n Expected {}, got {} \n " . format ( expected_result , program ))
83
- print ( "==> FAILED: \" {} \" " . format ( test_file ))
187
+ @ tester . add_test
188
+ def one_address ( test ):
189
+ _ , returncode = build ( infile ( test . name ), outfile ( test . name ))
84
190
85
- else :
86
- print ( "==> PASSED: \" {} \" " . format ( test_file ) )
191
+ if returncode :
192
+ test . error ( f"Build for { outfile ( self ) } exited with code { returncode } " )
87
193
88
- TESTS_PASSED = TESTS_PASSED + 1
194
+ test . is_equal ([ 1 , 1 , 3 ], unpack ( outfile ( test . name )))
89
195
196
+ @tester .add_test
197
+ def label (test ):
198
+ _ , returncode = build (infile (test .name ), outfile (test .name ))
90
199
91
- ##
92
- ## EMULATOR TESTS
93
- ##
200
+ if returncode :
201
+ test .error (f"Build for { outfile (self )} exited with code { returncode } " )
202
+
203
+ test .is_equal ([ 0 , 0 , - 1 ], unpack (outfile (test .name )))
94
204
95
- emulator_tests = {}
205
+ @tester .add_test
206
+ def identifier (test ):
207
+ _ , returncode = build (infile (test .name ), outfile (test .name ))
96
208
209
+ if returncode :
210
+ test .error (f"Build for { outfile (self )} exited with code { returncode } " )
211
+
212
+ test .is_equal ([ 0 , 0 , - 1 ], unpack (outfile (test .name )))
97
213
98
- ##
99
- ## Test Statistics
100
- ##
214
+ @tester .add_test
215
+ def complex (test ):
216
+ _ , returncode = build (infile (test .name ), outfile (test .name ))
217
+
218
+ if returncode :
219
+ test .error (f"Build for { outfile (self )} exited with code { returncode } " )
101
220
102
- TOTAL_TESTS = len (assembler_tests ) + len (emulator_tests )
221
+ test .is_equal ([ 0 , 1 , 3 , 0 , 1 , 6 , 0 , 0 , - 1 ], unpack (outfile (test .name )))
222
+
223
+
224
+ ## EMULATOR TESTS
225
+ # todo(jrm): Write the emulator tests
103
226
104
- # Total tests passed vs total tests run.
105
- print ("\n {} / {} tests passed" .format (TESTS_PASSED ,
106
- len (assembler_tests )+ len (emulator_tests )))
107
227
108
- # Total tests that errored.
109
- print ( " {} tests errored" . format ( TESTS_ERRORED ) )
228
+ # Run the tests
229
+ tester . run ( )
110
230
111
- sys .exit (TOTAL_TESTS - TESTS_PASSED )
231
+ # todo(jrm) have tester.run() return the number of tests failed.
232
+ # sys.exit(TOTAL_TESTS - TESTS_PASSED)
0 commit comments