1import pickle 2import unittest 3from test import test_support as support 4 5turtle = support.import_module('turtle') 6Vec2D = turtle.Vec2D 7 8test_config = """\ 9width = 0.75 10height = 0.8 11canvwidth = 500 12canvheight = 200 13leftright = 100 14topbottom = 100 15mode = world 16colormode = 255 17delay = 100 18undobuffersize = 10000 19shape = circle 20pencolor = red 21fillcolor = blue 22resizemode = auto 23visible = None 24language = english 25exampleturtle = turtle 26examplescreen = screen 27title = Python Turtle Graphics 28using_IDLE = '' 29""" 30 31test_config_two = """\ 32# Comments! 33# Testing comments! 34pencolor = red 35fillcolor = blue 36visible = False 37language = english 38# Some more 39# comments 40using_IDLE = False 41""" 42 43invalid_test_config = """ 44pencolor = red 45fillcolor: blue 46visible = False 47""" 48 49 50class TurtleConfigTest(unittest.TestCase): 51 52 def get_cfg_file(self, cfg_str): 53 self.addCleanup(support.unlink, support.TESTFN) 54 with open(support.TESTFN, 'w') as f: 55 f.write(cfg_str) 56 return support.TESTFN 57 58 def test_config_dict(self): 59 60 cfg_name = self.get_cfg_file(test_config) 61 parsed_cfg = turtle.config_dict(cfg_name) 62 63 expected = { 64 'width' : 0.75, 65 'height' : 0.8, 66 'canvwidth' : 500, 67 'canvheight': 200, 68 'leftright': 100, 69 'topbottom': 100, 70 'mode': 'world', 71 'colormode': 255, 72 'delay': 100, 73 'undobuffersize': 10000, 74 'shape': 'circle', 75 'pencolor' : 'red', 76 'fillcolor' : 'blue', 77 'resizemode' : 'auto', 78 'visible' : None, 79 'language': 'english', 80 'exampleturtle': 'turtle', 81 'examplescreen': 'screen', 82 'title': 'Python Turtle Graphics', 83 'using_IDLE': '', 84 } 85 86 self.assertEqual(parsed_cfg, expected) 87 88 def test_partial_config_dict_with_commments(self): 89 90 cfg_name = self.get_cfg_file(test_config_two) 91 parsed_cfg = turtle.config_dict(cfg_name) 92 93 expected = { 94 'pencolor': 'red', 95 'fillcolor': 'blue', 96 'visible': False, 97 'language': 'english', 98 'using_IDLE': False, 99 } 100 101 self.assertEqual(parsed_cfg, expected) 102 103 def test_config_dict_invalid(self): 104 105 cfg_name = self.get_cfg_file(invalid_test_config) 106 107 with support.captured_stdout() as stdout: 108 parsed_cfg = turtle.config_dict(cfg_name) 109 110 err_msg = stdout.getvalue() 111 112 self.assertIn('Bad line in config-file ', err_msg) 113 self.assertIn('fillcolor: blue', err_msg) 114 115 self.assertEqual(parsed_cfg, { 116 'pencolor': 'red', 117 'visible': False, 118 }) 119 120 121class VectorComparisonMixin: 122 123 def assertVectorsAlmostEqual(self, vec1, vec2): 124 if len(vec1) != len(vec2): 125 self.fail("Tuples are not of equal size") 126 for idx, (i, j) in enumerate(zip(vec1, vec2)): 127 self.assertAlmostEqual( 128 i, j, msg='values at index {} do not match'.format(idx)) 129 130 131class TestVec2D(VectorComparisonMixin, unittest.TestCase): 132 133 def test_constructor(self): 134 vec = Vec2D(0.5, 2) 135 self.assertEqual(vec[0], 0.5) 136 self.assertEqual(vec[1], 2) 137 self.assertIsInstance(vec, Vec2D) 138 139 self.assertRaises(TypeError, Vec2D) 140 self.assertRaises(TypeError, Vec2D, 0) 141 self.assertRaises(TypeError, Vec2D, (0, 1)) 142 self.assertRaises(TypeError, Vec2D, vec) 143 self.assertRaises(TypeError, Vec2D, 0, 1, 2) 144 145 def test_repr(self): 146 vec = Vec2D(0.567, 1.234) 147 self.assertEqual(repr(vec), '(0.57,1.23)') 148 149 def test_equality(self): 150 vec1 = Vec2D(0, 1) 151 vec2 = Vec2D(0.0, 1) 152 vec3 = Vec2D(42, 1) 153 self.assertEqual(vec1, vec2) 154 self.assertEqual(vec1, tuple(vec1)) 155 self.assertEqual(tuple(vec1), vec1) 156 self.assertNotEqual(vec1, vec3) 157 self.assertNotEqual(vec2, vec3) 158 159 def test_pickling(self): 160 vec = Vec2D(0.5, 2) 161 for proto in range(pickle.HIGHEST_PROTOCOL + 1): 162 pickled = pickle.dumps(vec, protocol=proto) 163 unpickled = pickle.loads(pickled) 164 self.assertEqual(unpickled, vec) 165 self.assertIsInstance(unpickled, Vec2D) 166 167 def _assert_arithmetic_cases(self, test_cases, lambda_operator): 168 for test_case in test_cases: 169 ((first, second), expected) = test_case 170 171 op1 = Vec2D(*first) 172 op2 = Vec2D(*second) 173 174 result = lambda_operator(op1, op2) 175 176 expected = Vec2D(*expected) 177 178 self.assertVectorsAlmostEqual(result, expected) 179 180 def test_vector_addition(self): 181 182 test_cases = [ 183 (((0, 0), (1, 1)), (1.0, 1.0)), 184 (((-1, 0), (2, 2)), (1, 2)), 185 (((1.5, 0), (1, 1)), (2.5, 1)), 186 ] 187 188 self._assert_arithmetic_cases(test_cases, lambda x, y: x + y) 189 190 def test_vector_subtraction(self): 191 192 test_cases = [ 193 (((0, 0), (1, 1)), (-1, -1)), 194 (((10.625, 0.125), (10, 0)), (0.625, 0.125)), 195 ] 196 197 self._assert_arithmetic_cases(test_cases, lambda x, y: x - y) 198 199 def test_vector_multiply(self): 200 201 vec1 = Vec2D(10, 10) 202 vec2 = Vec2D(0.5, 3) 203 answer = vec1 * vec2 204 expected = 35 205 self.assertAlmostEqual(answer, expected) 206 207 vec = Vec2D(0.5, 3) 208 answer = vec * 10 209 expected = Vec2D(5, 30) 210 self.assertVectorsAlmostEqual(answer, expected) 211 212 def test_vector_negative(self): 213 vec = Vec2D(10, -10) 214 expected = (-10, 10) 215 self.assertVectorsAlmostEqual(-vec, expected) 216 217 def test_distance(self): 218 vec = Vec2D(6, 8) 219 expected = 10 220 self.assertEqual(abs(vec), expected) 221 222 vec = Vec2D(0, 0) 223 expected = 0 224 self.assertEqual(abs(vec), expected) 225 226 vec = Vec2D(2.5, 6) 227 expected = 6.5 228 self.assertEqual(abs(vec), expected) 229 230 def test_rotate(self): 231 232 cases = [ 233 (((0, 0), 0), (0, 0)), 234 (((0, 1), 90), (-1, 0)), 235 (((0, 1), -90), (1, 0)), 236 (((1, 0), 180), (-1, 0)), 237 (((1, 0), 360), (1, 0)), 238 ] 239 240 for case in cases: 241 (vec, rot), expected = case 242 vec = Vec2D(*vec) 243 got = vec.rotate(rot) 244 self.assertVectorsAlmostEqual(got, expected) 245 246 247class TestTNavigator(VectorComparisonMixin, unittest.TestCase): 248 249 def setUp(self): 250 self.nav = turtle.TNavigator() 251 252 def test_goto(self): 253 self.nav.goto(100, -100) 254 self.assertAlmostEqual(self.nav.xcor(), 100) 255 self.assertAlmostEqual(self.nav.ycor(), -100) 256 257 def test_pos(self): 258 self.assertEqual(self.nav.pos(), self.nav._position) 259 self.nav.goto(100, -100) 260 self.assertEqual(self.nav.pos(), self.nav._position) 261 262 def test_left(self): 263 self.assertEqual(self.nav._orient, (1.0, 0)) 264 self.nav.left(90) 265 self.assertVectorsAlmostEqual(self.nav._orient, (0.0, 1.0)) 266 267 def test_right(self): 268 self.assertEqual(self.nav._orient, (1.0, 0)) 269 self.nav.right(90) 270 self.assertVectorsAlmostEqual(self.nav._orient, (0, -1.0)) 271 272 def test_reset(self): 273 self.nav.goto(100, -100) 274 self.assertAlmostEqual(self.nav.xcor(), 100) 275 self.assertAlmostEqual(self.nav.ycor(), -100) 276 self.nav.reset() 277 self.assertAlmostEqual(self.nav.xcor(), 0) 278 self.assertAlmostEqual(self.nav.ycor(), 0) 279 280 def test_forward(self): 281 self.nav.forward(150) 282 expected = Vec2D(150, 0) 283 self.assertVectorsAlmostEqual(self.nav.position(), expected) 284 285 self.nav.reset() 286 self.nav.left(90) 287 self.nav.forward(150) 288 expected = Vec2D(0, 150) 289 self.assertVectorsAlmostEqual(self.nav.position(), expected) 290 291 self.assertRaises(TypeError, self.nav.forward, 'skldjfldsk') 292 293 def test_backwards(self): 294 self.nav.back(200) 295 expected = Vec2D(-200, 0) 296 self.assertVectorsAlmostEqual(self.nav.position(), expected) 297 298 self.nav.reset() 299 self.nav.right(90) 300 self.nav.back(200) 301 expected = Vec2D(0, 200) 302 self.assertVectorsAlmostEqual(self.nav.position(), expected) 303 304 def test_distance(self): 305 self.nav.forward(100) 306 expected = 100 307 self.assertAlmostEqual(self.nav.distance(Vec2D(0,0)), expected) 308 309 def test_radians_and_degrees(self): 310 self.nav.left(90) 311 self.assertAlmostEqual(self.nav.heading(), 90) 312 self.nav.radians() 313 self.assertAlmostEqual(self.nav.heading(), 1.57079633) 314 self.nav.degrees() 315 self.assertAlmostEqual(self.nav.heading(), 90) 316 317 def test_towards(self): 318 319 coordinates = [ 320 # coordinates, expected 321 ((100, 0), 0.0), 322 ((100, 100), 45.0), 323 ((0, 100), 90.0), 324 ((-100, 100), 135.0), 325 ((-100, 0), 180.0), 326 ((-100, -100), 225.0), 327 ((0, -100), 270.0), 328 ((100, -100), 315.0), 329 ] 330 331 for (x, y), expected in coordinates: 332 self.assertEqual(self.nav.towards(x, y), expected) 333 self.assertEqual(self.nav.towards((x, y)), expected) 334 self.assertEqual(self.nav.towards(Vec2D(x, y)), expected) 335 336 def test_heading(self): 337 338 self.nav.left(90) 339 self.assertAlmostEqual(self.nav.heading(), 90) 340 self.nav.left(45) 341 self.assertAlmostEqual(self.nav.heading(), 135) 342 self.nav.right(1.6) 343 self.assertAlmostEqual(self.nav.heading(), 133.4) 344 self.assertRaises(TypeError, self.nav.right, 'sdkfjdsf') 345 self.nav.reset() 346 347 rotations = [10, 20, 170, 300] 348 result = sum(rotations) % 360 349 for num in rotations: 350 self.nav.left(num) 351 self.assertEqual(self.nav.heading(), result) 352 self.nav.reset() 353 354 result = (360-sum(rotations)) % 360 355 for num in rotations: 356 self.nav.right(num) 357 self.assertEqual(self.nav.heading(), result) 358 self.nav.reset() 359 360 rotations = [10, 20, -170, 300, -210, 34.3, -50.2, -10, -29.98, 500] 361 sum_so_far = 0 362 for num in rotations: 363 if num < 0: 364 self.nav.right(abs(num)) 365 else: 366 self.nav.left(num) 367 sum_so_far += num 368 self.assertAlmostEqual(self.nav.heading(), sum_so_far % 360) 369 370 def test_setheading(self): 371 self.nav.setheading(102.32) 372 self.assertAlmostEqual(self.nav.heading(), 102.32) 373 self.nav.setheading(-123.23) 374 self.assertAlmostEqual(self.nav.heading(), (-123.23) % 360) 375 self.nav.setheading(-1000.34) 376 self.assertAlmostEqual(self.nav.heading(), (-1000.34) % 360) 377 self.nav.setheading(300000) 378 self.assertAlmostEqual(self.nav.heading(), 300000%360) 379 380 def test_positions(self): 381 self.nav.forward(100) 382 self.nav.left(90) 383 self.nav.forward(-200) 384 self.assertVectorsAlmostEqual(self.nav.pos(), (100.0, -200.0)) 385 386 def test_setx_and_sety(self): 387 self.nav.setx(-1023.2334) 388 self.nav.sety(193323.234) 389 self.assertVectorsAlmostEqual(self.nav.pos(), (-1023.2334, 193323.234)) 390 391 def test_home(self): 392 self.nav.left(30) 393 self.nav.forward(-100000) 394 self.nav.home() 395 self.assertVectorsAlmostEqual(self.nav.pos(), (0,0)) 396 self.assertAlmostEqual(self.nav.heading(), 0) 397 398 def test_distance_method(self): 399 self.assertAlmostEqual(self.nav.distance(30, 40), 50) 400 vec = Vec2D(0.22, .001) 401 self.assertAlmostEqual(self.nav.distance(vec), 0.22000227271553355) 402 another_turtle = turtle.TNavigator() 403 another_turtle.left(90) 404 another_turtle.forward(10000) 405 self.assertAlmostEqual(self.nav.distance(another_turtle), 10000) 406 407 408class TestTPen(unittest.TestCase): 409 410 def test_pendown_and_penup(self): 411 412 tpen = turtle.TPen() 413 414 self.assertTrue(tpen.isdown()) 415 tpen.penup() 416 self.assertFalse(tpen.isdown()) 417 tpen.pendown() 418 self.assertTrue(tpen.isdown()) 419 420 def test_showturtle_hideturtle_and_isvisible(self): 421 422 tpen = turtle.TPen() 423 424 self.assertTrue(tpen.isvisible()) 425 tpen.hideturtle() 426 self.assertFalse(tpen.isvisible()) 427 tpen.showturtle() 428 self.assertTrue(tpen.isvisible()) 429 430 431def test_main(): 432 support.run_unittest(TurtleConfigTest, TestVec2D, TestTNavigator, TestTPen) 433 434if __name__ == '__main__': 435 test_main() 436