Completed
Push — master ( 1e7b7c...87d517 )
by Kyle
01:04
created

DrawingTests.str_check()   A

Complexity

Conditions 3

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
dl 0
loc 11
rs 9.85
c 0
b 0
f 0
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