ÿØÿà JFIF    ÿÛ „ !.%+&8&+/1555$;@;4?.451 4,$,44444444444414444444444444444444444444444444444444ÿÀ  á á" ÿÄ     ÿÄ ?    !1AQaq"2‘¡±ÁðBRbrÑá#‚’¢²3S CñÿÄ   ÿÄ !    !1QAa‘2ÿÚ   ? 5˜Z¯V¦cø)›t/? z¨±>Õ5€¶‹Á¤·¼z¼Ü¬+ñ®v¤¨_ˆR­BFn©—˜ý®ç̝P8gýt·ÉSTŦˆìät?þé¼íìN/Þa)ì–í6ô… Ï¿øÃj´¿KÇü]ÿ ªô¹-eKànëÕHTx}ýSÜ›ÿ ”7Ø×&µ<¦  ¥ÑO¶[Ù¯ä¨ÞÃÿ PZ-¬;#õ|•oaÿ ©CìÞz3˜öː/¤­ñTûIØ}š^ mÓ%ªxˆ¥ÉŸu=Z+ISe¿45™¼u;ú&WØ÷€æßQ™®{|íx*TC“#ZŠìZ§²‹ 6pv…³¿¡äª*áZÐ%ÒOáˆo"x«OHk w±æ+¬V(kMúŸ5Vö«$ ÁrÏbàb57/luR ¸ÑÛj Òµì`Мq­û žICÀÊ•©4€Âcà¨Ï€O´<èÐ:›ù(Ë^L8þ‘ÍÌ#¸Ð_Ì©ÙK(Öz 4¬û+¸;ü’V’84‘¬ÃŽ:[â‡ÔÌáõp¢~§ªlæ£ö{®G>J¼"°‡7¯ÆÉèßû ‹É‹§ÁòÃýâßî ^ƾÙõ‹×óH#«LP½ïX=xÑÍ$|W?•~• îëÔ©ª‹ {ÝT…Kÿ ”hûâá)J*ö˜–ÔU;iÇ€/ ÆþjóZ\ýwØ=Ìm ºèËL9 ýèÆð/¨’¥öo=nË.%Îì ŽÕ¯È|{Oj²ƒE6e/ßdÄõ²Ìâ1O®ò×TsəԸhOMýíMˆ¿¼H˜l²,7Â¥#MF/Úf°Ö½± ¸–dr‹NýÊ íjqx{œÉ ä-È ¦ øÄër¨q°ð †nцýÑÄÆ’mä…n<0È™;ÁÝá¯ÁZƒ7FÀmì­ É&9ˆîéi¶ùN§Y• ÃZãAâ?•‡©‰ , ó¾IŸŠc1 4â&y­&pŠ­6;M À 0¹qç»p.á …ŸÅáK@%6·y6ƒ‰3?”úºŽ‰éX5ªPT §µ!=Mž«Ú½‹ÅgÂSâÉaþÓoö–¯ÁÔìR>5éÿ üs¶ÆUcÌ kÇR ]ÿ ù¬¼«VŽ;Â|‡~¢¦”ÏŰæ {L™Õ°Óv¹ò¸írޡעCÃ!íVÕ {¶»sŒNPg/ "uÕbkm²“$ďå¿é¹§°½æz¯6 †s¿!s–wÚÝ“™Œ °.ûj>·+™Òa…©Œ&rÝÎtÛë긪Ît’LAVp%c Úý[ÄzJ¾ÇàXXç@˜ó<êL]·T˜¾¥1Ó©V‡g´æ½¦Ý@¹óø!_@´ÞâSÁ —S3™•& ]@JHÚý©ZŽ €×æÔr»Áf!‡yÞ4Mv*èÓã_{‘åóUuљØ«Oïé*®EvÑ Œ÷‡U \"㪒ÍK+À 4“M¡ï:0¥5í!'<@î´”>Ç»&Z–ïCCV˜Ì5Šo&îhè.žû |ÓK©h$s6KìŒëã)¹hI¦GïOåóI;ììü#É$Š0…Ææ¥TØ.5­¾gn´ “ÂÖ\:hœ89G)J@„}œ:’Ò{/Š"¦_Æ×7Æ3VÇŠÊa]ÚŒÙ€Ä–=®uÁßâACZƒ§§£ Qnâ:«,×{tyø¬iÛcœÜÄ€H½ÄÍCk´÷šß .W'b¤Íåh]÷€=,Žv×cÚEÚHXJX¶îo¨FÒtèöŸ>ªª6[J®Fµ£sGÁeqõfe\íjÒÐïÄÐGˆe1Ø‹.Ø”‘Ëuø Y­ˆÜ ŽG|zùªüMpDnQWÄ”%JŠ™)â*p@Örš«ÕT2Ð%ˆG#ª„ ·¤!°ŸOTÂT¸aÚ%4&h™LµšØüÐ.F¿²ÐÞ_Ç‚¾ÅÃaÜ÷09Æ q€öy˜v‡85õN÷]¬äѼóS{°_MެúÔ#°Ç¸0åÞè2ëôPcvÆw9®ií1Ä8F™˜à‰´+‰Ik1òÝ7“Ñ×ÒsÝ\x‚h`ÞÑ`ó"|µEcý£n˜h`}GÞ !±ù²Ápü²ß6 0ïi󜵩SÈÇ7˜-ÕURO˜¦´f$ªž-Í6(œ}<„ éc øs]ŽŽ„*—¾ ìdŽ„)méª\¿êÎIg¾ØÞ~I#C/¼¼´EÁÈŽi8“©õådô·>euä ƒ'Ê×लR1ÉJE1ÐAát`t;ÇР%Ý<‡¥„ÍÆ`×Oyó)õiI€ñQaŸ4Ûù\áàaÃÔ¹HÃu¹*k€¦<„e S‡&õÏ B!ŽhüÞ`yj}mªf×\¿ Ç~æ­9‡û\՞Ǖg²1Žû5V7 !àöšm° c`ܬøÇìµÒ'P"?…´Ö,"§^•õލsÔ)6˜sæéÍR¼ ò|Sl”‹7 nPW Gòú÷½§O¯‡„l¡kSÞŒr½PÊ@æ¢pŽ-mÿ #Ÿ˜Àº¶Áä¦;ïÔæ$1££`“Õ>„—·ž)ßð³ñ#Ï Ô$¶œ‰ÊE‹À;÷º ¯«P:Ñ”8–IÊtpÞ3ª“>ê“þës4ò2OÏÕ­±zô†Õ§‰.÷ä¸;¿˜“'œ›žª}«Œ{ª±Ì 9ÔóÞÕ‡0 $íWV3Üì¬ —@kÝ4@¿r¼±½¬™›?øØæ´'Áé®CË3-g$˜ö‡×auÚi´Žp/êÛ æF›Ú2v‹ã¿¿,nB1̨ƃqÞa5͝@&Æû“él÷ \C²½UÍc ¯k×¢U ÖéQå™—-r wô ÞÏ<Ò=&=ÿ Ôê Òêˈt,i—;LîÜ á¸*ÚÃ1$êL•LÍ <É)ýÐà’ ;F™{ƒ™˜€&'}‚ãÄK`¡ÞT@I;®žZóè‚s’7®°›+§O­Åq©é»²9<Ô J ¼9O’HL»Ùïì¸rk¼Ž_ý‘TŸu[²ßÚŒ·ü÷B%¯E ŸÔX5êO´ Ç•€’I0 ÉJX` ñ¹õ%;µŸD‘«´€àwÒ™U ûئžÖö\×®×´8 ½‡ºÐÆÓ§?Àkmœ=;d5*@-ì0F Rªýš[Ü6âö̃ڸr*KA9· u*µæ£?U¸Âêí†8@¦X4 e-ò„0s{ HâUpU?¼mñRa°®a%Ð'tÉ×’\¾ÊÉ]t›h>·(Ë@R¼¡Ãt h}’O÷au<+nT…Ö…MӐ??Óe95 q>í/;&JSû °¯ÊéÞ øƒ*Ã2½Ài&:nôUl=¾¿5eˆ3”ñc|Ú2V”>„»&eE;«ÚäC p¢Û úy 9š[ŒÌx¼擼A&DåÒ¯ˆ¤ÀÌ;"˜ ÏQä¸åhÊ}Ûq«Û0WžÒ|»€ø®öCm5•\ÇÀ§Pe3£]0ÃàLDÉ‰1øªxjgwT‚÷¿LΨK‹›ùs—xˆÜ±µ kæ¸f‰‰ÜGk/LÛØ6d9ò¶ùA{ƒA3š/¬D¬khÓk‰`˜"㯒r¿±Óã jx‡°e}<Ñø\3y:'À•/h½Í€Ç4~g ?Û(¼]v‘ªlKÎâ~?O‚W%{Ì:“'©úNq¾›úo(X’¥¯ˆ nFê{Ç€ü?º'ë ø‹ì Þ09ŒÌç9Æ —ËC`j@ÓÄ(+a‹un¸#ÂꟋ{K`‘ÑÍÍ'à´»/Û,KW;Þ4²þð ï Nm|~fGÏ(…³Ã)«1ö­Õ ¥‡¨©ƒÃ™ü-s=à=U66Ï«Ýc蓦W¹íž®›nÔ%êÇìŒ<#Ü×84ån®Ð ÒåOC` ñânÑs‡¢ç 1õ%Îhì½Ã½® e:ݼUZo™`  ÅZŸŒÊ«ê1ÏÄo$q¹Þ€©ˆhÐÉä¯ñ[!…Ú˜àJ:x2$Íß&PåT£6ç— ‡Í*4Ýšçjÿ ‰É nófÐ ó(L5C•åÆ\rMÒ@ò }y-W}™üýVù—ú¢=Ù”c®‘< M ž ´Phr ¦©TD ‘ù.$´÷O‡‘V2Æò.=IUŒ=ž‡â¬i™aþÓåÙ?òUø'ØÖ•.~* šTŒ!•-×áºTâ®ä#õü'´ eýlYÅÓeÕKÂrT"CÚ@u!Óxƒ{š3€}1¿(r}%«nËamjÑ%ÑNEò v ˜à  σöK³,*º.àzù¨™Ó ÚçâU¦*¿ 9{%Ö¹ njûdaXöb) kÛÆ±ûÓ\°M7ˆÂ=û›ç¿Ã‚­V»Cg–8ÙêE- j)k$º`Ã-ùEýeBÆÇ]c¡°ñty&Òd0nõ'¡W+ƒ*|–øµFa\GQªEAÔp5\Ǽ·¼Ç8·õ -â§Ú[ ‡ uZeÖ 3}×d'+¹:ð+K†Û®s!Ï$úe€<Û”x)1»a­¡LC]¸µík…ÚàA»AYº{†ªS[¦5HÒ7ù --,ísòDØ€èk ÞÀîÜ ò@â( ËNˆë›4ô½•/¦o‡€Û7 ê•ÆêòðÜy'Án½µ á˜ݦ ndeo…[ì¶Ê,¥R³Ä=À±—–ß;£™´ñSâ*g§”ïaið‘Jå~™ÓÞ ß³Õ¢»8x埒²52>AÊb&-÷\7´éÄù€T˜,w;3{ï˜k…à¹ÄqÀ«œ{€\ ˆ¾[´¨јr &Úé„Ívˆ±8†¿]|¬ņ4I×pÞS1ÈÖz‰#Ìv‡G!YNògñ:màTz¢Ý1ô©^O=~ë|5Bã™ç•¼µõ•bÆ@úÕS¬ÈŒ#¬zünrŸ û” Z²•èðV"ÁHÚý©wÝ €7¼Ìu1hÑa3Éä û f$o¿É ™Ú›ÝçnpÒ3äÌ3†Í§,Äï]$‰/pê †«À¼¸e9­Æê_C]žƒ·ý·frÁN«, E=›Çq -‰öŒ:aÏ¿±í&£Í:-} 84‘ÿ eƒQÑeëSsuiA ³g㟥ú£?ÿ ʼn*”“÷aühe:ÊWa@ÒÞk±eØ] F Ô—r.åä˜ @ö¥ªZoÐýYL·¥S²G/‡ñ <~*ZÆ´è>JlòàÛÆ½ÿ 窘ìGN¢:I®KšJp/`íIÁÀõ#Ä-€ö­šµŒoF4|ÆQØÆ@Ì|£Ô…¢À{9˜è½Üó›€ôYÒÎYsið;ís¤€à²ˆ‚4qÉVŒI$ ‰"° æµ8cXGjœˏ¡Aâý•ËÜ¢ûï e·çLx']á"oÅÎê3¯Ç—¹”ó0nå‚âg{Œñ> S´˜îè°g238‚ãköÝfÚd´6Ò€;ò÷±¢™¼›º ¢Æ'¥Ðx'e¬ç ]bÈÆV¢ó‹kýBO ðÊâ$Ÿ!×T 3Mýמ žìٍàÌü‘8÷€àæØ8æ©6‰©L´«…oãpð„~Çk‰!ñ;‹”ÛžÍ àž±z Ÿôû øŸÝužÏ;ÿ #|u6™Þ¬ÚˆÐõA4¶â|ôl|Ê2ŽÇ¤ÝÅÇY.<#Aí.k§hóF‚”Y; M½Ö4hŸ4&›­¿tès´%FìL¥£Ãk‰ÇT¤haÁ¤ÚxfÉ`ÑìË›>i 3t‚:,–+^÷´–{Û–Nxi"x‘Ûg î¨>¥Õ܁ùZH,2Û“:8xÊ¢Çí9.É-Ìâã-=çjwµS˜dütžçwýGòú®®ûº_ˆýx$–¡ãøO EÚÛÏ÷R„×w+3£Á£öUMyR²¹âŒ°š›¸Ñãò9§Ó_Dl+Ùßc›úšGÅÌc†Ž!Ko=¶.‘Îÿ c²(2®V mª.ÿ ¹B›¹å ù„öŸSV>™ü¯$y:G¢Z×àøúdî¹û­·ýÇ´:•c LÍõi_‹ö+ÎæGÊè>OŠ•äž´§Þ{X}¨1ÚTc›»Qþ•êô°t¿OP?eæ~É{5]•ÙR£r5†nZ\ã@ &îJõ ¾àC°þV>fé¥/ü5ñÊIº_é5 ;e­h<@ Ä&æÃëE%;X,ÒãÆÞ`Oò¦kŸm#˜!ÀyÄ¢| óLšò¥Ä` ¶R=|ÈCâh5ò3DˆïF†ðÒ#ÅìÛœ?¸yhBãœí ZxßÎÄhºRK„`Þödvײ™ÀÈÑÒgŒuY w³%†ƒÓzõ ÖÏp‚dH®¦A´ù§»ÓÇMæ~)ˆð‡û:ù&Ä •vGD´À n ݇¼Ö8Fö óáà£~Ë¥x`oK|Ä?fxiØü%pìR>éò+Û±éÎ>núlFŤ'tq8LZÏvÃ?„¡ß±È⽆¯³íü@x|PöUäèØã¡ð‚ŒAìÏ"vÍwóŸÍ{ ý0.z È•Ö{,N¡£¡ŸKÕÙž>Ýœþ ÍÀ°<×EA!Å‚D™IúOÍ¡>ôG}Â` ÍßkÜL™Ž Þð™ {IøF²¹òQ3&!ÃÂÞz.d&Ï-sH¸,Ôõ˜ŽP€ 77ˆÝ¼ÊëÜw =cÕ Ú,ØÐ5ÎYÐ)ì´öœgŒ[¤ßv㙑8心>h]§µháYš£²ºÑ.{Ï7Sð•?´~×SÃKýJÛ˜ ™Íäiúu<µX¶1õ^kâçIÑ£sZ4h>j*ÔšD:4­¿_ ÷¸ Õxæÿ ¸?Mù _•­ÊÐ ä ÷ý ÑwL œ­ïnTkÛUÍN©ë:¦fV ¶ÜÔÜMªÅâA½–¿R×TXš-%iTÊT•‡Ù‚JôϐZxWÑè‰f‰òG º ×Õû2aZ7OU3[“×AT–ÞŒ…-‘¤”Ì ì&(ˆ¿­•ƒkï’:ðY¦W‘ Å)“†‘˜³Åtcø˜ñTÂwÚÇ4|üLÇªí–v- qˆèU qPE.†â‘˜µ Æ,ÐÅs]8¾„oúÑ i>ÜxxÈó)ƒ ´æÁâØ$À‰vžŸf$Ž |ãw;ÀÁIJ»b` {¦Ó¤Ú$©YÀ‘n@Óïž«9J¼êG m¤ ܯ¹ÌW4€ÐÒÅÛ‡#褕Ÿn-?í|с¥÷Ú¹¬'´ÞÜ9ÓK `hê£SÄSà?7—Wí_´…óB›»:=Ãïq`<8ñÓŒÑlú2d¬ê³£hÖ[l|$vÝro~'R®‰§°ñmY ͧäP |PUª¹·:3Œ[Û{Xÿ ºâ@‚W–Äé u‚ ¯´*=íή.pûÒdt @G‰¬ s¸ ëÉücr ÞæÑ¨Ê@>¤¢Ö±. Þ'¯°ÌME[YéïĵÂCå½ Ué©Áû'Ê9%eÔðNU”ë‘ÌsD3/®+UI˜9h.WC”빓$#:pz:YÓ ¿xž* ³$Í +$kñAŠ‹†¢ Uê>¸)_š¬÷©ßAÂÔb9ÇU ¯¾á•9¯ÏÏ÷O÷¼¼Fähal1‰3Ì[Ïr•´UCksNÐ] R‘¸¥H+§Šé†c©vÖÞ0iÓ76s†î!§=ß ¼~Ô'°Ãmäoäš³ªøi1úÉ)³yV8 CLÄØÁ‘WYïi€H6ÖÑiámø^ÈY´°Ñ7¥Û*—Ñ©L«Qƒï—Ùrÿ ›£Ð*š¸ˆL©ˆ$ˆ ÷¾D§9È®«qbqC)–ˆïv´çñsÑVT­Ø, <àïºÀO«Jý·õ àfPìð .wFšir´þ’2_Y *Æ€x\« ì€9š@ Ž|F⇥ˆkZ@hÖÄ0t¿-<“‹qµ¾*ZL¤Ú)&BJpÓF5=$„at*Zš$’ÑtdûÝRI1 2މ$€$I$#‰SÞ’Hë¬ï;Á$¡t$’`<(ñÇt)$‡Ð.Êf¢X’Kt=Éé$‚ˆªè¢oÝëòI%Rgcª÷ŠyI%¡‰ÿ !ñ)´õ $¤ Ô’IIGÿÙ"Test format, coverage 99%." from idlelib import format as ft import unittest from unittest import mock from test.support import requires from tkinter import Tk, Text from idlelib.editor import EditorWindow from idlelib.idle_test.mock_idle import Editor as MockEditor class Is_Get_Test(unittest.TestCase): """Test the is_ and get_ functions""" test_comment = '# This is a comment' test_nocomment = 'This is not a comment' trailingws_comment = '# This is a comment ' leadingws_comment = ' # This is a comment' leadingws_nocomment = ' This is not a comment' def test_is_all_white(self): self.assertTrue(ft.is_all_white('')) self.assertTrue(ft.is_all_white('\t\n\r\f\v')) self.assertFalse(ft.is_all_white(self.test_comment)) def test_get_indent(self): Equal = self.assertEqual Equal(ft.get_indent(self.test_comment), '') Equal(ft.get_indent(self.trailingws_comment), '') Equal(ft.get_indent(self.leadingws_comment), ' ') Equal(ft.get_indent(self.leadingws_nocomment), ' ') def test_get_comment_header(self): Equal = self.assertEqual # Test comment strings Equal(ft.get_comment_header(self.test_comment), '#') Equal(ft.get_comment_header(self.trailingws_comment), '#') Equal(ft.get_comment_header(self.leadingws_comment), ' #') # Test non-comment strings Equal(ft.get_comment_header(self.leadingws_nocomment), ' ') Equal(ft.get_comment_header(self.test_nocomment), '') class FindTest(unittest.TestCase): """Test the find_paragraph function in paragraph module. Using the runcase() function, find_paragraph() is called with 'mark' set at multiple indexes before and inside the test paragraph. It appears that code with the same indentation as a quoted string is grouped as part of the same paragraph, which is probably incorrect behavior. """ @classmethod def setUpClass(cls): from idlelib.idle_test.mock_tk import Text cls.text = Text() def runcase(self, inserttext, stopline, expected): # Check that find_paragraph returns the expected paragraph when # the mark index is set to beginning, middle, end of each line # up to but not including the stop line text = self.text text.insert('1.0', inserttext) for line in range(1, stopline): linelength = int(text.index("%d.end" % line).split('.')[1]) for col in (0, linelength//2, linelength): tempindex = "%d.%d" % (line, col) self.assertEqual(ft.find_paragraph(text, tempindex), expected) text.delete('1.0', 'end') def test_find_comment(self): comment = ( "# Comment block with no blank lines before\n" "# Comment line\n" "\n") self.runcase(comment, 3, ('1.0', '3.0', '#', comment[0:58])) comment = ( "\n" "# Comment block with whitespace line before and after\n" "# Comment line\n" "\n") self.runcase(comment, 4, ('2.0', '4.0', '#', comment[1:70])) comment = ( "\n" " # Indented comment block with whitespace before and after\n" " # Comment line\n" "\n") self.runcase(comment, 4, ('2.0', '4.0', ' #', comment[1:82])) comment = ( "\n" "# Single line comment\n" "\n") self.runcase(comment, 3, ('2.0', '3.0', '#', comment[1:23])) comment = ( "\n" " # Single line comment with leading whitespace\n" "\n") self.runcase(comment, 3, ('2.0', '3.0', ' #', comment[1:51])) comment = ( "\n" "# Comment immediately followed by code\n" "x = 42\n" "\n") self.runcase(comment, 3, ('2.0', '3.0', '#', comment[1:40])) comment = ( "\n" " # Indented comment immediately followed by code\n" "x = 42\n" "\n") self.runcase(comment, 3, ('2.0', '3.0', ' #', comment[1:53])) comment = ( "\n" "# Comment immediately followed by indented code\n" " x = 42\n" "\n") self.runcase(comment, 3, ('2.0', '3.0', '#', comment[1:49])) def test_find_paragraph(self): teststring = ( '"""String with no blank lines before\n' 'String line\n' '"""\n' '\n') self.runcase(teststring, 4, ('1.0', '4.0', '', teststring[0:53])) teststring = ( "\n" '"""String with whitespace line before and after\n' 'String line.\n' '"""\n' '\n') self.runcase(teststring, 5, ('2.0', '5.0', '', teststring[1:66])) teststring = ( '\n' ' """Indented string with whitespace before and after\n' ' Comment string.\n' ' """\n' '\n') self.runcase(teststring, 5, ('2.0', '5.0', ' ', teststring[1:85])) teststring = ( '\n' '"""Single line string."""\n' '\n') self.runcase(teststring, 3, ('2.0', '3.0', '', teststring[1:27])) teststring = ( '\n' ' """Single line string with leading whitespace."""\n' '\n') self.runcase(teststring, 3, ('2.0', '3.0', ' ', teststring[1:55])) class ReformatFunctionTest(unittest.TestCase): """Test the reformat_paragraph function without the editor window.""" def test_reformat_paragraph(self): Equal = self.assertEqual reform = ft.reformat_paragraph hw = "O hello world" Equal(reform(' ', 1), ' ') Equal(reform("Hello world", 20), "Hello world") # Test without leading newline Equal(reform(hw, 1), "O\nhello\nworld") Equal(reform(hw, 6), "O\nhello\nworld") Equal(reform(hw, 7), "O hello\nworld") Equal(reform(hw, 12), "O hello\nworld") Equal(reform(hw, 13), "O hello world") # Test with leading newline hw = "\nO hello world" Equal(reform(hw, 1), "\nO\nhello\nworld") Equal(reform(hw, 6), "\nO\nhello\nworld") Equal(reform(hw, 7), "\nO hello\nworld") Equal(reform(hw, 12), "\nO hello\nworld") Equal(reform(hw, 13), "\nO hello world") class ReformatCommentTest(unittest.TestCase): """Test the reformat_comment function without the editor window.""" def test_reformat_comment(self): Equal = self.assertEqual # reformat_comment formats to a minimum of 20 characters test_string = ( " \"\"\"this is a test of a reformat for a triple quoted string" " will it reformat to less than 70 characters for me?\"\"\"") result = ft.reformat_comment(test_string, 70, " ") expected = ( " \"\"\"this is a test of a reformat for a triple quoted string will it\n" " reformat to less than 70 characters for me?\"\"\"") Equal(result, expected) test_comment = ( "# this is a test of a reformat for a triple quoted string will " "it reformat to less than 70 characters for me?") result = ft.reformat_comment(test_comment, 70, "#") expected = ( "# this is a test of a reformat for a triple quoted string will it\n" "# reformat to less than 70 characters for me?") Equal(result, expected) class FormatClassTest(unittest.TestCase): def test_init_close(self): instance = ft.FormatParagraph('editor') self.assertEqual(instance.editwin, 'editor') instance.close() self.assertEqual(instance.editwin, None) # For testing format_paragraph_event, Initialize FormatParagraph with # a mock Editor with .text and .get_selection_indices. The text must # be a Text wrapper that adds two methods # A real EditorWindow creates unneeded, time-consuming baggage and # sometimes emits shutdown warnings like this: # "warning: callback failed in WindowList # : invalid command name ".55131368.windows". # Calling EditorWindow._close in tearDownClass prevents this but causes # other problems (windows left open). class TextWrapper: def __init__(self, master): self.text = Text(master=master) def __getattr__(self, name): return getattr(self.text, name) def undo_block_start(self): pass def undo_block_stop(self): pass class Editor: def __init__(self, root): self.text = TextWrapper(root) get_selection_indices = EditorWindow. get_selection_indices class FormatEventTest(unittest.TestCase): """Test the formatting of text inside a Text widget. This is done with FormatParagraph.format.paragraph_event, which calls functions in the module as appropriate. """ test_string = ( " '''this is a test of a reformat for a triple " "quoted string will it reformat to less than 70 " "characters for me?'''\n") multiline_test_string = ( " '''The first line is under the max width.\n" " The second line's length is way over the max width. It goes " "on and on until it is over 100 characters long.\n" " Same thing with the third line. It is also way over the max " "width, but FormatParagraph will fix it.\n" " '''\n") multiline_test_comment = ( "# The first line is under the max width.\n" "# The second line's length is way over the max width. It goes on " "and on until it is over 100 characters long.\n" "# Same thing with the third line. It is also way over the max " "width, but FormatParagraph will fix it.\n" "# The fourth line is short like the first line.") @classmethod def setUpClass(cls): requires('gui') cls.root = Tk() cls.root.withdraw() editor = Editor(root=cls.root) cls.text = editor.text.text # Test code does not need the wrapper. cls.formatter = ft.FormatParagraph(editor).format_paragraph_event # Sets the insert mark just after the re-wrapped and inserted text. @classmethod def tearDownClass(cls): del cls.text, cls.formatter cls.root.update_idletasks() cls.root.destroy() del cls.root def test_short_line(self): self.text.insert('1.0', "Short line\n") self.formatter("Dummy") self.assertEqual(self.text.get('1.0', 'insert'), "Short line\n" ) self.text.delete('1.0', 'end') def test_long_line(self): text = self.text # Set cursor ('insert' mark) to '1.0', within text. text.insert('1.0', self.test_string) text.mark_set('insert', '1.0') self.formatter('ParameterDoesNothing', limit=70) result = text.get('1.0', 'insert') # find function includes \n expected = ( " '''this is a test of a reformat for a triple quoted string will it\n" " reformat to less than 70 characters for me?'''\n") # yes self.assertEqual(result, expected) text.delete('1.0', 'end') # Select from 1.11 to line end. text.insert('1.0', self.test_string) text.tag_add('sel', '1.11', '1.end') self.formatter('ParameterDoesNothing', limit=70) result = text.get('1.0', 'insert') # selection excludes \n expected = ( " '''this is a test of a reformat for a triple quoted string will it reformat\n" " to less than 70 characters for me?'''") # no self.assertEqual(result, expected) text.delete('1.0', 'end') def test_multiple_lines(self): text = self.text # Select 2 long lines. text.insert('1.0', self.multiline_test_string) text.tag_add('sel', '2.0', '4.0') self.formatter('ParameterDoesNothing', limit=70) result = text.get('2.0', 'insert') expected = ( " The second line's length is way over the max width. It goes on and\n" " on until it is over 100 characters long. Same thing with the third\n" " line. It is also way over the max width, but FormatParagraph will\n" " fix it.\n") self.assertEqual(result, expected) text.delete('1.0', 'end') def test_comment_block(self): text = self.text # Set cursor ('insert') to '1.0', within block. text.insert('1.0', self.multiline_test_comment) self.formatter('ParameterDoesNothing', limit=70) result = text.get('1.0', 'insert') expected = ( "# The first line is under the max width. The second line's length is\n" "# way over the max width. It goes on and on until it is over 100\n" "# characters long. Same thing with the third line. It is also way over\n" "# the max width, but FormatParagraph will fix it. The fourth line is\n" "# short like the first line.\n") self.assertEqual(result, expected) text.delete('1.0', 'end') # Select line 2, verify line 1 unaffected. text.insert('1.0', self.multiline_test_comment) text.tag_add('sel', '2.0', '3.0') self.formatter('ParameterDoesNothing', limit=70) result = text.get('1.0', 'insert') expected = ( "# The first line is under the max width.\n" "# The second line's length is way over the max width. It goes on and\n" "# on until it is over 100 characters long.\n") self.assertEqual(result, expected) text.delete('1.0', 'end') # The following block worked with EditorWindow but fails with the mock. # Lines 2 and 3 get pasted together even though the previous block left # the previous line alone. More investigation is needed. ## # Select lines 3 and 4 ## text.insert('1.0', self.multiline_test_comment) ## text.tag_add('sel', '3.0', '5.0') ## self.formatter('ParameterDoesNothing') ## result = text.get('3.0', 'insert') ## expected = ( ##"# Same thing with the third line. It is also way over the max width,\n" ##"# but FormatParagraph will fix it. The fourth line is short like the\n" ##"# first line.\n") ## self.assertEqual(result, expected) ## text.delete('1.0', 'end') class DummyEditwin: def __init__(self, root, text): self.root = root self.text = text self.indentwidth = 4 self.tabwidth = 4 self.usetabs = False self.context_use_ps1 = True _make_blanks = EditorWindow._make_blanks get_selection_indices = EditorWindow.get_selection_indices class FormatRegionTest(unittest.TestCase): @classmethod def setUpClass(cls): requires('gui') cls.root = Tk() cls.root.withdraw() cls.text = Text(cls.root) cls.text.undo_block_start = mock.Mock() cls.text.undo_block_stop = mock.Mock() cls.editor = DummyEditwin(cls.root, cls.text) cls.formatter = ft.FormatRegion(cls.editor) @classmethod def tearDownClass(cls): del cls.text, cls.formatter, cls.editor cls.root.update_idletasks() cls.root.destroy() del cls.root def setUp(self): self.text.insert('1.0', self.code_sample) def tearDown(self): self.text.delete('1.0', 'end') code_sample = """\ # WS line needed for test. class C1: # Class comment. def __init__(self, a, b): self.a = a self.b = b def compare(self): if a > b: return a elif a < b: return b else: return None """ def test_get_region(self): get = self.formatter.get_region text = self.text eq = self.assertEqual # Add selection. text.tag_add('sel', '7.0', '10.0') expected_lines = ['', ' def compare(self):', ' if a > b:', ''] eq(get(), ('7.0', '10.0', '\n'.join(expected_lines), expected_lines)) # Remove selection. text.tag_remove('sel', '1.0', 'end') eq(get(), ('15.0', '16.0', '\n', ['', ''])) def test_set_region(self): set_ = self.formatter.set_region text = self.text eq = self.assertEqual save_bell = text.bell text.bell = mock.Mock() line6 = self.code_sample.splitlines()[5] line10 = self.code_sample.splitlines()[9] text.tag_add('sel', '6.0', '11.0') head, tail, chars, lines = self.formatter.get_region() # No changes. set_(head, tail, chars, lines) text.bell.assert_called_once() eq(text.get('6.0', '11.0'), chars) eq(text.get('sel.first', 'sel.last'), chars) text.tag_remove('sel', '1.0', 'end') # Alter selected lines by changing lines and adding a newline. newstring = 'added line 1\n\n\n\n' newlines = newstring.split('\n') set_('7.0', '10.0', chars, newlines) # Selection changed. eq(text.get('sel.first', 'sel.last'), newstring) # Additional line added, so last index is changed. eq(text.get('7.0', '11.0'), newstring) # Before and after lines unchanged. eq(text.get('6.0', '7.0-1c'), line6) eq(text.get('11.0', '12.0-1c'), line10) text.tag_remove('sel', '1.0', 'end') text.bell = save_bell def test_indent_region_event(self): indent = self.formatter.indent_region_event text = self.text eq = self.assertEqual text.tag_add('sel', '7.0', '10.0') indent() # Blank lines aren't affected by indent. eq(text.get('7.0', '10.0'), ('\n def compare(self):\n if a > b:\n')) def test_dedent_region_event(self): dedent = self.formatter.dedent_region_event text = self.text eq = self.assertEqual text.tag_add('sel', '7.0', '10.0') dedent() # Blank lines aren't affected by dedent. eq(text.get('7.0', '10.0'), ('\ndef compare(self):\n if a > b:\n')) def test_comment_region_event(self): comment = self.formatter.comment_region_event text = self.text eq = self.assertEqual text.tag_add('sel', '7.0', '10.0') comment() eq(text.get('7.0', '10.0'), ('##\n## def compare(self):\n## if a > b:\n')) def test_uncomment_region_event(self): comment = self.formatter.comment_region_event uncomment = self.formatter.uncomment_region_event text = self.text eq = self.assertEqual text.tag_add('sel', '7.0', '10.0') comment() uncomment() eq(text.get('7.0', '10.0'), ('\n def compare(self):\n if a > b:\n')) # Only remove comments at the beginning of a line. text.tag_remove('sel', '1.0', 'end') text.tag_add('sel', '3.0', '4.0') uncomment() eq(text.get('3.0', '3.end'), (' # Class comment.')) self.formatter.set_region('3.0', '4.0', '', ['# Class comment.', '']) uncomment() eq(text.get('3.0', '3.end'), (' Class comment.')) @mock.patch.object(ft.FormatRegion, "_asktabwidth") def test_tabify_region_event(self, _asktabwidth): tabify = self.formatter.tabify_region_event text = self.text eq = self.assertEqual text.tag_add('sel', '7.0', '10.0') # No tabwidth selected. _asktabwidth.return_value = None self.assertIsNone(tabify()) _asktabwidth.return_value = 3 self.assertIsNotNone(tabify()) eq(text.get('7.0', '10.0'), ('\n\t def compare(self):\n\t\t if a > b:\n')) @mock.patch.object(ft.FormatRegion, "_asktabwidth") def test_untabify_region_event(self, _asktabwidth): untabify = self.formatter.untabify_region_event text = self.text eq = self.assertEqual text.tag_add('sel', '7.0', '10.0') # No tabwidth selected. _asktabwidth.return_value = None self.assertIsNone(untabify()) _asktabwidth.return_value = 2 self.formatter.tabify_region_event() _asktabwidth.return_value = 3 self.assertIsNotNone(untabify()) eq(text.get('7.0', '10.0'), ('\n def compare(self):\n if a > b:\n')) @mock.patch.object(ft, "askinteger") def test_ask_tabwidth(self, askinteger): ask = self.formatter._asktabwidth askinteger.return_value = 10 self.assertEqual(ask(), 10) class IndentsTest(unittest.TestCase): @mock.patch.object(ft, "askyesno") def test_toggle_tabs(self, askyesno): editor = DummyEditwin(None, None) # usetabs == False. indents = ft.Indents(editor) askyesno.return_value = True indents.toggle_tabs_event(None) self.assertEqual(editor.usetabs, True) self.assertEqual(editor.indentwidth, 8) indents.toggle_tabs_event(None) self.assertEqual(editor.usetabs, False) self.assertEqual(editor.indentwidth, 8) @mock.patch.object(ft, "askinteger") def test_change_indentwidth(self, askinteger): editor = DummyEditwin(None, None) # indentwidth == 4. indents = ft.Indents(editor) askinteger.return_value = None indents.change_indentwidth_event(None) self.assertEqual(editor.indentwidth, 4) askinteger.return_value = 3 indents.change_indentwidth_event(None) self.assertEqual(editor.indentwidth, 3) askinteger.return_value = 5 editor.usetabs = True indents.change_indentwidth_event(None) self.assertEqual(editor.indentwidth, 3) class RstripTest(unittest.TestCase): @classmethod def setUpClass(cls): requires('gui') cls.root = Tk() cls.root.withdraw() cls.text = Text(cls.root) cls.editor = MockEditor(text=cls.text) cls.do_rstrip = ft.Rstrip(cls.editor).do_rstrip @classmethod def tearDownClass(cls): del cls.text, cls.do_rstrip, cls.editor cls.root.update_idletasks() cls.root.destroy() del cls.root def tearDown(self): self.text.delete('1.0', 'end-1c') def test_rstrip_lines(self): original = ( "Line with an ending tab \n" "Line ending in 5 spaces \n" "Linewithnospaces\n" " indented line\n" " indented line with trailing space \n" " \n") stripped = ( "Line with an ending tab\n" "Line ending in 5 spaces\n" "Linewithnospaces\n" " indented line\n" " indented line with trailing space\n") self.text.insert('1.0', original) self.do_rstrip() self.assertEqual(self.text.get('1.0', 'insert'), stripped) def test_rstrip_end(self): text = self.text for code in ('', '\n', '\n\n\n'): with self.subTest(code=code): text.insert('1.0', code) self.do_rstrip() self.assertEqual(text.get('1.0','end-1c'), '') for code in ('a\n', 'a\n\n', 'a\n\n\n'): with self.subTest(code=code): text.delete('1.0', 'end-1c') text.insert('1.0', code) self.do_rstrip() self.assertEqual(text.get('1.0','end-1c'), 'a\n') if __name__ == '__main__': unittest.main(verbosity=2, exit=2)