PRESUBMIT_test.py 238 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738
  1. #!/usr/bin/env python3
  2. # Copyright 2012 The Chromium Authors
  3. # Use of this source code is governed by a BSD-style license that can be
  4. # found in the LICENSE file.
  5. import io
  6. import os.path
  7. import subprocess
  8. import textwrap
  9. import unittest
  10. import PRESUBMIT
  11. from PRESUBMIT_test_mocks import MockFile, MockAffectedFile
  12. from PRESUBMIT_test_mocks import MockInputApi, MockOutputApi
  13. _TEST_DATA_DIR = 'base/test/data/presubmit'
  14. class VersionControlConflictsTest(unittest.TestCase):
  15. def testTypicalConflict(self):
  16. lines = [
  17. '<<<<<<< HEAD', ' base::ScopedTempDir temp_dir_;', '=======',
  18. ' ScopedTempDir temp_dir_;', '>>>>>>> master'
  19. ]
  20. errors = PRESUBMIT._CheckForVersionControlConflictsInFile(
  21. MockInputApi(), MockFile('some/path/foo_platform.cc', lines))
  22. self.assertEqual(3, len(errors))
  23. self.assertTrue('1' in errors[0])
  24. self.assertTrue('3' in errors[1])
  25. self.assertTrue('5' in errors[2])
  26. def testIgnoresReadmes(self):
  27. lines = [
  28. 'A First Level Header', '====================', '',
  29. 'A Second Level Header', '---------------------'
  30. ]
  31. errors = PRESUBMIT._CheckForVersionControlConflictsInFile(
  32. MockInputApi(), MockFile('some/polymer/README.md', lines))
  33. self.assertEqual(0, len(errors))
  34. class BadExtensionsTest(unittest.TestCase):
  35. def testBadRejFile(self):
  36. mock_input_api = MockInputApi()
  37. mock_input_api.files = [
  38. MockFile('some/path/foo.cc', ''),
  39. MockFile('some/path/foo.cc.rej', ''),
  40. MockFile('some/path2/bar.h.rej', ''),
  41. ]
  42. results = PRESUBMIT.CheckPatchFiles(mock_input_api, MockOutputApi())
  43. self.assertEqual(1, len(results))
  44. self.assertEqual(2, len(results[0].items))
  45. self.assertTrue('foo.cc.rej' in results[0].items[0])
  46. self.assertTrue('bar.h.rej' in results[0].items[1])
  47. def testBadOrigFile(self):
  48. mock_input_api = MockInputApi()
  49. mock_input_api.files = [
  50. MockFile('other/path/qux.h.orig', ''),
  51. MockFile('other/path/qux.h', ''),
  52. MockFile('other/path/qux.cc', ''),
  53. ]
  54. results = PRESUBMIT.CheckPatchFiles(mock_input_api, MockOutputApi())
  55. self.assertEqual(1, len(results))
  56. self.assertEqual(1, len(results[0].items))
  57. self.assertTrue('qux.h.orig' in results[0].items[0])
  58. def testGoodFiles(self):
  59. mock_input_api = MockInputApi()
  60. mock_input_api.files = [
  61. MockFile('other/path/qux.h', ''),
  62. MockFile('other/path/qux.cc', ''),
  63. ]
  64. results = PRESUBMIT.CheckPatchFiles(mock_input_api, MockOutputApi())
  65. self.assertEqual(0, len(results))
  66. class CheckForSuperfluousStlIncludesInHeadersTest(unittest.TestCase):
  67. def testGoodFiles(self):
  68. mock_input_api = MockInputApi()
  69. mock_input_api.files = [
  70. # The check is not smart enough to figure out which definitions correspond
  71. # to which header.
  72. MockFile('other/path/foo.h', ['#include <string>', 'std::vector']),
  73. # The check is not smart enough to do IWYU.
  74. MockFile('other/path/bar.h',
  75. ['#include "base/check.h"', 'std::vector']),
  76. MockFile('other/path/qux.h',
  77. ['#include "base/stl_util.h"', 'foobar']),
  78. MockFile('other/path/baz.h',
  79. ['#include "set/vector.h"', 'bazzab']),
  80. # The check is only for header files.
  81. MockFile('other/path/not_checked.cc',
  82. ['#include <vector>', 'bazbaz']),
  83. ]
  84. results = PRESUBMIT.CheckForSuperfluousStlIncludesInHeaders(
  85. mock_input_api, MockOutputApi())
  86. self.assertEqual(0, len(results))
  87. def testBadFiles(self):
  88. mock_input_api = MockInputApi()
  89. mock_input_api.files = [
  90. MockFile('other/path/foo.h', ['#include <vector>', 'vector']),
  91. MockFile(
  92. 'other/path/bar.h',
  93. ['#include <limits>', '#include <set>', 'no_std_namespace']),
  94. ]
  95. results = PRESUBMIT.CheckForSuperfluousStlIncludesInHeaders(
  96. mock_input_api, MockOutputApi())
  97. self.assertEqual(1, len(results))
  98. self.assertTrue('foo.h: Includes STL' in results[0].message)
  99. self.assertTrue('bar.h: Includes STL' in results[0].message)
  100. class CheckSingletonInHeadersTest(unittest.TestCase):
  101. def testSingletonInArbitraryHeader(self):
  102. diff_singleton_h = [
  103. 'base::subtle::AtomicWord '
  104. 'base::Singleton<Type, Traits, DifferentiatingType>::'
  105. ]
  106. diff_foo_h = [
  107. '// base::Singleton<Foo> in comment.',
  108. 'friend class base::Singleton<Foo>'
  109. ]
  110. diff_foo2_h = [' //Foo* bar = base::Singleton<Foo>::get();']
  111. diff_bad_h = ['Foo* foo = base::Singleton<Foo>::get();']
  112. mock_input_api = MockInputApi()
  113. mock_input_api.files = [
  114. MockAffectedFile('base/memory/singleton.h', diff_singleton_h),
  115. MockAffectedFile('foo.h', diff_foo_h),
  116. MockAffectedFile('foo2.h', diff_foo2_h),
  117. MockAffectedFile('bad.h', diff_bad_h)
  118. ]
  119. warnings = PRESUBMIT.CheckSingletonInHeaders(mock_input_api,
  120. MockOutputApi())
  121. self.assertEqual(1, len(warnings))
  122. self.assertEqual(1, len(warnings[0].items))
  123. self.assertEqual('error', warnings[0].type)
  124. self.assertTrue('Found base::Singleton<T>' in warnings[0].message)
  125. def testSingletonInCC(self):
  126. diff_cc = ['Foo* foo = base::Singleton<Foo>::get();']
  127. mock_input_api = MockInputApi()
  128. mock_input_api.files = [MockAffectedFile('some/path/foo.cc', diff_cc)]
  129. warnings = PRESUBMIT.CheckSingletonInHeaders(mock_input_api,
  130. MockOutputApi())
  131. self.assertEqual(0, len(warnings))
  132. class DeprecatedOSMacroNamesTest(unittest.TestCase):
  133. def testDeprecatedOSMacroNames(self):
  134. lines = [
  135. '#if defined(OS_WIN)', ' #elif defined(OS_WINDOW)',
  136. ' # if defined(OS_MAC) || defined(OS_CHROME)'
  137. ]
  138. errors = PRESUBMIT._CheckForDeprecatedOSMacrosInFile(
  139. MockInputApi(), MockFile('some/path/foo_platform.cc', lines))
  140. self.assertEqual(len(lines) + 1, len(errors))
  141. self.assertTrue(
  142. ':1: defined(OS_WIN) -> BUILDFLAG(IS_WIN)' in errors[0])
  143. class InvalidIfDefinedMacroNamesTest(unittest.TestCase):
  144. def testInvalidIfDefinedMacroNames(self):
  145. lines = [
  146. '#if defined(TARGET_IPHONE_SIMULATOR)',
  147. '#if !defined(TARGET_IPHONE_SIMULATOR)',
  148. '#elif defined(TARGET_IPHONE_SIMULATOR)',
  149. '#ifdef TARGET_IPHONE_SIMULATOR',
  150. ' # ifdef TARGET_IPHONE_SIMULATOR',
  151. '# if defined(VALID) || defined(TARGET_IPHONE_SIMULATOR)',
  152. '# else // defined(TARGET_IPHONE_SIMULATOR)',
  153. '#endif // defined(TARGET_IPHONE_SIMULATOR)'
  154. ]
  155. errors = PRESUBMIT._CheckForInvalidIfDefinedMacrosInFile(
  156. MockInputApi(), MockFile('some/path/source.mm', lines))
  157. self.assertEqual(len(lines), len(errors))
  158. def testValidIfDefinedMacroNames(self):
  159. lines = [
  160. '#if defined(FOO)', '#ifdef BAR', '#if TARGET_IPHONE_SIMULATOR'
  161. ]
  162. errors = PRESUBMIT._CheckForInvalidIfDefinedMacrosInFile(
  163. MockInputApi(), MockFile('some/path/source.cc', lines))
  164. self.assertEqual(0, len(errors))
  165. class CheckNoUNIT_TESTInSourceFilesTest(unittest.TestCase):
  166. def testUnitTestMacros(self):
  167. lines = [
  168. '#if defined(UNIT_TEST)', '#if defined UNIT_TEST',
  169. '#if !defined(UNIT_TEST)', '#elif defined(UNIT_TEST)',
  170. '#ifdef UNIT_TEST', ' # ifdef UNIT_TEST', '#ifndef UNIT_TEST',
  171. '# if defined(VALID) || defined(UNIT_TEST)',
  172. '# if defined(UNIT_TEST) && defined(VALID)',
  173. '# else // defined(UNIT_TEST)', '#endif // defined(UNIT_TEST)'
  174. ]
  175. errors = PRESUBMIT._CheckNoUNIT_TESTInSourceFiles(
  176. MockInputApi(), MockFile('some/path/source.cc', lines))
  177. self.assertEqual(len(lines), len(errors))
  178. def testNotUnitTestMacros(self):
  179. lines = [
  180. '// Comment about "#if defined(UNIT_TEST)"',
  181. '/* Comment about #if defined(UNIT_TEST)" */',
  182. '#ifndef UNIT_TEST_H', '#define UNIT_TEST_H',
  183. '#ifndef TEST_UNIT_TEST', '#define TEST_UNIT_TEST',
  184. '#if defined(_UNIT_TEST)', '#if defined(UNIT_TEST_)',
  185. '#ifdef _UNIT_TEST', '#ifdef UNIT_TEST_', '#ifndef _UNIT_TEST',
  186. '#ifndef UNIT_TEST_'
  187. ]
  188. errors = PRESUBMIT._CheckNoUNIT_TESTInSourceFiles(
  189. MockInputApi(), MockFile('some/path/source.cc', lines))
  190. self.assertEqual(0, len(errors))
  191. class CheckEachPerfettoTestDataFileHasDepsEntry(unittest.TestCase):
  192. def testNewSha256FileNoDEPS(self):
  193. input_api = MockInputApi()
  194. input_api.files = [
  195. MockFile('base/tracing/test/data_sha256/new.pftrace.sha256', []),
  196. ]
  197. results = PRESUBMIT.CheckEachPerfettoTestDataFileHasDepsEntry(
  198. input_api, MockOutputApi())
  199. self.assertEqual(
  200. ('You must update the DEPS file when you update a .sha256 file '
  201. 'in base/tracing/test/data_sha256'), results[0].message)
  202. def testNewSha256FileSuccess(self):
  203. input_api = MockInputApi()
  204. new_deps = """deps = {
  205. 'src/base/tracing/test/data': {
  206. 'bucket': 'perfetto',
  207. 'objects': [
  208. {
  209. 'object_name': 'test_data/new.pftrace-a1b2c3f4',
  210. 'sha256sum': 'a1b2c3f4',
  211. 'size_bytes': 1,
  212. 'generation': 1,
  213. 'output_file': 'new.pftrace'
  214. },
  215. ],
  216. 'dep_type': 'gcs'
  217. },
  218. }""".splitlines()
  219. input_api.files = [
  220. MockFile('base/tracing/test/data_sha256/new.pftrace.sha256',
  221. ['a1b2c3f4']),
  222. MockFile('DEPS', new_deps,
  223. ["deps={'src/base/tracing/test/data':{}}"]),
  224. ]
  225. results = PRESUBMIT.CheckEachPerfettoTestDataFileHasDepsEntry(
  226. input_api, MockOutputApi())
  227. self.assertEqual(0, len(results))
  228. def testNewSha256FileWrongSha256(self):
  229. input_api = MockInputApi()
  230. new_deps = """deps = {
  231. 'src/base/tracing/test/data': {
  232. 'bucket': 'perfetto',
  233. 'objects': [
  234. {
  235. 'object_name': 'test_data/new.pftrace-a1b2c3f4',
  236. 'sha256sum': 'wrong_hash',
  237. 'size_bytes': 1,
  238. 'generation': 1,
  239. 'output_file': 'new.pftrace'
  240. },
  241. ],
  242. 'dep_type': 'gcs'
  243. },
  244. }""".splitlines()
  245. f = MockFile('base/tracing/test/data_sha256/new.pftrace.sha256',
  246. ['a1b2c3f4'])
  247. input_api.files = [
  248. f,
  249. MockFile('DEPS', new_deps,
  250. ["deps={'src/base/tracing/test/data':{}}"]),
  251. ]
  252. results = PRESUBMIT.CheckEachPerfettoTestDataFileHasDepsEntry(
  253. input_api, MockOutputApi())
  254. self.assertEqual(
  255. ('No corresponding DEPS entry found for %s. '
  256. 'Run `base/tracing/test/test_data.py get_deps --filepath %s` '
  257. 'to generate the DEPS entry.' % (f.LocalPath(), f.LocalPath())),
  258. results[0].message)
  259. def testDeleteSha256File(self):
  260. input_api = MockInputApi()
  261. old_deps = """deps = {
  262. 'src/base/tracing/test/data': {
  263. 'bucket': 'perfetto',
  264. 'objects': [
  265. {
  266. 'object_name': 'test_data/new.pftrace-a1b2c3f4',
  267. 'sha256sum': 'a1b2c3f4',
  268. 'size_bytes': 1,
  269. 'generation': 1,
  270. 'output_file': 'new.pftrace'
  271. },
  272. ],
  273. 'dep_type': 'gcs'
  274. },
  275. }""".splitlines()
  276. f = MockFile('base/tracing/test/data_sha256/new.pftrace.sha256', [],
  277. ['a1b2c3f4'],
  278. action='D')
  279. input_api.files = [
  280. f,
  281. MockFile('DEPS', old_deps, old_deps),
  282. ]
  283. results = PRESUBMIT.CheckEachPerfettoTestDataFileHasDepsEntry(
  284. input_api, MockOutputApi())
  285. self.assertEqual((
  286. 'You deleted %s so you must also remove the corresponding DEPS entry.'
  287. % f.LocalPath()), results[0].message)
  288. def testDeleteSha256Success(self):
  289. input_api = MockInputApi()
  290. new_deps = """deps = {
  291. 'src/base/tracing/test/data': {
  292. 'bucket': 'perfetto',
  293. 'objects': [],
  294. 'dep_type': 'gcs'
  295. },
  296. }""".splitlines()
  297. old_deps = """deps = {
  298. 'src/base/tracing/test/data': {
  299. 'bucket': 'perfetto',
  300. 'objects': [
  301. {
  302. 'object_name': 'test_data/new.pftrace-a1b2c3f4',
  303. 'sha256sum': 'a1b2c3f4',
  304. 'size_bytes': 1,
  305. 'generation': 1,
  306. 'output_file': 'new.pftrace'
  307. },
  308. ],
  309. 'dep_type': 'gcs'
  310. },
  311. }""".splitlines()
  312. f = MockFile('base/tracing/test/data_sha256/new.pftrace.sha256', [],
  313. ['a1b2c3f4'],
  314. action='D')
  315. input_api.files = [
  316. f,
  317. MockFile('DEPS', new_deps, old_deps),
  318. ]
  319. results = PRESUBMIT.CheckEachPerfettoTestDataFileHasDepsEntry(
  320. input_api, MockOutputApi())
  321. self.assertEqual(0, len(results))
  322. class CheckAddedDepsHaveTestApprovalsTest(unittest.TestCase):
  323. def setUp(self):
  324. self.input_api = input_api = MockInputApi()
  325. input_api.environ = {}
  326. input_api.owners_client = self.FakeOwnersClient()
  327. input_api.gerrit = self.fakeGerrit()
  328. input_api.change.issue = 123
  329. self.mockOwnersAndReviewers("owner", set(["reviewer"]))
  330. self.mockListSubmodules([])
  331. def calculate(self, old_include_rules, old_specific_include_rules,
  332. new_include_rules, new_specific_include_rules):
  333. return PRESUBMIT._CalculateAddedDeps(
  334. os.path, 'include_rules = %r\nspecific_include_rules = %r' %
  335. (old_include_rules, old_specific_include_rules),
  336. 'include_rules = %r\nspecific_include_rules = %r' %
  337. (new_include_rules, new_specific_include_rules))
  338. def testCalculateAddedDeps(self):
  339. old_include_rules = [
  340. '+base',
  341. '-chrome',
  342. '+content',
  343. '-grit',
  344. '-grit/",',
  345. '+jni/fooblat.h',
  346. '!sandbox',
  347. ]
  348. old_specific_include_rules = {
  349. 'compositor\.*': {
  350. '+cc',
  351. },
  352. }
  353. new_include_rules = [
  354. '-ash',
  355. '+base',
  356. '+chrome',
  357. '+components',
  358. '+content',
  359. '+grit',
  360. '+grit/generated_resources.h",',
  361. '+grit/",',
  362. '+jni/fooblat.h',
  363. '+policy',
  364. '+' + os.path.join('third_party', 'WebKit'),
  365. ]
  366. new_specific_include_rules = {
  367. 'compositor\.*': {
  368. '+cc',
  369. },
  370. 'widget\.*': {
  371. '+gpu',
  372. },
  373. }
  374. expected = set([
  375. os.path.join('chrome', 'DEPS'),
  376. os.path.join('gpu', 'DEPS'),
  377. os.path.join('components', 'DEPS'),
  378. os.path.join('policy', 'DEPS'),
  379. os.path.join('third_party', 'WebKit', 'DEPS'),
  380. ])
  381. self.assertEqual(
  382. expected,
  383. self.calculate(old_include_rules, old_specific_include_rules,
  384. new_include_rules, new_specific_include_rules))
  385. def testCalculateAddedDepsIgnoresPermutations(self):
  386. old_include_rules = [
  387. '+base',
  388. '+chrome',
  389. ]
  390. new_include_rules = [
  391. '+chrome',
  392. '+base',
  393. ]
  394. self.assertEqual(
  395. set(), self.calculate(old_include_rules, {}, new_include_rules,
  396. {}))
  397. def testFindAddedDepsThatRequireReview(self):
  398. caring = ['new_usages_require_review = True']
  399. self.input_api.InitFiles([
  400. MockAffectedFile('cares/DEPS', caring),
  401. MockAffectedFile('cares/inherits/DEPS', []),
  402. MockAffectedFile('willynilly/DEPS', []),
  403. MockAffectedFile('willynilly/butactually/DEPS', caring),
  404. ])
  405. expected = {
  406. 'cares': True,
  407. 'cares/sub/sub': True,
  408. 'cares/inherits': True,
  409. 'cares/inherits/sub': True,
  410. 'willynilly': False,
  411. 'willynilly/butactually': True,
  412. 'willynilly/butactually/sub': True,
  413. }
  414. results = PRESUBMIT._FindAddedDepsThatRequireReview(
  415. self.input_api, set(expected))
  416. actual = {k: k in results for k in expected}
  417. self.assertEqual(expected, actual)
  418. class FakeOwnersClient(object):
  419. APPROVED = "APPROVED"
  420. PENDING = "PENDING"
  421. returns = {}
  422. def ListOwners(self, *args, **kwargs):
  423. return self.returns.get(self.ListOwners.__name__, "")
  424. def mockListOwners(self, owners):
  425. self.returns[self.ListOwners.__name__] = owners
  426. def GetFilesApprovalStatus(self, *args, **kwargs):
  427. return self.returns.get(self.GetFilesApprovalStatus.__name__, {})
  428. def mockGetFilesApprovalStatus(self, status):
  429. self.returns[self.GetFilesApprovalStatus.__name__] = status
  430. def SuggestOwners(self, *args, **kwargs):
  431. return ["eng1", "eng2", "eng3"]
  432. class fakeGerrit(object):
  433. def IsOwnersOverrideApproved(self, issue):
  434. return False
  435. def mockOwnersAndReviewers(self, owner, reviewers):
  436. def mock(*args, **kwargs):
  437. return [owner, reviewers]
  438. self.input_api.canned_checks.GetCodereviewOwnerAndReviewers = mock
  439. def mockListSubmodules(self, paths):
  440. def mock(*args, **kwargs):
  441. return paths
  442. self.input_api.ListSubmodules = mock
  443. def testApprovedAdditionalDep(self):
  444. self.input_api.InitFiles([
  445. MockAffectedFile('pdf/DEPS',
  446. ['include_rules=["+v8/123", "+foo/bar"]']),
  447. MockAffectedFile('v8/DEPS', ['new_usages_require_review=True']),
  448. # Check that we ignore "DEPS" directories. Note there are real cases
  449. # of directories named "deps/" and, especially for case-insensitive file
  450. # systems we should prevent these from being considered.
  451. MockAffectedFile('foo/bar/DEPS/boofar', ['boofar file contents']),
  452. ])
  453. # mark the additional dep as approved.
  454. os_path = self.input_api.os_path
  455. self.input_api.owners_client.mockGetFilesApprovalStatus(
  456. {os_path.join('v8/123', 'DEPS'): self.FakeOwnersClient.APPROVED})
  457. results = PRESUBMIT.CheckAddedDepsHaveTargetApprovals(
  458. self.input_api, MockOutputApi())
  459. # Then, the check should pass.
  460. self.assertEqual([], results)
  461. def testUnapprovedAdditionalDep(self):
  462. self.input_api.InitFiles([
  463. MockAffectedFile('pdf/DEPS', ['include_rules=["+v8/123"]']),
  464. MockAffectedFile('v8/DEPS', ['new_usages_require_review=True']),
  465. ])
  466. # pending.
  467. os_path = self.input_api.os_path
  468. self.input_api.owners_client.mockGetFilesApprovalStatus(
  469. {os_path.join('v8/123', 'DEPS'): self.FakeOwnersClient.PENDING})
  470. results = PRESUBMIT.CheckAddedDepsHaveTargetApprovals(
  471. self.input_api, MockOutputApi())
  472. # the check should fail
  473. self.assertIn('You need LGTM', results[0].message)
  474. self.assertIn('+v8/123', results[0].message)
  475. # unless the added dep is from a submodule.
  476. self.mockListSubmodules(['v8'])
  477. results = PRESUBMIT.CheckAddedDepsHaveTargetApprovals(
  478. self.input_api, MockOutputApi())
  479. self.assertEqual([], results)
  480. class JSONParsingTest(unittest.TestCase):
  481. def testSuccess(self):
  482. input_api = MockInputApi()
  483. filename = 'valid_json.json'
  484. contents = [
  485. '// This is a comment.', '{', ' "key1": ["value1", "value2"],',
  486. ' "key2": 3 // This is an inline comment.', '}'
  487. ]
  488. input_api.files = [MockFile(filename, contents)]
  489. self.assertEqual(None,
  490. PRESUBMIT._GetJSONParseError(input_api, filename))
  491. def testFailure(self):
  492. input_api = MockInputApi()
  493. test_data = [
  494. ('invalid_json_1.json', ['{ x }'], 'Expecting property name'),
  495. ('invalid_json_2.json', ['// Hello world!', '{ "hello": "world }'],
  496. 'Unterminated string starting at:'),
  497. ('invalid_json_3.json', ['{ "a": "b", "c": "d", }'],
  498. 'Expecting property name'),
  499. ('invalid_json_4.json', ['{ "a": "b" "c": "d" }'],
  500. "Expecting ',' delimiter:"),
  501. ]
  502. input_api.files = [
  503. MockFile(filename, contents)
  504. for (filename, contents, _) in test_data
  505. ]
  506. for (filename, _, expected_error) in test_data:
  507. actual_error = PRESUBMIT._GetJSONParseError(input_api, filename)
  508. self.assertTrue(
  509. expected_error in str(actual_error),
  510. "'%s' not found in '%s'" % (expected_error, actual_error))
  511. def testNoEatComments(self):
  512. input_api = MockInputApi()
  513. file_with_comments = 'file_with_comments.json'
  514. contents_with_comments = [
  515. '// This is a comment.', '{', ' "key1": ["value1", "value2"],',
  516. ' "key2": 3 // This is an inline comment.', '}'
  517. ]
  518. file_without_comments = 'file_without_comments.json'
  519. contents_without_comments = [
  520. '{', ' "key1": ["value1", "value2"],', ' "key2": 3', '}'
  521. ]
  522. input_api.files = [
  523. MockFile(file_with_comments, contents_with_comments),
  524. MockFile(file_without_comments, contents_without_comments)
  525. ]
  526. self.assertNotEqual(
  527. None,
  528. str(
  529. PRESUBMIT._GetJSONParseError(input_api,
  530. file_with_comments,
  531. eat_comments=False)))
  532. self.assertEqual(
  533. None,
  534. PRESUBMIT._GetJSONParseError(input_api,
  535. file_without_comments,
  536. eat_comments=False))
  537. class IDLParsingTest(unittest.TestCase):
  538. def testSuccess(self):
  539. input_api = MockInputApi()
  540. filename = 'valid_idl_basics.idl'
  541. contents = [
  542. '// Tests a valid IDL file.', 'namespace idl_basics {',
  543. ' enum EnumType {', ' name1,', ' name2', ' };', '',
  544. ' dictionary MyType1 {', ' DOMString a;', ' };', '',
  545. ' callback Callback1 = void();',
  546. ' callback Callback2 = void(long x);',
  547. ' callback Callback3 = void(MyType1 arg);',
  548. ' callback Callback4 = void(EnumType type);', '',
  549. ' interface Functions {', ' static void function1();',
  550. ' static void function2(long x);',
  551. ' static void function3(MyType1 arg);',
  552. ' static void function4(Callback1 cb);',
  553. ' static void function5(Callback2 cb);',
  554. ' static void function6(Callback3 cb);',
  555. ' static void function7(Callback4 cb);', ' };', '',
  556. ' interface Events {', ' static void onFoo1();',
  557. ' static void onFoo2(long x);',
  558. ' static void onFoo2(MyType1 arg);',
  559. ' static void onFoo3(EnumType type);', ' };', '};'
  560. ]
  561. input_api.files = [MockFile(filename, contents)]
  562. self.assertEqual(None,
  563. PRESUBMIT._GetIDLParseError(input_api, filename))
  564. def testFailure(self):
  565. input_api = MockInputApi()
  566. test_data = [
  567. ('invalid_idl_1.idl', [
  568. '//', 'namespace test {', ' dictionary {', ' DOMString s;',
  569. ' };', '};'
  570. ], 'Unexpected "{" after keyword "dictionary".\n'),
  571. # TODO(yoz): Disabled because it causes the IDL parser to hang.
  572. # See crbug.com/363830.
  573. # ('invalid_idl_2.idl',
  574. # (['namespace test {',
  575. # ' dictionary MissingSemicolon {',
  576. # ' DOMString a',
  577. # ' DOMString b;',
  578. # ' };',
  579. # '};'],
  580. # 'Unexpected symbol DOMString after symbol a.'),
  581. ('invalid_idl_3.idl', [
  582. '//', 'namespace test {', ' enum MissingComma {', ' name1',
  583. ' name2', ' };', '};'
  584. ], 'Unexpected symbol name2 after symbol name1.'),
  585. ('invalid_idl_4.idl', [
  586. '//', 'namespace test {', ' enum TrailingComma {',
  587. ' name1,', ' name2,', ' };', '};'
  588. ], 'Trailing comma in block.'),
  589. ('invalid_idl_5.idl',
  590. ['//', 'namespace test {', ' callback Callback1 = void(;',
  591. '};'], 'Unexpected ";" after "(".'),
  592. ('invalid_idl_6.idl', [
  593. '//', 'namespace test {',
  594. ' callback Callback1 = void(long );', '};'
  595. ], 'Unexpected ")" after symbol long.'),
  596. ('invalid_idl_7.idl', [
  597. '//', 'namespace test {', ' interace Events {',
  598. ' static void onFoo1();', ' };', '};'
  599. ], 'Unexpected symbol Events after symbol interace.'),
  600. ('invalid_idl_8.idl', [
  601. '//', 'namespace test {', ' interface NotEvent {',
  602. ' static void onFoo1();', ' };', '};'
  603. ], 'Did not process Interface Interface(NotEvent)'),
  604. ('invalid_idl_9.idl', [
  605. '//', 'namespace test {', ' interface {',
  606. ' static void function1();', ' };', '};'
  607. ], 'Interface missing name.'),
  608. ]
  609. input_api.files = [
  610. MockFile(filename, contents)
  611. for (filename, contents, _) in test_data
  612. ]
  613. for (filename, _, expected_error) in test_data:
  614. actual_error = PRESUBMIT._GetIDLParseError(input_api, filename)
  615. self.assertTrue(
  616. expected_error in str(actual_error),
  617. "'%s' not found in '%s'" % (expected_error, actual_error))
  618. class UserMetricsActionTest(unittest.TestCase):
  619. def testUserMetricsActionInActions(self):
  620. input_api = MockInputApi()
  621. file_with_user_action = 'file_with_user_action.cc'
  622. contents_with_user_action = ['base::UserMetricsAction("AboutChrome")']
  623. input_api.files = [
  624. MockFile(file_with_user_action, contents_with_user_action)
  625. ]
  626. self.assertEqual([],
  627. PRESUBMIT.CheckUserActionUpdate(
  628. input_api, MockOutputApi()))
  629. def testUserMetricsActionNotAddedToActions(self):
  630. input_api = MockInputApi()
  631. file_with_user_action = 'file_with_user_action.cc'
  632. contents_with_user_action = [
  633. 'base::UserMetricsAction("NotInActionsXml")'
  634. ]
  635. input_api.files = [
  636. MockFile(file_with_user_action, contents_with_user_action)
  637. ]
  638. output = PRESUBMIT.CheckUserActionUpdate(input_api, MockOutputApi())
  639. self.assertEqual(
  640. ('File %s line %d: %s is missing in '
  641. 'tools/metrics/actions/actions.xml. Please run '
  642. 'tools/metrics/actions/extract_actions.py to update.' %
  643. (file_with_user_action, 1, 'NotInActionsXml')), output[0].message)
  644. def testUserMetricsActionInTestFile(self):
  645. input_api = MockInputApi()
  646. file_with_user_action = 'file_with_user_action_unittest.cc'
  647. contents_with_user_action = [
  648. 'base::UserMetricsAction("NotInActionsXml")'
  649. ]
  650. input_api.files = [
  651. MockFile(file_with_user_action, contents_with_user_action)
  652. ]
  653. self.assertEqual([],
  654. PRESUBMIT.CheckUserActionUpdate(
  655. input_api, MockOutputApi()))
  656. class PydepsNeedsUpdatingTest(unittest.TestCase):
  657. class MockPopen:
  658. def __init__(self, stdout):
  659. self.stdout = io.StringIO(stdout)
  660. def wait(self):
  661. return 0
  662. class MockSubprocess:
  663. CalledProcessError = subprocess.CalledProcessError
  664. PIPE = 0
  665. def __init__(self):
  666. self._popen_func = None
  667. def SetPopenCallback(self, func):
  668. self._popen_func = func
  669. def Popen(self, cmd, *args, **kwargs):
  670. return PydepsNeedsUpdatingTest.MockPopen(self._popen_func(cmd))
  671. def _MockParseGclientArgs(self, is_android=True):
  672. return lambda: {'checkout_android': 'true' if is_android else 'false'}
  673. def setUp(self):
  674. mock_all_pydeps = ['A.pydeps', 'B.pydeps', 'D.pydeps']
  675. self.old_ALL_PYDEPS_FILES = PRESUBMIT._ALL_PYDEPS_FILES
  676. PRESUBMIT._ALL_PYDEPS_FILES = mock_all_pydeps
  677. mock_android_pydeps = ['D.pydeps']
  678. self.old_ANDROID_SPECIFIC_PYDEPS_FILES = (
  679. PRESUBMIT._ANDROID_SPECIFIC_PYDEPS_FILES)
  680. PRESUBMIT._ANDROID_SPECIFIC_PYDEPS_FILES = mock_android_pydeps
  681. self.old_ParseGclientArgs = PRESUBMIT._ParseGclientArgs
  682. PRESUBMIT._ParseGclientArgs = self._MockParseGclientArgs()
  683. self.mock_input_api = MockInputApi()
  684. self.mock_output_api = MockOutputApi()
  685. self.mock_input_api.subprocess = PydepsNeedsUpdatingTest.MockSubprocess(
  686. )
  687. self.checker = PRESUBMIT.PydepsChecker(self.mock_input_api,
  688. mock_all_pydeps)
  689. self.checker._file_cache = {
  690. 'A.pydeps':
  691. '# Generated by:\n# CMD --output A.pydeps A\nA.py\nC.py\n',
  692. 'B.pydeps':
  693. '# Generated by:\n# CMD --output B.pydeps B\nB.py\nC.py\n',
  694. 'D.pydeps': '# Generated by:\n# CMD --output D.pydeps D\nD.py\n',
  695. }
  696. def tearDown(self):
  697. PRESUBMIT._ALL_PYDEPS_FILES = self.old_ALL_PYDEPS_FILES
  698. PRESUBMIT._ANDROID_SPECIFIC_PYDEPS_FILES = (
  699. self.old_ANDROID_SPECIFIC_PYDEPS_FILES)
  700. PRESUBMIT._ParseGclientArgs = self.old_ParseGclientArgs
  701. def _RunCheck(self):
  702. return PRESUBMIT.CheckPydepsNeedsUpdating(
  703. self.mock_input_api,
  704. self.mock_output_api,
  705. checker_for_tests=self.checker)
  706. def testAddedPydep(self):
  707. # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
  708. if not self.mock_input_api.platform.startswith('linux'):
  709. return []
  710. self.mock_input_api.InitFiles([
  711. MockAffectedFile('new.pydeps', [], action='A'),
  712. ])
  713. results = self._RunCheck()
  714. self.assertEqual(1, len(results))
  715. self.assertIn('PYDEPS_FILES', str(results[0]))
  716. def testPydepNotInSrc(self):
  717. self.mock_input_api.InitFiles([
  718. MockAffectedFile('new.pydeps', [], action='A'),
  719. ])
  720. self.mock_input_api.os_path.exists = lambda x: False
  721. results = self._RunCheck()
  722. self.assertEqual(0, len(results))
  723. def testRemovedPydep(self):
  724. # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
  725. if not self.mock_input_api.platform.startswith('linux'):
  726. return []
  727. self.mock_input_api.InitFiles([
  728. MockAffectedFile(PRESUBMIT._ALL_PYDEPS_FILES[0], [], action='D'),
  729. ])
  730. results = self._RunCheck()
  731. self.assertEqual(1, len(results))
  732. self.assertIn('PYDEPS_FILES', str(results[0]))
  733. def testRandomPyIgnored(self):
  734. # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
  735. if not self.mock_input_api.platform.startswith('linux'):
  736. return []
  737. self.mock_input_api.files = [
  738. MockAffectedFile('random.py', []),
  739. ]
  740. results = self._RunCheck()
  741. self.assertEqual(0, len(results), 'Unexpected results: %r' % results)
  742. def testRelevantPyNoChange(self):
  743. # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
  744. if not self.mock_input_api.platform.startswith('linux'):
  745. return []
  746. self.mock_input_api.files = [
  747. MockAffectedFile('A.py', []),
  748. ]
  749. def popen_callback(cmd):
  750. self.assertEqual('CMD --output A.pydeps A --output ""', cmd)
  751. return self.checker._file_cache['A.pydeps']
  752. self.mock_input_api.subprocess.SetPopenCallback(popen_callback)
  753. results = self._RunCheck()
  754. self.assertEqual(0, len(results), 'Unexpected results: %r' % results)
  755. def testRelevantPyOneChange(self):
  756. # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
  757. if not self.mock_input_api.platform.startswith('linux'):
  758. return []
  759. self.mock_input_api.files = [
  760. MockAffectedFile('A.py', []),
  761. ]
  762. def popen_callback(cmd):
  763. self.assertEqual('CMD --output A.pydeps A --output ""', cmd)
  764. return 'changed data'
  765. self.mock_input_api.subprocess.SetPopenCallback(popen_callback)
  766. results = self._RunCheck()
  767. self.assertEqual(1, len(results))
  768. # Check that --output "" is not included.
  769. self.assertNotIn('""', str(results[0]))
  770. self.assertIn('File is stale', str(results[0]))
  771. def testRelevantPyTwoChanges(self):
  772. # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
  773. if not self.mock_input_api.platform.startswith('linux'):
  774. return []
  775. self.mock_input_api.files = [
  776. MockAffectedFile('C.py', []),
  777. ]
  778. def popen_callback(cmd):
  779. return 'changed data'
  780. self.mock_input_api.subprocess.SetPopenCallback(popen_callback)
  781. results = self._RunCheck()
  782. self.assertEqual(2, len(results))
  783. self.assertIn('File is stale', str(results[0]))
  784. self.assertIn('File is stale', str(results[1]))
  785. def testRelevantAndroidPyInNonAndroidCheckout(self):
  786. # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
  787. if not self.mock_input_api.platform.startswith('linux'):
  788. return []
  789. self.mock_input_api.files = [
  790. MockAffectedFile('D.py', []),
  791. ]
  792. def popen_callback(cmd):
  793. self.assertEqual('CMD --output D.pydeps D --output ""', cmd)
  794. return 'changed data'
  795. self.mock_input_api.subprocess.SetPopenCallback(popen_callback)
  796. PRESUBMIT._ParseGclientArgs = self._MockParseGclientArgs(
  797. is_android=False)
  798. results = self._RunCheck()
  799. self.assertEqual(1, len(results))
  800. self.assertIn('Android', str(results[0]))
  801. self.assertIn('D.pydeps', str(results[0]))
  802. def testGnPathsAndMissingOutputFlag(self):
  803. # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
  804. if not self.mock_input_api.platform.startswith('linux'):
  805. return []
  806. self.checker._file_cache = {
  807. 'A.pydeps':
  808. '# Generated by:\n# CMD --gn-paths A\n//A.py\n//C.py\n',
  809. 'B.pydeps':
  810. '# Generated by:\n# CMD --gn-paths B\n//B.py\n//C.py\n',
  811. 'D.pydeps': '# Generated by:\n# CMD --gn-paths D\n//D.py\n',
  812. }
  813. self.mock_input_api.files = [
  814. MockAffectedFile('A.py', []),
  815. ]
  816. def popen_callback(cmd):
  817. self.assertEqual('CMD --gn-paths A --output A.pydeps --output ""',
  818. cmd)
  819. return 'changed data'
  820. self.mock_input_api.subprocess.SetPopenCallback(popen_callback)
  821. results = self._RunCheck()
  822. self.assertEqual(1, len(results))
  823. self.assertIn('File is stale', str(results[0]))
  824. class IncludeGuardTest(unittest.TestCase):
  825. def testIncludeGuardChecks(self):
  826. mock_input_api = MockInputApi()
  827. mock_output_api = MockOutputApi()
  828. mock_input_api.files = [
  829. MockAffectedFile('content/browser/thing/foo.h', [
  830. '// Comment',
  831. '#ifndef CONTENT_BROWSER_THING_FOO_H_',
  832. '#define CONTENT_BROWSER_THING_FOO_H_',
  833. 'struct McBoatFace;',
  834. '#endif // CONTENT_BROWSER_THING_FOO_H_',
  835. ]),
  836. MockAffectedFile('content/browser/thing/bar.h', [
  837. '#ifndef CONTENT_BROWSER_THING_BAR_H_',
  838. '#define CONTENT_BROWSER_THING_BAR_H_',
  839. 'namespace content {',
  840. '#endif // CONTENT_BROWSER_THING_BAR_H_',
  841. '} // namespace content',
  842. ]),
  843. MockAffectedFile('content/browser/test1.h', [
  844. 'namespace content {',
  845. '} // namespace content',
  846. ]),
  847. MockAffectedFile('content\\browser\\win.h', [
  848. '#ifndef CONTENT_BROWSER_WIN_H_',
  849. '#define CONTENT_BROWSER_WIN_H_',
  850. 'struct McBoatFace;',
  851. '#endif // CONTENT_BROWSER_WIN_H_',
  852. ]),
  853. MockAffectedFile('content/browser/test2.h', [
  854. '// Comment',
  855. '#ifndef CONTENT_BROWSER_TEST2_H_',
  856. 'struct McBoatFace;',
  857. '#endif // CONTENT_BROWSER_TEST2_H_',
  858. ]),
  859. MockAffectedFile('content/browser/internal.h', [
  860. '// Comment',
  861. '#ifndef CONTENT_BROWSER_INTERNAL_H_',
  862. '#define CONTENT_BROWSER_INTERNAL_H_',
  863. '// Comment',
  864. '#ifndef INTERNAL_CONTENT_BROWSER_INTERNAL_H_',
  865. '#define INTERNAL_CONTENT_BROWSER_INTERNAL_H_',
  866. 'namespace internal {',
  867. '} // namespace internal',
  868. '#endif // INTERNAL_CONTENT_BROWSER_THING_BAR_H_',
  869. 'namespace content {',
  870. '} // namespace content',
  871. '#endif // CONTENT_BROWSER_THING_BAR_H_',
  872. ]),
  873. MockAffectedFile('content/browser/thing/foo.cc', [
  874. '// This is a non-header.',
  875. ]),
  876. MockAffectedFile('content/browser/disabled.h', [
  877. '// no-include-guard-because-multiply-included',
  878. 'struct McBoatFace;',
  879. ]),
  880. # New files don't allow misspelled include guards.
  881. MockAffectedFile('content/browser/spleling.h', [
  882. '#ifndef CONTENT_BROWSER_SPLLEING_H_',
  883. '#define CONTENT_BROWSER_SPLLEING_H_',
  884. 'struct McBoatFace;',
  885. '#endif // CONTENT_BROWSER_SPLLEING_H_',
  886. ]),
  887. # New files don't allow + in include guards.
  888. MockAffectedFile('content/browser/foo+bar.h', [
  889. '#ifndef CONTENT_BROWSER_FOO+BAR_H_',
  890. '#define CONTENT_BROWSER_FOO+BAR_H_',
  891. 'struct McBoatFace;',
  892. '#endif // CONTENT_BROWSER_FOO+BAR_H_',
  893. ]),
  894. # Old files allow misspelled include guards (for now).
  895. MockAffectedFile('chrome/old.h', [
  896. '// New contents',
  897. '#ifndef CHROME_ODL_H_',
  898. '#define CHROME_ODL_H_',
  899. '#endif // CHROME_ODL_H_',
  900. ], [
  901. '// Old contents',
  902. '#ifndef CHROME_ODL_H_',
  903. '#define CHROME_ODL_H_',
  904. '#endif // CHROME_ODL_H_',
  905. ],
  906. action='M'),
  907. # Using a Blink style include guard outside Blink is wrong.
  908. MockAffectedFile('content/NotInBlink.h', [
  909. '#ifndef NotInBlink_h',
  910. '#define NotInBlink_h',
  911. 'struct McBoatFace;',
  912. '#endif // NotInBlink_h',
  913. ]),
  914. # Using a Blink style include guard in Blink is no longer ok.
  915. MockAffectedFile('third_party/blink/InBlink.h', [
  916. '#ifndef InBlink_h',
  917. '#define InBlink_h',
  918. 'struct McBoatFace;',
  919. '#endif // InBlink_h',
  920. ]),
  921. # Using a bad include guard in Blink is not ok.
  922. MockAffectedFile('third_party/blink/AlsoInBlink.h', [
  923. '#ifndef WrongInBlink_h',
  924. '#define WrongInBlink_h',
  925. 'struct McBoatFace;',
  926. '#endif // WrongInBlink_h',
  927. ]),
  928. # Using a bad include guard in Blink is not supposed to be accepted even
  929. # if it's an old file. However the current presubmit has accepted this
  930. # for a while.
  931. MockAffectedFile('third_party/blink/StillInBlink.h', [
  932. '// New contents',
  933. '#ifndef AcceptedInBlink_h',
  934. '#define AcceptedInBlink_h',
  935. 'struct McBoatFace;',
  936. '#endif // AcceptedInBlink_h',
  937. ], [
  938. '// Old contents',
  939. '#ifndef AcceptedInBlink_h',
  940. '#define AcceptedInBlink_h',
  941. 'struct McBoatFace;',
  942. '#endif // AcceptedInBlink_h',
  943. ],
  944. action='M'),
  945. # Using a non-Chromium include guard in third_party
  946. # (outside blink) is accepted.
  947. MockAffectedFile('third_party/foo/some_file.h', [
  948. '#ifndef REQUIRED_RPCNDR_H_',
  949. '#define REQUIRED_RPCNDR_H_',
  950. 'struct SomeFileFoo;',
  951. '#endif // REQUIRED_RPCNDR_H_',
  952. ]),
  953. # Not having proper include guard in *_message_generator.h
  954. # for old IPC messages is allowed.
  955. MockAffectedFile('content/common/content_message_generator.h', [
  956. '#undef CONTENT_COMMON_FOO_MESSAGES_H_',
  957. '#include "content/common/foo_messages.h"',
  958. '#ifndef CONTENT_COMMON_FOO_MESSAGES_H_',
  959. '#error "Failed to include content/common/foo_messages.h"',
  960. '#endif',
  961. ]),
  962. MockAffectedFile('chrome/renderer/thing/qux.h', [
  963. '// Comment',
  964. '#ifndef CHROME_RENDERER_THING_QUX_H_',
  965. '#define CHROME_RENDERER_THING_QUX_H_',
  966. 'struct Boaty;',
  967. '#endif',
  968. ]),
  969. ]
  970. msgs = PRESUBMIT.CheckForIncludeGuards(mock_input_api, mock_output_api)
  971. expected_fail_count = 10
  972. self.assertEqual(
  973. expected_fail_count, len(msgs), 'Expected %d items, found %d: %s' %
  974. (expected_fail_count, len(msgs), msgs))
  975. self.assertEqual(msgs[0].items, ['content/browser/thing/bar.h'])
  976. self.assertEqual(
  977. msgs[0].message, 'Include guard CONTENT_BROWSER_THING_BAR_H_ '
  978. 'not covering the whole file')
  979. self.assertIn('content/browser/test1.h', msgs[1].message)
  980. self.assertIn('Recommended name: CONTENT_BROWSER_TEST1_H_',
  981. msgs[1].message)
  982. self.assertEqual(msgs[2].items, ['content/browser/test2.h:3'])
  983. self.assertEqual(
  984. msgs[2].message, 'Missing "#define CONTENT_BROWSER_TEST2_H_" for '
  985. 'include guard')
  986. self.assertIn('content/browser/internal.h', msgs[3].message)
  987. self.assertIn(
  988. 'Recommended #endif comment: // CONTENT_BROWSER_INTERNAL_H_',
  989. msgs[3].message)
  990. self.assertEqual(msgs[4].items, ['content/browser/spleling.h:1'])
  991. self.assertEqual(
  992. msgs[4].message, 'Header using the wrong include guard name '
  993. 'CONTENT_BROWSER_SPLLEING_H_')
  994. self.assertIn('content/browser/foo+bar.h', msgs[5].message)
  995. self.assertIn('Recommended name: CONTENT_BROWSER_FOO_BAR_H_',
  996. msgs[5].message)
  997. self.assertEqual(msgs[6].items, ['content/NotInBlink.h:1'])
  998. self.assertEqual(
  999. msgs[6].message, 'Header using the wrong include guard name '
  1000. 'NotInBlink_h')
  1001. self.assertEqual(msgs[7].items, ['third_party/blink/InBlink.h:1'])
  1002. self.assertEqual(
  1003. msgs[7].message, 'Header using the wrong include guard name '
  1004. 'InBlink_h')
  1005. self.assertEqual(msgs[8].items, ['third_party/blink/AlsoInBlink.h:1'])
  1006. self.assertEqual(
  1007. msgs[8].message, 'Header using the wrong include guard name '
  1008. 'WrongInBlink_h')
  1009. self.assertIn('chrome/renderer/thing/qux.h', msgs[9].message)
  1010. self.assertIn(
  1011. 'Recommended #endif comment: // CHROME_RENDERER_THING_QUX_H_',
  1012. msgs[9].message)
  1013. class AccessibilityRelnotesFieldTest(unittest.TestCase):
  1014. def testRelnotesPresent(self):
  1015. mock_input_api = MockInputApi()
  1016. mock_output_api = MockOutputApi()
  1017. mock_input_api.files = [
  1018. MockAffectedFile('ui/accessibility/foo.bar', [''])
  1019. ]
  1020. mock_input_api.change.DescriptionText = lambda: 'Commit description'
  1021. mock_input_api.change.footers['AX-Relnotes'] = [
  1022. 'Important user facing change'
  1023. ]
  1024. msgs = PRESUBMIT.CheckAccessibilityRelnotesField(
  1025. mock_input_api, mock_output_api)
  1026. self.assertEqual(
  1027. 0, len(msgs),
  1028. 'Expected %d messages, found %d: %s' % (0, len(msgs), msgs))
  1029. def testRelnotesMissingFromAccessibilityChange(self):
  1030. mock_input_api = MockInputApi()
  1031. mock_output_api = MockOutputApi()
  1032. mock_input_api.files = [
  1033. MockAffectedFile('some/file', ['']),
  1034. MockAffectedFile('ui/accessibility/foo.bar', ['']),
  1035. MockAffectedFile('some/other/file', [''])
  1036. ]
  1037. mock_input_api.change.DescriptionText = lambda: 'Commit description'
  1038. msgs = PRESUBMIT.CheckAccessibilityRelnotesField(
  1039. mock_input_api, mock_output_api)
  1040. self.assertEqual(
  1041. 1, len(msgs),
  1042. 'Expected %d messages, found %d: %s' % (1, len(msgs), msgs))
  1043. self.assertTrue(
  1044. "Missing 'AX-Relnotes:' field" in msgs[0].message,
  1045. 'Missing AX-Relnotes field message not found in errors')
  1046. # The relnotes footer is not required for changes which do not touch any
  1047. # accessibility directories.
  1048. def testIgnoresNonAccessibilityCode(self):
  1049. mock_input_api = MockInputApi()
  1050. mock_output_api = MockOutputApi()
  1051. mock_input_api.files = [
  1052. MockAffectedFile('some/file', ['']),
  1053. MockAffectedFile('some/other/file', [''])
  1054. ]
  1055. mock_input_api.change.DescriptionText = lambda: 'Commit description'
  1056. msgs = PRESUBMIT.CheckAccessibilityRelnotesField(
  1057. mock_input_api, mock_output_api)
  1058. self.assertEqual(
  1059. 0, len(msgs),
  1060. 'Expected %d messages, found %d: %s' % (0, len(msgs), msgs))
  1061. # Test that our presubmit correctly raises an error for a set of known paths.
  1062. def testExpectedPaths(self):
  1063. filesToTest = [
  1064. "chrome/browser/accessibility/foo.py",
  1065. "chrome/browser/ash/arc/accessibility/foo.cc",
  1066. "chrome/browser/ui/views/accessibility/foo.h",
  1067. "chrome/browser/extensions/api/automation/foo.h",
  1068. "chrome/browser/extensions/api/automation_internal/foo.cc",
  1069. "chrome/renderer/extensions/accessibility_foo.h",
  1070. "chrome/tests/data/accessibility/foo.html",
  1071. "content/browser/accessibility/foo.cc",
  1072. "content/renderer/accessibility/foo.h",
  1073. "content/tests/data/accessibility/foo.cc",
  1074. "extensions/renderer/api/automation/foo.h",
  1075. "ui/accessibility/foo/bar/baz.cc",
  1076. "ui/views/accessibility/foo/bar/baz.h",
  1077. ]
  1078. for testFile in filesToTest:
  1079. mock_input_api = MockInputApi()
  1080. mock_output_api = MockOutputApi()
  1081. mock_input_api.files = [MockAffectedFile(testFile, [''])]
  1082. mock_input_api.change.DescriptionText = lambda: 'Commit description'
  1083. msgs = PRESUBMIT.CheckAccessibilityRelnotesField(
  1084. mock_input_api, mock_output_api)
  1085. self.assertEqual(
  1086. 1, len(msgs),
  1087. 'Expected %d messages, found %d: %s, for file %s' %
  1088. (1, len(msgs), msgs, testFile))
  1089. self.assertTrue(
  1090. "Missing 'AX-Relnotes:' field" in msgs[0].message,
  1091. ('Missing AX-Relnotes field message not found in errors '
  1092. ' for file %s' % (testFile)))
  1093. # Test that AX-Relnotes field can appear in the commit description (as long
  1094. # as it appears at the beginning of a line).
  1095. def testRelnotesInCommitDescription(self):
  1096. mock_input_api = MockInputApi()
  1097. mock_output_api = MockOutputApi()
  1098. mock_input_api.files = [
  1099. MockAffectedFile('ui/accessibility/foo.bar', ['']),
  1100. ]
  1101. mock_input_api.change.DescriptionText = lambda: (
  1102. 'Description:\n' +
  1103. 'AX-Relnotes: solves all accessibility issues forever')
  1104. msgs = PRESUBMIT.CheckAccessibilityRelnotesField(
  1105. mock_input_api, mock_output_api)
  1106. self.assertEqual(
  1107. 0, len(msgs),
  1108. 'Expected %d messages, found %d: %s' % (0, len(msgs), msgs))
  1109. # Test that we don't match AX-Relnotes if it appears in the middle of a line.
  1110. def testRelnotesMustAppearAtBeginningOfLine(self):
  1111. mock_input_api = MockInputApi()
  1112. mock_output_api = MockOutputApi()
  1113. mock_input_api.files = [
  1114. MockAffectedFile('ui/accessibility/foo.bar', ['']),
  1115. ]
  1116. mock_input_api.change.DescriptionText = lambda: (
  1117. 'Description:\n' +
  1118. 'This change has no AX-Relnotes: we should print a warning')
  1119. msgs = PRESUBMIT.CheckAccessibilityRelnotesField(
  1120. mock_input_api, mock_output_api)
  1121. self.assertTrue(
  1122. "Missing 'AX-Relnotes:' field" in msgs[0].message,
  1123. 'Missing AX-Relnotes field message not found in errors')
  1124. # Tests that the AX-Relnotes field can be lowercase and use a '=' in place
  1125. # of a ':'.
  1126. def testRelnotesLowercaseWithEqualSign(self):
  1127. mock_input_api = MockInputApi()
  1128. mock_output_api = MockOutputApi()
  1129. mock_input_api.files = [
  1130. MockAffectedFile('ui/accessibility/foo.bar', ['']),
  1131. ]
  1132. mock_input_api.change.DescriptionText = lambda: (
  1133. 'Description:\n' +
  1134. 'ax-relnotes= this is a valid format for accessibility relnotes')
  1135. msgs = PRESUBMIT.CheckAccessibilityRelnotesField(
  1136. mock_input_api, mock_output_api)
  1137. self.assertEqual(
  1138. 0, len(msgs),
  1139. 'Expected %d messages, found %d: %s' % (0, len(msgs), msgs))
  1140. class AccessibilityAriaElementAttributeGettersTest(unittest.TestCase):
  1141. # Test warning is surfaced for various possible uses of bad methods.
  1142. def testMatchingLines(self):
  1143. mock_input_api = MockInputApi()
  1144. mock_input_api.files = [
  1145. MockFile(
  1146. "third_party/blink/renderer/core/accessibility/ax_object.h",
  1147. [
  1148. "->getAttribute(html_names::kAriaCheckedAttr)",
  1149. "node->hasAttribute(html_names::kRoleAttr)",
  1150. "->FastHasAttribute(html_names::kAriaLabelAttr)",
  1151. " .FastGetAttribute(html_names::kAriaCurrentAttr);",
  1152. ],
  1153. action='M'
  1154. ),
  1155. MockFile(
  1156. "third_party/blink/renderer/core/accessibility/ax_table.cc",
  1157. [
  1158. "bool result = node->hasAttribute(html_names::kFooAttr);",
  1159. "foo->getAttribute(html_names::kAriaInvalidValueAttr)",
  1160. "foo->GetAriaCurrentState(html_names::kAriaCurrentStateAttr)",
  1161. ],
  1162. action='M'
  1163. ),
  1164. ]
  1165. results = PRESUBMIT.CheckAccessibilityAriaElementAttributeGetters(mock_input_api, MockOutputApi())
  1166. self.assertEqual(1, len(results))
  1167. self.assertEqual(5, len(results[0].items))
  1168. self.assertIn("ax_object.h:1", results[0].items[0])
  1169. self.assertIn("ax_object.h:2", results[0].items[1])
  1170. self.assertIn("ax_object.h:3", results[0].items[2])
  1171. self.assertIn("ax_object.h:4", results[0].items[3])
  1172. self.assertIn("ax_table.cc:2", results[0].items[4])
  1173. self.assertIn("Please use ARIA-specific attribute access", results[0].message)
  1174. # Test no warnings for files that are not accessibility related.
  1175. def testNonMatchingFiles(self):
  1176. mock_input_api = MockInputApi()
  1177. mock_input_api.files = [
  1178. MockFile(
  1179. "content/browser/foobar/foo.cc",
  1180. ["->getAttribute(html_names::kAriaCheckedAttr)"],
  1181. action='M'),
  1182. MockFile(
  1183. "third_party/blink/renderer/core/foo.cc",
  1184. ["node->hasAttribute(html_names::kRoleAttr)"],
  1185. action='M'),
  1186. ]
  1187. results = PRESUBMIT.CheckAccessibilityAriaElementAttributeGetters(mock_input_api, MockOutputApi())
  1188. self.assertEqual(0, len(results))
  1189. # Test no warning when methods are used with different attribute params.
  1190. def testNoBadParam(self):
  1191. mock_input_api = MockInputApi()
  1192. mock_input_api.files = [
  1193. MockFile(
  1194. "third_party/blink/renderer/core/accessibility/ax_object.h",
  1195. [
  1196. "->getAttribute(html_names::kCheckedAttr)",
  1197. "->hasAttribute(html_names::kIdAttr)",
  1198. ],
  1199. action='M'
  1200. )
  1201. ]
  1202. results = PRESUBMIT.CheckAccessibilityAriaElementAttributeGetters(mock_input_api, MockOutputApi())
  1203. self.assertEqual(0, len(results))
  1204. # Test no warning when attribute params are used for different methods.
  1205. def testNoMethod(self):
  1206. mock_input_api = MockInputApi()
  1207. mock_input_api.files = [
  1208. MockFile(
  1209. "third_party/blink/renderer/core/accessibility/ax_object.cc",
  1210. [
  1211. "foo(html_names::kAriaCheckedAttr)",
  1212. "bar(html_names::kRoleAttr)"
  1213. ],
  1214. action='M'
  1215. )
  1216. ]
  1217. results = PRESUBMIT.CheckAccessibilityAriaElementAttributeGetters(mock_input_api, MockOutputApi())
  1218. self.assertEqual(0, len(results))
  1219. class AndroidDeprecatedTestAnnotationTest(unittest.TestCase):
  1220. def testCheckAndroidTestAnnotationUsage(self):
  1221. mock_input_api = MockInputApi()
  1222. mock_output_api = MockOutputApi()
  1223. mock_input_api.files = [
  1224. MockAffectedFile('LalaLand.java', ['random stuff']),
  1225. MockAffectedFile('CorrectUsage.java', [
  1226. 'import androidx.test.filters.LargeTest;',
  1227. 'import androidx.test.filters.MediumTest;',
  1228. 'import androidx.test.filters.SmallTest;',
  1229. ]),
  1230. MockAffectedFile('UsedDeprecatedLargeTestAnnotation.java', [
  1231. 'import android.test.suitebuilder.annotation.LargeTest;',
  1232. ]),
  1233. MockAffectedFile('UsedDeprecatedMediumTestAnnotation.java', [
  1234. 'import android.test.suitebuilder.annotation.MediumTest;',
  1235. ]),
  1236. MockAffectedFile('UsedDeprecatedSmallTestAnnotation.java', [
  1237. 'import android.test.suitebuilder.annotation.SmallTest;',
  1238. ]),
  1239. MockAffectedFile('UsedDeprecatedSmokeAnnotation.java', [
  1240. 'import android.test.suitebuilder.annotation.Smoke;',
  1241. ])
  1242. ]
  1243. msgs = PRESUBMIT._CheckAndroidTestAnnotationUsage(
  1244. mock_input_api, mock_output_api)
  1245. self.assertEqual(
  1246. 1, len(msgs),
  1247. 'Expected %d items, found %d: %s' % (1, len(msgs), msgs))
  1248. self.assertEqual(
  1249. 4, len(msgs[0].items), 'Expected %d items, found %d: %s' %
  1250. (4, len(msgs[0].items), msgs[0].items))
  1251. self.assertTrue(
  1252. 'UsedDeprecatedLargeTestAnnotation.java:1' in msgs[0].items,
  1253. 'UsedDeprecatedLargeTestAnnotation not found in errors')
  1254. self.assertTrue(
  1255. 'UsedDeprecatedMediumTestAnnotation.java:1' in msgs[0].items,
  1256. 'UsedDeprecatedMediumTestAnnotation not found in errors')
  1257. self.assertTrue(
  1258. 'UsedDeprecatedSmallTestAnnotation.java:1' in msgs[0].items,
  1259. 'UsedDeprecatedSmallTestAnnotation not found in errors')
  1260. self.assertTrue(
  1261. 'UsedDeprecatedSmokeAnnotation.java:1' in msgs[0].items,
  1262. 'UsedDeprecatedSmokeAnnotation not found in errors')
  1263. class AndroidBannedImportTest(unittest.TestCase):
  1264. def testCheckAndroidNoBannedImports(self):
  1265. mock_input_api = MockInputApi()
  1266. mock_output_api = MockOutputApi()
  1267. test_files = [
  1268. MockAffectedFile('RandomStufff.java', ['random stuff']),
  1269. MockAffectedFile('NoBannedImports.java', [
  1270. 'import androidx.test.filters.LargeTest;',
  1271. 'import androidx.test.filters.MediumTest;',
  1272. 'import androidx.test.filters.SmallTest;',
  1273. ]),
  1274. MockAffectedFile('BannedUri.java', [
  1275. 'import java.net.URI;',
  1276. ]),
  1277. MockAffectedFile('BannedTargetApi.java', [
  1278. 'import android.annotation.TargetApi;',
  1279. ]),
  1280. MockAffectedFile('BannedActivityTestRule.java', [
  1281. 'import androidx.test.rule.ActivityTestRule;',
  1282. ]),
  1283. MockAffectedFile('BannedVectorDrawableCompat.java', [
  1284. 'import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;',
  1285. ])
  1286. ]
  1287. msgs = []
  1288. for file in test_files:
  1289. mock_input_api.files = [file]
  1290. msgs.append(
  1291. PRESUBMIT._CheckAndroidNoBannedImports(mock_input_api,
  1292. mock_output_api))
  1293. self.assertEqual(0, len(msgs[0]))
  1294. self.assertEqual(0, len(msgs[1]))
  1295. self.assertTrue(msgs[2][0].message.startswith(
  1296. textwrap.dedent("""\
  1297. Banned imports were used.
  1298. BannedUri.java:1:""")))
  1299. self.assertTrue(msgs[3][0].message.startswith(
  1300. textwrap.dedent("""\
  1301. Banned imports were used.
  1302. BannedTargetApi.java:1:""")))
  1303. self.assertTrue(msgs[4][0].message.startswith(
  1304. textwrap.dedent("""\
  1305. Banned imports were used.
  1306. BannedActivityTestRule.java:1:""")))
  1307. self.assertTrue(msgs[5][0].message.startswith(
  1308. textwrap.dedent("""\
  1309. Banned imports were used.
  1310. BannedVectorDrawableCompat.java:1:""")))
  1311. class CheckNoDownstreamDepsTest(unittest.TestCase):
  1312. def testInvalidDepFromUpstream(self):
  1313. mock_input_api = MockInputApi()
  1314. mock_output_api = MockOutputApi()
  1315. mock_input_api.files = [
  1316. MockAffectedFile('BUILD.gn',
  1317. ['deps = [', ' "//clank/target:test",', ']']),
  1318. MockAffectedFile('chrome/android/BUILD.gn',
  1319. ['deps = [ "//clank/target:test" ]']),
  1320. MockAffectedFile(
  1321. 'chrome/chrome_java_deps.gni',
  1322. ['java_deps = [', ' "//clank/target:test",', ']']),
  1323. ]
  1324. mock_input_api.change.RepositoryRoot = lambda: 'chromium/src'
  1325. msgs = PRESUBMIT.CheckNoUpstreamDepsOnClank(mock_input_api,
  1326. mock_output_api)
  1327. self.assertEqual(
  1328. 1, len(msgs),
  1329. 'Expected %d items, found %d: %s' % (1, len(msgs), msgs))
  1330. self.assertEqual(
  1331. 3, len(msgs[0].items), 'Expected %d items, found %d: %s' %
  1332. (3, len(msgs[0].items), msgs[0].items))
  1333. self.assertTrue(any('BUILD.gn:2' in item for item in msgs[0].items),
  1334. 'BUILD.gn not found in errors')
  1335. self.assertTrue(
  1336. any('chrome/android/BUILD.gn:1' in item for item in msgs[0].items),
  1337. 'chrome/android/BUILD.gn:1 not found in errors')
  1338. self.assertTrue(
  1339. any('chrome/chrome_java_deps.gni:2' in item
  1340. for item in msgs[0].items),
  1341. 'chrome/chrome_java_deps.gni:2 not found in errors')
  1342. def testAllowsComments(self):
  1343. mock_input_api = MockInputApi()
  1344. mock_output_api = MockOutputApi()
  1345. mock_input_api.files = [
  1346. MockAffectedFile('BUILD.gn', [
  1347. '# real implementation in //clank/target:test',
  1348. ]),
  1349. ]
  1350. mock_input_api.change.RepositoryRoot = lambda: 'chromium/src'
  1351. msgs = PRESUBMIT.CheckNoUpstreamDepsOnClank(mock_input_api,
  1352. mock_output_api)
  1353. self.assertEqual(
  1354. 0, len(msgs),
  1355. 'Expected %d items, found %d: %s' % (0, len(msgs), msgs))
  1356. def testOnlyChecksBuildFiles(self):
  1357. mock_input_api = MockInputApi()
  1358. mock_output_api = MockOutputApi()
  1359. mock_input_api.files = [
  1360. MockAffectedFile('README.md',
  1361. ['DEPS = [ "//clank/target:test" ]']),
  1362. MockAffectedFile('chrome/android/java/file.java',
  1363. ['//clank/ only function']),
  1364. ]
  1365. mock_input_api.change.RepositoryRoot = lambda: 'chromium/src'
  1366. msgs = PRESUBMIT.CheckNoUpstreamDepsOnClank(mock_input_api,
  1367. mock_output_api)
  1368. self.assertEqual(
  1369. 0, len(msgs),
  1370. 'Expected %d items, found %d: %s' % (0, len(msgs), msgs))
  1371. def testValidDepFromDownstream(self):
  1372. mock_input_api = MockInputApi()
  1373. mock_output_api = MockOutputApi()
  1374. mock_input_api.files = [
  1375. MockAffectedFile('BUILD.gn',
  1376. ['DEPS = [', ' "//clank/target:test",', ']']),
  1377. MockAffectedFile('java/BUILD.gn',
  1378. ['DEPS = [ "//clank/target:test" ]']),
  1379. ]
  1380. mock_input_api.change.RepositoryRoot = lambda: 'chromium/src/clank'
  1381. msgs = PRESUBMIT.CheckNoUpstreamDepsOnClank(mock_input_api,
  1382. mock_output_api)
  1383. self.assertEqual(
  1384. 0, len(msgs),
  1385. 'Expected %d items, found %d: %s' % (0, len(msgs), msgs))
  1386. class AndroidDebuggableBuildTest(unittest.TestCase):
  1387. def testCheckAndroidDebuggableBuild(self):
  1388. mock_input_api = MockInputApi()
  1389. mock_output_api = MockOutputApi()
  1390. mock_input_api.files = [
  1391. MockAffectedFile('RandomStuff.java', ['random stuff']),
  1392. MockAffectedFile('CorrectUsage.java', [
  1393. 'import org.chromium.base.BuildInfo;',
  1394. 'some random stuff',
  1395. 'boolean isOsDebuggable = BuildInfo.isDebugAndroid();',
  1396. ]),
  1397. MockAffectedFile('JustCheckUserdebugBuild.java', [
  1398. 'import android.os.Build;',
  1399. 'some random stuff',
  1400. 'boolean isOsDebuggable = Build.TYPE.equals("userdebug")',
  1401. ]),
  1402. MockAffectedFile('JustCheckEngineeringBuild.java', [
  1403. 'import android.os.Build;',
  1404. 'some random stuff',
  1405. 'boolean isOsDebuggable = "eng".equals(Build.TYPE)',
  1406. ]),
  1407. MockAffectedFile('UsedBuildType.java', [
  1408. 'import android.os.Build;',
  1409. 'some random stuff',
  1410. 'boolean isOsDebuggable = Build.TYPE.equals("userdebug")'
  1411. '|| "eng".equals(Build.TYPE)',
  1412. ]),
  1413. MockAffectedFile('UsedExplicitBuildType.java', [
  1414. 'some random stuff',
  1415. 'boolean isOsDebuggable = android.os.Build.TYPE.equals("userdebug")'
  1416. '|| "eng".equals(android.os.Build.TYPE)',
  1417. ]),
  1418. ]
  1419. msgs = PRESUBMIT._CheckAndroidDebuggableBuild(mock_input_api,
  1420. mock_output_api)
  1421. self.assertEqual(
  1422. 1, len(msgs),
  1423. 'Expected %d items, found %d: %s' % (1, len(msgs), msgs))
  1424. self.assertEqual(
  1425. 4, len(msgs[0].items), 'Expected %d items, found %d: %s' %
  1426. (4, len(msgs[0].items), msgs[0].items))
  1427. self.assertTrue('JustCheckUserdebugBuild.java:3' in msgs[0].items)
  1428. self.assertTrue('JustCheckEngineeringBuild.java:3' in msgs[0].items)
  1429. self.assertTrue('UsedBuildType.java:3' in msgs[0].items)
  1430. self.assertTrue('UsedExplicitBuildType.java:2' in msgs[0].items)
  1431. class LogUsageTest(unittest.TestCase):
  1432. def testCheckAndroidCrLogUsage(self):
  1433. mock_input_api = MockInputApi()
  1434. mock_output_api = MockOutputApi()
  1435. mock_input_api.files = [
  1436. MockAffectedFile('RandomStuff.java', ['random stuff']),
  1437. MockAffectedFile('HasAndroidLog.java', [
  1438. 'import android.util.Log;',
  1439. 'some random stuff',
  1440. 'Log.d("TAG", "foo");',
  1441. ]),
  1442. MockAffectedFile('HasExplicitUtilLog.java', [
  1443. 'some random stuff',
  1444. 'android.util.Log.d("TAG", "foo");',
  1445. ]),
  1446. MockAffectedFile('IsInBasePackage.java', [
  1447. 'package org.chromium.base;',
  1448. 'private static final String TAG = "cr_Foo";',
  1449. 'Log.d(TAG, "foo");',
  1450. ]),
  1451. MockAffectedFile('IsInBasePackageButImportsLog.java', [
  1452. 'package org.chromium.base;',
  1453. 'import android.util.Log;',
  1454. 'private static final String TAG = "cr_Foo";',
  1455. 'Log.d(TAG, "foo");',
  1456. ]),
  1457. MockAffectedFile('HasBothLog.java', [
  1458. 'import org.chromium.base.Log;',
  1459. 'some random stuff',
  1460. 'private static final String TAG = "cr_Foo";',
  1461. 'Log.d(TAG, "foo");',
  1462. 'android.util.Log.d("TAG", "foo");',
  1463. ]),
  1464. MockAffectedFile('HasCorrectTag.java', [
  1465. 'import org.chromium.base.Log;',
  1466. 'some random stuff',
  1467. 'private static final String TAG = "cr_Foo";',
  1468. 'Log.d(TAG, "foo");',
  1469. ]),
  1470. MockAffectedFile('HasOldTag.java', [
  1471. 'import org.chromium.base.Log;',
  1472. 'some random stuff',
  1473. 'private static final String TAG = "cr.Foo";',
  1474. 'Log.d(TAG, "foo");',
  1475. ]),
  1476. MockAffectedFile('HasDottedTag.java', [
  1477. 'import org.chromium.base.Log;',
  1478. 'some random stuff',
  1479. 'private static final String TAG = "cr_foo.bar";',
  1480. 'Log.d(TAG, "foo");',
  1481. ]),
  1482. MockAffectedFile('HasDottedTagPublic.java', [
  1483. 'import org.chromium.base.Log;',
  1484. 'some random stuff',
  1485. 'public static final String TAG = "cr_foo.bar";',
  1486. 'Log.d(TAG, "foo");',
  1487. ]),
  1488. MockAffectedFile('HasNoTagDecl.java', [
  1489. 'import org.chromium.base.Log;',
  1490. 'some random stuff',
  1491. 'Log.d(TAG, "foo");',
  1492. ]),
  1493. MockAffectedFile('HasIncorrectTagDecl.java', [
  1494. 'import org.chromium.base.Log;',
  1495. 'private static final String TAHG = "cr_Foo";',
  1496. 'some random stuff',
  1497. 'Log.d(TAG, "foo");',
  1498. ]),
  1499. MockAffectedFile('HasInlineTag.java', [
  1500. 'import org.chromium.base.Log;',
  1501. 'some random stuff',
  1502. 'private static final String TAG = "cr_Foo";',
  1503. 'Log.d("TAG", "foo");',
  1504. ]),
  1505. MockAffectedFile('HasInlineTagWithSpace.java', [
  1506. 'import org.chromium.base.Log;',
  1507. 'some random stuff',
  1508. 'private static final String TAG = "cr_Foo";',
  1509. 'Log.d("log message", "foo");',
  1510. ]),
  1511. MockAffectedFile('HasUnprefixedTag.java', [
  1512. 'import org.chromium.base.Log;',
  1513. 'some random stuff',
  1514. 'private static final String TAG = "rubbish";',
  1515. 'Log.d(TAG, "foo");',
  1516. ]),
  1517. MockAffectedFile('HasTooLongTag.java', [
  1518. 'import org.chromium.base.Log;',
  1519. 'some random stuff',
  1520. 'private static final String TAG = "21_characters_long___";',
  1521. 'Log.d(TAG, "foo");',
  1522. ]),
  1523. MockAffectedFile('HasTooLongTagWithNoLogCallsInDiff.java', [
  1524. 'import org.chromium.base.Log;',
  1525. 'some random stuff',
  1526. 'private static final String TAG = "21_characters_long___";',
  1527. ]),
  1528. ]
  1529. msgs = PRESUBMIT._CheckAndroidCrLogUsage(mock_input_api,
  1530. mock_output_api)
  1531. self.assertEqual(
  1532. 5, len(msgs),
  1533. 'Expected %d items, found %d: %s' % (5, len(msgs), msgs))
  1534. # Declaration format
  1535. nb = len(msgs[0].items)
  1536. self.assertEqual(
  1537. 2, nb, 'Expected %d items, found %d: %s' % (2, nb, msgs[0].items))
  1538. self.assertTrue('HasNoTagDecl.java' in msgs[0].items)
  1539. self.assertTrue('HasIncorrectTagDecl.java' in msgs[0].items)
  1540. # Tag length
  1541. nb = len(msgs[1].items)
  1542. self.assertEqual(
  1543. 2, nb, 'Expected %d items, found %d: %s' % (2, nb, msgs[1].items))
  1544. self.assertTrue('HasTooLongTag.java' in msgs[1].items)
  1545. self.assertTrue(
  1546. 'HasTooLongTagWithNoLogCallsInDiff.java' in msgs[1].items)
  1547. # Tag must be a variable named TAG
  1548. nb = len(msgs[2].items)
  1549. self.assertEqual(
  1550. 3, nb, 'Expected %d items, found %d: %s' % (3, nb, msgs[2].items))
  1551. self.assertTrue('HasBothLog.java:5' in msgs[2].items)
  1552. self.assertTrue('HasInlineTag.java:4' in msgs[2].items)
  1553. self.assertTrue('HasInlineTagWithSpace.java:4' in msgs[2].items)
  1554. # Util Log usage
  1555. nb = len(msgs[3].items)
  1556. self.assertEqual(
  1557. 3, nb, 'Expected %d items, found %d: %s' % (3, nb, msgs[3].items))
  1558. self.assertTrue('HasAndroidLog.java:3' in msgs[3].items)
  1559. self.assertTrue('HasExplicitUtilLog.java:2' in msgs[3].items)
  1560. self.assertTrue('IsInBasePackageButImportsLog.java:4' in msgs[3].items)
  1561. # Tag must not contain
  1562. nb = len(msgs[4].items)
  1563. self.assertEqual(
  1564. 3, nb, 'Expected %d items, found %d: %s' % (2, nb, msgs[4].items))
  1565. self.assertTrue('HasDottedTag.java' in msgs[4].items)
  1566. self.assertTrue('HasDottedTagPublic.java' in msgs[4].items)
  1567. self.assertTrue('HasOldTag.java' in msgs[4].items)
  1568. class GoogleAnswerUrlFormatTest(unittest.TestCase):
  1569. def testCatchAnswerUrlId(self):
  1570. input_api = MockInputApi()
  1571. input_api.files = [
  1572. MockFile('somewhere/file.cc', [
  1573. 'char* host = '
  1574. ' "https://support.google.com/chrome/answer/123456";'
  1575. ]),
  1576. MockFile('somewhere_else/file.cc', [
  1577. 'char* host = '
  1578. ' "https://support.google.com/chrome/a/answer/123456";'
  1579. ]),
  1580. ]
  1581. warnings = PRESUBMIT.CheckGoogleSupportAnswerUrlOnUpload(
  1582. input_api, MockOutputApi())
  1583. self.assertEqual(1, len(warnings))
  1584. self.assertEqual(2, len(warnings[0].items))
  1585. def testAllowAnswerUrlParam(self):
  1586. input_api = MockInputApi()
  1587. input_api.files = [
  1588. MockFile('somewhere/file.cc', [
  1589. 'char* host = '
  1590. ' "https://support.google.com/chrome/?p=cpn_crash_reports";'
  1591. ]),
  1592. ]
  1593. warnings = PRESUBMIT.CheckGoogleSupportAnswerUrlOnUpload(
  1594. input_api, MockOutputApi())
  1595. self.assertEqual(0, len(warnings))
  1596. class HardcodedGoogleHostsTest(unittest.TestCase):
  1597. def testWarnOnAssignedLiterals(self):
  1598. input_api = MockInputApi()
  1599. input_api.files = [
  1600. MockFile('content/file.cc',
  1601. ['char* host = "https://www.google.com";']),
  1602. MockFile('content/file.cc',
  1603. ['char* host = "https://www.googleapis.com";']),
  1604. MockFile('content/file.cc',
  1605. ['char* host = "https://clients1.google.com";']),
  1606. ]
  1607. warnings = PRESUBMIT.CheckHardcodedGoogleHostsInLowerLayers(
  1608. input_api, MockOutputApi())
  1609. self.assertEqual(1, len(warnings))
  1610. self.assertEqual(3, len(warnings[0].items))
  1611. def testAllowInComment(self):
  1612. input_api = MockInputApi()
  1613. input_api.files = [
  1614. MockFile('content/file.cc',
  1615. ['char* host = "https://www.aol.com"; // google.com'])
  1616. ]
  1617. warnings = PRESUBMIT.CheckHardcodedGoogleHostsInLowerLayers(
  1618. input_api, MockOutputApi())
  1619. self.assertEqual(0, len(warnings))
  1620. class ChromeOsSyncedPrefRegistrationTest(unittest.TestCase):
  1621. def testWarnsOnChromeOsDirectories(self):
  1622. files = [
  1623. MockFile('ash/file.cc', ['PrefRegistrySyncable::SYNCABLE_PREF']),
  1624. MockFile('chrome/browser/chromeos/file.cc',
  1625. ['PrefRegistrySyncable::SYNCABLE_PREF']),
  1626. MockFile('chromeos/file.cc',
  1627. ['PrefRegistrySyncable::SYNCABLE_PREF']),
  1628. MockFile('components/arc/file.cc',
  1629. ['PrefRegistrySyncable::SYNCABLE_PREF']),
  1630. MockFile('components/exo/file.cc',
  1631. ['PrefRegistrySyncable::SYNCABLE_PREF']),
  1632. ]
  1633. input_api = MockInputApi()
  1634. for file in files:
  1635. input_api.files = [file]
  1636. warnings = PRESUBMIT.CheckChromeOsSyncedPrefRegistration(
  1637. input_api, MockOutputApi())
  1638. self.assertEqual(1, len(warnings))
  1639. def testDoesNotWarnOnSyncOsPref(self):
  1640. input_api = MockInputApi()
  1641. input_api.files = [
  1642. MockFile('chromeos/file.cc',
  1643. ['PrefRegistrySyncable::SYNCABLE_OS_PREF']),
  1644. ]
  1645. warnings = PRESUBMIT.CheckChromeOsSyncedPrefRegistration(
  1646. input_api, MockOutputApi())
  1647. self.assertEqual(0, len(warnings))
  1648. def testDoesNotWarnOnOtherDirectories(self):
  1649. input_api = MockInputApi()
  1650. input_api.files = [
  1651. MockFile('chrome/browser/ui/file.cc',
  1652. ['PrefRegistrySyncable::SYNCABLE_PREF']),
  1653. MockFile('components/sync/file.cc',
  1654. ['PrefRegistrySyncable::SYNCABLE_PREF']),
  1655. MockFile('content/browser/file.cc',
  1656. ['PrefRegistrySyncable::SYNCABLE_PREF']),
  1657. MockFile('a/notchromeos/file.cc',
  1658. ['PrefRegistrySyncable::SYNCABLE_PREF']),
  1659. ]
  1660. warnings = PRESUBMIT.CheckChromeOsSyncedPrefRegistration(
  1661. input_api, MockOutputApi())
  1662. self.assertEqual(0, len(warnings))
  1663. def testSeparateWarningForPriorityPrefs(self):
  1664. input_api = MockInputApi()
  1665. input_api.files = [
  1666. MockFile('chromeos/file.cc', [
  1667. 'PrefRegistrySyncable::SYNCABLE_PREF',
  1668. 'PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF'
  1669. ]),
  1670. ]
  1671. warnings = PRESUBMIT.CheckChromeOsSyncedPrefRegistration(
  1672. input_api, MockOutputApi())
  1673. self.assertEqual(2, len(warnings))
  1674. class ForwardDeclarationTest(unittest.TestCase):
  1675. def testCheckHeadersOnlyOutsideThirdParty(self):
  1676. mock_input_api = MockInputApi()
  1677. mock_input_api.files = [
  1678. MockAffectedFile('somewhere/file.cc', ['class DummyClass;']),
  1679. MockAffectedFile('third_party/header.h', ['class DummyClass;'])
  1680. ]
  1681. warnings = PRESUBMIT.CheckUselessForwardDeclarations(
  1682. mock_input_api, MockOutputApi())
  1683. self.assertEqual(0, len(warnings))
  1684. def testNoNestedDeclaration(self):
  1685. mock_input_api = MockInputApi()
  1686. mock_input_api.files = [
  1687. MockAffectedFile('somewhere/header.h', [
  1688. 'class SomeClass {', ' protected:', ' class NotAMatch;', '};'
  1689. ])
  1690. ]
  1691. warnings = PRESUBMIT.CheckUselessForwardDeclarations(
  1692. mock_input_api, MockOutputApi())
  1693. self.assertEqual(0, len(warnings))
  1694. def testSubStrings(self):
  1695. mock_input_api = MockInputApi()
  1696. mock_input_api.files = [
  1697. MockAffectedFile('somewhere/header.h', [
  1698. 'class NotUsefulClass;', 'struct SomeStruct;',
  1699. 'UsefulClass *p1;', 'SomeStructPtr *p2;'
  1700. ])
  1701. ]
  1702. warnings = PRESUBMIT.CheckUselessForwardDeclarations(
  1703. mock_input_api, MockOutputApi())
  1704. self.assertEqual(2, len(warnings))
  1705. def testUselessForwardDeclaration(self):
  1706. mock_input_api = MockInputApi()
  1707. mock_input_api.files = [
  1708. MockAffectedFile('somewhere/header.h', [
  1709. 'class DummyClass;', 'struct DummyStruct;',
  1710. 'class UsefulClass;', 'std::unique_ptr<UsefulClass> p;'
  1711. ])
  1712. ]
  1713. warnings = PRESUBMIT.CheckUselessForwardDeclarations(
  1714. mock_input_api, MockOutputApi())
  1715. self.assertEqual(2, len(warnings))
  1716. def testBlinkHeaders(self):
  1717. mock_input_api = MockInputApi()
  1718. mock_input_api.files = [
  1719. MockAffectedFile('third_party/blink/header.h', [
  1720. 'class DummyClass;',
  1721. 'struct DummyStruct;',
  1722. ]),
  1723. MockAffectedFile('third_party\\blink\\header.h', [
  1724. 'class DummyClass;',
  1725. 'struct DummyStruct;',
  1726. ])
  1727. ]
  1728. warnings = PRESUBMIT.CheckUselessForwardDeclarations(
  1729. mock_input_api, MockOutputApi())
  1730. self.assertEqual(4, len(warnings))
  1731. class RelativeIncludesTest(unittest.TestCase):
  1732. def testThirdPartyNotWebKitIgnored(self):
  1733. mock_input_api = MockInputApi()
  1734. mock_input_api.files = [
  1735. MockAffectedFile('third_party/test.cpp', '#include "../header.h"'),
  1736. MockAffectedFile('third_party/test/test.cpp',
  1737. '#include "../header.h"'),
  1738. ]
  1739. mock_output_api = MockOutputApi()
  1740. errors = PRESUBMIT.CheckForRelativeIncludes(mock_input_api,
  1741. mock_output_api)
  1742. self.assertEqual(0, len(errors))
  1743. def testNonCppFileIgnored(self):
  1744. mock_input_api = MockInputApi()
  1745. mock_input_api.files = [
  1746. MockAffectedFile('test.py', '#include "../header.h"'),
  1747. ]
  1748. mock_output_api = MockOutputApi()
  1749. errors = PRESUBMIT.CheckForRelativeIncludes(mock_input_api,
  1750. mock_output_api)
  1751. self.assertEqual(0, len(errors))
  1752. def testInnocuousChangesAllowed(self):
  1753. mock_input_api = MockInputApi()
  1754. mock_input_api.files = [
  1755. MockAffectedFile('test.cpp', '#include "header.h"'),
  1756. MockAffectedFile('test2.cpp', '../'),
  1757. ]
  1758. mock_output_api = MockOutputApi()
  1759. errors = PRESUBMIT.CheckForRelativeIncludes(mock_input_api,
  1760. mock_output_api)
  1761. self.assertEqual(0, len(errors))
  1762. def testRelativeIncludeNonWebKitProducesError(self):
  1763. mock_input_api = MockInputApi()
  1764. mock_input_api.files = [
  1765. MockAffectedFile('test.cpp', ['#include "../header.h"']),
  1766. ]
  1767. mock_output_api = MockOutputApi()
  1768. errors = PRESUBMIT.CheckForRelativeIncludes(mock_input_api,
  1769. mock_output_api)
  1770. self.assertEqual(1, len(errors))
  1771. def testRelativeIncludeWebKitProducesError(self):
  1772. mock_input_api = MockInputApi()
  1773. mock_input_api.files = [
  1774. MockAffectedFile('third_party/blink/test.cpp',
  1775. ['#include "../header.h']),
  1776. ]
  1777. mock_output_api = MockOutputApi()
  1778. errors = PRESUBMIT.CheckForRelativeIncludes(mock_input_api,
  1779. mock_output_api)
  1780. self.assertEqual(1, len(errors))
  1781. class CCIncludeTest(unittest.TestCase):
  1782. def testThirdPartyNotBlinkIgnored(self):
  1783. mock_input_api = MockInputApi()
  1784. mock_input_api.files = [
  1785. MockAffectedFile('third_party/test.cpp', '#include "file.cc"'),
  1786. ]
  1787. mock_output_api = MockOutputApi()
  1788. errors = PRESUBMIT.CheckForCcIncludes(mock_input_api, mock_output_api)
  1789. self.assertEqual(0, len(errors))
  1790. def testPythonFileIgnored(self):
  1791. mock_input_api = MockInputApi()
  1792. mock_input_api.files = [
  1793. MockAffectedFile('test.py', '#include "file.cc"'),
  1794. ]
  1795. mock_output_api = MockOutputApi()
  1796. errors = PRESUBMIT.CheckForCcIncludes(mock_input_api, mock_output_api)
  1797. self.assertEqual(0, len(errors))
  1798. def testIncFilesAccepted(self):
  1799. mock_input_api = MockInputApi()
  1800. mock_input_api.files = [
  1801. MockAffectedFile('test.py', '#include "file.inc"'),
  1802. ]
  1803. mock_output_api = MockOutputApi()
  1804. errors = PRESUBMIT.CheckForCcIncludes(mock_input_api, mock_output_api)
  1805. self.assertEqual(0, len(errors))
  1806. def testInnocuousChangesAllowed(self):
  1807. mock_input_api = MockInputApi()
  1808. mock_input_api.files = [
  1809. MockAffectedFile('test.cpp', '#include "header.h"'),
  1810. MockAffectedFile('test2.cpp', 'Something "file.cc"'),
  1811. ]
  1812. mock_output_api = MockOutputApi()
  1813. errors = PRESUBMIT.CheckForCcIncludes(mock_input_api, mock_output_api)
  1814. self.assertEqual(0, len(errors))
  1815. def testCcIncludeNonBlinkProducesError(self):
  1816. mock_input_api = MockInputApi()
  1817. mock_input_api.files = [
  1818. MockAffectedFile('test.cpp', ['#include "file.cc"']),
  1819. ]
  1820. mock_output_api = MockOutputApi()
  1821. errors = PRESUBMIT.CheckForCcIncludes(mock_input_api, mock_output_api)
  1822. self.assertEqual(1, len(errors))
  1823. def testCppIncludeBlinkProducesError(self):
  1824. mock_input_api = MockInputApi()
  1825. mock_input_api.files = [
  1826. MockAffectedFile('third_party/blink/test.cpp',
  1827. ['#include "foo/file.cpp"']),
  1828. ]
  1829. mock_output_api = MockOutputApi()
  1830. errors = PRESUBMIT.CheckForCcIncludes(mock_input_api, mock_output_api)
  1831. self.assertEqual(1, len(errors))
  1832. class GnGlobForwardTest(unittest.TestCase):
  1833. def testAddBareGlobs(self):
  1834. mock_input_api = MockInputApi()
  1835. mock_input_api.files = [
  1836. MockAffectedFile('base/stuff.gni',
  1837. ['forward_variables_from(invoker, "*")']),
  1838. MockAffectedFile('base/BUILD.gn',
  1839. ['forward_variables_from(invoker, "*")']),
  1840. ]
  1841. warnings = PRESUBMIT.CheckGnGlobForward(mock_input_api,
  1842. MockOutputApi())
  1843. self.assertEqual(1, len(warnings))
  1844. msg = '\n'.join(warnings[0].items)
  1845. self.assertIn('base/stuff.gni', msg)
  1846. # Should not check .gn files. Local templates don't need to care about
  1847. # visibility / testonly.
  1848. self.assertNotIn('base/BUILD.gn', msg)
  1849. def testValidUses(self):
  1850. mock_input_api = MockInputApi()
  1851. mock_input_api.files = [
  1852. MockAffectedFile('base/stuff.gni',
  1853. ['forward_variables_from(invoker, "*", [])']),
  1854. MockAffectedFile('base/stuff2.gni', [
  1855. 'forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY)'
  1856. ]),
  1857. MockAffectedFile(
  1858. 'base/stuff3.gni',
  1859. ['forward_variables_from(invoker, [ "testonly" ])']),
  1860. ]
  1861. warnings = PRESUBMIT.CheckGnGlobForward(mock_input_api,
  1862. MockOutputApi())
  1863. self.assertEqual([], warnings)
  1864. class GnRebasePathTest(unittest.TestCase):
  1865. def testAddAbsolutePath(self):
  1866. mock_input_api = MockInputApi()
  1867. mock_input_api.files = [
  1868. MockAffectedFile('base/BUILD.gn',
  1869. ['rebase_path("$target_gen_dir", "//")']),
  1870. MockAffectedFile('base/root/BUILD.gn',
  1871. ['rebase_path("$target_gen_dir", "/")']),
  1872. MockAffectedFile('base/variable/BUILD.gn',
  1873. ['rebase_path(target_gen_dir, "/")']),
  1874. ]
  1875. warnings = PRESUBMIT.CheckGnRebasePath(mock_input_api, MockOutputApi())
  1876. self.assertEqual(1, len(warnings))
  1877. msg = '\n'.join(warnings[0].items)
  1878. self.assertIn('base/BUILD.gn', msg)
  1879. self.assertIn('base/root/BUILD.gn', msg)
  1880. self.assertIn('base/variable/BUILD.gn', msg)
  1881. self.assertEqual(3, len(warnings[0].items))
  1882. def testValidUses(self):
  1883. mock_input_api = MockInputApi()
  1884. mock_input_api.files = [
  1885. MockAffectedFile(
  1886. 'base/foo/BUILD.gn',
  1887. ['rebase_path("$target_gen_dir", root_build_dir)']),
  1888. MockAffectedFile(
  1889. 'base/bar/BUILD.gn',
  1890. ['rebase_path("$target_gen_dir", root_build_dir, "/")']),
  1891. MockAffectedFile('base/baz/BUILD.gn',
  1892. ['rebase_path(target_gen_dir, root_build_dir)']),
  1893. MockAffectedFile(
  1894. 'base/baz/BUILD.gn',
  1895. ['rebase_path(target_gen_dir, "//some/arbitrary/path")']),
  1896. MockAffectedFile('base/okay_slash/BUILD.gn',
  1897. ['rebase_path(".", "//")']),
  1898. ]
  1899. warnings = PRESUBMIT.CheckGnRebasePath(mock_input_api, MockOutputApi())
  1900. self.assertEqual([], warnings)
  1901. class NewHeaderWithoutGnChangeTest(unittest.TestCase):
  1902. def testAddHeaderWithoutGn(self):
  1903. mock_input_api = MockInputApi()
  1904. mock_input_api.files = [
  1905. MockAffectedFile('base/stuff.h', ''),
  1906. ]
  1907. warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
  1908. mock_input_api, MockOutputApi())
  1909. self.assertEqual(1, len(warnings))
  1910. self.assertTrue('base/stuff.h' in warnings[0].items)
  1911. def testModifyHeader(self):
  1912. mock_input_api = MockInputApi()
  1913. mock_input_api.files = [
  1914. MockAffectedFile('base/stuff.h', '', action='M'),
  1915. ]
  1916. warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
  1917. mock_input_api, MockOutputApi())
  1918. self.assertEqual(0, len(warnings))
  1919. def testDeleteHeader(self):
  1920. mock_input_api = MockInputApi()
  1921. mock_input_api.files = [
  1922. MockAffectedFile('base/stuff.h', '', action='D'),
  1923. ]
  1924. warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
  1925. mock_input_api, MockOutputApi())
  1926. self.assertEqual(0, len(warnings))
  1927. def testAddHeaderWithGn(self):
  1928. mock_input_api = MockInputApi()
  1929. mock_input_api.files = [
  1930. MockAffectedFile('base/stuff.h', ''),
  1931. MockAffectedFile('base/BUILD.gn', 'stuff.h'),
  1932. ]
  1933. warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
  1934. mock_input_api, MockOutputApi())
  1935. self.assertEqual(0, len(warnings))
  1936. def testAddHeaderWithGni(self):
  1937. mock_input_api = MockInputApi()
  1938. mock_input_api.files = [
  1939. MockAffectedFile('base/stuff.h', ''),
  1940. MockAffectedFile('base/files.gni', 'stuff.h'),
  1941. ]
  1942. warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
  1943. mock_input_api, MockOutputApi())
  1944. self.assertEqual(0, len(warnings))
  1945. def testAddHeaderWithOther(self):
  1946. mock_input_api = MockInputApi()
  1947. mock_input_api.files = [
  1948. MockAffectedFile('base/stuff.h', ''),
  1949. MockAffectedFile('base/stuff.cc', 'stuff.h'),
  1950. ]
  1951. warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
  1952. mock_input_api, MockOutputApi())
  1953. self.assertEqual(1, len(warnings))
  1954. def testAddHeaderWithWrongGn(self):
  1955. mock_input_api = MockInputApi()
  1956. mock_input_api.files = [
  1957. MockAffectedFile('base/stuff.h', ''),
  1958. MockAffectedFile('base/BUILD.gn', 'stuff_h'),
  1959. ]
  1960. warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
  1961. mock_input_api, MockOutputApi())
  1962. self.assertEqual(1, len(warnings))
  1963. def testAddHeadersWithGn(self):
  1964. mock_input_api = MockInputApi()
  1965. mock_input_api.files = [
  1966. MockAffectedFile('base/stuff.h', ''),
  1967. MockAffectedFile('base/another.h', ''),
  1968. MockAffectedFile('base/BUILD.gn', 'another.h\nstuff.h'),
  1969. ]
  1970. warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
  1971. mock_input_api, MockOutputApi())
  1972. self.assertEqual(0, len(warnings))
  1973. def testAddHeadersWithWrongGn(self):
  1974. mock_input_api = MockInputApi()
  1975. mock_input_api.files = [
  1976. MockAffectedFile('base/stuff.h', ''),
  1977. MockAffectedFile('base/another.h', ''),
  1978. MockAffectedFile('base/BUILD.gn', 'another_h\nstuff.h'),
  1979. ]
  1980. warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
  1981. mock_input_api, MockOutputApi())
  1982. self.assertEqual(1, len(warnings))
  1983. self.assertFalse('base/stuff.h' in warnings[0].items)
  1984. self.assertTrue('base/another.h' in warnings[0].items)
  1985. def testAddHeadersWithWrongGn2(self):
  1986. mock_input_api = MockInputApi()
  1987. mock_input_api.files = [
  1988. MockAffectedFile('base/stuff.h', ''),
  1989. MockAffectedFile('base/another.h', ''),
  1990. MockAffectedFile('base/BUILD.gn', 'another_h\nstuff_h'),
  1991. ]
  1992. warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
  1993. mock_input_api, MockOutputApi())
  1994. self.assertEqual(1, len(warnings))
  1995. self.assertTrue('base/stuff.h' in warnings[0].items)
  1996. self.assertTrue('base/another.h' in warnings[0].items)
  1997. class CorrectProductNameInMessagesTest(unittest.TestCase):
  1998. def testProductNameInDesc(self):
  1999. mock_input_api = MockInputApi()
  2000. mock_input_api.files = [
  2001. MockAffectedFile('chrome/app/google_chrome_strings.grd', [
  2002. '<message name="Foo" desc="Welcome to Chrome">',
  2003. ' Welcome to Chrome!',
  2004. '</message>',
  2005. ]),
  2006. MockAffectedFile('chrome/app/chromium_strings.grd', [
  2007. '<message name="Bar" desc="Welcome to Chrome">',
  2008. ' Welcome to Chromium!',
  2009. '</message>',
  2010. ]),
  2011. ]
  2012. warnings = PRESUBMIT.CheckCorrectProductNameInMessages(
  2013. mock_input_api, MockOutputApi())
  2014. self.assertEqual(0, len(warnings))
  2015. def testChromeInChromium(self):
  2016. mock_input_api = MockInputApi()
  2017. mock_input_api.files = [
  2018. MockAffectedFile('chrome/app/google_chrome_strings.grd', [
  2019. '<message name="Foo" desc="Welcome to Chrome">',
  2020. ' Welcome to Chrome!',
  2021. '</message>',
  2022. ]),
  2023. MockAffectedFile('chrome/app/chromium_strings.grd', [
  2024. '<message name="Bar" desc="Welcome to Chrome">',
  2025. ' Welcome to Chrome!',
  2026. '</message>',
  2027. ]),
  2028. ]
  2029. warnings = PRESUBMIT.CheckCorrectProductNameInMessages(
  2030. mock_input_api, MockOutputApi())
  2031. self.assertEqual(1, len(warnings))
  2032. self.assertTrue(
  2033. 'chrome/app/chromium_strings.grd' in warnings[0].items[0])
  2034. def testChromiumInChrome(self):
  2035. mock_input_api = MockInputApi()
  2036. mock_input_api.files = [
  2037. MockAffectedFile('chrome/app/google_chrome_strings.grd', [
  2038. '<message name="Foo" desc="Welcome to Chrome">',
  2039. ' Welcome to Chromium!',
  2040. '</message>',
  2041. ]),
  2042. MockAffectedFile('chrome/app/chromium_strings.grd', [
  2043. '<message name="Bar" desc="Welcome to Chrome">',
  2044. ' Welcome to Chromium!',
  2045. '</message>',
  2046. ]),
  2047. ]
  2048. warnings = PRESUBMIT.CheckCorrectProductNameInMessages(
  2049. mock_input_api, MockOutputApi())
  2050. self.assertEqual(1, len(warnings))
  2051. self.assertTrue(
  2052. 'chrome/app/google_chrome_strings.grd:2' in warnings[0].items[0])
  2053. def testChromeForTestingInChromium(self):
  2054. mock_input_api = MockInputApi()
  2055. mock_input_api.files = [
  2056. MockAffectedFile('chrome/app/chromium_strings.grd', [
  2057. '<message name="Bar" desc="Welcome to Chrome">',
  2058. ' Welcome to Chrome for Testing!',
  2059. '</message>',
  2060. ]),
  2061. ]
  2062. warnings = PRESUBMIT.CheckCorrectProductNameInMessages(
  2063. mock_input_api, MockOutputApi())
  2064. self.assertEqual(0, len(warnings))
  2065. def testChromeForTestingInChrome(self):
  2066. mock_input_api = MockInputApi()
  2067. mock_input_api.files = [
  2068. MockAffectedFile('chrome/app/google_chrome_strings.grd', [
  2069. '<message name="Bar" desc="Welcome to Chrome">',
  2070. ' Welcome to Chrome for Testing!',
  2071. '</message>',
  2072. ]),
  2073. ]
  2074. warnings = PRESUBMIT.CheckCorrectProductNameInMessages(
  2075. mock_input_api, MockOutputApi())
  2076. self.assertEqual(1, len(warnings))
  2077. self.assertTrue(
  2078. 'chrome/app/google_chrome_strings.grd:2' in warnings[0].items[0])
  2079. def testMultipleInstances(self):
  2080. mock_input_api = MockInputApi()
  2081. mock_input_api.files = [
  2082. MockAffectedFile('chrome/app/chromium_strings.grd', [
  2083. '<message name="Bar" desc="Welcome to Chrome">',
  2084. ' Welcome to Chrome!',
  2085. '</message>',
  2086. '<message name="Baz" desc="A correct message">',
  2087. ' Chromium is the software you are using.',
  2088. '</message>',
  2089. '<message name="Bat" desc="An incorrect message">',
  2090. ' Google Chrome is the software you are using.',
  2091. '</message>',
  2092. ]),
  2093. ]
  2094. warnings = PRESUBMIT.CheckCorrectProductNameInMessages(
  2095. mock_input_api, MockOutputApi())
  2096. self.assertEqual(1, len(warnings))
  2097. self.assertTrue(
  2098. 'chrome/app/chromium_strings.grd:2' in warnings[0].items[0])
  2099. self.assertTrue(
  2100. 'chrome/app/chromium_strings.grd:8' in warnings[0].items[1])
  2101. def testMultipleWarnings(self):
  2102. mock_input_api = MockInputApi()
  2103. mock_input_api.files = [
  2104. MockAffectedFile('chrome/app/chromium_strings.grd', [
  2105. '<message name="Bar" desc="Welcome to Chrome">',
  2106. ' Welcome to Chrome!',
  2107. '</message>',
  2108. '<message name="Baz" desc="A correct message">',
  2109. ' Chromium is the software you are using.',
  2110. '</message>',
  2111. '<message name="Bat" desc="An incorrect message">',
  2112. ' Google Chrome is the software you are using.',
  2113. '</message>',
  2114. ]),
  2115. MockAffectedFile(
  2116. 'components/components_google_chrome_strings.grd', [
  2117. '<message name="Bar" desc="Welcome to Chrome">',
  2118. ' Welcome to Chrome!',
  2119. '</message>',
  2120. '<message name="Baz" desc="A correct message">',
  2121. ' Chromium is the software you are using.',
  2122. '</message>',
  2123. '<message name="Bat" desc="An incorrect message">',
  2124. ' Google Chrome is the software you are using.',
  2125. '</message>',
  2126. ]),
  2127. ]
  2128. warnings = PRESUBMIT.CheckCorrectProductNameInMessages(
  2129. mock_input_api, MockOutputApi())
  2130. self.assertEqual(2, len(warnings))
  2131. self.assertTrue('components/components_google_chrome_strings.grd:5' in
  2132. warnings[0].items[0])
  2133. self.assertTrue(
  2134. 'chrome/app/chromium_strings.grd:2' in warnings[1].items[0])
  2135. self.assertTrue(
  2136. 'chrome/app/chromium_strings.grd:8' in warnings[1].items[1])
  2137. class _SecurityOwnersTestCase(unittest.TestCase):
  2138. def _createMockInputApi(self):
  2139. mock_input_api = MockInputApi()
  2140. def FakeRepositoryRoot():
  2141. return mock_input_api.os_path.join('chromium', 'src')
  2142. mock_input_api.change.RepositoryRoot = FakeRepositoryRoot
  2143. self._injectFakeOwnersClient(
  2144. mock_input_api, ['apple@chromium.org', 'orange@chromium.org'])
  2145. return mock_input_api
  2146. def _setupFakeChange(self, input_api):
  2147. class FakeGerrit(object):
  2148. def IsOwnersOverrideApproved(self, issue):
  2149. return False
  2150. input_api.change.issue = 123
  2151. input_api.gerrit = FakeGerrit()
  2152. def _injectFakeOwnersClient(self, input_api, owners):
  2153. class FakeOwnersClient(object):
  2154. def ListOwners(self, f):
  2155. return owners
  2156. input_api.owners_client = FakeOwnersClient()
  2157. def _injectFakeChangeOwnerAndReviewers(self, input_api, owner, reviewers):
  2158. def MockOwnerAndReviewers(input_api,
  2159. email_regexp,
  2160. approval_needed=False):
  2161. return [owner, reviewers]
  2162. input_api.canned_checks.GetCodereviewOwnerAndReviewers = \
  2163. MockOwnerAndReviewers
  2164. class IpcSecurityOwnerTest(_SecurityOwnersTestCase):
  2165. _test_cases = [
  2166. ('*_messages.cc', 'scary_messages.cc'),
  2167. ('*_messages*.h', 'scary_messages.h'),
  2168. ('*_messages*.h', 'scary_messages_android.h'),
  2169. ('*_param_traits*.*', 'scary_param_traits.h'),
  2170. ('*_param_traits*.*', 'scary_param_traits_win.h'),
  2171. ('*.mojom', 'scary.mojom'),
  2172. ('*_mojom_traits*.*', 'scary_mojom_traits.h'),
  2173. ('*_mojom_traits*.*', 'scary_mojom_traits_mac.h'),
  2174. ('*_type_converter*.*', 'scary_type_converter.h'),
  2175. ('*_type_converter*.*', 'scary_type_converter_nacl.h'),
  2176. ('*.aidl', 'scary.aidl'),
  2177. ]
  2178. def testHasCorrectPerFileRulesAndSecurityReviewer(self):
  2179. mock_input_api = self._createMockInputApi()
  2180. new_owners_file_path = mock_input_api.os_path.join(
  2181. 'services', 'goat', 'public', 'OWNERS')
  2182. new_owners_file = [
  2183. 'per-file *.mojom=set noparent',
  2184. 'per-file *.mojom=file://ipc/SECURITY_OWNERS'
  2185. ]
  2186. def FakeReadFile(filename):
  2187. self.assertEqual(
  2188. mock_input_api.os_path.join('chromium', 'src',
  2189. new_owners_file_path), filename)
  2190. return '\n'.join(new_owners_file)
  2191. mock_input_api.ReadFile = FakeReadFile
  2192. mock_input_api.files = [
  2193. MockAffectedFile(new_owners_file_path, new_owners_file),
  2194. MockAffectedFile(
  2195. mock_input_api.os_path.join('services', 'goat', 'public',
  2196. 'goat.mojom'),
  2197. ['// Scary contents.'])
  2198. ]
  2199. self._setupFakeChange(mock_input_api)
  2200. self._injectFakeChangeOwnerAndReviewers(mock_input_api,
  2201. 'owner@chromium.org',
  2202. ['orange@chromium.org'])
  2203. mock_input_api.is_committing = True
  2204. mock_input_api.dry_run = False
  2205. mock_output_api = MockOutputApi()
  2206. results = PRESUBMIT.CheckSecurityOwners(mock_input_api,
  2207. mock_output_api)
  2208. self.assertEqual(0, len(results))
  2209. def testMissingSecurityReviewerAtUpload(self):
  2210. mock_input_api = self._createMockInputApi()
  2211. new_owners_file_path = mock_input_api.os_path.join(
  2212. 'services', 'goat', 'public', 'OWNERS')
  2213. new_owners_file = [
  2214. 'per-file *.mojom=set noparent',
  2215. 'per-file *.mojom=file://ipc/SECURITY_OWNERS'
  2216. ]
  2217. def FakeReadFile(filename):
  2218. self.assertEqual(
  2219. mock_input_api.os_path.join('chromium', 'src',
  2220. new_owners_file_path), filename)
  2221. return '\n'.join(new_owners_file)
  2222. mock_input_api.ReadFile = FakeReadFile
  2223. mock_input_api.files = [
  2224. MockAffectedFile(new_owners_file_path, new_owners_file),
  2225. MockAffectedFile(
  2226. mock_input_api.os_path.join('services', 'goat', 'public',
  2227. 'goat.mojom'),
  2228. ['// Scary contents.'])
  2229. ]
  2230. self._setupFakeChange(mock_input_api)
  2231. self._injectFakeChangeOwnerAndReviewers(mock_input_api,
  2232. 'owner@chromium.org',
  2233. ['banana@chromium.org'])
  2234. mock_input_api.is_committing = False
  2235. mock_input_api.dry_run = False
  2236. mock_output_api = MockOutputApi()
  2237. results = PRESUBMIT.CheckSecurityOwners(mock_input_api,
  2238. mock_output_api)
  2239. self.assertEqual(1, len(results))
  2240. self.assertEqual('notify', results[0].type)
  2241. self.assertEqual(
  2242. 'Review from an owner in ipc/SECURITY_OWNERS is required for the '
  2243. 'following newly-added files:', results[0].message)
  2244. def testMissingSecurityReviewerAtDryRunCommit(self):
  2245. mock_input_api = self._createMockInputApi()
  2246. new_owners_file_path = mock_input_api.os_path.join(
  2247. 'services', 'goat', 'public', 'OWNERS')
  2248. new_owners_file = [
  2249. 'per-file *.mojom=set noparent',
  2250. 'per-file *.mojom=file://ipc/SECURITY_OWNERS'
  2251. ]
  2252. def FakeReadFile(filename):
  2253. self.assertEqual(
  2254. mock_input_api.os_path.join('chromium', 'src',
  2255. new_owners_file_path), filename)
  2256. return '\n'.join(new_owners_file)
  2257. mock_input_api.ReadFile = FakeReadFile
  2258. mock_input_api.files = [
  2259. MockAffectedFile(new_owners_file_path, new_owners_file),
  2260. MockAffectedFile(
  2261. mock_input_api.os_path.join('services', 'goat', 'public',
  2262. 'goat.mojom'),
  2263. ['// Scary contents.'])
  2264. ]
  2265. self._setupFakeChange(mock_input_api)
  2266. self._injectFakeChangeOwnerAndReviewers(mock_input_api,
  2267. 'owner@chromium.org',
  2268. ['banana@chromium.org'])
  2269. mock_input_api.is_committing = True
  2270. mock_input_api.dry_run = True
  2271. mock_output_api = MockOutputApi()
  2272. results = PRESUBMIT.CheckSecurityOwners(mock_input_api,
  2273. mock_output_api)
  2274. self.assertEqual(1, len(results))
  2275. self.assertEqual('error', results[0].type)
  2276. self.assertEqual(
  2277. 'Review from an owner in ipc/SECURITY_OWNERS is required for the '
  2278. 'following newly-added files:', results[0].message)
  2279. def testMissingSecurityApprovalAtRealCommit(self):
  2280. mock_input_api = self._createMockInputApi()
  2281. new_owners_file_path = mock_input_api.os_path.join(
  2282. 'services', 'goat', 'public', 'OWNERS')
  2283. new_owners_file = [
  2284. 'per-file *.mojom=set noparent',
  2285. 'per-file *.mojom=file://ipc/SECURITY_OWNERS'
  2286. ]
  2287. def FakeReadFile(filename):
  2288. self.assertEqual(
  2289. mock_input_api.os_path.join('chromium', 'src',
  2290. new_owners_file_path), filename)
  2291. return '\n'.join(new_owners_file)
  2292. mock_input_api.ReadFile = FakeReadFile
  2293. mock_input_api.files = [
  2294. MockAffectedFile(new_owners_file_path, new_owners_file),
  2295. MockAffectedFile(
  2296. mock_input_api.os_path.join('services', 'goat', 'public',
  2297. 'goat.mojom'),
  2298. ['// Scary contents.'])
  2299. ]
  2300. self._setupFakeChange(mock_input_api)
  2301. self._injectFakeChangeOwnerAndReviewers(mock_input_api,
  2302. 'owner@chromium.org',
  2303. ['banana@chromium.org'])
  2304. mock_input_api.is_committing = True
  2305. mock_input_api.dry_run = False
  2306. mock_output_api = MockOutputApi()
  2307. results = PRESUBMIT.CheckSecurityOwners(mock_input_api,
  2308. mock_output_api)
  2309. self.assertEqual('error', results[0].type)
  2310. self.assertEqual(
  2311. 'Review from an owner in ipc/SECURITY_OWNERS is required for the '
  2312. 'following newly-added files:', results[0].message)
  2313. def testIpcChangeNeedsSecurityOwner(self):
  2314. for is_committing in [True, False]:
  2315. for pattern, filename in self._test_cases:
  2316. with self.subTest(
  2317. line=
  2318. f'is_committing={is_committing}, filename={filename}'):
  2319. mock_input_api = self._createMockInputApi()
  2320. mock_input_api.files = [
  2321. MockAffectedFile(
  2322. mock_input_api.os_path.join(
  2323. 'services', 'goat', 'public', filename),
  2324. ['// Scary contents.'])
  2325. ]
  2326. self._setupFakeChange(mock_input_api)
  2327. self._injectFakeChangeOwnerAndReviewers(
  2328. mock_input_api, 'owner@chromium.org',
  2329. ['banana@chromium.org'])
  2330. mock_input_api.is_committing = is_committing
  2331. mock_input_api.dry_run = False
  2332. mock_output_api = MockOutputApi()
  2333. results = PRESUBMIT.CheckSecurityOwners(
  2334. mock_input_api, mock_output_api)
  2335. self.assertEqual(1, len(results))
  2336. self.assertEqual('error', results[0].type)
  2337. self.assertTrue(results[0].message.replace(
  2338. '\\', '/'
  2339. ).startswith(
  2340. 'Found missing OWNERS lines for security-sensitive files. '
  2341. 'Please add the following lines to services/goat/public/OWNERS:'
  2342. ))
  2343. self.assertEqual(['ipc-security-reviews@chromium.org'],
  2344. mock_output_api.more_cc)
  2345. def testServiceManifestChangeNeedsSecurityOwner(self):
  2346. mock_input_api = self._createMockInputApi()
  2347. mock_input_api.files = [
  2348. MockAffectedFile(
  2349. mock_input_api.os_path.join('services', 'goat', 'public',
  2350. 'cpp', 'manifest.cc'),
  2351. [
  2352. '#include "services/goat/public/cpp/manifest.h"',
  2353. 'const service_manager::Manifest& GetManifest() {}',
  2354. ])
  2355. ]
  2356. self._setupFakeChange(mock_input_api)
  2357. self._injectFakeChangeOwnerAndReviewers(mock_input_api,
  2358. 'owner@chromium.org',
  2359. ['banana@chromium.org'])
  2360. mock_output_api = MockOutputApi()
  2361. errors = PRESUBMIT.CheckSecurityOwners(mock_input_api, mock_output_api)
  2362. self.assertEqual(1, len(errors))
  2363. self.assertTrue(errors[0].message.replace('\\', '/').startswith(
  2364. 'Found missing OWNERS lines for security-sensitive files. '
  2365. 'Please add the following lines to services/goat/public/cpp/OWNERS:'
  2366. ))
  2367. self.assertEqual(['ipc-security-reviews@chromium.org'],
  2368. mock_output_api.more_cc)
  2369. def testNonServiceManifestSourceChangesDoNotRequireSecurityOwner(self):
  2370. mock_input_api = self._createMockInputApi()
  2371. self._injectFakeChangeOwnerAndReviewers(mock_input_api,
  2372. 'owner@chromium.org',
  2373. ['banana@chromium.org'])
  2374. mock_input_api.files = [
  2375. MockAffectedFile('some/non/service/thing/foo_manifest.cc', [
  2376. 'const char kNoEnforcement[] = "not a manifest!";',
  2377. ])
  2378. ]
  2379. mock_output_api = MockOutputApi()
  2380. errors = PRESUBMIT.CheckSecurityOwners(mock_input_api, mock_output_api)
  2381. self.assertEqual([], errors)
  2382. self.assertEqual([], mock_output_api.more_cc)
  2383. class FuchsiaSecurityOwnerTest(_SecurityOwnersTestCase):
  2384. def testFidlChangeNeedsSecurityOwner(self):
  2385. mock_input_api = self._createMockInputApi()
  2386. mock_input_api.files = [
  2387. MockAffectedFile('potentially/scary/ipc.fidl',
  2388. ['library test.fidl'])
  2389. ]
  2390. self._setupFakeChange(mock_input_api)
  2391. self._injectFakeChangeOwnerAndReviewers(mock_input_api,
  2392. 'owner@chromium.org',
  2393. ['banana@chromium.org'])
  2394. mock_output_api = MockOutputApi()
  2395. errors = PRESUBMIT.CheckSecurityOwners(mock_input_api, mock_output_api)
  2396. self.assertEqual(1, len(errors))
  2397. self.assertTrue(errors[0].message.replace('\\', '/').startswith(
  2398. 'Found missing OWNERS lines for security-sensitive files. '
  2399. 'Please add the following lines to potentially/scary/OWNERS:'))
  2400. def testComponentManifestV1ChangeNeedsSecurityOwner(self):
  2401. mock_input_api = self._createMockInputApi()
  2402. mock_input_api.files = [
  2403. MockAffectedFile('potentially/scary/v2_manifest.cmx',
  2404. ['{ "that is no": "manifest!" }'])
  2405. ]
  2406. self._setupFakeChange(mock_input_api)
  2407. self._injectFakeChangeOwnerAndReviewers(mock_input_api,
  2408. 'owner@chromium.org',
  2409. ['banana@chromium.org'])
  2410. mock_output_api = MockOutputApi()
  2411. errors = PRESUBMIT.CheckSecurityOwners(mock_input_api, mock_output_api)
  2412. self.assertEqual(1, len(errors))
  2413. self.assertTrue(errors[0].message.replace('\\', '/').startswith(
  2414. 'Found missing OWNERS lines for security-sensitive files. '
  2415. 'Please add the following lines to potentially/scary/OWNERS:'))
  2416. def testComponentManifestV2NeedsSecurityOwner(self):
  2417. mock_input_api = self._createMockInputApi()
  2418. mock_input_api.files = [
  2419. MockAffectedFile('potentially/scary/v2_manifest.cml',
  2420. ['{ "that is no": "manifest!" }'])
  2421. ]
  2422. self._setupFakeChange(mock_input_api)
  2423. self._injectFakeChangeOwnerAndReviewers(mock_input_api,
  2424. 'owner@chromium.org',
  2425. ['banana@chromium.org'])
  2426. mock_output_api = MockOutputApi()
  2427. errors = PRESUBMIT.CheckSecurityOwners(mock_input_api, mock_output_api)
  2428. self.assertEqual(1, len(errors))
  2429. self.assertTrue(errors[0].message.replace('\\', '/').startswith(
  2430. 'Found missing OWNERS lines for security-sensitive files. '
  2431. 'Please add the following lines to potentially/scary/OWNERS:'))
  2432. def testThirdPartyTestsDoNotRequireSecurityOwner(self):
  2433. mock_input_api = MockInputApi()
  2434. self._injectFakeChangeOwnerAndReviewers(mock_input_api,
  2435. 'owner@chromium.org',
  2436. ['banana@chromium.org'])
  2437. mock_input_api.files = [
  2438. MockAffectedFile('third_party/crashpad/test/tests.cmx', [
  2439. 'const char kNoEnforcement[] = "Security?!? Pah!";',
  2440. ])
  2441. ]
  2442. mock_output_api = MockOutputApi()
  2443. errors = PRESUBMIT.CheckSecurityOwners(mock_input_api, mock_output_api)
  2444. self.assertEqual([], errors)
  2445. def testOtherFuchsiaChangesDoNotRequireSecurityOwner(self):
  2446. mock_input_api = MockInputApi()
  2447. self._injectFakeChangeOwnerAndReviewers(mock_input_api,
  2448. 'owner@chromium.org',
  2449. ['banana@chromium.org'])
  2450. mock_input_api.files = [
  2451. MockAffectedFile(
  2452. 'some/non/service/thing/fuchsia_fidl_cml_cmx_magic.cc', [
  2453. 'const char kNoEnforcement[] = "Security?!? Pah!";',
  2454. ])
  2455. ]
  2456. mock_output_api = MockOutputApi()
  2457. errors = PRESUBMIT.CheckSecurityOwners(mock_input_api, mock_output_api)
  2458. self.assertEqual([], errors)
  2459. class SecurityChangeTest(_SecurityOwnersTestCase):
  2460. def testDiffGetServiceSandboxType(self):
  2461. mock_input_api = MockInputApi()
  2462. mock_input_api.files = [
  2463. MockAffectedFile('services/goat/teleporter_host.cc', [
  2464. 'template <>', 'inline content::SandboxType',
  2465. 'content::GetServiceSandboxType<chrome::mojom::GoatTeleporter>() {',
  2466. '#if defined(OS_WIN)', ' return SandboxType::kGoaty;',
  2467. '#else', ' return SandboxType::kNoSandbox;',
  2468. '#endif // !defined(OS_WIN)', '}'
  2469. ]),
  2470. ]
  2471. files_to_functions = PRESUBMIT._GetFilesUsingSecurityCriticalFunctions(
  2472. mock_input_api)
  2473. self.assertEqual(
  2474. {
  2475. 'services/goat/teleporter_host.cc':
  2476. set(['content::GetServiceSandboxType<>()'])
  2477. }, files_to_functions)
  2478. def testDiffRemovingLine(self):
  2479. mock_input_api = MockInputApi()
  2480. mock_file = MockAffectedFile('services/goat/teleporter_host.cc', '')
  2481. mock_file._scm_diff = """--- old 2020-05-04 14:08:25.000000000 -0400
  2482. +++ new 2020-05-04 14:08:32.000000000 -0400
  2483. @@ -1,5 +1,4 @@
  2484. template <>
  2485. inline content::SandboxType
  2486. -content::GetServiceSandboxType<chrome::mojom::GoatTeleporter>() {
  2487. #if defined(OS_WIN)
  2488. return SandboxType::kGoaty;
  2489. """
  2490. mock_input_api.files = [mock_file]
  2491. files_to_functions = PRESUBMIT._GetFilesUsingSecurityCriticalFunctions(
  2492. mock_input_api)
  2493. self.assertEqual(
  2494. {
  2495. 'services/goat/teleporter_host.cc':
  2496. set(['content::GetServiceSandboxType<>()'])
  2497. }, files_to_functions)
  2498. def testChangeOwnersMissing(self):
  2499. mock_input_api = self._createMockInputApi()
  2500. self._setupFakeChange(mock_input_api)
  2501. self._injectFakeChangeOwnerAndReviewers(mock_input_api,
  2502. 'owner@chromium.org',
  2503. ['banana@chromium.org'])
  2504. mock_input_api.is_committing = False
  2505. mock_input_api.files = [
  2506. MockAffectedFile('file.cc',
  2507. ['GetServiceSandboxType<Goat>(Sandbox)'])
  2508. ]
  2509. mock_output_api = MockOutputApi()
  2510. result = PRESUBMIT.CheckSecurityChanges(mock_input_api,
  2511. mock_output_api)
  2512. self.assertEqual(1, len(result))
  2513. self.assertEqual(result[0].type, 'notify')
  2514. self.assertEqual(result[0].message,
  2515. 'The following files change calls to security-sensitive functions\n' \
  2516. 'that need to be reviewed by ipc/SECURITY_OWNERS.\n'
  2517. ' file.cc\n'
  2518. ' content::GetServiceSandboxType<>()\n\n')
  2519. def testChangeOwnersMissingAtCommit(self):
  2520. mock_input_api = self._createMockInputApi()
  2521. self._setupFakeChange(mock_input_api)
  2522. self._injectFakeChangeOwnerAndReviewers(mock_input_api,
  2523. 'owner@chromium.org',
  2524. ['banana@chromium.org'])
  2525. mock_input_api.is_committing = True
  2526. mock_input_api.dry_run = False
  2527. mock_input_api.files = [
  2528. MockAffectedFile('file.cc',
  2529. ['GetServiceSandboxType<mojom::Goat>()'])
  2530. ]
  2531. mock_output_api = MockOutputApi()
  2532. result = PRESUBMIT.CheckSecurityChanges(mock_input_api,
  2533. mock_output_api)
  2534. self.assertEqual(1, len(result))
  2535. self.assertEqual(result[0].type, 'error')
  2536. self.assertEqual(result[0].message,
  2537. 'The following files change calls to security-sensitive functions\n' \
  2538. 'that need to be reviewed by ipc/SECURITY_OWNERS.\n'
  2539. ' file.cc\n'
  2540. ' content::GetServiceSandboxType<>()\n\n')
  2541. def testChangeOwnersPresent(self):
  2542. mock_input_api = self._createMockInputApi()
  2543. self._injectFakeChangeOwnerAndReviewers(
  2544. mock_input_api, 'owner@chromium.org',
  2545. ['apple@chromium.org', 'banana@chromium.org'])
  2546. mock_input_api.files = [
  2547. MockAffectedFile('file.cc', ['WithSandboxType(Sandbox)'])
  2548. ]
  2549. mock_output_api = MockOutputApi()
  2550. result = PRESUBMIT.CheckSecurityChanges(mock_input_api,
  2551. mock_output_api)
  2552. self.assertEqual(0, len(result))
  2553. def testChangeOwnerIsSecurityOwner(self):
  2554. mock_input_api = self._createMockInputApi()
  2555. self._setupFakeChange(mock_input_api)
  2556. self._injectFakeChangeOwnerAndReviewers(mock_input_api,
  2557. 'orange@chromium.org',
  2558. ['pear@chromium.org'])
  2559. mock_input_api.files = [
  2560. MockAffectedFile('file.cc', ['GetServiceSandboxType<T>(Sandbox)'])
  2561. ]
  2562. mock_output_api = MockOutputApi()
  2563. result = PRESUBMIT.CheckSecurityChanges(mock_input_api,
  2564. mock_output_api)
  2565. self.assertEqual(1, len(result))
  2566. class BannedTypeCheckTest(unittest.TestCase):
  2567. def testBannedJsFunctions(self):
  2568. input_api = MockInputApi()
  2569. input_api.files = [
  2570. MockFile('ash/webui/file.js', ['chrome.send(something);']),
  2571. MockFile('some/js/ok/file.js', ['chrome.send(something);']),
  2572. ]
  2573. results = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
  2574. self.assertEqual(1, len(results))
  2575. self.assertTrue('ash/webui/file.js' in results[0].message)
  2576. self.assertFalse('some/js/ok/file.js' in results[0].message)
  2577. def testBannedJavaFunctions(self):
  2578. input_api = MockInputApi()
  2579. input_api.files = [
  2580. MockFile('some/java/problematic/diskread.java',
  2581. ['StrictMode.allowThreadDiskReads();']),
  2582. MockFile('some/java/problematic/diskwrite.java',
  2583. ['StrictMode.allowThreadDiskWrites();']),
  2584. MockFile('some/java/ok/diskwrite.java',
  2585. ['StrictModeContext.allowDiskWrites();']),
  2586. MockFile('some/java/problematic/waitidleforsync.java',
  2587. ['instrumentation.waitForIdleSync();']),
  2588. MockFile('some/java/problematic/registerreceiver.java',
  2589. ['context.registerReceiver();']),
  2590. MockFile('some/java/problematic/property.java',
  2591. ['new Property<abc, Integer>;']),
  2592. MockFile('some/java/problematic/requestlayout.java',
  2593. ['requestLayout();']),
  2594. MockFile('some/java/problematic/lastprofile.java',
  2595. ['ProfileManager.getLastUsedRegularProfile();']),
  2596. MockFile('some/java/problematic/getdrawable1.java',
  2597. ['ResourcesCompat.getDrawable();']),
  2598. MockFile('some/java/problematic/getdrawable2.java',
  2599. ['getResources().getDrawable();']),
  2600. ]
  2601. errors = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
  2602. self.assertEqual(2, len(errors))
  2603. self.assertTrue(
  2604. 'some/java/problematic/diskread.java' in errors[0].message)
  2605. self.assertTrue(
  2606. 'some/java/problematic/diskwrite.java' in errors[0].message)
  2607. self.assertFalse('some/java/ok/diskwrite.java' in errors[0].message)
  2608. self.assertFalse('some/java/ok/diskwrite.java' in errors[1].message)
  2609. self.assertTrue(
  2610. 'some/java/problematic/waitidleforsync.java' in errors[0].message)
  2611. self.assertTrue(
  2612. 'some/java/problematic/registerreceiver.java' in errors[1].message)
  2613. self.assertTrue(
  2614. 'some/java/problematic/property.java' in errors[0].message)
  2615. self.assertTrue(
  2616. 'some/java/problematic/requestlayout.java' in errors[0].message)
  2617. self.assertTrue(
  2618. 'some/java/problematic/lastprofile.java' in errors[0].message)
  2619. self.assertTrue(
  2620. 'some/java/problematic/getdrawable1.java' in errors[0].message)
  2621. self.assertTrue(
  2622. 'some/java/problematic/getdrawable2.java' in errors[0].message)
  2623. def testBannedCppFunctions(self):
  2624. input_api = MockInputApi()
  2625. input_api.files = [
  2626. MockFile('some/cpp/problematic/file.cc', ['using namespace std;']),
  2627. MockFile('third_party/blink/problematic/file.cc',
  2628. ['GetInterfaceProvider()']),
  2629. MockFile('some/cpp/ok/file.cc', ['using std::string;']),
  2630. MockFile('some/cpp/problematic/file2.cc',
  2631. ['set_owned_by_client()']),
  2632. MockFile('some/cpp/nocheck/file.cc',
  2633. ['using namespace std; // nocheck']),
  2634. MockFile('some/cpp/comment/file.cc',
  2635. [' // A comment about `using namespace std;`']),
  2636. MockFile('some/cpp/problematic/file3.cc', [
  2637. 'params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET'
  2638. ]),
  2639. MockFile('some/cpp/problematic/file4.cc', [
  2640. 'params.ownership = Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET'
  2641. ]),
  2642. MockFile('some/cpp/problematic/file5.cc', [
  2643. 'Browser* browser = chrome::FindBrowserWithTab(web_contents)'
  2644. ]),
  2645. MockFile('allowed_ranges_usage.cc', ['std::ranges::begin(vec)']),
  2646. MockFile('banned_ranges_usage.cc',
  2647. ['std::ranges::subrange(first, last)']),
  2648. MockFile('views_usage.cc', ['std::views::all(vec)']),
  2649. ]
  2650. results = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
  2651. # warnings are results[0], errors are results[1]
  2652. self.assertEqual(2, len(results))
  2653. self.assertTrue('some/cpp/problematic/file.cc' in results[1].message)
  2654. self.assertTrue(
  2655. 'third_party/blink/problematic/file.cc' in results[0].message)
  2656. self.assertTrue('some/cpp/ok/file.cc' not in results[1].message)
  2657. self.assertTrue('some/cpp/problematic/file2.cc' in results[0].message)
  2658. self.assertTrue('some/cpp/problematic/file3.cc' in results[0].message)
  2659. self.assertTrue('some/cpp/problematic/file4.cc' in results[0].message)
  2660. self.assertTrue('some/cpp/problematic/file5.cc' in results[0].message)
  2661. self.assertFalse('some/cpp/nocheck/file.cc' in results[0].message)
  2662. self.assertFalse('some/cpp/nocheck/file.cc' in results[1].message)
  2663. self.assertFalse('some/cpp/comment/file.cc' in results[0].message)
  2664. self.assertFalse('some/cpp/comment/file.cc' in results[1].message)
  2665. self.assertFalse('allowed_ranges_usage.cc' in results[0].message)
  2666. self.assertFalse('allowed_ranges_usage.cc' in results[1].message)
  2667. self.assertTrue('banned_ranges_usage.cc' in results[1].message)
  2668. self.assertTrue('views_usage.cc' in results[1].message)
  2669. def testBannedCppRandomFunctions(self):
  2670. banned_rngs = [
  2671. 'absl::BitGen',
  2672. 'absl::InsecureBitGen',
  2673. 'std::linear_congruential_engine',
  2674. 'std::mersenne_twister_engine',
  2675. 'std::subtract_with_carry_engine',
  2676. 'std::discard_block_engine',
  2677. 'std::independent_bits_engine',
  2678. 'std::shuffle_order_engine',
  2679. 'std::minstd_rand0',
  2680. 'std::minstd_rand',
  2681. 'std::mt19937',
  2682. 'std::mt19937_64',
  2683. 'std::ranlux24_base',
  2684. 'std::ranlux48_base',
  2685. 'std::ranlux24',
  2686. 'std::ranlux48',
  2687. 'std::knuth_b',
  2688. 'std::default_random_engine',
  2689. 'std::random_device',
  2690. ]
  2691. for banned_rng in banned_rngs:
  2692. input_api = MockInputApi()
  2693. input_api.files = [
  2694. MockFile('some/cpp/problematic/file.cc',
  2695. [f'{banned_rng} engine;']),
  2696. MockFile('third_party/blink/problematic/file.cc',
  2697. [f'{banned_rng} engine;']),
  2698. MockFile('third_party/ok/file.cc', [f'{banned_rng} engine;']),
  2699. ]
  2700. results = PRESUBMIT.CheckNoBannedFunctions(input_api,
  2701. MockOutputApi())
  2702. self.assertEqual(1, len(results), banned_rng)
  2703. self.assertTrue(
  2704. 'some/cpp/problematic/file.cc' in results[0].message,
  2705. banned_rng)
  2706. self.assertTrue(
  2707. 'third_party/blink/problematic/file.cc' in results[0].message,
  2708. banned_rng)
  2709. self.assertFalse('third_party/ok/file.cc' in results[0].message,
  2710. banned_rng)
  2711. def testBannedIosObjcFunctions(self):
  2712. input_api = MockInputApi()
  2713. input_api.files = [
  2714. MockFile('some/ios/file.mm',
  2715. ['TEST(SomeClassTest, SomeInteraction) {', '}']),
  2716. MockFile('some/mac/file.mm',
  2717. ['TEST(SomeClassTest, SomeInteraction) {', '}']),
  2718. MockFile('another/ios_file.mm',
  2719. ['class SomeTest : public testing::Test {};']),
  2720. MockFile(
  2721. 'some/ios/file_egtest.mm',
  2722. ['- (void)testSomething { EXPECT_OCMOCK_VERIFY(aMock); }']),
  2723. MockFile('some/ios/file_unittest.mm', [
  2724. 'TEST_F(SomeTest, TestThis) { EXPECT_OCMOCK_VERIFY(aMock); }'
  2725. ]),
  2726. ]
  2727. errors = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
  2728. self.assertEqual(1, len(errors))
  2729. self.assertTrue('some/ios/file.mm' in errors[0].message)
  2730. self.assertTrue('another/ios_file.mm' in errors[0].message)
  2731. self.assertTrue('some/mac/file.mm' not in errors[0].message)
  2732. self.assertTrue('some/ios/file_egtest.mm' in errors[0].message)
  2733. self.assertTrue('some/ios/file_unittest.mm' not in errors[0].message)
  2734. def testBannedMojoFunctions(self):
  2735. input_api = MockInputApi()
  2736. input_api.files = [
  2737. MockFile('some/cpp/problematic/file2.cc', ['mojo::ConvertTo<>']),
  2738. MockFile('third_party/blink/ok/file3.cc', ['mojo::ConvertTo<>']),
  2739. MockFile('content/renderer/ok/file3.cc', ['mojo::ConvertTo<>']),
  2740. ]
  2741. results = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
  2742. # warnings are results[0], errors are results[1]
  2743. self.assertEqual(1, len(results))
  2744. self.assertTrue('some/cpp/problematic/file2.cc' in results[0].message)
  2745. self.assertTrue(
  2746. 'third_party/blink/ok/file3.cc' not in results[0].message)
  2747. self.assertTrue(
  2748. 'content/renderer/ok/file3.cc' not in results[0].message)
  2749. def testBannedMojomPatterns(self):
  2750. input_api = MockInputApi()
  2751. input_api.files = [
  2752. MockFile(
  2753. 'bad.mojom',
  2754. ['struct Bad {', ' handle<shared_buffer> buffer;', '};']),
  2755. MockFile('good.mojom', [
  2756. 'struct Good {',
  2757. ' mojo_base.mojom.ReadOnlySharedMemoryRegion region1;',
  2758. ' mojo_base.mojom.WritableSharedMemoryRegion region2;',
  2759. ' mojo_base.mojom.UnsafeSharedMemoryRegion region3;', '};'
  2760. ]),
  2761. ]
  2762. results = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
  2763. # warnings are results[0], errors are results[1]
  2764. self.assertEqual(1, len(results))
  2765. self.assertTrue('bad.mojom' in results[0].message)
  2766. self.assertTrue('good.mojom' not in results[0].message)
  2767. class NoProductionCodeUsingTestOnlyFunctionsTest(unittest.TestCase):
  2768. def testTruePositives(self):
  2769. mock_input_api = MockInputApi()
  2770. mock_input_api.files = [
  2771. MockFile('some/path/foo.cc', ['foo_for_testing();']),
  2772. MockFile('some/path/foo.mm', ['FooForTesting();']),
  2773. MockFile('some/path/foo.cxx', ['FooForTests();']),
  2774. MockFile('some/path/foo.cpp', ['foo_for_test();']),
  2775. ]
  2776. results = PRESUBMIT.CheckNoProductionCodeUsingTestOnlyFunctions(
  2777. mock_input_api, MockOutputApi())
  2778. self.assertEqual(1, len(results))
  2779. self.assertEqual(4, len(results[0].items))
  2780. self.assertTrue('foo.cc' in results[0].items[0])
  2781. self.assertTrue('foo.mm' in results[0].items[1])
  2782. self.assertTrue('foo.cxx' in results[0].items[2])
  2783. self.assertTrue('foo.cpp' in results[0].items[3])
  2784. def testFalsePositives(self):
  2785. mock_input_api = MockInputApi()
  2786. mock_input_api.files = [
  2787. MockFile('some/path/foo.h', ['foo_for_testing();']),
  2788. MockFile('some/path/foo.mm', ['FooForTesting() {']),
  2789. MockFile('some/path/foo.cc', ['::FooForTests();']),
  2790. MockFile('some/path/foo.cpp', ['// foo_for_test();']),
  2791. MockFile('some/path/foo.cxx', ['foo_for_test(); // IN-TEST']),
  2792. ]
  2793. results = PRESUBMIT.CheckNoProductionCodeUsingTestOnlyFunctions(
  2794. mock_input_api, MockOutputApi())
  2795. self.assertEqual(0, len(results))
  2796. def testAllowedFiles(self):
  2797. mock_input_api = MockInputApi()
  2798. mock_input_api.files = [
  2799. MockFile('path/foo_unittest.cc', ['foo_for_testing();']),
  2800. MockFile('path/bar_unittest_mac.cc', ['foo_for_testing();']),
  2801. MockFile('path/baz_unittests.cc', ['foo_for_testing();']),
  2802. ]
  2803. results = PRESUBMIT.CheckNoProductionCodeUsingTestOnlyFunctions(
  2804. mock_input_api, MockOutputApi())
  2805. self.assertEqual(0, len(results))
  2806. class NoProductionJavaCodeUsingTestOnlyFunctionsTest(unittest.TestCase):
  2807. def testTruePositives(self):
  2808. mock_input_api = MockInputApi()
  2809. mock_input_api.files = [
  2810. MockFile('dir/java/src/foo.java', ['FooForTesting();']),
  2811. MockFile('dir/java/src/bar.java', ['FooForTests(x);']),
  2812. MockFile('dir/java/src/baz.java', ['FooForTest(', 'y', ');']),
  2813. MockFile('dir/java/src/mult.java', [
  2814. 'int x = SomethingLongHere()',
  2815. ' * SomethingLongHereForTesting();'
  2816. ])
  2817. ]
  2818. results = PRESUBMIT.CheckNoProductionCodeUsingTestOnlyFunctionsJava(
  2819. mock_input_api, MockOutputApi())
  2820. self.assertEqual(1, len(results))
  2821. self.assertEqual(4, len(results[0].items))
  2822. self.assertTrue('foo.java' in results[0].items[0])
  2823. self.assertTrue('bar.java' in results[0].items[1])
  2824. self.assertTrue('baz.java' in results[0].items[2])
  2825. self.assertTrue('mult.java' in results[0].items[3])
  2826. def testFalsePositives(self):
  2827. mock_input_api = MockInputApi()
  2828. mock_input_api.files = [
  2829. MockFile('dir/java/src/foo.xml', ['FooForTesting();']),
  2830. MockFile('dir/java/src/foo.java', ['FooForTests() {']),
  2831. MockFile('dir/java/src/bar.java', ['// FooForTest();']),
  2832. MockFile('dir/java/src/bar2.java', ['x = 1; // FooForTest();']),
  2833. MockFile('dir/java/src/bar3.java', ['@VisibleForTesting']),
  2834. MockFile('dir/java/src/bar4.java', ['@VisibleForTesting()']),
  2835. MockFile('dir/java/src/bar5.java', [
  2836. '@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)'
  2837. ]),
  2838. MockFile('dir/javatests/src/baz.java', ['FooForTest(', 'y', ');']),
  2839. MockFile('dir/junit/src/baz.java', ['FooForTest(', 'y', ');']),
  2840. MockFile('dir/junit/src/javadoc.java',
  2841. ['/** Use FooForTest(); to obtain foo in tests.'
  2842. ' */']),
  2843. MockFile(
  2844. 'dir/junit/src/javadoc2.java',
  2845. ['/** ', ' * Use FooForTest(); to obtain foo in tests.'
  2846. ' */']),
  2847. MockFile('dir/java/src/bar6.java',
  2848. ['FooForTesting(); // IN-TEST']),
  2849. ]
  2850. results = PRESUBMIT.CheckNoProductionCodeUsingTestOnlyFunctionsJava(
  2851. mock_input_api, MockOutputApi())
  2852. self.assertEqual(0, len(results))
  2853. class NewImagesWarningTest(unittest.TestCase):
  2854. def testTruePositives(self):
  2855. mock_input_api = MockInputApi()
  2856. mock_input_api.files = [
  2857. MockFile('dir/android/res/drawable/foo.png', []),
  2858. MockFile('dir/android/res/drawable-v21/bar.svg', []),
  2859. MockFile('dir/android/res/mipmap-v21-en/baz.webp', []),
  2860. MockFile('dir/android/res_gshoe/drawable-mdpi/foobar.png', []),
  2861. ]
  2862. results = PRESUBMIT._CheckNewImagesWarning(mock_input_api,
  2863. MockOutputApi())
  2864. self.assertEqual(1, len(results))
  2865. self.assertEqual(4, len(results[0].items))
  2866. self.assertTrue('foo.png' in results[0].items[0].LocalPath())
  2867. self.assertTrue('bar.svg' in results[0].items[1].LocalPath())
  2868. self.assertTrue('baz.webp' in results[0].items[2].LocalPath())
  2869. self.assertTrue('foobar.png' in results[0].items[3].LocalPath())
  2870. def testFalsePositives(self):
  2871. mock_input_api = MockInputApi()
  2872. mock_input_api.files = [
  2873. MockFile('dir/pngs/README.md', []),
  2874. MockFile('java/test/res/drawable/foo.png', []),
  2875. MockFile('third_party/blink/foo.png', []),
  2876. MockFile('dir/third_party/libpng/src/foo.cc', ['foobar']),
  2877. MockFile('dir/resources.webp/.gitignore', ['foo.png']),
  2878. ]
  2879. results = PRESUBMIT._CheckNewImagesWarning(mock_input_api,
  2880. MockOutputApi())
  2881. self.assertEqual(0, len(results))
  2882. class ProductIconsTest(unittest.TestCase):
  2883. def test(self):
  2884. mock_input_api = MockInputApi()
  2885. mock_input_api.files = [
  2886. MockFile('components/vector_icons/google_jetpack.icon', []),
  2887. MockFile('components/vector_icons/generic_jetpack.icon', []),
  2888. ]
  2889. results = PRESUBMIT.CheckNoProductIconsAddedToPublicRepo(
  2890. mock_input_api, MockOutputApi())
  2891. self.assertEqual(1, len(results))
  2892. self.assertEqual(1, len(results[0].items))
  2893. self.assertTrue('google_jetpack.icon' in results[0].items[0])
  2894. class CheckUniquePtrTest(unittest.TestCase):
  2895. def testTruePositivesNullptr(self):
  2896. mock_input_api = MockInputApi()
  2897. mock_input_api.files = [
  2898. MockFile('dir/baz.cc', ['std::unique_ptr<T>()']),
  2899. MockFile('dir/baz-p.cc', ['std::unique_ptr<T<P>>()']),
  2900. ]
  2901. results = PRESUBMIT.CheckUniquePtrOnUpload(mock_input_api,
  2902. MockOutputApi())
  2903. self.assertEqual(1, len(results))
  2904. self.assertTrue('nullptr' in results[0].message)
  2905. self.assertEqual(2, len(results[0].items))
  2906. self.assertTrue('baz.cc' in results[0].items[0])
  2907. self.assertTrue('baz-p.cc' in results[0].items[1])
  2908. def testTruePositivesConstructor(self):
  2909. mock_input_api = MockInputApi()
  2910. mock_input_api.files = [
  2911. MockFile('dir/foo.cc', ['return std::unique_ptr<T>(foo);']),
  2912. MockFile('dir/bar.mm', ['bar = std::unique_ptr<T>(foo)']),
  2913. MockFile('dir/mult.cc', [
  2914. 'return',
  2915. ' std::unique_ptr<T>(barVeryVeryLongFooSoThatItWouldNotFitAbove);'
  2916. ]),
  2917. MockFile('dir/mult2.cc', [
  2918. 'barVeryVeryLongLongBaaaaaarSoThatTheLineLimitIsAlmostReached =',
  2919. ' std::unique_ptr<T>(foo);'
  2920. ]),
  2921. MockFile('dir/mult3.cc', [
  2922. 'bar = std::unique_ptr<T>(',
  2923. ' fooVeryVeryVeryLongStillGoingWellThisWillTakeAWhileFinallyThere);'
  2924. ]),
  2925. MockFile('dir/multi_arg.cc', [
  2926. 'auto p = std::unique_ptr<std::pair<T, D>>(new std::pair(T, D));'
  2927. ]),
  2928. ]
  2929. results = PRESUBMIT.CheckUniquePtrOnUpload(mock_input_api,
  2930. MockOutputApi())
  2931. self.assertEqual(1, len(results))
  2932. self.assertTrue('std::make_unique' in results[0].message)
  2933. self.assertEqual(6, len(results[0].items))
  2934. self.assertTrue('foo.cc' in results[0].items[0])
  2935. self.assertTrue('bar.mm' in results[0].items[1])
  2936. self.assertTrue('mult.cc' in results[0].items[2])
  2937. self.assertTrue('mult2.cc' in results[0].items[3])
  2938. self.assertTrue('mult3.cc' in results[0].items[4])
  2939. self.assertTrue('multi_arg.cc' in results[0].items[5])
  2940. def testFalsePositives(self):
  2941. mock_input_api = MockInputApi()
  2942. mock_input_api.files = [
  2943. MockFile('dir/foo.cc', ['return std::unique_ptr<T[]>(foo);']),
  2944. MockFile('dir/bar.mm', ['bar = std::unique_ptr<T[]>(foo)']),
  2945. MockFile('dir/file.cc', ['std::unique_ptr<T> p = Foo();']),
  2946. MockFile('dir/baz.cc',
  2947. ['std::unique_ptr<T> result = std::make_unique<T>();']),
  2948. MockFile('dir/baz2.cc',
  2949. ['std::unique_ptr<T> result = std::make_unique<T>(']),
  2950. MockFile('dir/nested.cc', ['set<std::unique_ptr<T>>();']),
  2951. MockFile('dir/nested2.cc', ['map<U, std::unique_ptr<T>>();']),
  2952. # Changed line is inside a multiline template block.
  2953. MockFile('dir/template.cc', [' std::unique_ptr<T>>(']),
  2954. MockFile('dir/template2.cc', [' std::unique_ptr<T>>()']),
  2955. # Two-argument invocation of std::unique_ptr is exempt because there is
  2956. # no equivalent using std::make_unique.
  2957. MockFile('dir/multi_arg.cc',
  2958. ['auto p = std::unique_ptr<T, D>(new T(), D());']),
  2959. ]
  2960. results = PRESUBMIT.CheckUniquePtrOnUpload(mock_input_api,
  2961. MockOutputApi())
  2962. self.assertEqual([], results)
  2963. class CheckNoDirectIncludesHeadersWhichRedefineStrCat(unittest.TestCase):
  2964. def testBlocksDirectIncludes(self):
  2965. mock_input_api = MockInputApi()
  2966. mock_input_api.files = [
  2967. MockFile('dir/foo_win.cc', ['#include "shlwapi.h"']),
  2968. MockFile('dir/bar.h', ['#include <propvarutil.h>']),
  2969. MockFile('dir/baz.h', ['#include <atlbase.h>']),
  2970. MockFile('dir/jumbo.h', ['#include "sphelper.h"']),
  2971. ]
  2972. results = PRESUBMIT.CheckNoStrCatRedefines(mock_input_api,
  2973. MockOutputApi())
  2974. self.assertEqual(1, len(results))
  2975. self.assertEqual(4, len(results[0].items))
  2976. self.assertTrue('StrCat' in results[0].message)
  2977. self.assertTrue('foo_win.cc' in results[0].items[0])
  2978. self.assertTrue('bar.h' in results[0].items[1])
  2979. self.assertTrue('baz.h' in results[0].items[2])
  2980. self.assertTrue('jumbo.h' in results[0].items[3])
  2981. def testAllowsToIncludeWrapper(self):
  2982. mock_input_api = MockInputApi()
  2983. mock_input_api.files = [
  2984. MockFile('dir/baz_win.cc', ['#include "base/win/shlwapi.h"']),
  2985. MockFile('dir/baz-win.h', ['#include "base/win/atl.h"']),
  2986. ]
  2987. results = PRESUBMIT.CheckNoStrCatRedefines(mock_input_api,
  2988. MockOutputApi())
  2989. self.assertEqual(0, len(results))
  2990. def testAllowsToCreateWrapper(self):
  2991. mock_input_api = MockInputApi()
  2992. mock_input_api.files = [
  2993. MockFile('base/win/shlwapi.h', [
  2994. '#include <shlwapi.h>',
  2995. '#include "base/win/windows_defines.inc"'
  2996. ]),
  2997. ]
  2998. results = PRESUBMIT.CheckNoStrCatRedefines(mock_input_api,
  2999. MockOutputApi())
  3000. self.assertEqual(0, len(results))
  3001. def testIgnoresNonImplAndHeaders(self):
  3002. mock_input_api = MockInputApi()
  3003. mock_input_api.files = [
  3004. MockFile('dir/foo_win.txt', ['#include "shlwapi.h"']),
  3005. MockFile('dir/bar.asm', ['#include <propvarutil.h>']),
  3006. ]
  3007. results = PRESUBMIT.CheckNoStrCatRedefines(mock_input_api,
  3008. MockOutputApi())
  3009. self.assertEqual(0, len(results))
  3010. class StringTest(unittest.TestCase):
  3011. """Tests ICU syntax check and translation screenshots check."""
  3012. # An empty grd file.
  3013. OLD_GRD_CONTENTS = """<?xml version="1.0" encoding="UTF-8"?>
  3014. <grit latest_public_release="1" current_release="1">
  3015. <release seq="1">
  3016. <messages></messages>
  3017. </release>
  3018. </grit>
  3019. """.splitlines()
  3020. # A grd file with a single message.
  3021. NEW_GRD_CONTENTS1 = """<?xml version="1.0" encoding="UTF-8"?>
  3022. <grit latest_public_release="1" current_release="1">
  3023. <release seq="1">
  3024. <messages>
  3025. <message name="IDS_TEST1">
  3026. Test string 1
  3027. </message>
  3028. <message name="IDS_TEST_STRING_NON_TRANSLATEABLE1"
  3029. translateable="false">
  3030. Non translateable message 1, should be ignored
  3031. </message>
  3032. <message name="IDS_TEST_STRING_ACCESSIBILITY"
  3033. is_accessibility_with_no_ui="true">
  3034. Accessibility label 1, should be ignored
  3035. </message>
  3036. </messages>
  3037. </release>
  3038. </grit>
  3039. """.splitlines()
  3040. # A grd file with two messages.
  3041. NEW_GRD_CONTENTS2 = """<?xml version="1.0" encoding="UTF-8"?>
  3042. <grit latest_public_release="1" current_release="1">
  3043. <release seq="1">
  3044. <messages>
  3045. <message name="IDS_TEST1">
  3046. Test string 1
  3047. </message>
  3048. <message name="IDS_TEST2">
  3049. Test string 2
  3050. </message>
  3051. <message name="IDS_TEST_STRING_NON_TRANSLATEABLE2"
  3052. translateable="false">
  3053. Non translateable message 2, should be ignored
  3054. </message>
  3055. </messages>
  3056. </release>
  3057. </grit>
  3058. """.splitlines()
  3059. # A grd file with one ICU syntax message without syntax errors.
  3060. NEW_GRD_CONTENTS_ICU_SYNTAX_OK1 = """<?xml version="1.0" encoding="UTF-8"?>
  3061. <grit latest_public_release="1" current_release="1">
  3062. <release seq="1">
  3063. <messages>
  3064. <message name="IDS_TEST1">
  3065. {NUM, plural,
  3066. =1 {Test text for numeric one}
  3067. other {Test text for plural with {NUM} as number}}
  3068. </message>
  3069. </messages>
  3070. </release>
  3071. </grit>
  3072. """.splitlines()
  3073. # A grd file with one ICU syntax message without syntax errors.
  3074. NEW_GRD_CONTENTS_ICU_SYNTAX_OK2 = """<?xml version="1.0" encoding="UTF-8"?>
  3075. <grit latest_public_release="1" current_release="1">
  3076. <release seq="1">
  3077. <messages>
  3078. <message name="IDS_TEST1">
  3079. {NUM, plural,
  3080. =1 {Different test text for numeric one}
  3081. other {Different test text for plural with {NUM} as number}}
  3082. </message>
  3083. </messages>
  3084. </release>
  3085. </grit>
  3086. """.splitlines()
  3087. # A grd file with multiple ICU syntax messages without syntax errors.
  3088. NEW_GRD_CONTENTS_ICU_SYNTAX_OK3 = """<?xml version="1.0" encoding="UTF-8"?>
  3089. <grit latest_public_release="1" current_release="1">
  3090. <release seq="1">
  3091. <messages>
  3092. <message name="IDS_TEST1">
  3093. {NUM, plural,
  3094. =0 {New test text for numeric zero}
  3095. =1 {Different test text for numeric one}
  3096. =2 {New test text for numeric two}
  3097. =3 {New test text for numeric three}
  3098. other {Different test text for plural with {NUM} as number}}
  3099. </message>
  3100. </messages>
  3101. </release>
  3102. </grit>
  3103. """.splitlines()
  3104. # A grd file with one ICU syntax message with syntax errors (misses a comma).
  3105. NEW_GRD_CONTENTS_ICU_SYNTAX_ERROR = """<?xml version="1.0" encoding="UTF-8"?>
  3106. <grit latest_public_release="1" current_release="1">
  3107. <release seq="1">
  3108. <messages>
  3109. <message name="IDS_TEST1">
  3110. {NUM, plural
  3111. =1 {Test text for numeric one}
  3112. other {Test text for plural with {NUM} as number}}
  3113. </message>
  3114. </messages>
  3115. </release>
  3116. </grit>
  3117. """.splitlines()
  3118. OLD_GRDP_CONTENTS = ('<?xml version="1.0" encoding="utf-8"?>',
  3119. '<grit-part>', '</grit-part>')
  3120. NEW_GRDP_CONTENTS1 = ('<?xml version="1.0" encoding="utf-8"?>',
  3121. '<grit-part>', '<message name="IDS_PART_TEST1">',
  3122. 'Part string 1', '</message>', '</grit-part>')
  3123. NEW_GRDP_CONTENTS2 = ('<?xml version="1.0" encoding="utf-8"?>',
  3124. '<grit-part>', '<message name="IDS_PART_TEST1">',
  3125. 'Part string 1', '</message>',
  3126. '<message name="IDS_PART_TEST2">', 'Part string 2',
  3127. '</message>', '</grit-part>')
  3128. NEW_GRDP_CONTENTS3 = (
  3129. '<?xml version="1.0" encoding="utf-8"?>', '<grit-part>',
  3130. '<message name="IDS_PART_TEST1" desc="Description with typo.">',
  3131. 'Part string 1', '</message>', '</grit-part>')
  3132. NEW_GRDP_CONTENTS4 = (
  3133. '<?xml version="1.0" encoding="utf-8"?>', '<grit-part>',
  3134. '<message name="IDS_PART_TEST1" desc="Description with typo fixed.">',
  3135. 'Part string 1', '</message>', '</grit-part>')
  3136. NEW_GRDP_CONTENTS5 = (
  3137. '<?xml version="1.0" encoding="utf-8"?>', '<grit-part>',
  3138. '<message name="IDS_PART_TEST1" meaning="Meaning with typo.">',
  3139. 'Part string 1', '</message>', '</grit-part>')
  3140. NEW_GRDP_CONTENTS6 = (
  3141. '<?xml version="1.0" encoding="utf-8"?>', '<grit-part>',
  3142. '<message name="IDS_PART_TEST1" meaning="Meaning with typo fixed.">',
  3143. 'Part string 1', '</message>', '</grit-part>')
  3144. # A grdp file with one ICU syntax message without syntax errors.
  3145. NEW_GRDP_CONTENTS_ICU_SYNTAX_OK1 = (
  3146. '<?xml version="1.0" encoding="utf-8"?>', '<grit-part>',
  3147. '<message name="IDS_PART_TEST1">', '{NUM, plural,',
  3148. '=1 {Test text for numeric one}',
  3149. 'other {Test text for plural with {NUM} as number}}', '</message>',
  3150. '</grit-part>')
  3151. # A grdp file with one ICU syntax message without syntax errors.
  3152. NEW_GRDP_CONTENTS_ICU_SYNTAX_OK2 = (
  3153. '<?xml version="1.0" encoding="utf-8"?>', '<grit-part>',
  3154. '<message name="IDS_PART_TEST1">', '{NUM, plural,',
  3155. '=1 {Different test text for numeric one}',
  3156. 'other {Different test text for plural with {NUM} as number}}',
  3157. '</message>', '</grit-part>')
  3158. # A grdp file with multiple ICU syntax messages without syntax errors.
  3159. NEW_GRDP_CONTENTS_ICU_SYNTAX_OK3 = (
  3160. '<?xml version="1.0" encoding="utf-8"?>', '<grit-part>',
  3161. '<message name="IDS_PART_TEST1">', '{NUM, plural,',
  3162. '=0 {New test text for numeric zero}',
  3163. '=1 {Different test text for numeric one}',
  3164. '=2 {New test text for numeric two}',
  3165. '=3 {New test text for numeric three}',
  3166. 'other {Different test text for plural with {NUM} as number}}',
  3167. '</message>', '</grit-part>')
  3168. # A grdp file with one ICU syntax message with syntax errors (superfluous
  3169. # space).
  3170. NEW_GRDP_CONTENTS_ICU_SYNTAX_ERROR = (
  3171. '<?xml version="1.0" encoding="utf-8"?>', '<grit-part>',
  3172. '<message name="IDS_PART_TEST1">', '{NUM, plural,',
  3173. '= 1 {Test text for numeric one}',
  3174. 'other {Test text for plural with {NUM} as number}}', '</message>',
  3175. '</grit-part>')
  3176. VALID_SHA1 = ('0000000000000000000000000000000000000000', )
  3177. DO_NOT_UPLOAD_PNG_MESSAGE = ('Do not include actual screenshots in the '
  3178. 'changelist. Run '
  3179. 'tools/translate/upload_screenshots.py to '
  3180. 'upload them instead:')
  3181. ADD_SIGNATURES_MESSAGE = ('You are adding UI strings.\n'
  3182. 'To ensure the best translations, take '
  3183. 'screenshots of the relevant UI '
  3184. '(https://g.co/chrome/translation) and add '
  3185. 'these files to your changelist:')
  3186. REMOVE_SIGNATURES_MESSAGE = ('You removed strings associated with these '
  3187. 'files. Remove:')
  3188. ICU_SYNTAX_ERROR_MESSAGE = (
  3189. 'ICU syntax errors were found in the following '
  3190. 'strings (problems or feedback? Contact '
  3191. 'rainhard@chromium.org):')
  3192. SHA1_FORMAT_MESSAGE = (
  3193. 'The following files do not seem to contain valid sha1 '
  3194. 'hashes. Make sure they contain hashes created by '
  3195. 'tools/translate/upload_screenshots.py:')
  3196. def makeInputApi(self, files):
  3197. input_api = MockInputApi()
  3198. input_api.InitFiles(files)
  3199. return input_api
  3200. """ CL modified and added messages, but didn't add any screenshots."""
  3201. def testNoScreenshots(self):
  3202. # No new strings (file contents same). Should not warn.
  3203. input_api = self.makeInputApi([
  3204. MockAffectedFile('test.grd',
  3205. self.NEW_GRD_CONTENTS1,
  3206. self.NEW_GRD_CONTENTS1,
  3207. action='M'),
  3208. MockAffectedFile('part.grdp',
  3209. self.NEW_GRDP_CONTENTS1,
  3210. self.NEW_GRDP_CONTENTS1,
  3211. action='M')
  3212. ])
  3213. warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  3214. self.assertEqual(0, len(warnings))
  3215. # Add two new strings. Should have two warnings.
  3216. input_api = self.makeInputApi([
  3217. MockAffectedFile('test.grd',
  3218. self.NEW_GRD_CONTENTS2,
  3219. self.NEW_GRD_CONTENTS1,
  3220. action='M'),
  3221. MockAffectedFile('part.grdp',
  3222. self.NEW_GRDP_CONTENTS2,
  3223. self.NEW_GRDP_CONTENTS1,
  3224. action='M')
  3225. ])
  3226. warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  3227. self.assertEqual(1, len(warnings))
  3228. self.assertEqual(self.ADD_SIGNATURES_MESSAGE, warnings[0].message)
  3229. self.assertEqual('error', warnings[0].type)
  3230. self.assertEqual([
  3231. os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
  3232. os.path.join('test_grd', 'IDS_TEST2.png.sha1')
  3233. ], warnings[0].items)
  3234. # Add four new strings. Should have four warnings.
  3235. input_api = self.makeInputApi([
  3236. MockAffectedFile('test.grd',
  3237. self.NEW_GRD_CONTENTS2,
  3238. self.OLD_GRD_CONTENTS,
  3239. action='M'),
  3240. MockAffectedFile('part.grdp',
  3241. self.NEW_GRDP_CONTENTS2,
  3242. self.OLD_GRDP_CONTENTS,
  3243. action='M')
  3244. ])
  3245. warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  3246. self.assertEqual(1, len(warnings))
  3247. self.assertEqual('error', warnings[0].type)
  3248. self.assertEqual(self.ADD_SIGNATURES_MESSAGE, warnings[0].message)
  3249. self.assertEqual([
  3250. os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
  3251. os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
  3252. os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
  3253. os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
  3254. ], warnings[0].items)
  3255. def testModifiedMessageDescription(self):
  3256. # CL modified a message description for a message that does not yet have a
  3257. # screenshot. Should not warn.
  3258. input_api = self.makeInputApi([
  3259. MockAffectedFile('part.grdp',
  3260. self.NEW_GRDP_CONTENTS3,
  3261. self.NEW_GRDP_CONTENTS4,
  3262. action='M')
  3263. ])
  3264. warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  3265. self.assertEqual(0, len(warnings))
  3266. # CL modified a message description for a message that already has a
  3267. # screenshot. Should not warn.
  3268. input_api = self.makeInputApi([
  3269. MockAffectedFile('part.grdp',
  3270. self.NEW_GRDP_CONTENTS3,
  3271. self.NEW_GRDP_CONTENTS4,
  3272. action='M'),
  3273. MockFile(os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
  3274. self.VALID_SHA1,
  3275. action='A')
  3276. ])
  3277. warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  3278. self.assertEqual(0, len(warnings))
  3279. def testModifiedMessageMeaning(self):
  3280. # CL modified a message meaning for a message that does not yet have a
  3281. # screenshot. Should warn.
  3282. input_api = self.makeInputApi([
  3283. MockAffectedFile('part.grdp',
  3284. self.NEW_GRDP_CONTENTS5,
  3285. self.NEW_GRDP_CONTENTS6,
  3286. action='M')
  3287. ])
  3288. warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  3289. self.assertEqual(1, len(warnings))
  3290. # CL modified a message meaning for a message that already has a
  3291. # screenshot. Should not warn.
  3292. input_api = self.makeInputApi([
  3293. MockAffectedFile('part.grdp',
  3294. self.NEW_GRDP_CONTENTS5,
  3295. self.NEW_GRDP_CONTENTS6,
  3296. action='M'),
  3297. MockFile(os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
  3298. self.VALID_SHA1,
  3299. action='A')
  3300. ])
  3301. warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  3302. self.assertEqual(0, len(warnings))
  3303. def testModifiedIntroducedInvalidSha1(self):
  3304. # CL modified a message and the sha1 file changed to invalid
  3305. input_api = self.makeInputApi([
  3306. MockAffectedFile('part.grdp',
  3307. self.NEW_GRDP_CONTENTS5,
  3308. self.NEW_GRDP_CONTENTS6,
  3309. action='M'),
  3310. MockAffectedFile(os.path.join('part_grdp',
  3311. 'IDS_PART_TEST1.png.sha1'),
  3312. ('some invalid sha1', ),
  3313. self.VALID_SHA1,
  3314. action='M')
  3315. ])
  3316. warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  3317. self.assertEqual(1, len(warnings))
  3318. def testPngAddedSha1NotAdded(self):
  3319. # CL added one new message in a grd file and added the png file associated
  3320. # with it, but did not add the corresponding sha1 file. This should warn
  3321. # twice:
  3322. # - Once for the added png file (because we don't want developers to upload
  3323. # actual images)
  3324. # - Once for the missing .sha1 file
  3325. input_api = self.makeInputApi([
  3326. MockAffectedFile('test.grd',
  3327. self.NEW_GRD_CONTENTS1,
  3328. self.OLD_GRD_CONTENTS,
  3329. action='M'),
  3330. MockAffectedFile(os.path.join('test_grd', 'IDS_TEST1.png'),
  3331. 'binary',
  3332. action='A')
  3333. ])
  3334. warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  3335. self.assertEqual(2, len(warnings))
  3336. self.assertEqual('error', warnings[0].type)
  3337. self.assertEqual(self.DO_NOT_UPLOAD_PNG_MESSAGE, warnings[0].message)
  3338. self.assertEqual([os.path.join('test_grd', 'IDS_TEST1.png')],
  3339. warnings[0].items)
  3340. self.assertEqual('error', warnings[1].type)
  3341. self.assertEqual(self.ADD_SIGNATURES_MESSAGE, warnings[1].message)
  3342. self.assertEqual([os.path.join('test_grd', 'IDS_TEST1.png.sha1')],
  3343. warnings[1].items)
  3344. # CL added two messages (one in grd, one in grdp) and added the png files
  3345. # associated with the messages, but did not add the corresponding sha1
  3346. # files. This should warn twice:
  3347. # - Once for the added png files (because we don't want developers to upload
  3348. # actual images)
  3349. # - Once for the missing .sha1 files
  3350. input_api = self.makeInputApi([
  3351. # Modified files:
  3352. MockAffectedFile('test.grd',
  3353. self.NEW_GRD_CONTENTS1,
  3354. self.OLD_GRD_CONTENTS,
  3355. action='M'),
  3356. MockAffectedFile('part.grdp',
  3357. self.NEW_GRDP_CONTENTS1,
  3358. self.OLD_GRDP_CONTENTS,
  3359. action='M'),
  3360. # Added files:
  3361. MockAffectedFile(os.path.join('test_grd', 'IDS_TEST1.png'),
  3362. 'binary',
  3363. action='A'),
  3364. MockAffectedFile(os.path.join('part_grdp', 'IDS_PART_TEST1.png'),
  3365. 'binary',
  3366. action='A')
  3367. ])
  3368. warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  3369. self.assertEqual(2, len(warnings))
  3370. self.assertEqual('error', warnings[0].type)
  3371. self.assertEqual(self.DO_NOT_UPLOAD_PNG_MESSAGE, warnings[0].message)
  3372. self.assertEqual([
  3373. os.path.join('part_grdp', 'IDS_PART_TEST1.png'),
  3374. os.path.join('test_grd', 'IDS_TEST1.png')
  3375. ], warnings[0].items)
  3376. self.assertEqual('error', warnings[0].type)
  3377. self.assertEqual(self.ADD_SIGNATURES_MESSAGE, warnings[1].message)
  3378. self.assertEqual([
  3379. os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
  3380. os.path.join('test_grd', 'IDS_TEST1.png.sha1')
  3381. ], warnings[1].items)
  3382. def testScreenshotsWithSha1(self):
  3383. # CL added four messages (two each in a grd and grdp) and their
  3384. # corresponding .sha1 files. No warnings.
  3385. input_api = self.makeInputApi([
  3386. # Modified files:
  3387. MockAffectedFile('test.grd',
  3388. self.NEW_GRD_CONTENTS2,
  3389. self.OLD_GRD_CONTENTS,
  3390. action='M'),
  3391. MockAffectedFile('part.grdp',
  3392. self.NEW_GRDP_CONTENTS2,
  3393. self.OLD_GRDP_CONTENTS,
  3394. action='M'),
  3395. # Added files:
  3396. MockFile(os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
  3397. self.VALID_SHA1,
  3398. action='A'),
  3399. MockFile(os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
  3400. ('0000000000000000000000000000000000000000', ''),
  3401. action='A'),
  3402. MockFile(os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
  3403. self.VALID_SHA1,
  3404. action='A'),
  3405. MockFile(os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
  3406. self.VALID_SHA1,
  3407. action='A'),
  3408. ])
  3409. warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  3410. self.assertEqual([], warnings)
  3411. def testScreenshotsWithInvalidSha1(self):
  3412. input_api = self.makeInputApi([
  3413. # Modified files:
  3414. MockAffectedFile('test.grd',
  3415. self.NEW_GRD_CONTENTS2,
  3416. self.OLD_GRD_CONTENTS,
  3417. action='M'),
  3418. MockAffectedFile('part.grdp',
  3419. self.NEW_GRDP_CONTENTS2,
  3420. self.OLD_GRDP_CONTENTS,
  3421. action='M'),
  3422. # Added files:
  3423. MockFile(os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
  3424. self.VALID_SHA1,
  3425. action='A'),
  3426. MockFile(os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
  3427. ('‰PNG', 'test'),
  3428. action='A'),
  3429. MockFile(os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
  3430. self.VALID_SHA1,
  3431. action='A'),
  3432. MockFile(os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
  3433. self.VALID_SHA1,
  3434. action='A'),
  3435. ])
  3436. warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  3437. self.assertEqual(1, len(warnings))
  3438. self.assertEqual('error', warnings[0].type)
  3439. self.assertEqual(self.SHA1_FORMAT_MESSAGE, warnings[0].message)
  3440. self.assertEqual([os.path.join('test_grd', 'IDS_TEST2.png.sha1')],
  3441. warnings[0].items)
  3442. def testScreenshotsRemovedWithSha1(self):
  3443. # Replace new contents with old contents in grd and grp files, removing
  3444. # IDS_TEST1, IDS_TEST2, IDS_PART_TEST1 and IDS_PART_TEST2.
  3445. # Should warn to remove the sha1 files associated with these strings.
  3446. input_api = self.makeInputApi([
  3447. # Modified files:
  3448. MockAffectedFile(
  3449. 'test.grd',
  3450. self.OLD_GRD_CONTENTS, # new_contents
  3451. self.NEW_GRD_CONTENTS2, # old_contents
  3452. action='M'),
  3453. MockAffectedFile(
  3454. 'part.grdp',
  3455. self.OLD_GRDP_CONTENTS, # new_contents
  3456. self.NEW_GRDP_CONTENTS2, # old_contents
  3457. action='M'),
  3458. # Unmodified files:
  3459. MockFile(os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
  3460. self.VALID_SHA1, ''),
  3461. MockFile(os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
  3462. self.VALID_SHA1, ''),
  3463. MockFile(os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
  3464. self.VALID_SHA1, ''),
  3465. MockFile(os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
  3466. self.VALID_SHA1, '')
  3467. ])
  3468. warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  3469. self.assertEqual(1, len(warnings))
  3470. self.assertEqual('error', warnings[0].type)
  3471. self.assertEqual(self.REMOVE_SIGNATURES_MESSAGE, warnings[0].message)
  3472. self.assertEqual([
  3473. os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
  3474. os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
  3475. os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
  3476. os.path.join('test_grd', 'IDS_TEST2.png.sha1')
  3477. ], warnings[0].items)
  3478. # Same as above, but this time one of the .sha1 files is also removed.
  3479. input_api = self.makeInputApi([
  3480. # Modified files:
  3481. MockAffectedFile(
  3482. 'test.grd',
  3483. self.OLD_GRD_CONTENTS, # new_contents
  3484. self.NEW_GRD_CONTENTS2, # old_contents
  3485. action='M'),
  3486. MockAffectedFile(
  3487. 'part.grdp',
  3488. self.OLD_GRDP_CONTENTS, # new_contents
  3489. self.NEW_GRDP_CONTENTS2, # old_contents
  3490. action='M'),
  3491. # Unmodified files:
  3492. MockFile(os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
  3493. self.VALID_SHA1, ''),
  3494. MockFile(os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
  3495. self.VALID_SHA1, ''),
  3496. # Deleted files:
  3497. MockAffectedFile(os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
  3498. '',
  3499. 'old_contents',
  3500. action='D'),
  3501. MockAffectedFile(os.path.join('part_grdp',
  3502. 'IDS_PART_TEST2.png.sha1'),
  3503. '',
  3504. 'old_contents',
  3505. action='D')
  3506. ])
  3507. warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  3508. self.assertEqual(1, len(warnings))
  3509. self.assertEqual('error', warnings[0].type)
  3510. self.assertEqual(self.REMOVE_SIGNATURES_MESSAGE, warnings[0].message)
  3511. self.assertEqual([
  3512. os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
  3513. os.path.join('test_grd', 'IDS_TEST1.png.sha1')
  3514. ], warnings[0].items)
  3515. # Remove all sha1 files. There should be no warnings.
  3516. input_api = self.makeInputApi([
  3517. # Modified files:
  3518. MockAffectedFile('test.grd',
  3519. self.OLD_GRD_CONTENTS,
  3520. self.NEW_GRD_CONTENTS2,
  3521. action='M'),
  3522. MockAffectedFile('part.grdp',
  3523. self.OLD_GRDP_CONTENTS,
  3524. self.NEW_GRDP_CONTENTS2,
  3525. action='M'),
  3526. # Deleted files:
  3527. MockFile(os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
  3528. self.VALID_SHA1,
  3529. action='D'),
  3530. MockFile(os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
  3531. self.VALID_SHA1,
  3532. action='D'),
  3533. MockFile(os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
  3534. self.VALID_SHA1,
  3535. action='D'),
  3536. MockFile(os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
  3537. self.VALID_SHA1,
  3538. action='D')
  3539. ])
  3540. warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  3541. self.assertEqual([], warnings)
  3542. def testIcuSyntax(self):
  3543. # Add valid ICU syntax string. Should not raise an error.
  3544. input_api = self.makeInputApi([
  3545. MockAffectedFile('test.grd',
  3546. self.NEW_GRD_CONTENTS_ICU_SYNTAX_OK2,
  3547. self.NEW_GRD_CONTENTS1,
  3548. action='M'),
  3549. MockAffectedFile('part.grdp',
  3550. self.NEW_GRDP_CONTENTS_ICU_SYNTAX_OK2,
  3551. self.NEW_GRDP_CONTENTS1,
  3552. action='M')
  3553. ])
  3554. results = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  3555. # We expect no ICU syntax errors.
  3556. icu_errors = [
  3557. e for e in results if e.message == self.ICU_SYNTAX_ERROR_MESSAGE
  3558. ]
  3559. self.assertEqual(0, len(icu_errors))
  3560. # Valid changes in ICU syntax. Should not raise an error.
  3561. input_api = self.makeInputApi([
  3562. MockAffectedFile('test.grd',
  3563. self.NEW_GRD_CONTENTS_ICU_SYNTAX_OK2,
  3564. self.NEW_GRD_CONTENTS_ICU_SYNTAX_OK1,
  3565. action='M'),
  3566. MockAffectedFile('part.grdp',
  3567. self.NEW_GRDP_CONTENTS_ICU_SYNTAX_OK2,
  3568. self.NEW_GRDP_CONTENTS_ICU_SYNTAX_OK1,
  3569. action='M')
  3570. ])
  3571. results = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  3572. # We expect no ICU syntax errors.
  3573. icu_errors = [
  3574. e for e in results if e.message == self.ICU_SYNTAX_ERROR_MESSAGE
  3575. ]
  3576. self.assertEqual(0, len(icu_errors))
  3577. # Valid changes in ICU syntax. Should not raise an error.
  3578. input_api = self.makeInputApi([
  3579. MockAffectedFile('test.grd',
  3580. self.NEW_GRD_CONTENTS_ICU_SYNTAX_OK3,
  3581. self.NEW_GRD_CONTENTS_ICU_SYNTAX_OK1,
  3582. action='M'),
  3583. MockAffectedFile('part.grdp',
  3584. self.NEW_GRDP_CONTENTS_ICU_SYNTAX_OK3,
  3585. self.NEW_GRDP_CONTENTS_ICU_SYNTAX_OK1,
  3586. action='M')
  3587. ])
  3588. results = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  3589. # We expect no ICU syntax errors.
  3590. icu_errors = [
  3591. e for e in results if e.message == self.ICU_SYNTAX_ERROR_MESSAGE
  3592. ]
  3593. self.assertEqual(0, len(icu_errors))
  3594. # Add invalid ICU syntax strings. Should raise two errors.
  3595. input_api = self.makeInputApi([
  3596. MockAffectedFile('test.grd',
  3597. self.NEW_GRD_CONTENTS_ICU_SYNTAX_ERROR,
  3598. self.NEW_GRD_CONTENTS1,
  3599. action='M'),
  3600. MockAffectedFile('part.grdp',
  3601. self.NEW_GRDP_CONTENTS_ICU_SYNTAX_ERROR,
  3602. self.NEW_GRD_CONTENTS1,
  3603. action='M')
  3604. ])
  3605. results = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  3606. # We expect 2 ICU syntax errors.
  3607. icu_errors = [
  3608. e for e in results if e.message == self.ICU_SYNTAX_ERROR_MESSAGE
  3609. ]
  3610. self.assertEqual(1, len(icu_errors))
  3611. self.assertEqual([
  3612. 'IDS_TEST1: This message looks like an ICU plural, but does not follow '
  3613. 'ICU syntax.',
  3614. 'IDS_PART_TEST1: Variant "= 1" is not valid for plural message'
  3615. ], icu_errors[0].items)
  3616. # Change two strings to have ICU syntax errors. Should raise two errors.
  3617. input_api = self.makeInputApi([
  3618. MockAffectedFile('test.grd',
  3619. self.NEW_GRD_CONTENTS_ICU_SYNTAX_ERROR,
  3620. self.NEW_GRD_CONTENTS_ICU_SYNTAX_OK1,
  3621. action='M'),
  3622. MockAffectedFile('part.grdp',
  3623. self.NEW_GRDP_CONTENTS_ICU_SYNTAX_ERROR,
  3624. self.NEW_GRDP_CONTENTS_ICU_SYNTAX_OK1,
  3625. action='M')
  3626. ])
  3627. results = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  3628. # We expect 2 ICU syntax errors.
  3629. icu_errors = [
  3630. e for e in results if e.message == self.ICU_SYNTAX_ERROR_MESSAGE
  3631. ]
  3632. self.assertEqual(1, len(icu_errors))
  3633. self.assertEqual([
  3634. 'IDS_TEST1: This message looks like an ICU plural, but does not follow '
  3635. 'ICU syntax.',
  3636. 'IDS_PART_TEST1: Variant "= 1" is not valid for plural message'
  3637. ], icu_errors[0].items)
  3638. class TranslationExpectationsTest(unittest.TestCase):
  3639. ERROR_MESSAGE_FORMAT = (
  3640. "Failed to get a list of translatable grd files. "
  3641. "This happens when:\n"
  3642. " - One of the modified grd or grdp files cannot be parsed or\n"
  3643. " - %s is not updated.\n"
  3644. "Stack:\n")
  3645. REPO_ROOT = os.path.join('tools', 'translation', 'testdata')
  3646. # This lists all .grd files under REPO_ROOT.
  3647. EXPECTATIONS = os.path.join(REPO_ROOT, "translation_expectations.pyl")
  3648. # This lists all .grd files under REPO_ROOT except unlisted.grd.
  3649. EXPECTATIONS_WITHOUT_UNLISTED_FILE = os.path.join(
  3650. REPO_ROOT, "translation_expectations_without_unlisted_file.pyl")
  3651. # Tests that the presubmit doesn't return when no grd or grdp files are
  3652. # modified.
  3653. def testExpectationsNoModifiedGrd(self):
  3654. input_api = MockInputApi()
  3655. input_api.files = [
  3656. MockAffectedFile('not_used.txt',
  3657. 'not used',
  3658. 'not used',
  3659. action='M')
  3660. ]
  3661. # Fake list of all grd files in the repo. This list is missing all grd/grdps
  3662. # under tools/translation/testdata. This is OK because the presubmit won't
  3663. # run in the first place since there are no modified grd/grps in input_api.
  3664. grd_files = ['doesnt_exist_doesnt_matter.grd']
  3665. warnings = PRESUBMIT.CheckTranslationExpectations(
  3666. input_api, MockOutputApi(), self.REPO_ROOT, self.EXPECTATIONS,
  3667. grd_files)
  3668. self.assertEqual(0, len(warnings))
  3669. # Tests that the list of files passed to the presubmit matches the list of
  3670. # files in the expectations.
  3671. def testExpectationsSuccess(self):
  3672. # Mock input file list needs a grd or grdp file in order to run the
  3673. # presubmit. The file itself doesn't matter.
  3674. input_api = MockInputApi()
  3675. input_api.files = [
  3676. MockAffectedFile('dummy.grd', 'not used', 'not used', action='M')
  3677. ]
  3678. # List of all grd files in the repo.
  3679. grd_files = [
  3680. 'test.grd', 'unlisted.grd', 'not_translated.grd', 'internal.grd'
  3681. ]
  3682. warnings = PRESUBMIT.CheckTranslationExpectations(
  3683. input_api, MockOutputApi(), self.REPO_ROOT, self.EXPECTATIONS,
  3684. grd_files)
  3685. self.assertEqual(0, len(warnings))
  3686. # Tests that the presubmit warns when a file is listed in expectations, but
  3687. # does not actually exist.
  3688. def testExpectationsMissingFile(self):
  3689. # Mock input file list needs a grd or grdp file in order to run the
  3690. # presubmit.
  3691. input_api = MockInputApi()
  3692. input_api.files = [
  3693. MockAffectedFile('dummy.grd', 'not used', 'not used', action='M')
  3694. ]
  3695. # unlisted.grd is listed under tools/translation/testdata but is not
  3696. # included in translation expectations.
  3697. grd_files = ['unlisted.grd', 'not_translated.grd', 'internal.grd']
  3698. warnings = PRESUBMIT.CheckTranslationExpectations(
  3699. input_api, MockOutputApi(), self.REPO_ROOT, self.EXPECTATIONS,
  3700. grd_files)
  3701. self.assertEqual(1, len(warnings))
  3702. self.assertTrue(warnings[0].message.startswith(
  3703. self.ERROR_MESSAGE_FORMAT % self.EXPECTATIONS))
  3704. self.assertTrue(
  3705. ("test.grd is listed in the translation expectations, "
  3706. "but this grd file does not exist") in warnings[0].message)
  3707. # Tests that the presubmit warns when a file is not listed in expectations but
  3708. # does actually exist.
  3709. def testExpectationsUnlistedFile(self):
  3710. # Mock input file list needs a grd or grdp file in order to run the
  3711. # presubmit.
  3712. input_api = MockInputApi()
  3713. input_api.files = [
  3714. MockAffectedFile('dummy.grd', 'not used', 'not used', action='M')
  3715. ]
  3716. # unlisted.grd is listed under tools/translation/testdata but is not
  3717. # included in translation expectations.
  3718. grd_files = [
  3719. 'test.grd', 'unlisted.grd', 'not_translated.grd', 'internal.grd'
  3720. ]
  3721. warnings = PRESUBMIT.CheckTranslationExpectations(
  3722. input_api, MockOutputApi(), self.REPO_ROOT,
  3723. self.EXPECTATIONS_WITHOUT_UNLISTED_FILE, grd_files)
  3724. self.assertEqual(1, len(warnings))
  3725. self.assertTrue(warnings[0].message.startswith(
  3726. self.ERROR_MESSAGE_FORMAT %
  3727. self.EXPECTATIONS_WITHOUT_UNLISTED_FILE))
  3728. self.assertTrue(("unlisted.grd appears to be translatable "
  3729. "(because it contains <file> or <message> elements), "
  3730. "but is not listed in the translation expectations."
  3731. ) in warnings[0].message)
  3732. # Tests that the presubmit warns twice:
  3733. # - for a non-existing file listed in expectations
  3734. # - for an existing file not listed in expectations
  3735. def testMultipleWarnings(self):
  3736. # Mock input file list needs a grd or grdp file in order to run the
  3737. # presubmit.
  3738. input_api = MockInputApi()
  3739. input_api.files = [
  3740. MockAffectedFile('dummy.grd', 'not used', 'not used', action='M')
  3741. ]
  3742. # unlisted.grd is listed under tools/translation/testdata but is not
  3743. # included in translation expectations.
  3744. # test.grd is not listed under tools/translation/testdata but is included
  3745. # in translation expectations.
  3746. grd_files = ['unlisted.grd', 'not_translated.grd', 'internal.grd']
  3747. warnings = PRESUBMIT.CheckTranslationExpectations(
  3748. input_api, MockOutputApi(), self.REPO_ROOT,
  3749. self.EXPECTATIONS_WITHOUT_UNLISTED_FILE, grd_files)
  3750. self.assertEqual(1, len(warnings))
  3751. self.assertTrue(warnings[0].message.startswith(
  3752. self.ERROR_MESSAGE_FORMAT %
  3753. self.EXPECTATIONS_WITHOUT_UNLISTED_FILE))
  3754. self.assertTrue(("unlisted.grd appears to be translatable "
  3755. "(because it contains <file> or <message> elements), "
  3756. "but is not listed in the translation expectations."
  3757. ) in warnings[0].message)
  3758. self.assertTrue(
  3759. ("test.grd is listed in the translation expectations, "
  3760. "but this grd file does not exist") in warnings[0].message)
  3761. class DISABLETypoInTest(unittest.TestCase):
  3762. def testPositive(self):
  3763. # Verify the typo "DISABLE_" instead of "DISABLED_" in various contexts
  3764. # where the desire is to disable a test.
  3765. tests = [
  3766. # Disabled on one platform:
  3767. '#if defined(OS_WIN)\n'
  3768. '#define MAYBE_FoobarTest DISABLE_FoobarTest\n'
  3769. '#else\n'
  3770. '#define MAYBE_FoobarTest FoobarTest\n'
  3771. '#endif\n',
  3772. # Disabled on one platform spread cross lines:
  3773. '#if defined(OS_WIN)\n'
  3774. '#define MAYBE_FoobarTest \\\n'
  3775. ' DISABLE_FoobarTest\n'
  3776. '#else\n'
  3777. '#define MAYBE_FoobarTest FoobarTest\n'
  3778. '#endif\n',
  3779. # Disabled on all platforms:
  3780. ' TEST_F(FoobarTest, DISABLE_Foo)\n{\n}',
  3781. # Disabled on all platforms but multiple lines
  3782. ' TEST_F(FoobarTest,\n DISABLE_foo){\n}\n',
  3783. ]
  3784. for test in tests:
  3785. mock_input_api = MockInputApi()
  3786. mock_input_api.files = [
  3787. MockFile('some/path/foo_unittest.cc', test.splitlines()),
  3788. ]
  3789. results = PRESUBMIT.CheckNoDISABLETypoInTests(
  3790. mock_input_api, MockOutputApi())
  3791. self.assertEqual(
  3792. 1,
  3793. len(results),
  3794. msg=('expected len(results) == 1 but got %d in test: %s' %
  3795. (len(results), test)))
  3796. self.assertTrue(
  3797. 'foo_unittest.cc' in results[0].message,
  3798. msg=(
  3799. 'expected foo_unittest.cc in message but got %s in test %s'
  3800. % (results[0].message, test)))
  3801. def testIgnoreNotTestFiles(self):
  3802. mock_input_api = MockInputApi()
  3803. mock_input_api.files = [
  3804. MockFile('some/path/foo.cc', 'TEST_F(FoobarTest, DISABLE_Foo)'),
  3805. ]
  3806. results = PRESUBMIT.CheckNoDISABLETypoInTests(mock_input_api,
  3807. MockOutputApi())
  3808. self.assertEqual(0, len(results))
  3809. def testIgnoreDeletedFiles(self):
  3810. mock_input_api = MockInputApi()
  3811. mock_input_api.files = [
  3812. MockFile('some/path/foo.cc', 'TEST_F(FoobarTest, Foo)',
  3813. action='D'),
  3814. ]
  3815. results = PRESUBMIT.CheckNoDISABLETypoInTests(mock_input_api,
  3816. MockOutputApi())
  3817. self.assertEqual(0, len(results))
  3818. class ForgettingMAYBEInTests(unittest.TestCase):
  3819. def testPositive(self):
  3820. test = ('#if defined(HAS_ENERGY)\n'
  3821. '#define MAYBE_CastExplosion DISABLED_CastExplosion\n'
  3822. '#else\n'
  3823. '#define MAYBE_CastExplosion CastExplosion\n'
  3824. '#endif\n'
  3825. 'TEST_F(ArchWizard, CastExplosion) {\n'
  3826. '#if defined(ARCH_PRIEST_IN_PARTY)\n'
  3827. '#define MAYBE_ArchPriest ArchPriest\n'
  3828. '#else\n'
  3829. '#define MAYBE_ArchPriest DISABLED_ArchPriest\n'
  3830. '#endif\n'
  3831. 'TEST_F(ArchPriest, CastNaturesBounty) {\n'
  3832. '#if !defined(CRUSADER_IN_PARTY)\n'
  3833. '#define MAYBE_Crusader \\\n'
  3834. ' DISABLED_Crusader \n'
  3835. '#else\n'
  3836. '#define MAYBE_Crusader \\\n'
  3837. ' Crusader\n'
  3838. '#endif\n'
  3839. ' TEST_F(\n'
  3840. ' Crusader,\n'
  3841. ' CastTaunt) { }\n'
  3842. '#if defined(LEARNED_BASIC_SKILLS)\n'
  3843. '#define MAYBE_CastSteal \\\n'
  3844. ' DISABLED_CastSteal \n'
  3845. '#else\n'
  3846. '#define MAYBE_CastSteal \\\n'
  3847. ' CastSteal\n'
  3848. '#endif\n'
  3849. ' TEST_F(\n'
  3850. ' ThiefClass,\n'
  3851. ' CastSteal) { }\n')
  3852. mock_input_api = MockInputApi()
  3853. mock_input_api.files = [
  3854. MockFile('fantasyworld/classes_unittest.cc', test.splitlines()),
  3855. ]
  3856. results = PRESUBMIT.CheckForgettingMAYBEInTests(
  3857. mock_input_api, MockOutputApi())
  3858. self.assertEqual(4, len(results))
  3859. self.assertTrue('CastExplosion' in results[0].message)
  3860. self.assertTrue(
  3861. 'fantasyworld/classes_unittest.cc:2' in results[0].message)
  3862. self.assertTrue('ArchPriest' in results[1].message)
  3863. self.assertTrue(
  3864. 'fantasyworld/classes_unittest.cc:8' in results[1].message)
  3865. self.assertTrue('Crusader' in results[2].message)
  3866. self.assertTrue(
  3867. 'fantasyworld/classes_unittest.cc:14' in results[2].message)
  3868. self.assertTrue('CastSteal' in results[3].message)
  3869. self.assertTrue(
  3870. 'fantasyworld/classes_unittest.cc:24' in results[3].message)
  3871. def testNegative(self):
  3872. test = ('#if defined(HAS_ENERGY)\n'
  3873. '#define MAYBE_CastExplosion DISABLED_CastExplosion\n'
  3874. '#else\n'
  3875. '#define MAYBE_CastExplosion CastExplosion\n'
  3876. '#endif\n'
  3877. 'TEST_F(ArchWizard, MAYBE_CastExplosion) {\n'
  3878. '#if defined(ARCH_PRIEST_IN_PARTY)\n'
  3879. '#define MAYBE_ArchPriest ArchPriest\n'
  3880. '#else\n'
  3881. '#define MAYBE_ArchPriest DISABLED_ArchPriest\n'
  3882. '#endif\n'
  3883. 'TEST_F(MAYBE_ArchPriest, CastNaturesBounty) {\n'
  3884. '#if !defined(CRUSADER_IN_PARTY)\n'
  3885. '#define MAYBE_Crusader \\\n'
  3886. ' DISABLED_Crusader \n'
  3887. '#else\n'
  3888. '#define MAYBE_Crusader \\\n'
  3889. ' Crusader\n'
  3890. '#endif\n'
  3891. ' TEST_F(\n'
  3892. ' MAYBE_Crusader,\n'
  3893. ' CastTaunt) { }\n'
  3894. '#if defined(LEARNED_BASIC_SKILLS)\n'
  3895. '#define MAYBE_CastSteal \\\n'
  3896. ' DISABLED_CastSteal \n'
  3897. '#else\n'
  3898. '#define MAYBE_CastSteal \\\n'
  3899. ' CastSteal\n'
  3900. '#endif\n'
  3901. ' TEST_F(\n'
  3902. ' ThiefClass,\n'
  3903. ' MAYBE_CastSteal) { }\n')
  3904. mock_input_api = MockInputApi()
  3905. mock_input_api.files = [
  3906. MockFile('fantasyworld/classes_unittest.cc', test.splitlines()),
  3907. ]
  3908. results = PRESUBMIT.CheckForgettingMAYBEInTests(
  3909. mock_input_api, MockOutputApi())
  3910. self.assertEqual(0, len(results))
  3911. class CheckFuzzTargetsTest(unittest.TestCase):
  3912. def _check(self, files):
  3913. mock_input_api = MockInputApi()
  3914. mock_input_api.files = []
  3915. for fname, contents in files.items():
  3916. mock_input_api.files.append(MockFile(fname, contents.splitlines()))
  3917. return PRESUBMIT.CheckFuzzTargetsOnUpload(mock_input_api,
  3918. MockOutputApi())
  3919. def testLibFuzzerSourcesIgnored(self):
  3920. results = self._check({
  3921. "third_party/lib/Fuzzer/FuzzerDriver.cpp":
  3922. "LLVMFuzzerInitialize",
  3923. })
  3924. self.assertEqual(results, [])
  3925. def testNonCodeFilesIgnored(self):
  3926. results = self._check({
  3927. "README.md": "LLVMFuzzerInitialize",
  3928. })
  3929. self.assertEqual(results, [])
  3930. def testNoErrorHeaderPresent(self):
  3931. results = self._check({
  3932. "fuzzer.cc":
  3933. ("#include \"testing/libfuzzer/libfuzzer_exports.h\"\n" +
  3934. "LLVMFuzzerInitialize")
  3935. })
  3936. self.assertEqual(results, [])
  3937. def testErrorMissingHeader(self):
  3938. results = self._check({"fuzzer.cc": "LLVMFuzzerInitialize"})
  3939. self.assertEqual(len(results), 1)
  3940. self.assertEqual(results[0].items, ['fuzzer.cc'])
  3941. class SetNoParentTest(unittest.TestCase):
  3942. def testSetNoParentTopLevelAllowed(self):
  3943. mock_input_api = MockInputApi()
  3944. mock_input_api.files = [
  3945. MockAffectedFile('goat/OWNERS', [
  3946. 'set noparent',
  3947. 'jochen@chromium.org',
  3948. ])
  3949. ]
  3950. mock_output_api = MockOutputApi()
  3951. errors = PRESUBMIT.CheckSetNoParent(mock_input_api, mock_output_api)
  3952. self.assertEqual([], errors)
  3953. def testSetNoParentMissing(self):
  3954. mock_input_api = MockInputApi()
  3955. mock_input_api.files = [
  3956. MockAffectedFile('services/goat/OWNERS', [
  3957. 'set noparent',
  3958. 'jochen@chromium.org',
  3959. 'per-file *.json=set noparent',
  3960. 'per-file *.json=jochen@chromium.org',
  3961. ])
  3962. ]
  3963. mock_output_api = MockOutputApi()
  3964. errors = PRESUBMIT.CheckSetNoParent(mock_input_api, mock_output_api)
  3965. self.assertEqual(1, len(errors))
  3966. self.assertTrue('goat/OWNERS:1' in errors[0].long_text)
  3967. self.assertTrue('goat/OWNERS:3' in errors[0].long_text)
  3968. def testSetNoParentWithCorrectRule(self):
  3969. mock_input_api = MockInputApi()
  3970. mock_input_api.files = [
  3971. MockAffectedFile('services/goat/OWNERS', [
  3972. 'set noparent',
  3973. 'file://ipc/SECURITY_OWNERS',
  3974. 'per-file *.json=set noparent',
  3975. 'per-file *.json=file://ipc/SECURITY_OWNERS',
  3976. ])
  3977. ]
  3978. mock_output_api = MockOutputApi()
  3979. errors = PRESUBMIT.CheckSetNoParent(mock_input_api, mock_output_api)
  3980. self.assertEqual([], errors)
  3981. class MojomStabilityCheckTest(unittest.TestCase):
  3982. def runTestWithAffectedFiles(self, affected_files):
  3983. mock_input_api = MockInputApi()
  3984. mock_input_api.files = affected_files
  3985. mock_output_api = MockOutputApi()
  3986. return PRESUBMIT.CheckStableMojomChanges(mock_input_api,
  3987. mock_output_api)
  3988. def testSafeChangePasses(self):
  3989. errors = self.runTestWithAffectedFiles([
  3990. MockAffectedFile(
  3991. 'foo/foo.mojom',
  3992. ['[Stable] struct S { [MinVersion=1] int32 x; };'],
  3993. old_contents=['[Stable] struct S {};'])
  3994. ])
  3995. self.assertEqual([], errors)
  3996. def testBadChangeFails(self):
  3997. errors = self.runTestWithAffectedFiles([
  3998. MockAffectedFile('foo/foo.mojom',
  3999. ['[Stable] struct S { int32 x; };'],
  4000. old_contents=['[Stable] struct S {};'])
  4001. ])
  4002. self.assertEqual(1, len(errors))
  4003. self.assertTrue('not backward-compatible' in errors[0].message)
  4004. def testDeletedFile(self):
  4005. """Regression test for https://crbug.com/1091407."""
  4006. errors = self.runTestWithAffectedFiles([
  4007. MockAffectedFile('a.mojom', [],
  4008. old_contents=['struct S {};'],
  4009. action='D'),
  4010. MockAffectedFile(
  4011. 'b.mojom', ['struct S {}; struct T { S s; };'],
  4012. old_contents=['import "a.mojom"; struct T { S s; };'])
  4013. ])
  4014. self.assertEqual([], errors)
  4015. class CheckForUseOfChromeAppsDeprecationsTest(unittest.TestCase):
  4016. ERROR_MSG_PIECE = 'technologies which will soon be deprecated'
  4017. # Each positive test is also a naive negative test for the other cases.
  4018. def testWarningNMF(self):
  4019. mock_input_api = MockInputApi()
  4020. mock_input_api.files = [
  4021. MockAffectedFile(
  4022. 'foo.NMF', ['"program"', '"Z":"content"', 'B'],
  4023. ['"program"', 'B'],
  4024. scm_diff='\n'.join([
  4025. '--- foo.NMF.old 2020-12-02 20:40:54.430676385 +0100',
  4026. '+++ foo.NMF.new 2020-12-02 20:41:02.086700197 +0100',
  4027. '@@ -1,2 +1,3 @@', ' "program"', '+"Z":"content"', ' B'
  4028. ]),
  4029. action='M')
  4030. ]
  4031. mock_output_api = MockOutputApi()
  4032. errors = PRESUBMIT.CheckForUseOfChromeAppsDeprecations(
  4033. mock_input_api, mock_output_api)
  4034. self.assertEqual(1, len(errors))
  4035. self.assertTrue(self.ERROR_MSG_PIECE in errors[0].message)
  4036. self.assertTrue('foo.NMF' in errors[0].message)
  4037. def testWarningManifest(self):
  4038. mock_input_api = MockInputApi()
  4039. mock_input_api.files = [
  4040. MockAffectedFile(
  4041. 'manifest.json', ['"app":', '"Z":"content"', 'B'],
  4042. ['"app":"', 'B'],
  4043. scm_diff='\n'.join([
  4044. '--- manifest.json.old 2020-12-02 20:40:54.430676385 +0100',
  4045. '+++ manifest.json.new 2020-12-02 20:41:02.086700197 +0100',
  4046. '@@ -1,2 +1,3 @@', ' "app"', '+"Z":"content"', ' B'
  4047. ]),
  4048. action='M')
  4049. ]
  4050. mock_output_api = MockOutputApi()
  4051. errors = PRESUBMIT.CheckForUseOfChromeAppsDeprecations(
  4052. mock_input_api, mock_output_api)
  4053. self.assertEqual(1, len(errors))
  4054. self.assertTrue(self.ERROR_MSG_PIECE in errors[0].message)
  4055. self.assertTrue('manifest.json' in errors[0].message)
  4056. def testOKWarningManifestWithoutApp(self):
  4057. mock_input_api = MockInputApi()
  4058. mock_input_api.files = [
  4059. MockAffectedFile(
  4060. 'manifest.json', ['"name":', '"Z":"content"', 'B'],
  4061. ['"name":"', 'B'],
  4062. scm_diff='\n'.join([
  4063. '--- manifest.json.old 2020-12-02 20:40:54.430676385 +0100',
  4064. '+++ manifest.json.new 2020-12-02 20:41:02.086700197 +0100',
  4065. '@@ -1,2 +1,3 @@', ' "app"', '+"Z":"content"', ' B'
  4066. ]),
  4067. action='M')
  4068. ]
  4069. mock_output_api = MockOutputApi()
  4070. errors = PRESUBMIT.CheckForUseOfChromeAppsDeprecations(
  4071. mock_input_api, mock_output_api)
  4072. self.assertEqual(0, len(errors))
  4073. def testWarningPPAPI(self):
  4074. mock_input_api = MockInputApi()
  4075. mock_input_api.files = [
  4076. MockAffectedFile(
  4077. 'foo.hpp', ['A', '#include <ppapi.h>', 'B'], ['A', 'B'],
  4078. scm_diff='\n'.join([
  4079. '--- foo.hpp.old 2020-12-02 20:40:54.430676385 +0100',
  4080. '+++ foo.hpp.new 2020-12-02 20:41:02.086700197 +0100',
  4081. '@@ -1,2 +1,3 @@', ' A', '+#include <ppapi.h>', ' B'
  4082. ]),
  4083. action='M')
  4084. ]
  4085. mock_output_api = MockOutputApi()
  4086. errors = PRESUBMIT.CheckForUseOfChromeAppsDeprecations(
  4087. mock_input_api, mock_output_api)
  4088. self.assertEqual(1, len(errors))
  4089. self.assertTrue(self.ERROR_MSG_PIECE in errors[0].message)
  4090. self.assertTrue('foo.hpp' in errors[0].message)
  4091. def testNoWarningPPAPI(self):
  4092. mock_input_api = MockInputApi()
  4093. mock_input_api.files = [
  4094. MockAffectedFile(
  4095. 'foo.txt', ['A', 'Peppapig', 'B'], ['A', 'B'],
  4096. scm_diff='\n'.join([
  4097. '--- foo.txt.old 2020-12-02 20:40:54.430676385 +0100',
  4098. '+++ foo.txt.new 2020-12-02 20:41:02.086700197 +0100',
  4099. '@@ -1,2 +1,3 @@', ' A', '+Peppapig', ' B'
  4100. ]),
  4101. action='M')
  4102. ]
  4103. mock_output_api = MockOutputApi()
  4104. errors = PRESUBMIT.CheckForUseOfChromeAppsDeprecations(
  4105. mock_input_api, mock_output_api)
  4106. self.assertEqual(0, len(errors))
  4107. class CheckDeprecationOfPreferencesTest(unittest.TestCase):
  4108. # Test that a warning is generated if a preference registration is removed
  4109. # from a random file.
  4110. def testWarning(self):
  4111. mock_input_api = MockInputApi()
  4112. mock_input_api.files = [
  4113. MockAffectedFile(
  4114. 'foo.cc', ['A', 'B'],
  4115. ['A', 'prefs->RegisterStringPref("foo", "default");', 'B'],
  4116. scm_diff='\n'.join([
  4117. '--- foo.cc.old 2020-12-02 20:40:54.430676385 +0100',
  4118. '+++ foo.cc.new 2020-12-02 20:41:02.086700197 +0100',
  4119. '@@ -1,3 +1,2 @@', ' A',
  4120. '-prefs->RegisterStringPref("foo", "default");', ' B'
  4121. ]),
  4122. action='M')
  4123. ]
  4124. mock_output_api = MockOutputApi()
  4125. errors = PRESUBMIT.CheckDeprecationOfPreferences(
  4126. mock_input_api, mock_output_api)
  4127. self.assertEqual(1, len(errors))
  4128. self.assertTrue(
  4129. 'Discovered possible removal of preference registrations' in
  4130. errors[0].message)
  4131. # Test that a warning is inhibited if the preference registration was moved
  4132. # to the deprecation functions in browser prefs.
  4133. def testNoWarningForMigration(self):
  4134. mock_input_api = MockInputApi()
  4135. mock_input_api.files = [
  4136. # RegisterStringPref was removed from foo.cc.
  4137. MockAffectedFile(
  4138. 'foo.cc', ['A', 'B'],
  4139. ['A', 'prefs->RegisterStringPref("foo", "default");', 'B'],
  4140. scm_diff='\n'.join([
  4141. '--- foo.cc.old 2020-12-02 20:40:54.430676385 +0100',
  4142. '+++ foo.cc.new 2020-12-02 20:41:02.086700197 +0100',
  4143. '@@ -1,3 +1,2 @@', ' A',
  4144. '-prefs->RegisterStringPref("foo", "default");', ' B'
  4145. ]),
  4146. action='M'),
  4147. # But the preference was properly migrated.
  4148. MockAffectedFile(
  4149. 'chrome/browser/prefs/browser_prefs.cc', [
  4150. '// BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
  4151. '// END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
  4152. '// BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS',
  4153. 'prefs->RegisterStringPref("foo", "default");',
  4154. '// END_MIGRATE_OBSOLETE_PROFILE_PREFS',
  4155. ], [
  4156. '// BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
  4157. '// END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
  4158. '// BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS',
  4159. '// END_MIGRATE_OBSOLETE_PROFILE_PREFS',
  4160. ],
  4161. scm_diff='\n'.join([
  4162. '--- browser_prefs.cc.old 2020-12-02 20:51:40.812686731 +0100',
  4163. '+++ browser_prefs.cc.new 2020-12-02 20:52:02.936755539 +0100',
  4164. '@@ -2,3 +2,4 @@',
  4165. ' // END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
  4166. ' // BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS',
  4167. '+prefs->RegisterStringPref("foo", "default");',
  4168. ' // END_MIGRATE_OBSOLETE_PROFILE_PREFS'
  4169. ]),
  4170. action='M'),
  4171. ]
  4172. mock_output_api = MockOutputApi()
  4173. errors = PRESUBMIT.CheckDeprecationOfPreferences(
  4174. mock_input_api, mock_output_api)
  4175. self.assertEqual(0, len(errors))
  4176. # Test that a warning is NOT inhibited if the preference registration was
  4177. # moved to a place outside of the migration functions in browser_prefs.cc
  4178. def testWarningForImproperMigration(self):
  4179. mock_input_api = MockInputApi()
  4180. mock_input_api.files = [
  4181. # RegisterStringPref was removed from foo.cc.
  4182. MockAffectedFile(
  4183. 'foo.cc', ['A', 'B'],
  4184. ['A', 'prefs->RegisterStringPref("foo", "default");', 'B'],
  4185. scm_diff='\n'.join([
  4186. '--- foo.cc.old 2020-12-02 20:40:54.430676385 +0100',
  4187. '+++ foo.cc.new 2020-12-02 20:41:02.086700197 +0100',
  4188. '@@ -1,3 +1,2 @@', ' A',
  4189. '-prefs->RegisterStringPref("foo", "default");', ' B'
  4190. ]),
  4191. action='M'),
  4192. # The registration call was moved to a place in browser_prefs.cc that
  4193. # is outside the migration functions.
  4194. MockAffectedFile(
  4195. 'chrome/browser/prefs/browser_prefs.cc', [
  4196. 'prefs->RegisterStringPref("foo", "default");',
  4197. '// BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
  4198. '// END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
  4199. '// BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS',
  4200. '// END_MIGRATE_OBSOLETE_PROFILE_PREFS',
  4201. ], [
  4202. '// BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
  4203. '// END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
  4204. '// BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS',
  4205. '// END_MIGRATE_OBSOLETE_PROFILE_PREFS',
  4206. ],
  4207. scm_diff='\n'.join([
  4208. '--- browser_prefs.cc.old 2020-12-02 20:51:40.812686731 +0100',
  4209. '+++ browser_prefs.cc.new 2020-12-02 20:52:02.936755539 +0100',
  4210. '@@ -1,2 +1,3 @@',
  4211. '+prefs->RegisterStringPref("foo", "default");',
  4212. ' // BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
  4213. ' // END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS'
  4214. ]),
  4215. action='M'),
  4216. ]
  4217. mock_output_api = MockOutputApi()
  4218. errors = PRESUBMIT.CheckDeprecationOfPreferences(
  4219. mock_input_api, mock_output_api)
  4220. self.assertEqual(1, len(errors))
  4221. self.assertTrue(
  4222. 'Discovered possible removal of preference registrations' in
  4223. errors[0].message)
  4224. # Check that the presubmit fails if a marker line in browser_prefs.cc is
  4225. # deleted.
  4226. def testDeletedMarkerRaisesError(self):
  4227. mock_input_api = MockInputApi()
  4228. mock_input_api.files = [
  4229. MockAffectedFile(
  4230. 'chrome/browser/prefs/browser_prefs.cc',
  4231. [
  4232. '// BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
  4233. '// END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
  4234. '// BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS',
  4235. # The following line is deleted for this test
  4236. # '// END_MIGRATE_OBSOLETE_PROFILE_PREFS',
  4237. ])
  4238. ]
  4239. mock_output_api = MockOutputApi()
  4240. errors = PRESUBMIT.CheckDeprecationOfPreferences(
  4241. mock_input_api, mock_output_api)
  4242. self.assertEqual(1, len(errors))
  4243. self.assertEqual(
  4244. 'Broken .*MIGRATE_OBSOLETE_.*_PREFS markers in browser_prefs.cc.',
  4245. errors[0].message)
  4246. class CheckCrosApiNeedBrowserTestTest(unittest.TestCase):
  4247. def testWarning(self):
  4248. mock_input_api = MockInputApi()
  4249. mock_output_api = MockOutputApi()
  4250. mock_input_api.files = [
  4251. MockAffectedFile('chromeos/crosapi/mojom/example.mojom', [], action='A'),
  4252. ]
  4253. result = PRESUBMIT.CheckCrosApiNeedBrowserTest(mock_input_api, mock_output_api)
  4254. self.assertEqual(1, len(result))
  4255. self.assertEqual(result[0].type, 'warning')
  4256. def testNoWarningWithBrowserTest(self):
  4257. mock_input_api = MockInputApi()
  4258. mock_output_api = MockOutputApi()
  4259. mock_input_api.files = [
  4260. MockAffectedFile('chromeos/crosapi/mojom/example.mojom', [], action='A'),
  4261. MockAffectedFile('chrome/example_browsertest.cc', [], action='A'),
  4262. ]
  4263. result = PRESUBMIT.CheckCrosApiNeedBrowserTest(mock_input_api, mock_output_api)
  4264. self.assertEqual(0, len(result))
  4265. def testNoWarningModifyCrosapi(self):
  4266. mock_input_api = MockInputApi()
  4267. mock_output_api = MockOutputApi()
  4268. mock_input_api.files = [
  4269. MockAffectedFile('chromeos/crosapi/mojom/example.mojom', [], action='M'),
  4270. ]
  4271. result = PRESUBMIT.CheckCrosApiNeedBrowserTest(mock_input_api, mock_output_api)
  4272. self.assertEqual(0, len(result))
  4273. def testNoWarningAddNonMojomFile(self):
  4274. mock_input_api = MockInputApi()
  4275. mock_output_api = MockOutputApi()
  4276. mock_input_api.files = [
  4277. MockAffectedFile('chromeos/crosapi/mojom/example.cc', [], action='A'),
  4278. ]
  4279. result = PRESUBMIT.CheckCrosApiNeedBrowserTest(mock_input_api, mock_output_api)
  4280. self.assertEqual(0, len(result))
  4281. def testNoWarningNoneRelatedMojom(self):
  4282. mock_input_api = MockInputApi()
  4283. mock_output_api = MockOutputApi()
  4284. mock_input_api.files = [
  4285. MockAffectedFile('random/folder/example.mojom', [], action='A'),
  4286. ]
  4287. result = PRESUBMIT.CheckCrosApiNeedBrowserTest(mock_input_api, mock_output_api)
  4288. self.assertEqual(0, len(result))
  4289. class AssertAshOnlyCodeTest(unittest.TestCase):
  4290. def testErrorsOnlyOnAshDirectories(self):
  4291. files_in_ash = [
  4292. MockFile('ash/BUILD.gn', []),
  4293. MockFile('chrome/browser/ash/BUILD.gn', []),
  4294. ]
  4295. other_files = [
  4296. MockFile('chrome/browser/BUILD.gn', []),
  4297. MockFile('chrome/browser/foo/BUILD.gn', ['assert(is_chromeos_ash)']),
  4298. ]
  4299. input_api = MockInputApi()
  4300. input_api.files = files_in_ash
  4301. errors = PRESUBMIT.CheckAssertAshOnlyCode(input_api, MockOutputApi())
  4302. self.assertEqual(2, len(errors))
  4303. input_api.files = other_files
  4304. errors = PRESUBMIT.CheckAssertAshOnlyCode(input_api, MockOutputApi())
  4305. self.assertEqual(0, len(errors))
  4306. def testDoesNotErrorOnNonGNFiles(self):
  4307. input_api = MockInputApi()
  4308. input_api.files = [
  4309. MockFile('ash/test.h', ['assert(is_chromeos_ash)']),
  4310. MockFile('chrome/browser/ash/test.cc',
  4311. ['assert(is_chromeos_ash)']),
  4312. ]
  4313. errors = PRESUBMIT.CheckAssertAshOnlyCode(input_api, MockOutputApi())
  4314. self.assertEqual(0, len(errors))
  4315. def testDeletedFile(self):
  4316. input_api = MockInputApi()
  4317. input_api.files = [
  4318. MockFile('ash/BUILD.gn', []),
  4319. MockFile('ash/foo/BUILD.gn', [], action='D'),
  4320. ]
  4321. errors = PRESUBMIT.CheckAssertAshOnlyCode(input_api, MockOutputApi())
  4322. self.assertEqual(1, len(errors))
  4323. def testDoesNotErrorWithAssertion(self):
  4324. input_api = MockInputApi()
  4325. input_api.files = [
  4326. MockFile('ash/BUILD.gn', ['assert(is_chromeos_ash)']),
  4327. MockFile('chrome/browser/ash/BUILD.gn',
  4328. ['assert(is_chromeos_ash)']),
  4329. MockFile('chrome/browser/ash/1/BUILD.gn',
  4330. ['assert(is_chromeos)']),
  4331. MockFile('chrome/browser/ash/2/BUILD.gn',
  4332. ['assert(is_chromeos_ash)']),
  4333. MockFile('chrome/browser/ash/3/BUILD.gn',
  4334. ['assert(is_chromeos, "test")']),
  4335. MockFile('chrome/browser/ash/4/BUILD.gn',
  4336. ['assert(is_chromeos_ash, "test")']),
  4337. ]
  4338. errors = PRESUBMIT.CheckAssertAshOnlyCode(input_api, MockOutputApi())
  4339. self.assertEqual(0, len(errors))
  4340. class CheckRawPtrUsageTest(unittest.TestCase):
  4341. def testAllowedCases(self):
  4342. mock_input_api = MockInputApi()
  4343. mock_input_api.files = [
  4344. # Browser-side files are allowed.
  4345. MockAffectedFile('test10/browser/foo.h', ['raw_ptr<int>']),
  4346. MockAffectedFile('test11/browser/foo.cc', ['raw_ptr<int>']),
  4347. MockAffectedFile('test12/blink/common/foo.cc', ['raw_ptr<int>']),
  4348. MockAffectedFile('test13/blink/public/common/foo.cc',
  4349. ['raw_ptr<int>']),
  4350. MockAffectedFile('test14/blink/public/platform/foo.cc',
  4351. ['raw_ptr<int>']),
  4352. # Non-C++ files are allowed.
  4353. MockAffectedFile('test20/renderer/foo.md', ['raw_ptr<int>']),
  4354. # Renderer code is generally allowed (except specifically
  4355. # disallowed directories).
  4356. MockAffectedFile('test30/renderer/foo.cc', ['raw_ptr<int>']),
  4357. ]
  4358. mock_output_api = MockOutputApi()
  4359. errors = PRESUBMIT.CheckRawPtrUsage(mock_input_api, mock_output_api)
  4360. self.assertFalse(errors)
  4361. def testDisallowedCases(self):
  4362. mock_input_api = MockInputApi()
  4363. mock_input_api.files = [
  4364. MockAffectedFile('test1/third_party/blink/renderer/core/foo.h',
  4365. ['raw_ptr<int>']),
  4366. MockAffectedFile(
  4367. 'test2/third_party/blink/renderer/platform/heap/foo.cc',
  4368. ['raw_ptr<int>']),
  4369. MockAffectedFile(
  4370. 'test3/third_party/blink/renderer/platform/wtf/foo.cc',
  4371. ['raw_ptr<int>']),
  4372. MockAffectedFile(
  4373. 'test4/third_party/blink/renderer/platform/fonts/foo.h',
  4374. ['raw_ptr<int>']),
  4375. MockAffectedFile(
  4376. 'test5/third_party/blink/renderer/core/paint/foo.cc',
  4377. ['raw_ptr<int>']),
  4378. MockAffectedFile(
  4379. 'test6/third_party/blink/renderer/platform/graphics/compositing/foo.h',
  4380. ['raw_ptr<int>']),
  4381. MockAffectedFile(
  4382. 'test7/third_party/blink/renderer/platform/graphics/paint/foo.cc',
  4383. ['raw_ptr<int>']),
  4384. ]
  4385. mock_output_api = MockOutputApi()
  4386. errors = PRESUBMIT.CheckRawPtrUsage(mock_input_api, mock_output_api)
  4387. self.assertEqual(len(mock_input_api.files), len(errors))
  4388. for error in errors:
  4389. self.assertTrue(
  4390. 'raw_ptr<T> should not be used in this renderer code' in
  4391. error.message)
  4392. class CheckAdvancedMemorySafetyChecksUsageTest(unittest.TestCase):
  4393. def testAllowedCases(self):
  4394. mock_input_api = MockInputApi()
  4395. mock_input_api.files = [
  4396. # Non-C++ files are allowed.
  4397. MockAffectedFile('test20/renderer/foo.md',
  4398. ['ADVANCED_MEMORY_SAFETY_CHECKS()']),
  4399. # Mentions in a comment are allowed.
  4400. MockAffectedFile('test30/renderer/foo.cc',
  4401. ['//ADVANCED_MEMORY_SAFETY_CHECKS()']),
  4402. ]
  4403. mock_output_api = MockOutputApi()
  4404. errors = PRESUBMIT.CheckAdvancedMemorySafetyChecksUsage(
  4405. mock_input_api, mock_output_api)
  4406. self.assertFalse(errors)
  4407. def testDisallowedCases(self):
  4408. mock_input_api = MockInputApi()
  4409. mock_input_api.files = [
  4410. MockAffectedFile('test1/foo.h',
  4411. ['ADVANCED_MEMORY_SAFETY_CHECKS()']),
  4412. MockAffectedFile('test2/foo.cc',
  4413. ['ADVANCED_MEMORY_SAFETY_CHECKS()']),
  4414. ]
  4415. mock_output_api = MockOutputApi()
  4416. errors = PRESUBMIT.CheckAdvancedMemorySafetyChecksUsage(
  4417. mock_input_api, mock_output_api)
  4418. self.assertEqual(1, len(errors))
  4419. self.assertTrue('ADVANCED_MEMORY_SAFETY_CHECKS() macro is managed by'
  4420. in errors[0].message)
  4421. class AssertPythonShebangTest(unittest.TestCase):
  4422. def testError(self):
  4423. input_api = MockInputApi()
  4424. input_api.files = [
  4425. MockFile('ash/test.py', ['#!/usr/bin/python']),
  4426. MockFile('chrome/test.py', ['#!/usr/bin/python2']),
  4427. MockFile('third_party/blink/test.py', ['#!/usr/bin/python3']),
  4428. MockFile('empty.py', []),
  4429. ]
  4430. errors = PRESUBMIT.CheckPythonShebang(input_api, MockOutputApi())
  4431. self.assertEqual(3, len(errors))
  4432. def testNonError(self):
  4433. input_api = MockInputApi()
  4434. input_api.files = [
  4435. MockFile('chrome/browser/BUILD.gn', ['#!/usr/bin/python']),
  4436. MockFile('third_party/blink/web_tests/external/test.py',
  4437. ['#!/usr/bin/python2']),
  4438. MockFile('third_party/test/test.py', ['#!/usr/bin/python3']),
  4439. ]
  4440. errors = PRESUBMIT.CheckPythonShebang(input_api, MockOutputApi())
  4441. self.assertEqual(0, len(errors))
  4442. class VerifyDcheckParentheses(unittest.TestCase):
  4443. def testPermissibleUsage(self):
  4444. input_api = MockInputApi()
  4445. input_api.files = [
  4446. MockFile('okay1.cc', ['DCHECK_IS_ON()']),
  4447. MockFile('okay2.cc', ['#if DCHECK_IS_ON()']),
  4448. # Other constructs that aren't exactly `DCHECK_IS_ON()` do their
  4449. # own thing at their own risk.
  4450. MockFile('okay3.cc', ['PA_DCHECK_IS_ON']),
  4451. MockFile('okay4.cc', ['#if PA_DCHECK_IS_ON']),
  4452. MockFile('okay6.cc', ['PA_BUILDFLAG(PA_DCHECK_IS_ON)']),
  4453. ]
  4454. errors = PRESUBMIT.CheckDCHECK_IS_ONHasBraces(input_api,
  4455. MockOutputApi())
  4456. self.assertEqual(0, len(errors))
  4457. def testMissingParentheses(self):
  4458. input_api = MockInputApi()
  4459. input_api.files = [
  4460. MockFile('bad1.cc', ['DCHECK_IS_ON']),
  4461. MockFile('bad2.cc', ['#if DCHECK_IS_ON']),
  4462. MockFile('bad3.cc', ['DCHECK_IS_ON && foo']),
  4463. ]
  4464. errors = PRESUBMIT.CheckDCHECK_IS_ONHasBraces(input_api,
  4465. MockOutputApi())
  4466. self.assertEqual(3, len(errors))
  4467. for error in errors:
  4468. self.assertRegex(error.message, r'DCHECK_IS_ON().+parentheses')
  4469. class CheckAndroidTestAnnotations(unittest.TestCase):
  4470. """Test the CheckAndroidTestAnnotations presubmit check."""
  4471. def testBatchTruePositives(self):
  4472. """Examples of when there is no @Batch or @DoNotBatch is correctly flagged.
  4473. """
  4474. mock_input = MockInputApi()
  4475. mock_input.files = [
  4476. MockFile('path/OneTest.java', ['public class OneTest']),
  4477. MockFile('path/TwoTest.java', ['public class TwoTest']),
  4478. MockFile('path/ThreeTest.java', [
  4479. '@Batch(Batch.PER_CLASS)',
  4480. '@RunWith(BaseRobolectricTestRunner.class)',
  4481. 'public class Three {'
  4482. ]),
  4483. MockFile('path/FourTest.java', [
  4484. '@DoNotBatch(reason = "placeholder reason 1")',
  4485. '@RunWith(BaseRobolectricTestRunner.class)',
  4486. 'public class Four {'
  4487. ]),
  4488. ]
  4489. errors = PRESUBMIT.CheckAndroidTestAnnotations(mock_input, MockOutputApi())
  4490. self.assertEqual(2, len(errors))
  4491. self.assertEqual(2, len(errors[0].items))
  4492. self.assertIn('OneTest.java', errors[0].items[0])
  4493. self.assertIn('TwoTest.java', errors[0].items[1])
  4494. self.assertEqual(2, len(errors[1].items))
  4495. self.assertIn('ThreeTest.java', errors[1].items[0])
  4496. self.assertIn('FourTest.java', errors[1].items[1])
  4497. def testBatchAnnotationsPresent(self):
  4498. """Examples of when there is @Batch or @DoNotBatch is correctly flagged."""
  4499. mock_input = MockInputApi()
  4500. mock_input.files = [
  4501. MockFile('path/OneTest.java',
  4502. ['@Batch(Batch.PER_CLASS)', 'public class One {']),
  4503. MockFile('path/TwoTest.java', [
  4504. '@DoNotBatch(reason = "placeholder reasons.")',
  4505. 'public class Two {'
  4506. ]),
  4507. MockFile('path/ThreeTest.java', [
  4508. '@Batch(Batch.PER_CLASS)',
  4509. 'public class Three extends BaseTestA {'
  4510. ], [
  4511. '@Batch(Batch.PER_CLASS)',
  4512. 'public class Three extends BaseTestB {'
  4513. ]),
  4514. MockFile('path/FourTest.java', [
  4515. '@DoNotBatch(reason = "placeholder reason 1")',
  4516. 'public class Four extends BaseTestA {'
  4517. ], [
  4518. '@DoNotBatch(reason = "placeholder reason 2")',
  4519. 'public class Four extends BaseTestB {'
  4520. ]),
  4521. MockFile('path/FiveTest.java', [
  4522. 'import androidx.test.uiautomator.UiDevice;',
  4523. 'public class Five extends BaseTestA {'
  4524. ], [
  4525. 'import androidx.test.uiautomator.UiDevice;',
  4526. 'public class Five extends BaseTestB {'
  4527. ]),
  4528. MockFile('path/SixTest.java', [
  4529. '@RunWith(BaseRobolectricTestRunner.class)',
  4530. 'public class Six extends BaseTestA {'
  4531. ], [
  4532. '@RunWith(BaseRobolectricTestRunner.class)',
  4533. 'public class Six extends BaseTestB {'
  4534. ]),
  4535. MockFile('path/SevenTest.java', [
  4536. '@RunWith(BaseRobolectricTestRunner.class)',
  4537. 'public class Seven extends BaseTestA {'
  4538. ], [
  4539. '@RunWith(BaseRobolectricTestRunner.class)',
  4540. 'public class Seven extends BaseTestB {'
  4541. ]),
  4542. MockFile(
  4543. 'path/OtherClass.java',
  4544. ['public class OtherClass {'],
  4545. ),
  4546. MockFile('path/PRESUBMIT.py', [
  4547. '@Batch(Batch.PER_CLASS)',
  4548. '@DoNotBatch(reason = "placeholder reason)'
  4549. ]),
  4550. MockFile(
  4551. 'path/AnnotationTest.java',
  4552. ['public @interface SomeAnnotation {'],
  4553. ),
  4554. ]
  4555. errors = PRESUBMIT.CheckAndroidTestAnnotations(mock_input, MockOutputApi())
  4556. self.assertEqual(0, len(errors))
  4557. def testWrongRobolectricTestRunner(self):
  4558. mock_input = MockInputApi()
  4559. mock_input.files = [
  4560. MockFile('path/OneTest.java', [
  4561. '@RunWith(RobolectricTestRunner.class)',
  4562. 'public class ThreeTest {'
  4563. ]),
  4564. MockFile('path/TwoTest.java', [
  4565. 'import org.chromium.base.test.BaseRobolectricTestRule;',
  4566. '@RunWith(RobolectricTestRunner.class)',
  4567. 'public class TwoTest {'
  4568. ]),
  4569. MockFile('path/ThreeTest.java', [
  4570. '@RunWith(FooRobolectricTestRunner.class)',
  4571. 'public class ThreeTest {'
  4572. ]),
  4573. MockFile('webapks/FourTest.java', [
  4574. '@RunWith(RobolectricTestRunner.class)',
  4575. 'public class ThreeTest {'
  4576. ]),
  4577. ]
  4578. errors = PRESUBMIT.CheckAndroidTestAnnotations(mock_input, MockOutputApi())
  4579. self.assertEqual(1, len(errors))
  4580. self.assertEqual(1, len(errors[0].items))
  4581. self.assertIn('OneTest.java', errors[0].items[0])
  4582. class CheckMockAnnotation(unittest.TestCase):
  4583. """Test the CheckMockAnnotation presubmit check."""
  4584. def testTruePositives(self):
  4585. """Examples of @Mock or @Spy being used and nothing should be flagged."""
  4586. mock_input = MockInputApi()
  4587. mock_input.files = [
  4588. MockFile('path/OneTest.java', [
  4589. 'import a.b.c.Bar;',
  4590. 'import a.b.c.Foo;',
  4591. '@Mock public static Foo f = new Foo();',
  4592. 'Mockito.mock(new Bar(a, b, c))'
  4593. ]),
  4594. MockFile('path/TwoTest.java', [
  4595. 'package x.y.z;',
  4596. 'import static org.mockito.Mockito.spy;',
  4597. '@Spy',
  4598. 'public static FooBar<Baz> f;',
  4599. 'a = spy(Baz.class)'
  4600. ]),
  4601. ]
  4602. errors = PRESUBMIT.CheckMockAnnotation(mock_input, MockOutputApi())
  4603. self.assertEqual(1, len(errors))
  4604. self.assertEqual(2, len(errors[0].items))
  4605. self.assertIn('a.b.c.Bar in path/OneTest.java', errors[0].items)
  4606. self.assertIn('x.y.z.Baz in path/TwoTest.java', errors[0].items)
  4607. def testTrueNegatives(self):
  4608. """Examples of when we should not be flagging mock() or spy() calls."""
  4609. mock_input = MockInputApi()
  4610. mock_input.files = [
  4611. MockFile('path/OneTest.java', [
  4612. 'package a.b.c;',
  4613. 'import org.chromium.base.test.BaseRobolectricTestRunner;',
  4614. 'Mockito.mock(Abc.class)'
  4615. ]),
  4616. MockFile('path/TwoTest.java', [
  4617. 'package a.b.c;',
  4618. 'import androidx.test.uiautomator.UiDevice;',
  4619. 'Mockito.spy(new Def())'
  4620. ]),
  4621. MockFile('path/ThreeTest.java', [
  4622. 'package a.b.c;',
  4623. 'import static org.mockito.Mockito.spy;',
  4624. '@Spy',
  4625. 'public static Foo f = new Abc();',
  4626. 'a = spy(Foo.class)'
  4627. ]),
  4628. MockFile('path/FourTest.java', [
  4629. 'package a.b.c;',
  4630. 'import static org.mockito.Mockito.mock;',
  4631. '@Spy',
  4632. 'public static Bar b = new Abc(a, b, c, d);',
  4633. ' mock(new Bar(a,b,c))'
  4634. ]),
  4635. MockFile('path/FiveTest.java', [
  4636. 'package a.b.c;',
  4637. '@Mock',
  4638. 'public static Baz<abc> b;',
  4639. 'Mockito.mock(Baz.class)'
  4640. ]),
  4641. MockFile('path/SixTest.java', [
  4642. 'package a.b.c;',
  4643. 'import android.view.View;',
  4644. 'import java.ArrayList;',
  4645. 'Mockito.spy(new View())',
  4646. 'Mockito.mock(ArrayList.class)'
  4647. ]),
  4648. MockFile('path/SevenTest.java', [
  4649. 'package a.b.c;',
  4650. '@Mock private static Seven s;',
  4651. 'Mockito.mock(Seven.class)'
  4652. ]),
  4653. MockFile('path/EightTest.java', [
  4654. 'package a.b.c;',
  4655. '@Spy Eight e = new Eight2();',
  4656. 'Mockito.py(new Eight())'
  4657. ]),
  4658. ]
  4659. errors = PRESUBMIT.CheckMockAnnotation(mock_input, MockOutputApi())
  4660. self.assertEqual(0, len(errors))
  4661. class AssertNoJsInIosTest(unittest.TestCase):
  4662. def testErrorJs(self):
  4663. input_api = MockInputApi()
  4664. input_api.files = [
  4665. MockFile('components/feature/ios/resources/script.js', []),
  4666. MockFile('ios/chrome/feature/resources/script.js', []),
  4667. ]
  4668. results = PRESUBMIT.CheckNoJsInIos(input_api, MockOutputApi())
  4669. self.assertEqual(1, len(results))
  4670. self.assertEqual('error', results[0].type)
  4671. self.assertEqual(2, len(results[0].items))
  4672. def testNonError(self):
  4673. input_api = MockInputApi()
  4674. input_api.files = [
  4675. MockFile('chrome/resources/script.js', []),
  4676. MockFile('components/feature/ios/resources/script.ts', []),
  4677. MockFile('ios/chrome/feature/resources/script.ts', []),
  4678. MockFile('ios/web/feature/resources/script.ts', []),
  4679. MockFile('ios/third_party/script.js', []),
  4680. MockFile('third_party/ios/script.js', []),
  4681. ]
  4682. results = PRESUBMIT.CheckNoJsInIos(input_api, MockOutputApi())
  4683. self.assertEqual(0, len(results))
  4684. def testExistingFilesWarningOnly(self):
  4685. input_api = MockInputApi()
  4686. input_api.files = [
  4687. MockFile('ios/chrome/feature/resources/script.js', [], action='M'),
  4688. MockFile('ios/chrome/feature/resources/script2.js', [], action='D'),
  4689. ]
  4690. results = PRESUBMIT.CheckNoJsInIos(input_api, MockOutputApi())
  4691. self.assertEqual(1, len(results))
  4692. self.assertEqual('warning', results[0].type)
  4693. self.assertEqual(1, len(results[0].items))
  4694. def testMovedScriptWarningOnly(self):
  4695. input_api = MockInputApi()
  4696. input_api.files = [
  4697. MockFile('ios/chrome/feature/resources/script.js', [], action='D'),
  4698. MockFile('ios/chrome/renamed_feature/resources/script.js', [], action='A'),
  4699. ]
  4700. results = PRESUBMIT.CheckNoJsInIos(input_api, MockOutputApi())
  4701. self.assertEqual(1, len(results))
  4702. self.assertEqual('warning', results[0].type)
  4703. self.assertEqual(1, len(results[0].items))
  4704. class CheckNoAbbreviationInPngFileNameTest(unittest.TestCase):
  4705. def testHasAbbreviation(self):
  4706. """test png file names with abbreviation that fails the check"""
  4707. input_api = MockInputApi()
  4708. input_api.files = [
  4709. MockFile('image_a.png', [], action='A'),
  4710. MockFile('image_a_.png', [], action='A'),
  4711. MockFile('image_a_name.png', [], action='A'),
  4712. MockFile('chrome/ui/feature_name/resources/image_a.png', [],
  4713. action='A'),
  4714. MockFile('chrome/ui/feature_name/resources/image_a_.png', [],
  4715. action='A'),
  4716. MockFile('chrome/ui/feature_name/resources/image_a_name.png', [],
  4717. action='A'),
  4718. ]
  4719. results = PRESUBMIT.CheckNoAbbreviationInPngFileName(
  4720. input_api, MockOutputApi())
  4721. self.assertEqual(1, len(results))
  4722. self.assertEqual('error', results[0].type)
  4723. self.assertEqual(len(input_api.files), len(results[0].items))
  4724. def testNoAbbreviation(self):
  4725. """test png file names without abbreviation that passes the check"""
  4726. input_api = MockInputApi()
  4727. input_api.files = [
  4728. MockFile('a.png', [], action='A'),
  4729. MockFile('_a.png', [], action='A'),
  4730. MockFile('image.png', [], action='A'),
  4731. MockFile('image_ab_.png', [], action='A'),
  4732. MockFile('image_ab_name.png', [], action='A'),
  4733. # These paths used to fail because `feature_a_name` matched the regex by mistake.
  4734. # They should pass now because the path components ahead of the file name are ignored in the check.
  4735. MockFile('chrome/ui/feature_a_name/resources/a.png', [],
  4736. action='A'),
  4737. MockFile('chrome/ui/feature_a_name/resources/_a.png', [],
  4738. action='A'),
  4739. MockFile('chrome/ui/feature_a_name/resources/image.png', [],
  4740. action='A'),
  4741. MockFile('chrome/ui/feature_a_name/resources/image_ab_.png', [],
  4742. action='A'),
  4743. MockFile('chrome/ui/feature_a_name/resources/image_ab_name.png',
  4744. [],
  4745. action='A'),
  4746. ]
  4747. results = PRESUBMIT.CheckNoAbbreviationInPngFileName(
  4748. input_api, MockOutputApi())
  4749. self.assertEqual(0, len(results))
  4750. class CheckDanglingUntriagedTest(unittest.TestCase):
  4751. def testError(self):
  4752. """Test patch adding dangling pointers are reported"""
  4753. mock_input_api = MockInputApi()
  4754. mock_output_api = MockOutputApi()
  4755. mock_input_api.change.DescriptionText = lambda: "description"
  4756. mock_input_api.files = [
  4757. MockAffectedFile(
  4758. local_path="foo/foo.cc",
  4759. old_contents=["raw_ptr<T>"],
  4760. new_contents=["raw_ptr<T, DanglingUntriaged>"],
  4761. )
  4762. ]
  4763. msgs = PRESUBMIT.CheckDanglingUntriaged(mock_input_api,
  4764. mock_output_api)
  4765. self.assertEqual(len(msgs), 1)
  4766. self.assertEqual(len(msgs[0].message), 10)
  4767. self.assertEqual(
  4768. msgs[0].message[0],
  4769. "Unexpected new occurrences of `DanglingUntriaged` detected. Please",
  4770. )
  4771. class CheckDanglingUntriagedTest(unittest.TestCase):
  4772. def testError(self):
  4773. """Test patch adding dangling pointers are reported"""
  4774. mock_input_api = MockInputApi()
  4775. mock_output_api = MockOutputApi()
  4776. mock_input_api.change.DescriptionText = lambda: "description"
  4777. mock_input_api.files = [
  4778. MockAffectedFile(
  4779. local_path="foo/foo.cc",
  4780. old_contents=["raw_ptr<T>"],
  4781. new_contents=["raw_ptr<T, DanglingUntriaged>"],
  4782. )
  4783. ]
  4784. msgs = PRESUBMIT.CheckDanglingUntriaged(mock_input_api,
  4785. mock_output_api)
  4786. self.assertEqual(len(msgs), 1)
  4787. self.assertTrue(
  4788. ("Unexpected new occurrences of `DanglingUntriaged` detected"
  4789. in msgs[0].message))
  4790. def testNonCppFile(self):
  4791. """Test patch adding dangling pointers are not reported in non C++ files"""
  4792. mock_input_api = MockInputApi()
  4793. mock_output_api = MockOutputApi()
  4794. mock_input_api.change.DescriptionText = lambda: "description"
  4795. mock_input_api.files = [
  4796. MockAffectedFile(
  4797. local_path="foo/README.md",
  4798. old_contents=[""],
  4799. new_contents=["The DanglingUntriaged annotation means"],
  4800. )
  4801. ]
  4802. msgs = PRESUBMIT.CheckDanglingUntriaged(mock_input_api,
  4803. mock_output_api)
  4804. self.assertEqual(len(msgs), 0)
  4805. def testDeveloperAcknowledgeInCommitDescription(self):
  4806. """Test patch adding dangling pointers, but acknowledged by the developers
  4807. aren't reported"""
  4808. mock_input_api = MockInputApi()
  4809. mock_output_api = MockOutputApi()
  4810. mock_input_api.files = [
  4811. MockAffectedFile(
  4812. local_path="foo/foo.cc",
  4813. old_contents=["raw_ptr<T>"],
  4814. new_contents=["raw_ptr<T, DanglingUntriaged>"],
  4815. )
  4816. ]
  4817. mock_input_api.change.DescriptionText = lambda: (
  4818. "DanglingUntriaged-notes: Sorry about this!")
  4819. msgs = PRESUBMIT.CheckDanglingUntriaged(mock_input_api,
  4820. mock_output_api)
  4821. self.assertEqual(len(msgs), 0)
  4822. def testDeveloperAcknowledgeInCommitFooter(self):
  4823. """Test patch adding dangling pointers, but acknowledged by the developers
  4824. aren't reported"""
  4825. mock_input_api = MockInputApi()
  4826. mock_output_api = MockOutputApi()
  4827. mock_input_api.files = [
  4828. MockAffectedFile(
  4829. local_path="foo/foo.cc",
  4830. old_contents=["raw_ptr<T>"],
  4831. new_contents=["raw_ptr<T, DanglingUntriaged>"],
  4832. )
  4833. ]
  4834. mock_input_api.change.DescriptionText = lambda: "description"
  4835. mock_input_api.change.footers["DanglingUntriaged-notes"] = ["Sorry!"]
  4836. msgs = PRESUBMIT.CheckDanglingUntriaged(mock_input_api,
  4837. mock_output_api)
  4838. self.assertEqual(len(msgs), 0)
  4839. def testCongrats(self):
  4840. """Test the presubmit congrats users removing dangling pointers"""
  4841. mock_input_api = MockInputApi()
  4842. mock_output_api = MockOutputApi()
  4843. mock_input_api.files = [
  4844. MockAffectedFile(
  4845. local_path="foo/foo.cc",
  4846. old_contents=["raw_ptr<T, DanglingUntriaged>"],
  4847. new_contents=["raw_ptr<T>"],
  4848. )
  4849. ]
  4850. mock_input_api.change.DescriptionText = lambda: (
  4851. "This patch fixes some DanglingUntriaged pointers!")
  4852. msgs = PRESUBMIT.CheckDanglingUntriaged(mock_input_api,
  4853. mock_output_api)
  4854. self.assertEqual(len(msgs), 1)
  4855. self.assertTrue(
  4856. "DanglingUntriaged pointers removed: 1" in msgs[0].message)
  4857. self.assertTrue("Thank you!" in msgs[0].message)
  4858. def testRenameFile(self):
  4859. """Patch that we do not warn about DanglingUntriaged when moving files"""
  4860. mock_input_api = MockInputApi()
  4861. mock_output_api = MockOutputApi()
  4862. mock_input_api.files = [
  4863. MockAffectedFile(
  4864. local_path="foo/foo.cc",
  4865. old_contents=["raw_ptr<T, DanglingUntriaged>"],
  4866. new_contents=[""],
  4867. action="D",
  4868. ),
  4869. MockAffectedFile(
  4870. local_path="foo/foo.cc",
  4871. old_contents=[""],
  4872. new_contents=["raw_ptr<T, DanglingUntriaged>"],
  4873. action="A",
  4874. ),
  4875. ]
  4876. mock_input_api.change.DescriptionText = lambda: (
  4877. "This patch moves files")
  4878. msgs = PRESUBMIT.CheckDanglingUntriaged(mock_input_api,
  4879. mock_output_api)
  4880. self.assertEqual(len(msgs), 0)
  4881. class CheckInlineConstexprDefinitionsInHeadersTest(unittest.TestCase):
  4882. def testNoInlineConstexprInHeaderFile(self):
  4883. """Tests that non-inlined constexpr variables in headers fail the test."""
  4884. input_api = MockInputApi()
  4885. input_api.files = [
  4886. MockAffectedFile('src/constants.h',
  4887. ['constexpr int kVersion = 5;'])
  4888. ]
  4889. warnings = PRESUBMIT.CheckInlineConstexprDefinitionsInHeaders(
  4890. input_api, MockOutputApi())
  4891. self.assertEqual(1, len(warnings))
  4892. def testNoInlineConstexprInHeaderFileInitializedFromFunction(self):
  4893. """Tests that non-inlined constexpr header variables that are initialized from a function fail."""
  4894. input_api = MockInputApi()
  4895. input_api.files = [
  4896. MockAffectedFile('src/constants.h',
  4897. ['constexpr int kVersion = GetVersion();'])
  4898. ]
  4899. warnings = PRESUBMIT.CheckInlineConstexprDefinitionsInHeaders(
  4900. input_api, MockOutputApi())
  4901. self.assertEqual(1, len(warnings))
  4902. def testNoInlineConstexprInHeaderFileInitializedWithExpression(self):
  4903. """Tests that non-inlined constexpr header variables initialized with an expression fail."""
  4904. input_api = MockInputApi()
  4905. input_api.files = [
  4906. MockAffectedFile('src/constants.h',
  4907. ['constexpr int kVersion = (4 + 5)*3;'])
  4908. ]
  4909. warnings = PRESUBMIT.CheckInlineConstexprDefinitionsInHeaders(
  4910. input_api, MockOutputApi())
  4911. self.assertEqual(1, len(warnings))
  4912. def testNoInlineConstexprInHeaderFileBraceInitialized(self):
  4913. """Tests that non-inlined constexpr header variables that are brace-initialized fail."""
  4914. input_api = MockInputApi()
  4915. input_api.files = [
  4916. MockAffectedFile('src/constants.h', ['constexpr int kVersion{5};'])
  4917. ]
  4918. warnings = PRESUBMIT.CheckInlineConstexprDefinitionsInHeaders(
  4919. input_api, MockOutputApi())
  4920. self.assertEqual(1, len(warnings))
  4921. def testNoInlineConstexprInHeaderWithAttribute(self):
  4922. """Tests that non-inlined constexpr header variables that have compiler attributes fail."""
  4923. input_api = MockInputApi()
  4924. input_api.files = [
  4925. MockAffectedFile('src/constants.h',
  4926. ['constexpr [[maybe_unused]] int kVersion{5};'])
  4927. ]
  4928. warnings = PRESUBMIT.CheckInlineConstexprDefinitionsInHeaders(
  4929. input_api, MockOutputApi())
  4930. self.assertEqual(1, len(warnings))
  4931. def testInlineConstexprInHeaderWithAttribute(self):
  4932. """Tests that inlined constexpr header variables that have compiler attributes pass."""
  4933. input_api = MockInputApi()
  4934. input_api.files = [
  4935. MockAffectedFile(
  4936. 'src/constants.h',
  4937. ['inline constexpr [[maybe_unused]] int kVersion{5};']),
  4938. MockAffectedFile(
  4939. 'src/constants.h',
  4940. ['constexpr inline [[maybe_unused]] int kVersion{5};']),
  4941. MockAffectedFile(
  4942. 'src/constants.h',
  4943. ['inline constexpr [[maybe_unused]] inline int kVersion{5};'])
  4944. ]
  4945. warnings = PRESUBMIT.CheckInlineConstexprDefinitionsInHeaders(
  4946. input_api, MockOutputApi())
  4947. self.assertEqual(0, len(warnings))
  4948. def testNoInlineConstexprInHeaderFileMultipleLines(self):
  4949. """Tests that non-inlined constexpr header variable definitions spanning multiple lines fail."""
  4950. input_api = MockInputApi()
  4951. lines = [
  4952. 'constexpr char kLongName =',
  4953. ' "This is a very long name of something.";'
  4954. ]
  4955. input_api.files = [MockAffectedFile('src/constants.h', lines)]
  4956. warnings = PRESUBMIT.CheckInlineConstexprDefinitionsInHeaders(
  4957. input_api, MockOutputApi())
  4958. self.assertEqual(1, len(warnings))
  4959. def testNoInlineConstexprInCCFile(self):
  4960. """Tests that non-inlined constexpr variables in .cc files pass the test."""
  4961. input_api = MockInputApi()
  4962. input_api.files = [
  4963. MockAffectedFile('src/implementation.cc',
  4964. ['constexpr int kVersion = 5;'])
  4965. ]
  4966. warnings = PRESUBMIT.CheckInlineConstexprDefinitionsInHeaders(
  4967. input_api, MockOutputApi())
  4968. self.assertEqual(0, len(warnings))
  4969. def testInlineConstexprInHeaderFile(self):
  4970. """Tests that inlined constexpr variables in header files pass the test."""
  4971. input_api = MockInputApi()
  4972. input_api.files = [
  4973. MockAffectedFile('src/constants.h',
  4974. ['constexpr inline int kX = 5;']),
  4975. MockAffectedFile('src/version.h',
  4976. ['inline constexpr float kY = 5.0f;'])
  4977. ]
  4978. warnings = PRESUBMIT.CheckInlineConstexprDefinitionsInHeaders(
  4979. input_api, MockOutputApi())
  4980. self.assertEqual(0, len(warnings))
  4981. def testConstexprStandaloneFunctionInHeaderFile(self):
  4982. """Tests that non-inlined constexpr functions in headers pass the test."""
  4983. input_api = MockInputApi()
  4984. input_api.files = [
  4985. MockAffectedFile('src/helpers.h', ['constexpr int GetVersion();'])
  4986. ]
  4987. warnings = PRESUBMIT.CheckInlineConstexprDefinitionsInHeaders(
  4988. input_api, MockOutputApi())
  4989. self.assertEqual(0, len(warnings))
  4990. def testConstexprWithAbseilAttributeInHeader(self):
  4991. """Tests that non-inlined constexpr variables with Abseil-type prefixes in headers fail."""
  4992. input_api = MockInputApi()
  4993. input_api.files = [
  4994. MockAffectedFile('src/helpers.h',
  4995. ['ABSL_FOOFOO constexpr int i = 5;'])
  4996. ]
  4997. warnings = PRESUBMIT.CheckInlineConstexprDefinitionsInHeaders(
  4998. input_api, MockOutputApi())
  4999. self.assertEqual(1, len(warnings))
  5000. def testInlineConstexprWithAbseilAttributeInHeader(self):
  5001. """Tests that inlined constexpr variables with Abseil-type prefixes in headers pass."""
  5002. input_api = MockInputApi()
  5003. input_api.files = [
  5004. MockAffectedFile('src/helpers.h',
  5005. ['constexpr ABSL_FOO inline int i = 5;'])
  5006. ]
  5007. warnings = PRESUBMIT.CheckInlineConstexprDefinitionsInHeaders(
  5008. input_api, MockOutputApi())
  5009. self.assertEqual(0, len(warnings))
  5010. def testConstexprWithClangAttributeInHeader(self):
  5011. """Tests that non-inlined constexpr variables with attributes with colons in headers fail."""
  5012. input_api = MockInputApi()
  5013. input_api.files = [
  5014. MockAffectedFile('src/helpers.h',
  5015. ['[[clang::someattribute]] constexpr int i = 5;'])
  5016. ]
  5017. warnings = PRESUBMIT.CheckInlineConstexprDefinitionsInHeaders(
  5018. input_api, MockOutputApi())
  5019. self.assertEqual(1, len(warnings))
  5020. def testInlineConstexprWithClangAttributeInHeader(self):
  5021. """Tests that inlined constexpr variables with attributes with colons in headers pass."""
  5022. input_api = MockInputApi()
  5023. input_api.files = [
  5024. MockAffectedFile(
  5025. 'src/helpers.h',
  5026. ['constexpr [[clang::someattribute]] inline int i = 5;'])
  5027. ]
  5028. warnings = PRESUBMIT.CheckInlineConstexprDefinitionsInHeaders(
  5029. input_api, MockOutputApi())
  5030. self.assertEqual(0, len(warnings))
  5031. def testNoExplicitInlineConstexprInsideClassInHeaderFile(self):
  5032. """Tests that non-inlined constexpr class members pass the test."""
  5033. input_api = MockInputApi()
  5034. lines = [
  5035. 'class SomeClass {', ' public:',
  5036. ' static constexpr kVersion = 5;', '};'
  5037. ]
  5038. input_api.files = [MockAffectedFile('src/class.h', lines)]
  5039. warnings = PRESUBMIT.CheckInlineConstexprDefinitionsInHeaders(
  5040. input_api, MockOutputApi())
  5041. self.assertEqual(0, len(warnings))
  5042. def testTodoBugReferencesWithOldBugId(self):
  5043. """Tests that an old monorail bug ID in a TODO fails."""
  5044. input_api = MockInputApi()
  5045. input_api.files = [
  5046. MockAffectedFile('src/helpers.h', ['// TODO(crbug.com/12345)'])
  5047. ]
  5048. warnings = PRESUBMIT.CheckTodoBugReferences(input_api, MockOutputApi())
  5049. self.assertEqual(1, len(warnings))
  5050. def testTodoBugReferencesWithUpdatedBugId(self):
  5051. """Tests that a new issue tracker bug ID in a TODO passes."""
  5052. input_api = MockInputApi()
  5053. input_api.files = [
  5054. MockAffectedFile('src/helpers.h', ['// TODO(crbug.com/40781525)'])
  5055. ]
  5056. warnings = PRESUBMIT.CheckTodoBugReferences(input_api, MockOutputApi())
  5057. self.assertEqual(0, len(warnings))
  5058. class CheckDeprecatedSyncConsentFunctionsTest(unittest.TestCase):
  5059. """Test the presubmit for deprecated ConsentLevel::kSync functions."""
  5060. def testCppMobilePlatformPath(self):
  5061. input_api = MockInputApi()
  5062. input_api.files = [
  5063. MockFile('chrome/browser/android/file.cc', ['OtherFunction']),
  5064. MockFile('chrome/android/file.cc', ['HasSyncConsent']),
  5065. MockFile('ios/file.mm', ['CanSyncFeatureStart']),
  5066. MockFile('components/foo/ios/file.cc', ['IsSyncFeatureEnabled']),
  5067. MockFile('components/foo/delegate_android.cc',
  5068. ['IsSyncFeatureActive']),
  5069. MockFile('components/foo/delegate_ios.cc',
  5070. ['IsSyncFeatureActive']),
  5071. MockFile('components/foo/android_delegate.cc',
  5072. ['IsSyncFeatureActive']),
  5073. MockFile('components/foo/ios_delegate.cc',
  5074. ['IsSyncFeatureActive']),
  5075. ]
  5076. results = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
  5077. self.assertEqual(1, len(results))
  5078. self.assertFalse(
  5079. 'chrome/browser/android/file.cc' in results[0].message),
  5080. self.assertTrue('chrome/android/file.cc' in results[0].message),
  5081. self.assertTrue('ios/file.mm' in results[0].message),
  5082. self.assertTrue('components/foo/ios/file.cc' in results[0].message),
  5083. self.assertTrue(
  5084. 'components/foo/delegate_android.cc' in results[0].message),
  5085. self.assertTrue(
  5086. 'components/foo/delegate_ios.cc' in results[0].message),
  5087. self.assertTrue(
  5088. 'components/foo/android_delegate.cc' in results[0].message),
  5089. self.assertTrue(
  5090. 'components/foo/ios_delegate.cc' in results[0].message),
  5091. def testCppNonMobilePlatformPath(self):
  5092. input_api = MockInputApi()
  5093. input_api.files = [
  5094. MockFile('chrome/browser/file.cc', ['HasSyncConsent']),
  5095. MockFile('bios/file.cc', ['HasSyncConsent']),
  5096. MockFile('components/kiosk/file.cc', ['HasSyncConsent']),
  5097. ]
  5098. results = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
  5099. self.assertEqual(0, len(results))
  5100. def testJavaPath(self):
  5101. input_api = MockInputApi()
  5102. input_api.files = [
  5103. MockFile('components/foo/file1.java', ['otherFunction']),
  5104. MockFile('components/foo/file2.java', ['hasSyncConsent']),
  5105. MockFile('chrome/foo/file3.java', ['canSyncFeatureStart']),
  5106. MockFile('chrome/foo/file4.java', ['isSyncFeatureEnabled']),
  5107. MockFile('chrome/foo/file5.java', ['isSyncFeatureActive']),
  5108. ]
  5109. results = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
  5110. self.assertEqual(1, len(results))
  5111. self.assertFalse('components/foo/file1.java' in results[0].message),
  5112. self.assertTrue('components/foo/file2.java' in results[0].message),
  5113. self.assertTrue('chrome/foo/file3.java' in results[0].message),
  5114. self.assertTrue('chrome/foo/file4.java' in results[0].message),
  5115. self.assertTrue('chrome/foo/file5.java' in results[0].message),
  5116. class CheckAnonymousNamespaceTest(unittest.TestCase):
  5117. """Test the presubmit for anonymous namespaces."""
  5118. def testAnonymousNamespace(self):
  5119. input_api = MockInputApi()
  5120. input_api.files = [
  5121. MockFile('chrome/test.h', ['namespace {']),
  5122. MockFile('chrome/test.cc', ['namespace {']),
  5123. MockFile('chrome/test.java', ['namespace {']),
  5124. MockFile('chrome/test.cpp', ['namespace {']),
  5125. MockFile('chrome/test.txt', ['namespace {']),
  5126. ]
  5127. results = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
  5128. self.assertEqual(1, len(results))
  5129. self.assertTrue(
  5130. 'chrome/test.h' in results[0].message),
  5131. self.assertFalse(
  5132. 'chrome/test.cc' in results[0].message),
  5133. self.assertFalse(
  5134. 'chrome/test.java' in results[0].message),
  5135. self.assertFalse(
  5136. 'chrome/test.cpp' in results[0].message),
  5137. self.assertFalse(
  5138. 'chrome/test.txt' in results[0].message),
  5139. if __name__ == '__main__':
  5140. unittest.main()