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
|
|
|
|