mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 07:31:38 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			62 lines
		
	
	
	
		
			2.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			62 lines
		
	
	
	
		
			2.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
"""Utilities for testing with Tkinter"""
 | 
						|
import functools
 | 
						|
 | 
						|
 | 
						|
def run_in_tk_mainloop(delay=1):
 | 
						|
    """Decorator for running a test method with a real Tk mainloop.
 | 
						|
 | 
						|
    This starts a Tk mainloop before running the test, and stops it
 | 
						|
    at the end. This is faster and more robust than the common
 | 
						|
    alternative method of calling .update() and/or .update_idletasks().
 | 
						|
 | 
						|
    Test methods using this must be written as generator functions,
 | 
						|
    using "yield" to allow the mainloop to process events and "after"
 | 
						|
    callbacks, and then continue the test from that point.
 | 
						|
 | 
						|
    The delay argument is passed into root.after(...) calls as the number
 | 
						|
    of ms to wait before passing execution back to the generator function.
 | 
						|
 | 
						|
    This also assumes that the test class has a .root attribute,
 | 
						|
    which is a tkinter.Tk object.
 | 
						|
 | 
						|
    For example (from test_sidebar.py):
 | 
						|
 | 
						|
    @run_test_with_tk_mainloop()
 | 
						|
    def test_single_empty_input(self):
 | 
						|
        self.do_input('\n')
 | 
						|
        yield
 | 
						|
        self.assert_sidebar_lines_end_with(['>>>', '>>>'])
 | 
						|
    """
 | 
						|
    def decorator(test_method):
 | 
						|
        @functools.wraps(test_method)
 | 
						|
        def new_test_method(self):
 | 
						|
            test_generator = test_method(self)
 | 
						|
            root = self.root
 | 
						|
            # Exceptions raised by self.assert...() need to be raised
 | 
						|
            # outside of the after() callback in order for the test
 | 
						|
            # harness to capture them.
 | 
						|
            exception = None
 | 
						|
            def after_callback():
 | 
						|
                nonlocal exception
 | 
						|
                try:
 | 
						|
                    next(test_generator)
 | 
						|
                except StopIteration:
 | 
						|
                    root.quit()
 | 
						|
                except Exception as exc:
 | 
						|
                    exception = exc
 | 
						|
                    root.quit()
 | 
						|
                else:
 | 
						|
                    # Schedule the Tk mainloop to call this function again,
 | 
						|
                    # using a robust method of ensuring that it gets a
 | 
						|
                    # chance to process queued events before doing so.
 | 
						|
                    # See: https://stackoverflow.com/q/18499082#comment65004099_38817470
 | 
						|
                    root.after(delay, root.after_idle, after_callback)
 | 
						|
            root.after(0, root.after_idle, after_callback)
 | 
						|
            root.mainloop()
 | 
						|
 | 
						|
            if exception:
 | 
						|
                raise exception
 | 
						|
 | 
						|
        return new_test_method
 | 
						|
 | 
						|
    return decorator
 |