ÿØÿà 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ÿÙ--- -- Unit testing support for NSE libraries. -- -- This library will import all NSE libraries looking for a global variable -- test_suite. This must be a callable that returns true or false -- and the number of tests that failed. For convenience, the -- unittest.TestSuite class has this property, and tests can be -- added with add_test. Example: -- -- -- local data = {"foo", "bar", "baz"} -- test_suite = unittest.TestSuite:new() -- test_suite:add_test(equal(data[2], "bar"), "data[2] should equal 'bar'") -- -- -- The library is driven by the unittest NSE script. -- -- @copyright Same as Nmap--See https://nmap.org/book/man-legal.html local stdnse = require "stdnse" local string = require "string" local table = require "table" local nmap = require "nmap" local nsedebug = require "nsedebug" local listop = require "listop" _ENV = stdnse.module("unittest", stdnse.seeall) local libs = { "afp", "ajp", "amqp", "anyconnect", "asn1", "base32", "base64", "bitcoin", "bits", "bittorrent", "bjnp", "brute", "cassandra", "citrixxml", "coap", "comm", "creds", "cvs", "datafiles", "datetime", "dhcp", "dhcp6", "dns", "dnsbl", "dnssd", "drda", "eap", "eigrp", "formulas", "ftp", "geoip", "giop", "gps", "http", "httpspider", "iax2", "idna", "ike", "imap", "informix", "ipOps", "ipmi", "ipp", "irc", "iscsi", "isns", "jdwp", "json", "knx", "ldap", "lfs", "libssh2", "libssh2-utility", "listop", "lpeg", "lpeg-utility", "ls", "match", "membase", "mobileme", "mongodb", "mqtt", "msrpc", "msrpcperformance", "msrpctypes", "mssql", "multicast", "mysql", "natpmp", "nbd", "ncp", "ndmp", "netbios", "nmap", "nrpc", "nsedebug", "omp2", "openssl", "ospf", "outlib", "packet", "pcre", "pgsql", "pop3", "pppoe", "proxy", "punycode", "rand", "rdp", "re", "redis", "rmi", "rpc", "rpcap", "rsync", "rtsp", "sasl", "shortport", "sip", "slaxml", "smb", "smb2", "smbauth", "smtp", "snmp", "socks", "srvloc", "ssh1", "ssh2", "sslcert", "sslv2", "stdnse", "strbuf", --"strict", -- behaves oddly "stringaux", "stun", "tab", "tableaux", "target", "tftp", "tls", "tn3270", "tns", "unicode", "unittest", "unpwdb", "upnp", "url", "versant", "vnc", "vulns", "vuzedht", "wsdd", "xdmcp", "xmpp", "zlib", } -- This script-arg is documented in the unittest script to avoid cluttering -- NSEdoc of all the libraries which include this one. local am_testing = stdnse.get_script_args('unittest.run') ---Check whether tests are being run -- -- Libraries can use this function to avoid the overhead of creating tests if -- the user hasn't chosen to run them. Unittesting is turned on with the -- unittest.run script-arg. -- @return true if unittests are being run, false otherwise. function testing() return am_testing end --- -- Run tests provided by NSE libraries -- @param to_test A list (table) of libraries to test. If none is provided, all -- libraries are tested. run_tests = function(to_test) am_testing = true if to_test == nil then to_test = libs end local fails = stdnse.output_table() for _,lib in ipairs(to_test) do stdnse.debug1("Testing %s", lib) local status, thelib = pcall(require, lib) if not status then stdnse.debug1("Failed to load %s: %s", lib, thelib) else local failed = 0 if rawget(thelib,"test_suite") ~= nil then failed = thelib.test_suite() end if failed ~= 0 then fails[lib] = failed end end end return fails end --- The TestSuite class -- -- Holds and runs tests. TestSuite = { --- Creates a new TestSuite object -- -- @name TestSuite.new -- @return TestSuite object new = function(self) local o = {} setmetatable(o, self) self.__index = self o.tests = {} return o end, --- Set up test environment. Override this. -- @name TestSuite.setup setup = function(self) return true end, --- Tear down test environment. Override this. -- @name TestSuite.teardown teardown = function(self) return true end, --- Add a test. -- @name TestSuite.add_test -- @param test Function that will be called with the TestSuite object as its only parameter. -- @param description A description of the test being run add_test = function(self, test, description) self.tests[#self.tests+1] = {test, description} end, --- Run tests. -- Runs all tests in the TestSuite, and returns the number of failures. -- @name TestSuite.__call -- @return failures The number of tests that failed -- @return tests The number of tests run __call = function(self) local failures = 0 local passes = 0 self:setup() for _,test in ipairs(self.tests) do stdnse.debug2("| Test: %s...", test[2]) local status, note = test[1](self) local result local lvl = 2 if status then result = "Pass" passes = passes + 1 else result = "Fail" lvl = 1 if nmap.debugging() < 2 then stdnse.debug1("| Test: %s...", test[2]) end failures = failures + 1 end if note then stdnse.debug(lvl, "| \\_result: %s (%s)", result, note) else stdnse.debug(lvl, "| \\_result: %s", result) end end stdnse.debug1("|_%d of %d tests passed", passes, #self.tests) self:teardown() return failures, #self.tests end, } --- Test creation helper function. -- Turns a simple function into a test factory. -- @param test A function that returns true or false depending on test -- @param fmt A format string describing the failure condition using the -- arguments to the test function -- @return function that generates tests suitable for use in add_test make_test = function(test, fmt) return function(...) local args={...} local nargs = select("#", ...) return function(suite) if not test(table.unpack(args,1,nargs)) then return false, string.format(fmt, table.unpack(listop.map(nsedebug.tostr, args),1,nargs)) end return true end end end --- Test for nil -- @param value The value to test -- @return bool True if the value is nil, false otherwise. is_nil = function(value) return value == nil end is_nil = make_test(is_nil, "Expected nil, got %s") --- Test for not nil -- @param value The value to test -- @return bool True if the value is not nil, false otherwise. not_nil = function(value) return value ~= nil end not_nil = make_test(not_nil, "Expected not nil, got %s") --- Test for Lua type -- @param typ The type that value should be -- @param value The value to test -- @return bool True if type(value) == typ type_is = function (typ, value) return type(value) == typ end type_is = make_test(type_is, "Value is not a '%s': %s") --- Test tables for equality, 1 level deep -- @param a The first table to test -- @param b The second table to test -- @return bool True if #a == #b and a[i] == b[i] for every i<#a, false otherwise. table_equal = function(a, b) return function (suite) if #a ~= #b then return false, "Length not equal" end for i, v in ipairs(a) do if b[i] ~= v then return false, string.format("%s ~= %s at position %d", v, b[i], i) end end return true end end --- Test associative tables for equality, 1 level deep -- @param a The first table to test -- @param b The second table to test -- @return bool True if a[k] == b[k] for all k in a and b keys_equal = function(a, b) return function (suite) local seen = {} for k, v in pairs(a) do if b[k] ~= v then return false, ("%s ~= %s at key %s"):format(v, b[k], k) end seen[k] = true end for k, v in pairs(b) do if not seen[k] then return false, ("Key %s not present in table a"):format(k) end end return true end end --- Test two values for equality, recursively if necessary. -- -- This function checks that both values are indistinguishable in all -- but memory location. -- -- @param a The first value to test. -- @param b The second value to test -- @return bool True if values are indistinguishable, false otherwise. -- @return note Nil if values are indistinguishable, description of -- distinguishability otherwise. identical = function(a, b) return function(suite) local function _identical(val1, val2, path) local table_size = function(tbl) local count = 0 for k in pairs(tbl) do count = count + 1 end return count end -- Both values must be of the same type local t1, t2 = type(val1), type(val2) if t1 ~= t2 then return false, string.format("Types of %s are not equal: %s ~= %s", path, t1, t2) end -- For non-tables, we can make a direct comparison. if t1 ~= "table" then if val1 ~= val2 then return false, string.format("Values of %s are not equal: %s ~= %s", path, val1, val2) end return true end -- For tables, we must first check that they are of equal size. local len1, len2 = table_size(val1), table_size(val2) if len1 ~= len2 then return false, string.format("Sizes of %s are not equal: %s ~= %s", path, len1, len2) end -- Finally, we must recursively check all of the values in the tables. for k,v in pairs(val1) do -- Check that the key's value is identical in both tables, passing -- along the path of keys we have taken to get here. local status, note = _identical(val1[k], val2[k], string.format('%s["%s"]', path, k)) if not status then return false, note end end return true end return _identical(a, b, "") end end --- Test for equality -- @param a The first value to test -- @param b The second value to test -- @return bool True if a == b, false otherwise. equal = function(a, b) return a == b end equal = make_test(equal, "%s not equal to %s") --- Test for inequality -- @param a The first value to test -- @param b The second value to test -- @return bool True if a != b, false otherwise. not_equal = function(a, b) return a ~= b end not_equal = make_test(not_equal, "%s unexpectedly equal to %s") --- Test for truth -- @param value The value to test -- @return bool True if value is a boolean and true is_true = function(value) return value == true end is_true = make_test(is_true, "Expected true, got %s") --- Test for falsehood -- @param value The value to test -- @return bool True if value is a boolean and false is_false = function(value) return value == false end is_false = make_test(is_false, "Expected false, got %s") --- Test less than -- @param a The first value to test -- @param b The second value to test -- @return bool True if a < b, false otherwise. lt = function(a, b) return a < b end lt = make_test(lt, "%s not less than %s") --- Test less than or equal to -- @param a The first value to test -- @param b The second value to test -- @return bool True if a <= b, false otherwise. lte = function(a, b) return a <= b end lte = make_test(lte, "%s not less than %s") --- Test length -- @param t The table to test -- @param l The length to test -- @return bool True if the length of t is l length_is = function(t, l) return #t == l end length_is = make_test(length_is, "Length of %s is not %s") --- Expected failure test -- @param test The test to run -- @return function A test for expected failure of the test expected_failure = function(test) return function(suite) if test(suite) then return false, "Test unexpectedly passed" end return true, "Test failed as expected" end end if not testing() then return _ENV end -- Self test test_suite = TestSuite:new() test_suite:add_test(is_nil(test_suite["asdfdoesnotexist"]), "Nonexistent key does not exist") test_suite:add_test(equal(1+1336, 7 * 191), "Arithmetically equal expressions are equal") test_suite:add_test(not_equal( true, "true" ), "Boolean true not equal to string \"true\"") test_suite:add_test(is_true("test" == "test"), "Boolean expression evaluates to true") test_suite:add_test(is_false(1.9999 == 2.0), "Boolean expression evaluates to false") test_suite:add_test(lt(1, 999), "1 < 999") test_suite:add_test(lte(8, 8), "8 <= 8") test_suite:add_test(expected_failure(not_nil(nil)), "Test expected to fail fails") test_suite:add_test(expected_failure(expected_failure(is_nil(nil))), "Test expected to succeed does not fail") test_suite:add_test(keys_equal({one=1,two=2,[3]="three"},{[3]="three",one=1,two=2}), "identical tables are identical") test_suite:add_test(expected_failure(keys_equal({one=1,two=2},{[3]="three",one=1,two=2}), "dissimilar tables are dissimilar")) test_suite:add_test(identical(0, 0), "integer === integer") test_suite:add_test(identical(nil, nil), "nil === nil") test_suite:add_test(identical({}, {}), "{} === {}") test_suite:add_test(type_is("table", {}), "{} is a table") test_suite:add_test(length_is(test_suite.tests, 16), "Number of tests is 16") return _ENV;