def test_from_to_tcl(): image1 = teek.Image(file=SMILEY_PATH) teek.tcl_eval(None, 'proc returnArg {arg} {return $arg}') image2 = teek.tcl_call(teek.Image, 'returnArg', image1) assert image1.to_tcl() == image2.to_tcl() # no implicit copying assert image1 is not image2 # no implicit cache assert slow_content_eq_check(image1, image2)
def test_before_after_quit(): teek.tcl_eval(None, '') # make sure that a Tcl interpreter is running asd = [] teek.before_quit.connect(asd.append, args=['one']) teek.Menu().bind('<Destroy>', (lambda: asd.append('two'))) teek.after_quit.connect(asd.append, args=['three']) teek.quit() assert asd == ['one', 'two', 'three']
def test_font_magic_new_method(): font = teek.Font('a_font_with_this_name_does_not_exist') assert isinstance(font, teek.Font) assert not isinstance(font, teek.NamedFont) teek.tcl_eval(None, 'font create test_font_name') named_font = teek.Font('test_font_name') assert isinstance(named_font, teek.NamedFont) named_font.delete()
def test_from_and_to_tcl(): description = ["Helvetica", 42, "bold"] descriptiony_font = teek.Font(description) assert descriptiony_font.to_tcl() is description assert teek.Font.from_tcl(description) == descriptiony_font teek.tcl_eval(None, 'font create test_font_name') named_font = teek.NamedFont.from_tcl('test_font_name') assert isinstance(named_font, teek.NamedFont) assert named_font.to_tcl() == 'test_font_name' named_font.delete()
def test_from_and_to_tcl(): description = [get_test_family(), 42, 'bold'] descriptiony_font = teek.Font(description) assert descriptiony_font.to_tcl() == to_tcl(description) assert teek.Font.from_tcl(description) == descriptiony_font teek.tcl_eval(None, 'font create test_font_name') named_font = teek.NamedFont.from_tcl('test_font_name') assert isinstance(named_font, teek.NamedFont) assert named_font.to_tcl() == 'test_font_name' named_font.delete()
def test_spinbox(): asd = [] spinbox = teek.Spinbox(teek.Window(), from_=0, to=10, command=(lambda: asd.append('boo'))) assert asd == [] teek.tcl_eval( None, ''' set command [%s cget -command] $command $command ''' % spinbox.to_tcl()) assert asd == ['boo', 'boo']
def test_run_called_from_wrong_thread(handy_callback): # this starts the Tcl interpreter, we get different errors without this teek.tcl_eval(None, '') @handy_callback def thread_target(): with pytest.raises(RuntimeError) as error: teek.run() assert str(error.value) == "run() must be called from main thread" thread = threading.Thread(target=thread_target) thread.start() thread.join() assert thread_target.ran_once()
def thread_target(): with pytest.raises(teek.TclError) as error: teek.tcl_eval(None, "expr {1/0}") exc = error.value assert isinstance(exc, teek.TclError) assert exc.__traceback__ is not None # error_message is the traceback that python would display if this # error wasn't caught error_message = ''.join( traceback.format_exception(type(exc), exc, exc.__traceback__)) assert error_message.startswith("Traceback (most recent call last):\n") regex = (r'\n' r' File ".*test_threads\.py", line \d+, in thread_target\n' r' teek\.tcl_eval\(None, "expr {1/0}"\)\n') assert re.search(regex, error_message) is not None
def test_init_threads_errors(deinit_threads, handy_callback): @handy_callback def thread1_target(): # the Tcl interpreter isn't started yet, so this runs an error that is # not covered by the code below with pytest.raises(RuntimeError) as error: teek.tcl_eval(None, '') assert str(error.value) == "init_threads() wasn't called" thread1 = threading.Thread(target=thread1_target) thread1.start() thread1.join() assert thread1_target.ran_once() # this starts the Tcl interpreter teek.tcl_eval(None, '') @handy_callback def thread2_target(): with pytest.raises(RuntimeError) as error: teek.init_threads() assert (str( error.value) == "init_threads() must be called from main thread") for cb in [ functools.partial(teek.tcl_call, None, 'puts', 'hello'), functools.partial(teek.tcl_eval, None, 'puts hello') ]: with pytest.raises(RuntimeError) as error: cb() assert str(error.value) == "init_threads() wasn't called" thread2 = threading.Thread(target=thread2_target) thread2.start() thread2.join() assert thread2_target.ran_once() teek.init_threads() with pytest.raises(RuntimeError) as error: teek.init_threads() assert str(error.value) == "init_threads() was called twice" teek.after_idle(teek.quit) teek.run()
def test_empty_tuple_bug(): # after half a second, press escape in the widget of the dialog that # happens to be focused teek.after(500, lambda: teek.tcl_eval( None, "event generate [focus] <Escape>")) # do the dialog, tkinter should return an empty tuple which should be # converted to an empty string result = teek.tcl_call(str, 'tk_getSaveFile') # this threw an error assert result == ''
def test_screen_distances(): assert teek.ScreenDistance(123).pixels == 123 assert teek.ScreenDistance('123').pixels == 123 assert round(teek.ScreenDistance(123).fpixels, 3) == 123.0 assert round(teek.ScreenDistance('123').fpixels, 3) == 123.0 assert teek.ScreenDistance(123) == teek.ScreenDistance('123') assert hash(teek.ScreenDistance(123)) == hash(teek.ScreenDistance('123')) inch = teek.ScreenDistance('1i') centimeter = teek.ScreenDistance('1c') pixel = teek.ScreenDistance(1) assert round(inch.fpixels / centimeter.fpixels, 2) == 2.54 assert repr(inch) == "ScreenDistance('1i')" assert repr(centimeter) == "ScreenDistance('1c')" assert repr(pixel) == "ScreenDistance('1')" assert inch.to_tcl() == '1i' assert centimeter.to_tcl() == '1c' assert pixel.to_tcl() == '1' assert inch != centimeter assert inch > centimeter assert inch != 'asd' assert inch != '1i' with pytest.raises(TypeError): inch < '1i' teek.tcl_eval(None, 'proc returnArg {arg} {return $arg}') try: assert teek.tcl_eval(teek.ScreenDistance, 'returnArg 1i') == inch assert teek.tcl_eval(teek.ScreenDistance, 'returnArg 1c') == centimeter assert teek.tcl_eval(teek.ScreenDistance, 'returnArg 1') == pixel finally: teek.delete_command('returnArg') with pytest.raises(teek.TclError): teek.ScreenDistance('asdf asdf')
def test_scrollbar(fake_command, handy_callback): scrollbar = teek.Scrollbar(teek.Window()) assert scrollbar.get() == (0.0, 1.0) # testing the set method isn't as easy as you might think because get() # doesn't return the newly set arguments after calling set() with fake_command(scrollbar.to_tcl()) as called: scrollbar.set(1.2, 3.4) assert called == [['set', '1.2', '3.4']] # this tests the code that runs when the user scrolls the scrollbar log = [] scrollbar.config['command'].connect(lambda *args: log.append(args)) teek.tcl_eval( None, ''' set command [%s cget -command] $command moveto 1.2 $command scroll 1 units $command scroll 2 pages ''' % scrollbar.to_tcl()) assert log == [('moveto', 1.2), ('scroll', 1, 'units'), ('scroll', 2, 'pages')]
def test_eval_and_call(handy_commands, capfd): assert teek.tcl_eval(None, 'if {1 == 2} {puts omg}') is None assert teek.tcl_eval(str, 'list a b c') == 'a b c' assert teek.tcl_eval(int, 'expr 22 / 7') == 3 assert teek.tcl_eval(int, 'returnEmptyString') is None assert round(teek.tcl_eval(float, 'expr 22 / 7.0'), 2) == 3.14 assert teek.tcl_eval([int], 'list 1 2 3') == [1, 2, 3] assert teek.tcl_eval([str], 'list { a} {b } { c }') == [' a', 'b ', ' c '] assert (teek.tcl_eval((str, int, str, int), 'list a 1 b 2') == ('a', 1, 'b', 2)) assert teek.tcl_eval([int], 'list 0b11111111 0o377 0xff') == [255] * 3 assert teek.tcl_eval( {'a': int, 'c': bool}, 'dict create a 1 b 2') == {'a': 1, 'b': '2'} with pytest.raises(ValueError): teek.tcl_eval(int, 'returnArg lel') with pytest.raises(ValueError): teek.tcl_eval((str, str), 'list a b c') with pytest.raises(ValueError): teek.tcl_eval([int], 'list 1 2 3 yay') bools = ['true false', 'tru fal', 'yes no', 'y n', 'on off', '1 0'] for yes, no in map(str.split, bools): assert teek.tcl_call(bool, 'returnArg', yes.upper()) is True assert teek.tcl_call(bool, 'returnArg', no.upper()) is False assert teek.tcl_call(bool, 'returnArg', yes.lower()) is True assert teek.tcl_call(bool, 'returnArg', no.lower()) is False assert teek.tcl_eval(bool, 'returnEmptyString') is None with pytest.raises(ValueError): teek.tcl_eval(bool, 'returnArg lolwut') with pytest.raises(TypeError): teek.tcl_call(None, 'puts', object()) assert capfd.readouterr() == ('', '') with pytest.raises(TypeError): teek.tcl_eval(object(), 'puts hello') # tcl seems to use sometimes lf and sometimes crlf, lol output, errors = capfd.readouterr() assert output.replace('\r\n', '\n') == 'hello\n' assert not errors # forced to string converting relies on this test_data = [ ('ab', 'ab'), ('a b', 'a b'), (['a', 'b'], 'a b'), ([' a', 'b '], '{ a} {b }'), (['a ', ' b'], '{a } { b}'), (['a', 'b c'], 'a {b c}'), (('a', 'b c'), 'a {b c}'), (CustomSequence(), '{a b c} 123'), ] for before, after in test_data: assert teek.tcl_call(str, 'format', '%s', before) == after # test conversion to strings when teek.tcl_calling assert teek.tcl_call( str, 'list', True, 5, 3.14, [1, 2], {3: 4}, ('lol', 'wut'), ) == '1 5 3.14 {1 2} {3 4} {lol wut}' # special case: Tcl empty string is None in python teek.tcl_eval(None, 'proc returnEmpty {} { return {} }') class BrokenFromTcl: oh_no = False @classmethod def from_tcl(cls, tcl_string): cls.oh_no = True raise RuntimeError("NO!! THIS WASNT SUPPSOED TO RUN!!!") assert teek.tcl_call(BrokenFromTcl, 'returnEmpty') is None assert not BrokenFromTcl.oh_no assert capfd.readouterr() == ('', '')
def handy_commands(): teek.tcl_eval(None, 'proc returnArg {arg} { return $arg }') teek.tcl_eval(None, 'proc returnEmptyString {} {}') yield teek.delete_command('returnArg') teek.delete_command('returnEmptyString')
def thread1_target(): # the Tcl interpreter isn't started yet, so this runs an error that is # not covered by the code below with pytest.raises(RuntimeError) as error: teek.tcl_eval(None, '') assert str(error.value) == "init_threads() wasn't called"