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