1  import functools
       2  import unittest
       3  import tkinter
       4  import enum
       5  from test import support
       6  from test.test_tkinter.support import AbstractTkTest, AbstractDefaultRootTest
       7  
       8  support.requires('gui')
       9  
      10  class ESC[4;38;5;81mMiscTest(ESC[4;38;5;149mAbstractTkTest, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
      11  
      12      def test_all(self):
      13          self.assertIn("Widget", tkinter.__all__)
      14          # Check that variables from tkinter.constants are also in tkinter.__all__
      15          self.assertIn("CASCADE", tkinter.__all__)
      16          self.assertIsNotNone(tkinter.CASCADE)
      17          # Check that sys, re, and constants are not in tkinter.__all__
      18          self.assertNotIn("re", tkinter.__all__)
      19          self.assertNotIn("sys", tkinter.__all__)
      20          self.assertNotIn("constants", tkinter.__all__)
      21          # Check that an underscored functions is not in tkinter.__all__
      22          self.assertNotIn("_tkerror", tkinter.__all__)
      23          # Check that wantobjects is not in tkinter.__all__
      24          self.assertNotIn("wantobjects", tkinter.__all__)
      25  
      26      def test_repr(self):
      27          t = tkinter.Toplevel(self.root, name='top')
      28          f = tkinter.Frame(t, name='child')
      29          self.assertEqual(repr(f), '<tkinter.Frame object .top.child>')
      30  
      31      def test_generated_names(self):
      32          t = tkinter.Toplevel(self.root)
      33          f = tkinter.Frame(t)
      34          f2 = tkinter.Frame(t)
      35          b = tkinter.Button(f2)
      36          for name in str(b).split('.'):
      37              self.assertFalse(name.isidentifier(), msg=repr(name))
      38  
      39      def test_tk_setPalette(self):
      40          root = self.root
      41          root.tk_setPalette('black')
      42          self.assertEqual(root['background'], 'black')
      43          root.tk_setPalette('white')
      44          self.assertEqual(root['background'], 'white')
      45          self.assertRaisesRegex(tkinter.TclError,
      46                  '^unknown color name "spam"$',
      47                  root.tk_setPalette, 'spam')
      48  
      49          root.tk_setPalette(background='black')
      50          self.assertEqual(root['background'], 'black')
      51          root.tk_setPalette(background='blue', highlightColor='yellow')
      52          self.assertEqual(root['background'], 'blue')
      53          self.assertEqual(root['highlightcolor'], 'yellow')
      54          root.tk_setPalette(background='yellow', highlightColor='blue')
      55          self.assertEqual(root['background'], 'yellow')
      56          self.assertEqual(root['highlightcolor'], 'blue')
      57          self.assertRaisesRegex(tkinter.TclError,
      58                  '^unknown color name "spam"$',
      59                  root.tk_setPalette, background='spam')
      60          self.assertRaisesRegex(tkinter.TclError,
      61                  '^must specify a background color$',
      62                  root.tk_setPalette, spam='white')
      63          self.assertRaisesRegex(tkinter.TclError,
      64                  '^must specify a background color$',
      65                  root.tk_setPalette, highlightColor='blue')
      66  
      67      def test_after(self):
      68          root = self.root
      69  
      70          def callback(start=0, step=1):
      71              nonlocal count
      72              count = start + step
      73  
      74          # Without function, sleeps for ms.
      75          self.assertIsNone(root.after(1))
      76  
      77          # Set up with callback with no args.
      78          count = 0
      79          timer1 = root.after(0, callback)
      80          self.assertIn(timer1, root.tk.call('after', 'info'))
      81          (script, _) = root.tk.splitlist(root.tk.call('after', 'info', timer1))
      82          root.update()  # Process all pending events.
      83          self.assertEqual(count, 1)
      84          with self.assertRaises(tkinter.TclError):
      85              root.tk.call(script)
      86  
      87          # Set up with callback with args.
      88          count = 0
      89          timer1 = root.after(0, callback, 42, 11)
      90          root.update()  # Process all pending events.
      91          self.assertEqual(count, 53)
      92  
      93          # Cancel before called.
      94          timer1 = root.after(1000, callback)
      95          self.assertIn(timer1, root.tk.call('after', 'info'))
      96          (script, _) = root.tk.splitlist(root.tk.call('after', 'info', timer1))
      97          root.after_cancel(timer1)  # Cancel this event.
      98          self.assertEqual(count, 53)
      99          with self.assertRaises(tkinter.TclError):
     100              root.tk.call(script)
     101  
     102          # Call with a callable class
     103          count = 0
     104          timer1 = root.after(0, functools.partial(callback, 42, 11))
     105          root.update()  # Process all pending events.
     106          self.assertEqual(count, 53)
     107  
     108      def test_after_idle(self):
     109          root = self.root
     110  
     111          def callback(start=0, step=1):
     112              nonlocal count
     113              count = start + step
     114  
     115          # Set up with callback with no args.
     116          count = 0
     117          idle1 = root.after_idle(callback)
     118          self.assertIn(idle1, root.tk.call('after', 'info'))
     119          (script, _) = root.tk.splitlist(root.tk.call('after', 'info', idle1))
     120          root.update_idletasks()  # Process all pending events.
     121          self.assertEqual(count, 1)
     122          with self.assertRaises(tkinter.TclError):
     123              root.tk.call(script)
     124  
     125          # Set up with callback with args.
     126          count = 0
     127          idle1 = root.after_idle(callback, 42, 11)
     128          root.update_idletasks()  # Process all pending events.
     129          self.assertEqual(count, 53)
     130  
     131          # Cancel before called.
     132          idle1 = root.after_idle(callback)
     133          self.assertIn(idle1, root.tk.call('after', 'info'))
     134          (script, _) = root.tk.splitlist(root.tk.call('after', 'info', idle1))
     135          root.after_cancel(idle1)  # Cancel this event.
     136          self.assertEqual(count, 53)
     137          with self.assertRaises(tkinter.TclError):
     138              root.tk.call(script)
     139  
     140      def test_after_cancel(self):
     141          root = self.root
     142  
     143          def callback():
     144              nonlocal count
     145              count += 1
     146  
     147          timer1 = root.after(5000, callback)
     148          idle1 = root.after_idle(callback)
     149  
     150          # No value for id raises a ValueError.
     151          with self.assertRaises(ValueError):
     152              root.after_cancel(None)
     153  
     154          # Cancel timer event.
     155          count = 0
     156          (script, _) = root.tk.splitlist(root.tk.call('after', 'info', timer1))
     157          root.tk.call(script)
     158          self.assertEqual(count, 1)
     159          root.after_cancel(timer1)
     160          with self.assertRaises(tkinter.TclError):
     161              root.tk.call(script)
     162          self.assertEqual(count, 1)
     163          with self.assertRaises(tkinter.TclError):
     164              root.tk.call('after', 'info', timer1)
     165  
     166          # Cancel same event - nothing happens.
     167          root.after_cancel(timer1)
     168  
     169          # Cancel idle event.
     170          count = 0
     171          (script, _) = root.tk.splitlist(root.tk.call('after', 'info', idle1))
     172          root.tk.call(script)
     173          self.assertEqual(count, 1)
     174          root.after_cancel(idle1)
     175          with self.assertRaises(tkinter.TclError):
     176              root.tk.call(script)
     177          self.assertEqual(count, 1)
     178          with self.assertRaises(tkinter.TclError):
     179              root.tk.call('after', 'info', idle1)
     180  
     181      def test_clipboard(self):
     182          root = self.root
     183          root.clipboard_clear()
     184          root.clipboard_append('Ùñî')
     185          self.assertEqual(root.clipboard_get(), 'Ùñî')
     186          root.clipboard_append('çōđě')
     187          self.assertEqual(root.clipboard_get(), 'Ùñîçōđě')
     188          root.clipboard_clear()
     189          with self.assertRaises(tkinter.TclError):
     190              root.clipboard_get()
     191  
     192      def test_clipboard_astral(self):
     193          root = self.root
     194          root.clipboard_clear()
     195          root.clipboard_append('𝔘𝔫𝔦')
     196          self.assertEqual(root.clipboard_get(), '𝔘𝔫𝔦')
     197          root.clipboard_append('𝔠𝔬𝔡𝔢')
     198          self.assertEqual(root.clipboard_get(), '𝔘𝔫𝔦𝔠𝔬𝔡𝔢')
     199          root.clipboard_clear()
     200          with self.assertRaises(tkinter.TclError):
     201              root.clipboard_get()
     202  
     203      def test_winfo_rgb(self):
     204  
     205          def assertApprox(col1, col2):
     206              # A small amount of flexibility is required (bpo-45496)
     207              # 33 is ~0.05% of 65535, which is a reasonable margin
     208              for col1_channel, col2_channel in zip(col1, col2):
     209                  self.assertAlmostEqual(col1_channel, col2_channel, delta=33)
     210  
     211          root = self.root
     212          rgb = root.winfo_rgb
     213  
     214          # Color name.
     215          self.assertEqual(rgb('red'), (65535, 0, 0))
     216          self.assertEqual(rgb('dark slate blue'), (18504, 15677, 35723))
     217          # #RGB - extends each 4-bit hex value to be 16-bit.
     218          self.assertEqual(rgb('#F0F'), (0xFFFF, 0x0000, 0xFFFF))
     219          # #RRGGBB - extends each 8-bit hex value to be 16-bit.
     220          assertApprox(rgb('#4a3c8c'), (0x4a4a, 0x3c3c, 0x8c8c))
     221          # #RRRRGGGGBBBB
     222          assertApprox(rgb('#dede14143939'), (0xdede, 0x1414, 0x3939))
     223          # Invalid string.
     224          with self.assertRaises(tkinter.TclError):
     225              rgb('#123456789a')
     226          # RGB triplet is invalid input.
     227          with self.assertRaises(tkinter.TclError):
     228              rgb((111, 78, 55))
     229  
     230      def test_event_repr_defaults(self):
     231          e = tkinter.Event()
     232          e.serial = 12345
     233          e.num = '??'
     234          e.height = '??'
     235          e.keycode = '??'
     236          e.state = 0
     237          e.time = 123456789
     238          e.width = '??'
     239          e.x = '??'
     240          e.y = '??'
     241          e.char = ''
     242          e.keysym = '??'
     243          e.keysym_num = '??'
     244          e.type = '100'
     245          e.widget = '??'
     246          e.x_root = '??'
     247          e.y_root = '??'
     248          e.delta = 0
     249          self.assertEqual(repr(e), '<100 event>')
     250  
     251      def test_event_repr(self):
     252          e = tkinter.Event()
     253          e.serial = 12345
     254          e.num = 3
     255          e.focus = True
     256          e.height = 200
     257          e.keycode = 65
     258          e.state = 0x30405
     259          e.time = 123456789
     260          e.width = 300
     261          e.x = 10
     262          e.y = 20
     263          e.char = 'A'
     264          e.send_event = True
     265          e.keysym = 'Key-A'
     266          e.keysym_num = ord('A')
     267          e.type = tkinter.EventType.Configure
     268          e.widget = '.text'
     269          e.x_root = 1010
     270          e.y_root = 1020
     271          e.delta = -1
     272          self.assertEqual(repr(e),
     273                           "<Configure event send_event=True"
     274                           " state=Shift|Control|Button3|0x30000"
     275                           " keysym=Key-A keycode=65 char='A'"
     276                           " num=3 delta=-1 focus=True"
     277                           " x=10 y=20 width=300 height=200>")
     278  
     279      def test_eventtype_enum(self):
     280          class ESC[4;38;5;81mCheckedEventType(ESC[4;38;5;149menumESC[4;38;5;149m.ESC[4;38;5;149mStrEnum):
     281              KeyPress = '2'
     282              Key = KeyPress
     283              KeyRelease = '3'
     284              ButtonPress = '4'
     285              Button = ButtonPress
     286              ButtonRelease = '5'
     287              Motion = '6'
     288              Enter = '7'
     289              Leave = '8'
     290              FocusIn = '9'
     291              FocusOut = '10'
     292              Keymap = '11'           # undocumented
     293              Expose = '12'
     294              GraphicsExpose = '13'   # undocumented
     295              NoExpose = '14'         # undocumented
     296              Visibility = '15'
     297              Create = '16'
     298              Destroy = '17'
     299              Unmap = '18'
     300              Map = '19'
     301              MapRequest = '20'
     302              Reparent = '21'
     303              Configure = '22'
     304              ConfigureRequest = '23'
     305              Gravity = '24'
     306              ResizeRequest = '25'
     307              Circulate = '26'
     308              CirculateRequest = '27'
     309              Property = '28'
     310              SelectionClear = '29'   # undocumented
     311              SelectionRequest = '30' # undocumented
     312              Selection = '31'        # undocumented
     313              Colormap = '32'
     314              ClientMessage = '33'    # undocumented
     315              Mapping = '34'          # undocumented
     316              VirtualEvent = '35'     # undocumented
     317              Activate = '36'
     318              Deactivate = '37'
     319              MouseWheel = '38'
     320          enum._test_simple_enum(CheckedEventType, tkinter.EventType)
     321  
     322      def test_getboolean(self):
     323          for v in 'true', 'yes', 'on', '1', 't', 'y', 1, True:
     324              self.assertIs(self.root.getboolean(v), True)
     325          for v in 'false', 'no', 'off', '0', 'f', 'n', 0, False:
     326              self.assertIs(self.root.getboolean(v), False)
     327          self.assertRaises(ValueError, self.root.getboolean, 'yea')
     328          self.assertRaises(ValueError, self.root.getboolean, '')
     329          self.assertRaises(TypeError, self.root.getboolean, None)
     330          self.assertRaises(TypeError, self.root.getboolean, ())
     331  
     332      def test_mainloop(self):
     333          log = []
     334          def callback():
     335              log.append(1)
     336              self.root.after(100, self.root.quit)
     337          self.root.after(100, callback)
     338          self.root.mainloop(1)
     339          self.assertEqual(log, [])
     340          self.root.mainloop(0)
     341          self.assertEqual(log, [1])
     342          self.assertTrue(self.root.winfo_exists())
     343  
     344      def test_info_patchlevel(self):
     345          vi = self.root.info_patchlevel()
     346          f = tkinter.Frame(self.root)
     347          self.assertEqual(f.info_patchlevel(), vi)
     348          # The following is almost a copy of tests for sys.version_info.
     349          self.assertIsInstance(vi[:], tuple)
     350          self.assertEqual(len(vi), 5)
     351          self.assertIsInstance(vi[0], int)
     352          self.assertIsInstance(vi[1], int)
     353          self.assertIsInstance(vi[2], int)
     354          self.assertIn(vi[3], ("alpha", "beta", "candidate", "final"))
     355          self.assertIsInstance(vi[4], int)
     356          self.assertIsInstance(vi.major, int)
     357          self.assertIsInstance(vi.minor, int)
     358          self.assertIsInstance(vi.micro, int)
     359          self.assertIn(vi.releaselevel, ("alpha", "beta", "final"))
     360          self.assertIsInstance(vi.serial, int)
     361          self.assertEqual(vi[0], vi.major)
     362          self.assertEqual(vi[1], vi.minor)
     363          self.assertEqual(vi[2], vi.micro)
     364          self.assertEqual(vi[3], vi.releaselevel)
     365          self.assertEqual(vi[4], vi.serial)
     366          self.assertTrue(vi > (1,0,0))
     367          if vi.releaselevel == 'final':
     368              self.assertEqual(vi.serial, 0)
     369          else:
     370              self.assertEqual(vi.micro, 0)
     371          self.assertTrue(str(vi).startswith(f'{vi.major}.{vi.minor}'))
     372  
     373  
     374  class ESC[4;38;5;81mDefaultRootTest(ESC[4;38;5;149mAbstractDefaultRootTest, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     375  
     376      def test_default_root(self):
     377          self.assertIs(tkinter._support_default_root, True)
     378          self.assertIsNone(tkinter._default_root)
     379          root = tkinter.Tk()
     380          root2 = tkinter.Tk()
     381          root3 = tkinter.Tk()
     382          self.assertIs(tkinter._default_root, root)
     383          root2.destroy()
     384          self.assertIs(tkinter._default_root, root)
     385          root.destroy()
     386          self.assertIsNone(tkinter._default_root)
     387          root3.destroy()
     388          self.assertIsNone(tkinter._default_root)
     389  
     390      def test_no_default_root(self):
     391          self.assertIs(tkinter._support_default_root, True)
     392          self.assertIsNone(tkinter._default_root)
     393          root = tkinter.Tk()
     394          self.assertIs(tkinter._default_root, root)
     395          tkinter.NoDefaultRoot()
     396          self.assertIs(tkinter._support_default_root, False)
     397          self.assertFalse(hasattr(tkinter, '_default_root'))
     398          # repeated call is no-op
     399          tkinter.NoDefaultRoot()
     400          self.assertIs(tkinter._support_default_root, False)
     401          self.assertFalse(hasattr(tkinter, '_default_root'))
     402          root.destroy()
     403          self.assertIs(tkinter._support_default_root, False)
     404          self.assertFalse(hasattr(tkinter, '_default_root'))
     405          root = tkinter.Tk()
     406          self.assertIs(tkinter._support_default_root, False)
     407          self.assertFalse(hasattr(tkinter, '_default_root'))
     408          root.destroy()
     409  
     410      def test_getboolean(self):
     411          self.assertRaises(RuntimeError, tkinter.getboolean, '1')
     412          root = tkinter.Tk()
     413          self.assertIs(tkinter.getboolean('1'), True)
     414          self.assertRaises(ValueError, tkinter.getboolean, 'yea')
     415          root.destroy()
     416          tkinter.NoDefaultRoot()
     417          self.assertRaises(RuntimeError, tkinter.getboolean, '1')
     418  
     419      def test_mainloop(self):
     420          self.assertRaises(RuntimeError, tkinter.mainloop)
     421          root = tkinter.Tk()
     422          root.after_idle(root.quit)
     423          tkinter.mainloop()
     424          root.destroy()
     425          tkinter.NoDefaultRoot()
     426          self.assertRaises(RuntimeError, tkinter.mainloop)
     427  
     428  
     429  if __name__ == "__main__":
     430      unittest.main()