|
1
|
|
|
#!/usr/bin/env python |
|
2
|
|
|
import sys |
|
3
|
|
|
import os |
|
4
|
|
|
|
|
5
|
|
|
import unittest |
|
6
|
|
|
import random |
|
7
|
|
|
import itertools |
|
8
|
|
|
import copy |
|
9
|
|
|
import pickle |
|
10
|
|
|
import gc |
|
11
|
|
|
|
|
12
|
|
|
import tdl |
|
13
|
|
|
|
|
14
|
|
|
#ERROR_RANGE = 100 # a number to test out of bound errors |
|
15
|
|
|
WIDTH, HEIGHT = 30, 20 |
|
16
|
|
|
WINWIDTH, WINHEIGHT = 10, 10 |
|
17
|
|
|
|
|
18
|
|
|
DEFAULT_CHAR = (0x20, (0, 0, 0), (0, 0, 0)) |
|
19
|
|
|
|
|
20
|
|
|
IS_PYTHON2 = (sys.version_info[0] == 2) |
|
21
|
|
|
|
|
22
|
|
|
class TDLTemplate(unittest.TestCase): |
|
23
|
|
|
"Nearly all tests need tdl.init to be called" |
|
24
|
|
|
|
|
25
|
|
|
@classmethod |
|
26
|
|
|
def setUpClass(cls): |
|
27
|
|
|
cls.console = tdl.init(WIDTH, HEIGHT, 'TDL UnitTest', False, renderer='GLSL') |
|
28
|
|
|
# make a small window in the corner |
|
29
|
|
|
cls.window = tdl.Window(cls.console, 0, 0, WINWIDTH, WINHEIGHT) |
|
30
|
|
|
|
|
31
|
|
|
def setUp(self): |
|
32
|
|
|
tdl.event.get() |
|
33
|
|
|
self.console.set_colors((0,0,0), (0,0,0)) |
|
34
|
|
|
self.console.clear() |
|
35
|
|
|
|
|
36
|
|
|
@classmethod |
|
37
|
|
|
def tearDownClass(cls): |
|
38
|
|
|
del cls.console |
|
39
|
|
|
gc.collect() # make sure console.__del__ is called quickly |
|
40
|
|
|
|
|
41
|
|
|
def in_window(self, x, y): |
|
42
|
|
|
"returns True if this point is in the Window" |
|
43
|
|
|
return 0 <= x < WINWIDTH and 0 <= y < WINHEIGHT |
|
44
|
|
|
|
|
45
|
|
|
def randomize_console(self): |
|
46
|
|
|
"Randomize the console returning the random data" |
|
47
|
|
|
noise = [((x, y), self.get_random_character()) for x,y in self.get_drawables()] |
|
48
|
|
|
for (x, y), graphic in noise: |
|
49
|
|
|
self.console.draw_char(x, y, *graphic) |
|
50
|
|
|
return noise # [((x, y), (cg, fg, bg)), ...] |
|
51
|
|
|
|
|
52
|
|
|
def flush(self): |
|
53
|
|
|
'Pump events and refresh screen so show progress' |
|
54
|
|
|
#tdl.event.get() # no longer needed |
|
55
|
|
|
tdl.flush() |
|
56
|
|
|
|
|
57
|
|
|
def get_random_character(self): |
|
58
|
|
|
"returns a tuple with a random character and colors (ch, fg, bg)" |
|
59
|
|
|
return (random.getrandbits(8), self.get_random_color(), self.get_random_color()) |
|
60
|
|
|
|
|
61
|
|
|
def get_random_color(self): |
|
62
|
|
|
"returns a single random color" |
|
63
|
|
|
return (random.getrandbits(8), random.getrandbits(8), random.getrandbits(8)) |
|
64
|
|
|
|
|
65
|
|
|
def get_drawables(self, console=None): |
|
66
|
|
|
"""return a list of all drawable (x,y) positions |
|
67
|
|
|
defaults to self.console |
|
68
|
|
|
""" |
|
69
|
|
|
if console is None: |
|
70
|
|
|
console = self.console |
|
71
|
|
|
w, h = console.get_size() |
|
72
|
|
|
return itertools.product(range(w), range(h)) |
|
73
|
|
|
|
|
74
|
|
|
def get_undrawables(self, console=None): |
|
75
|
|
|
"""return a list of (x,y) positions that should raise errors when used |
|
76
|
|
|
positions are mostly random and will have at least one over the bounds of each side and each corner""" |
|
77
|
|
|
if console is None: |
|
78
|
|
|
console = self.console |
|
79
|
|
|
w, h = console.get_size() |
|
80
|
|
|
for y in range(-1, h+1): |
|
81
|
|
|
yield -w-1, y |
|
82
|
|
|
yield w, y |
|
83
|
|
|
for x in range(0, w): |
|
84
|
|
|
yield x, h |
|
85
|
|
|
yield x, -h-1 |
|
86
|
|
|
|
|
87
|
|
|
def compare_consoles(self, consoleA, consoleB, errorMsg='colors should be the same'): |
|
88
|
|
|
"Compare two console assuming they match and failing if they don't" |
|
89
|
|
|
self.assertEqual(consoleA.get_size(), consoleB.get_size(), 'consoles should be the same size') |
|
90
|
|
|
for x, y in self.get_drawables(consoleA): |
|
91
|
|
|
self.assertEqual(consoleA.get_char(x, y), |
|
92
|
|
|
consoleB.get_char(x, y), '%s, position: (%i, %i)' % (errorMsg, x, y)) |
|
93
|
|
|
|
|
94
|
|
|
class BasicTests(TDLTemplate): |
|
95
|
|
|
|
|
96
|
|
|
def test_clearConsole(self): |
|
97
|
|
|
self.randomize_console() |
|
98
|
|
|
_, fg, bg = self.get_random_character() |
|
99
|
|
|
ch = 0x20 # space |
|
100
|
|
|
self.console.clear(fg, bg) |
|
101
|
|
|
self.flush() |
|
102
|
|
|
for x,y in self.get_drawables(): |
|
103
|
|
|
self.assertEqual((ch, fg, bg), self.console.get_char(x, y), 'color should be changed with clear') |
|
104
|
|
|
_, fg2, bg2 = self.get_random_character() |
|
105
|
|
|
self.window.clear(fg2, bg2) |
|
106
|
|
|
self.flush() |
|
107
|
|
|
for x,y in self.get_drawables(): |
|
108
|
|
|
if self.in_window(x, y): |
|
109
|
|
|
self.assertEqual((ch, fg2, bg2), self.console.get_char(x, y), 'color in window should be changed') |
|
110
|
|
|
else: |
|
111
|
|
|
self.assertEqual((ch, fg, bg), self.console.get_char(x, y), 'color outside of window should persist') |
|
112
|
|
|
|
|
113
|
|
|
def test_cloneConsole(self): |
|
114
|
|
|
noiseData = self.randomize_console() |
|
115
|
|
|
clone = copy.copy(self.console) |
|
116
|
|
|
self.compare_consoles(self.console, clone, 'console clone should match root console') |
|
117
|
|
|
|
|
118
|
|
|
def test_pickleConsole(self): |
|
119
|
|
|
noiseData = self.randomize_console() |
|
120
|
|
|
pickled = pickle.dumps(self.console) |
|
121
|
|
|
clone = pickle.loads(pickled) |
|
122
|
|
|
self.compare_consoles(self.console, clone, 'pickled console should match root console') |
|
123
|
|
|
|
|
124
|
|
|
|
|
125
|
|
|
class DrawingTests(TDLTemplate): |
|
126
|
|
|
|
|
127
|
|
|
def test_draw_charTuples(self): |
|
128
|
|
|
"Test passing tuple colors and int characters to draw_char" |
|
129
|
|
|
record = {} |
|
130
|
|
|
for x,y in self.get_drawables(): |
|
131
|
|
|
ch, fg, bg = self.get_random_character() |
|
132
|
|
|
record[x,y] = (ch, fg, bg) |
|
133
|
|
|
self.console.draw_char(x, y, ch, fg, bg) |
|
134
|
|
|
self.assertEqual(record[x,y], self.console.get_char(x, y), 'console data should be overwritten') |
|
135
|
|
|
self.flush() # show progress |
|
136
|
|
|
|
|
137
|
|
|
for (x,y), data in record.items(): |
|
138
|
|
|
self.assertEqual(data, self.console.get_char(x, y), 'draw_char should not overwrite any other tiles') |
|
139
|
|
|
|
|
140
|
|
|
def test_draw_charWebcolor(self): |
|
141
|
|
|
"Test passing web style colors and string characters to draw_char" |
|
142
|
|
|
record = {} |
|
143
|
|
|
for x,y in self.get_drawables(): |
|
144
|
|
|
ch, fg, bg = self.get_random_character() |
|
145
|
|
|
record[x,y] = (ch, fg, bg) |
|
146
|
|
|
ch = chr(ch) |
|
147
|
|
|
fg = fg[0] << 16 | fg[1] << 8 | fg[2] # convert to a 0xRRGGBB style number |
|
148
|
|
|
bg = bg[0] << 16 | bg[1] << 8 | bg[2] |
|
149
|
|
|
self.console.draw_char(x, y, ch, fg, bg) |
|
150
|
|
|
self.assertEqual(record[x,y], self.console.get_char(x, y), 'console data should be overwritten') |
|
151
|
|
|
self.flush() # show progress |
|
152
|
|
|
for (x,y), data in record.items(): |
|
153
|
|
|
self.assertEqual(data, self.console.get_char(x, y), 'draw_char should not overwrite any other tiles') |
|
154
|
|
|
|
|
155
|
|
|
#@unittest.skipIf(not __debug__, 'python run with optimized flag, skipping an AssertionError test') |
|
156
|
|
|
#def test_draw_charErrors(self): |
|
157
|
|
|
# "test out of bounds assertion errors" |
|
158
|
|
|
# for x,y in self.get_undrawables(): |
|
159
|
|
|
# with self.assertRaisesRegexp(AssertionError, r"\(%i, %i\)" % (x, y)): |
|
160
|
|
|
# self.console.draw_char(x, y, *(self.get_random_character())) |
|
161
|
|
|
|
|
162
|
|
|
def test_draw_str(self): |
|
163
|
|
|
"""quick regression test for draw_str""" |
|
164
|
|
|
width, height = self.console.get_size() |
|
165
|
|
|
def str_check(array, string, desc): |
|
166
|
|
|
fg, bg = self.get_random_color(), self.get_random_color() |
|
167
|
|
|
self.console.clear() |
|
168
|
|
|
self.console.draw_str(0, 0, string, fg, bg) |
|
169
|
|
|
self.flush() |
|
170
|
|
|
i = 0 |
|
171
|
|
|
for y in range(height): |
|
172
|
|
|
for x in range(width): |
|
173
|
|
|
self.assertEqual(self.console.get_char(x, y), (array[i], fg, bg), |
|
174
|
|
|
'%s should be written out' % desc) |
|
175
|
|
|
i += 1 |
|
176
|
|
|
|
|
177
|
|
|
# array of numbers |
|
178
|
|
|
array = [random.getrandbits(8) for _ in range(width * height)] |
|
179
|
|
|
str_check(array, array, 'array of numbers') |
|
180
|
|
|
|
|
181
|
|
|
# array of strings |
|
182
|
|
|
#array = [random.getrandbits(8) for _ in range(width * height)] |
|
183
|
|
|
#array_str = [chr(c) for c in array] |
|
184
|
|
|
#str_check(array, array_str, 'array of characters') |
|
185
|
|
|
|
|
186
|
|
|
# standard string |
|
187
|
|
|
array = [random.getrandbits(8) for _ in range(width * height)] |
|
188
|
|
|
string = ''.join((chr(c) for c in array)) |
|
189
|
|
|
str_check(array, string, 'standatd string') |
|
190
|
|
|
|
|
191
|
|
|
# Unicode string - Python 2 |
|
192
|
|
|
if IS_PYTHON2: |
|
193
|
|
|
array = [random.getrandbits(7) for _ in range(width * height)] |
|
194
|
|
|
ucode = unicode().join((chr(c) for c in array)) |
|
195
|
|
|
str_check(array, ucode, 'Unicode string') |
|
196
|
|
|
|
|
197
|
|
|
|
|
198
|
|
|
def test_draw_strArray(self): |
|
199
|
|
|
"""strings will raise errors if they pass over the end of the console. |
|
200
|
|
|
The data will still be written however.""" |
|
201
|
|
|
width, height = self.console.get_size() |
|
202
|
|
|
for x,y in self.get_drawables(): |
|
203
|
|
|
string = [random.getrandbits(8) for _ in range(random.randint(2, 10))] |
|
204
|
|
|
fg, bg = self.get_random_color(), self.get_random_color() |
|
205
|
|
|
if len(string) > ((height - y) * width - x): # compare length of string to remaining space on the console |
|
206
|
|
|
with self.assertRaises(tdl.TDLError): # expect end of console error |
|
207
|
|
|
self.console.draw_str(x, y, string, fg, bg) |
|
208
|
|
|
else: |
|
209
|
|
|
self.console.draw_str(x, y, string, fg, bg) |
|
210
|
|
|
for ch in string: # inspect console for changes |
|
211
|
|
|
self.assertEqual(self.console.get_char(x, y), (ch, fg, bg), 'console data should be overwritten, even after an error') |
|
212
|
|
|
x += 1 |
|
213
|
|
|
if x == width: |
|
214
|
|
|
x = 0 |
|
215
|
|
|
y += 1 |
|
216
|
|
|
if y == height: |
|
217
|
|
|
break # end of console |
|
218
|
|
|
self.flush() # show progress |
|
219
|
|
|
|
|
220
|
|
|
#@unittest.skipIf(not __debug__, 'python run with optimized flag, skipping an AssertionError test') |
|
221
|
|
|
#def test_draw_strErrors(self): |
|
222
|
|
|
# "test out of bounds assertion errors" |
|
223
|
|
|
# for x,y in self.get_undrawables(): |
|
224
|
|
|
# with self.assertRaisesRegexp(AssertionError, r"\(%i, %i\)" % (x, y)): |
|
225
|
|
|
# self.console.draw_str(x, y, 'foo', self.get_random_color(), self.get_random_color()) |
|
226
|
|
|
|
|
227
|
|
|
def test_draw_rect(self): |
|
228
|
|
|
consoleCopy = tdl.Console(*(self.console.get_size())) |
|
229
|
|
|
for x,y in random.sample(list(self.get_drawables()), 20): |
|
230
|
|
|
consoleCopy.blit(self.console) # copy the console to compare untouched areas |
|
231
|
|
|
ch, fg, bg = self.get_random_character() |
|
232
|
|
|
width, height = self.console.get_size() |
|
233
|
|
|
width, height = random.randint(1, width - x), random.randint(1, height - y) |
|
234
|
|
|
self.console.draw_rect(x, y, width, height, ch, fg, bg) |
|
235
|
|
|
self.flush() # show progress |
|
236
|
|
|
for testX,testY in self.get_drawables(): |
|
237
|
|
|
if x <= testX < x + width and y <= testY < y + height: |
|
238
|
|
|
self.assertEqual(self.console.get_char(testX, testY), (ch, fg, bg), 'rectangle area should be overwritten') |
|
239
|
|
|
else: |
|
240
|
|
|
self.assertEqual(self.console.get_char(testX, testY), consoleCopy.get_char(testX, testY), 'this area should remain untouched') |
|
241
|
|
|
|
|
242
|
|
|
def test_draw_frame(self): |
|
243
|
|
|
consoleCopy = tdl.Console(*(self.console.get_size())) |
|
244
|
|
|
for x,y in random.sample(list(self.get_drawables()), 20): |
|
245
|
|
|
consoleCopy.blit(self.console) # copy the console to compare untouched areas |
|
246
|
|
|
ch, fg, bg = self.get_random_character() |
|
247
|
|
|
width, height = self.console.get_size() |
|
248
|
|
|
width, height = random.randint(1, width - x), random.randint(1, height - y) |
|
249
|
|
|
self.console.draw_frame(x, y, width, height, ch, fg, bg) |
|
250
|
|
|
self.flush() # show progress |
|
251
|
|
|
for testX,testY in self.get_drawables(): |
|
252
|
|
|
if x + 1 <= testX < x + width - 1 and y + 1 <= testY < y + height - 1: |
|
253
|
|
|
self.assertEqual(self.console.get_char(testX, testY), consoleCopy.get_char(testX, testY), 'inner frame should remain untouched') |
|
254
|
|
|
elif x <= testX < x + width and y <= testY < y + height: |
|
255
|
|
|
self.assertEqual(self.console.get_char(testX, testY), (ch, fg, bg), 'frame area should be overwritten') |
|
256
|
|
|
else: |
|
257
|
|
|
self.assertEqual(self.console.get_char(testX, testY), consoleCopy.get_char(testX, testY), 'outer frame should remain untouched') |
|
258
|
|
|
|
|
259
|
|
|
#@unittest.skipIf(not __debug__, 'python run with optimized flag, skipping an AssertionError test') |
|
260
|
|
|
#def test_draw_rectFrameErrors(self): |
|
261
|
|
|
# for x,y in self.get_drawables(): |
|
262
|
|
|
# ch, fg, bg = self.get_random_character() |
|
263
|
|
|
# width, height = self.console.get_size() |
|
264
|
|
|
# width, height = random.randint(x + width, x + width + ERROR_RANGE), random.randint(y + height, y + height + ERROR_RANGE) |
|
265
|
|
|
# with self.assertRaises(AssertionError): |
|
266
|
|
|
# self.console.draw_rect(x, y, width, height, ch, fg, bg) |
|
267
|
|
|
# with self.assertRaises(AssertionError): |
|
268
|
|
|
# self.console.draw_frame(x, y, width, height, ch, fg, bg) |
|
269
|
|
|
|
|
270
|
|
|
#@unittest.skip("Need this to be faster before unskipping") |
|
271
|
|
|
def test_scrolling(self): |
|
272
|
|
|
"""marks a spot and then scrolls the console, checks to make sure no |
|
273
|
|
|
other spots are marked, test also knows if it's out of bounds. |
|
274
|
|
|
|
|
275
|
|
|
This test is a bit slow, it could be made more efficent by marking |
|
276
|
|
|
several areas and not clearing the console every loop. |
|
277
|
|
|
""" |
|
278
|
|
|
scrollTests = set([(0, 0), (WIDTH, HEIGHT)]) # include zero and out of bounds |
|
279
|
|
|
while len(scrollTests) < 10: # add 3 more randoms |
|
280
|
|
|
scrollTests.add((random.randint(-WIDTH, WIDTH), |
|
281
|
|
|
random.randint(-HEIGHT, HEIGHT))) |
|
282
|
|
|
for sx, sy in scrollTests: |
|
283
|
|
|
noiseData = dict(self.randomize_console()) |
|
284
|
|
|
self.console.set_colors((0, 0, 0), (0, 0, 0)) |
|
285
|
|
|
self.console.scroll(sx, sy) |
|
286
|
|
|
self.flush() # show progress |
|
287
|
|
|
for x, y in self.get_drawables(): |
|
288
|
|
|
nX = x - sx |
|
289
|
|
|
nY = y - sy |
|
290
|
|
|
if (nX, nY) in noiseData: |
|
291
|
|
|
self.assertEqual(self.console.get_char(x, y), noiseData[nX, nY], 'random noise should be scrolled') |
|
292
|
|
|
else: |
|
293
|
|
|
self.assertEqual(self.console.get_char(x, y), DEFAULT_CHAR, 'scrolled away positions should be clear') |
|
294
|
|
|
|
|
295
|
|
|
|
|
296
|
|
|
def test_fps(): |
|
297
|
|
|
tdl.set_fps(0) |
|
298
|
|
|
tdl.get_fps() |
|
299
|
|
|
|
|
300
|
|
|
def suite(): |
|
301
|
|
|
loader = unittest.TestLoader() |
|
302
|
|
|
load = loader.loadTestsFromTestCase |
|
303
|
|
|
return unittest.TestSuite([load(BasicTests), load(DrawingTests)]) |
|
304
|
|
|
|
|
305
|
|
|
if __name__ == '__main__': |
|
306
|
|
|
suite = suite() |
|
307
|
|
|
unittest.TextTestRunner().run(suite) |
|
308
|
|
|
|
|
309
|
|
|
|