PRESUBMIT_test.py 175 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642
  1. #!/usr/bin/env python3
  2. # Copyright (c) 2012 The Chromium Authors. All rights reserved.
  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 unittest
  9. import PRESUBMIT
  10. from PRESUBMIT_test_mocks import MockFile, MockAffectedFile
  11. from PRESUBMIT_test_mocks import MockInputApi, MockOutputApi
  12. _TEST_DATA_DIR = 'base/test/data/presubmit'
  13. class VersionControlConflictsTest(unittest.TestCase):
  14. def testTypicalConflict(self):
  15. lines = ['<<<<<<< HEAD',
  16. ' base::ScopedTempDir temp_dir_;',
  17. '=======',
  18. ' ScopedTempDir temp_dir_;',
  19. '>>>>>>> master']
  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 = ['A First Level Header',
  28. '====================',
  29. '',
  30. 'A Second Level Header',
  31. '---------------------']
  32. errors = PRESUBMIT._CheckForVersionControlConflictsInFile(
  33. MockInputApi(), MockFile('some/polymer/README.md', lines))
  34. self.assertEqual(0, len(errors))
  35. class BadExtensionsTest(unittest.TestCase):
  36. def testBadRejFile(self):
  37. mock_input_api = MockInputApi()
  38. mock_input_api.files = [
  39. MockFile('some/path/foo.cc', ''),
  40. MockFile('some/path/foo.cc.rej', ''),
  41. MockFile('some/path2/bar.h.rej', ''),
  42. ]
  43. results = PRESUBMIT.CheckPatchFiles(mock_input_api, MockOutputApi())
  44. self.assertEqual(1, len(results))
  45. self.assertEqual(2, len(results[0].items))
  46. self.assertTrue('foo.cc.rej' in results[0].items[0])
  47. self.assertTrue('bar.h.rej' in results[0].items[1])
  48. def testBadOrigFile(self):
  49. mock_input_api = MockInputApi()
  50. mock_input_api.files = [
  51. MockFile('other/path/qux.h.orig', ''),
  52. MockFile('other/path/qux.h', ''),
  53. MockFile('other/path/qux.cc', ''),
  54. ]
  55. results = PRESUBMIT.CheckPatchFiles(mock_input_api, MockOutputApi())
  56. self.assertEqual(1, len(results))
  57. self.assertEqual(1, len(results[0].items))
  58. self.assertTrue('qux.h.orig' in results[0].items[0])
  59. def testGoodFiles(self):
  60. mock_input_api = MockInputApi()
  61. mock_input_api.files = [
  62. MockFile('other/path/qux.h', ''),
  63. MockFile('other/path/qux.cc', ''),
  64. ]
  65. results = PRESUBMIT.CheckPatchFiles(mock_input_api, MockOutputApi())
  66. self.assertEqual(0, len(results))
  67. class CheckForSuperfluousStlIncludesInHeadersTest(unittest.TestCase):
  68. def testGoodFiles(self):
  69. mock_input_api = MockInputApi()
  70. mock_input_api.files = [
  71. # The check is not smart enough to figure out which definitions correspond
  72. # to which header.
  73. MockFile('other/path/foo.h',
  74. ['#include <string>',
  75. 'std::vector']),
  76. # The check is not smart enough to do IWYU.
  77. MockFile('other/path/bar.h',
  78. ['#include "base/check.h"',
  79. 'std::vector']),
  80. MockFile('other/path/qux.h',
  81. ['#include "base/stl_util.h"',
  82. 'foobar']),
  83. MockFile('other/path/baz.h',
  84. ['#include "set/vector.h"',
  85. 'bazzab']),
  86. # The check is only for header files.
  87. MockFile('other/path/not_checked.cc',
  88. ['#include <vector>',
  89. 'bazbaz']),
  90. ]
  91. results = PRESUBMIT.CheckForSuperfluousStlIncludesInHeaders(
  92. mock_input_api, MockOutputApi())
  93. self.assertEqual(0, len(results))
  94. def testBadFiles(self):
  95. mock_input_api = MockInputApi()
  96. mock_input_api.files = [
  97. MockFile('other/path/foo.h',
  98. ['#include <vector>',
  99. 'vector']),
  100. MockFile('other/path/bar.h',
  101. ['#include <limits>',
  102. '#include <set>',
  103. 'no_std_namespace']),
  104. ]
  105. results = PRESUBMIT.CheckForSuperfluousStlIncludesInHeaders(
  106. mock_input_api, MockOutputApi())
  107. self.assertEqual(1, len(results))
  108. self.assertTrue('foo.h: Includes STL' in results[0].message)
  109. self.assertTrue('bar.h: Includes STL' in results[0].message)
  110. class CheckSingletonInHeadersTest(unittest.TestCase):
  111. def testSingletonInArbitraryHeader(self):
  112. diff_singleton_h = ['base::subtle::AtomicWord '
  113. 'base::Singleton<Type, Traits, DifferentiatingType>::']
  114. diff_foo_h = ['// base::Singleton<Foo> in comment.',
  115. 'friend class base::Singleton<Foo>']
  116. diff_foo2_h = [' //Foo* bar = base::Singleton<Foo>::get();']
  117. diff_bad_h = ['Foo* foo = base::Singleton<Foo>::get();']
  118. mock_input_api = MockInputApi()
  119. mock_input_api.files = [MockAffectedFile('base/memory/singleton.h',
  120. diff_singleton_h),
  121. MockAffectedFile('foo.h', diff_foo_h),
  122. MockAffectedFile('foo2.h', diff_foo2_h),
  123. MockAffectedFile('bad.h', diff_bad_h)]
  124. warnings = PRESUBMIT.CheckSingletonInHeaders(mock_input_api,
  125. MockOutputApi())
  126. self.assertEqual(1, len(warnings))
  127. self.assertEqual(1, len(warnings[0].items))
  128. self.assertEqual('error', warnings[0].type)
  129. self.assertTrue('Found base::Singleton<T>' in warnings[0].message)
  130. def testSingletonInCC(self):
  131. diff_cc = ['Foo* foo = base::Singleton<Foo>::get();']
  132. mock_input_api = MockInputApi()
  133. mock_input_api.files = [MockAffectedFile('some/path/foo.cc', diff_cc)]
  134. warnings = PRESUBMIT.CheckSingletonInHeaders(mock_input_api,
  135. MockOutputApi())
  136. self.assertEqual(0, len(warnings))
  137. class DeprecatedOSMacroNamesTest(unittest.TestCase):
  138. def testDeprecatedOSMacroNames(self):
  139. lines = ['#if defined(OS_WIN)',
  140. ' #elif defined(OS_WINDOW)',
  141. ' # if defined(OS_MAC) || defined(OS_CHROME)']
  142. errors = PRESUBMIT._CheckForDeprecatedOSMacrosInFile(
  143. MockInputApi(), MockFile('some/path/foo_platform.cc', lines))
  144. self.assertEqual(len(lines) + 1, len(errors))
  145. self.assertTrue(':1: defined(OS_WIN) -> BUILDFLAG(IS_WIN)' in errors[0])
  146. class InvalidIfDefinedMacroNamesTest(unittest.TestCase):
  147. def testInvalidIfDefinedMacroNames(self):
  148. lines = ['#if defined(TARGET_IPHONE_SIMULATOR)',
  149. '#if !defined(TARGET_IPHONE_SIMULATOR)',
  150. '#elif defined(TARGET_IPHONE_SIMULATOR)',
  151. '#ifdef TARGET_IPHONE_SIMULATOR',
  152. ' # ifdef TARGET_IPHONE_SIMULATOR',
  153. '# if defined(VALID) || defined(TARGET_IPHONE_SIMULATOR)',
  154. '# else // defined(TARGET_IPHONE_SIMULATOR)',
  155. '#endif // defined(TARGET_IPHONE_SIMULATOR)']
  156. errors = PRESUBMIT._CheckForInvalidIfDefinedMacrosInFile(
  157. MockInputApi(), MockFile('some/path/source.mm', lines))
  158. self.assertEqual(len(lines), len(errors))
  159. def testValidIfDefinedMacroNames(self):
  160. lines = ['#if defined(FOO)',
  161. '#ifdef BAR']
  162. errors = PRESUBMIT._CheckForInvalidIfDefinedMacrosInFile(
  163. MockInputApi(), MockFile('some/path/source.cc', lines))
  164. self.assertEqual(0, len(errors))
  165. class CheckAddedDepsHaveTestApprovalsTest(unittest.TestCase):
  166. def calculate(self, old_include_rules, old_specific_include_rules,
  167. new_include_rules, new_specific_include_rules):
  168. return PRESUBMIT._CalculateAddedDeps(
  169. os.path, 'include_rules = %r\nspecific_include_rules = %r' % (
  170. old_include_rules, old_specific_include_rules),
  171. 'include_rules = %r\nspecific_include_rules = %r' % (
  172. new_include_rules, new_specific_include_rules))
  173. def testCalculateAddedDeps(self):
  174. old_include_rules = [
  175. '+base',
  176. '-chrome',
  177. '+content',
  178. '-grit',
  179. '-grit/",',
  180. '+jni/fooblat.h',
  181. '!sandbox',
  182. ]
  183. old_specific_include_rules = {
  184. 'compositor\.*': {
  185. '+cc',
  186. },
  187. }
  188. new_include_rules = [
  189. '-ash',
  190. '+base',
  191. '+chrome',
  192. '+components',
  193. '+content',
  194. '+grit',
  195. '+grit/generated_resources.h",',
  196. '+grit/",',
  197. '+jni/fooblat.h',
  198. '+policy',
  199. '+' + os.path.join('third_party', 'WebKit'),
  200. ]
  201. new_specific_include_rules = {
  202. 'compositor\.*': {
  203. '+cc',
  204. },
  205. 'widget\.*': {
  206. '+gpu',
  207. },
  208. }
  209. expected = set([
  210. os.path.join('chrome', 'DEPS'),
  211. os.path.join('gpu', 'DEPS'),
  212. os.path.join('components', 'DEPS'),
  213. os.path.join('policy', 'DEPS'),
  214. os.path.join('third_party', 'WebKit', 'DEPS'),
  215. ])
  216. self.assertEqual(
  217. expected,
  218. self.calculate(old_include_rules, old_specific_include_rules,
  219. new_include_rules, new_specific_include_rules))
  220. def testCalculateAddedDepsIgnoresPermutations(self):
  221. old_include_rules = [
  222. '+base',
  223. '+chrome',
  224. ]
  225. new_include_rules = [
  226. '+chrome',
  227. '+base',
  228. ]
  229. self.assertEqual(set(),
  230. self.calculate(old_include_rules, {}, new_include_rules,
  231. {}))
  232. class JSONParsingTest(unittest.TestCase):
  233. def testSuccess(self):
  234. input_api = MockInputApi()
  235. filename = 'valid_json.json'
  236. contents = ['// This is a comment.',
  237. '{',
  238. ' "key1": ["value1", "value2"],',
  239. ' "key2": 3 // This is an inline comment.',
  240. '}'
  241. ]
  242. input_api.files = [MockFile(filename, contents)]
  243. self.assertEqual(None,
  244. PRESUBMIT._GetJSONParseError(input_api, filename))
  245. def testFailure(self):
  246. input_api = MockInputApi()
  247. test_data = [
  248. ('invalid_json_1.json',
  249. ['{ x }'],
  250. 'Expecting property name'),
  251. ('invalid_json_2.json',
  252. ['// Hello world!',
  253. '{ "hello": "world }'],
  254. 'Unterminated string starting at:'),
  255. ('invalid_json_3.json',
  256. ['{ "a": "b", "c": "d", }'],
  257. 'Expecting property name'),
  258. ('invalid_json_4.json',
  259. ['{ "a": "b" "c": "d" }'],
  260. "Expecting ',' delimiter:"),
  261. ]
  262. input_api.files = [MockFile(filename, contents)
  263. for (filename, contents, _) in test_data]
  264. for (filename, _, expected_error) in test_data:
  265. actual_error = PRESUBMIT._GetJSONParseError(input_api, filename)
  266. self.assertTrue(expected_error in str(actual_error),
  267. "'%s' not found in '%s'" % (expected_error, actual_error))
  268. def testNoEatComments(self):
  269. input_api = MockInputApi()
  270. file_with_comments = 'file_with_comments.json'
  271. contents_with_comments = ['// This is a comment.',
  272. '{',
  273. ' "key1": ["value1", "value2"],',
  274. ' "key2": 3 // This is an inline comment.',
  275. '}'
  276. ]
  277. file_without_comments = 'file_without_comments.json'
  278. contents_without_comments = ['{',
  279. ' "key1": ["value1", "value2"],',
  280. ' "key2": 3',
  281. '}'
  282. ]
  283. input_api.files = [MockFile(file_with_comments, contents_with_comments),
  284. MockFile(file_without_comments,
  285. contents_without_comments)]
  286. self.assertNotEqual(None,
  287. str(PRESUBMIT._GetJSONParseError(input_api,
  288. file_with_comments,
  289. eat_comments=False)))
  290. self.assertEqual(None,
  291. PRESUBMIT._GetJSONParseError(input_api,
  292. file_without_comments,
  293. eat_comments=False))
  294. class IDLParsingTest(unittest.TestCase):
  295. def testSuccess(self):
  296. input_api = MockInputApi()
  297. filename = 'valid_idl_basics.idl'
  298. contents = ['// Tests a valid IDL file.',
  299. 'namespace idl_basics {',
  300. ' enum EnumType {',
  301. ' name1,',
  302. ' name2',
  303. ' };',
  304. '',
  305. ' dictionary MyType1 {',
  306. ' DOMString a;',
  307. ' };',
  308. '',
  309. ' callback Callback1 = void();',
  310. ' callback Callback2 = void(long x);',
  311. ' callback Callback3 = void(MyType1 arg);',
  312. ' callback Callback4 = void(EnumType type);',
  313. '',
  314. ' interface Functions {',
  315. ' static void function1();',
  316. ' static void function2(long x);',
  317. ' static void function3(MyType1 arg);',
  318. ' static void function4(Callback1 cb);',
  319. ' static void function5(Callback2 cb);',
  320. ' static void function6(Callback3 cb);',
  321. ' static void function7(Callback4 cb);',
  322. ' };',
  323. '',
  324. ' interface Events {',
  325. ' static void onFoo1();',
  326. ' static void onFoo2(long x);',
  327. ' static void onFoo2(MyType1 arg);',
  328. ' static void onFoo3(EnumType type);',
  329. ' };',
  330. '};'
  331. ]
  332. input_api.files = [MockFile(filename, contents)]
  333. self.assertEqual(None,
  334. PRESUBMIT._GetIDLParseError(input_api, filename))
  335. def testFailure(self):
  336. input_api = MockInputApi()
  337. test_data = [
  338. ('invalid_idl_1.idl',
  339. ['//',
  340. 'namespace test {',
  341. ' dictionary {',
  342. ' DOMString s;',
  343. ' };',
  344. '};'],
  345. 'Unexpected "{" after keyword "dictionary".\n'),
  346. # TODO(yoz): Disabled because it causes the IDL parser to hang.
  347. # See crbug.com/363830.
  348. # ('invalid_idl_2.idl',
  349. # (['namespace test {',
  350. # ' dictionary MissingSemicolon {',
  351. # ' DOMString a',
  352. # ' DOMString b;',
  353. # ' };',
  354. # '};'],
  355. # 'Unexpected symbol DOMString after symbol a.'),
  356. ('invalid_idl_3.idl',
  357. ['//',
  358. 'namespace test {',
  359. ' enum MissingComma {',
  360. ' name1',
  361. ' name2',
  362. ' };',
  363. '};'],
  364. 'Unexpected symbol name2 after symbol name1.'),
  365. ('invalid_idl_4.idl',
  366. ['//',
  367. 'namespace test {',
  368. ' enum TrailingComma {',
  369. ' name1,',
  370. ' name2,',
  371. ' };',
  372. '};'],
  373. 'Trailing comma in block.'),
  374. ('invalid_idl_5.idl',
  375. ['//',
  376. 'namespace test {',
  377. ' callback Callback1 = void(;',
  378. '};'],
  379. 'Unexpected ";" after "(".'),
  380. ('invalid_idl_6.idl',
  381. ['//',
  382. 'namespace test {',
  383. ' callback Callback1 = void(long );',
  384. '};'],
  385. 'Unexpected ")" after symbol long.'),
  386. ('invalid_idl_7.idl',
  387. ['//',
  388. 'namespace test {',
  389. ' interace Events {',
  390. ' static void onFoo1();',
  391. ' };',
  392. '};'],
  393. 'Unexpected symbol Events after symbol interace.'),
  394. ('invalid_idl_8.idl',
  395. ['//',
  396. 'namespace test {',
  397. ' interface NotEvent {',
  398. ' static void onFoo1();',
  399. ' };',
  400. '};'],
  401. 'Did not process Interface Interface(NotEvent)'),
  402. ('invalid_idl_9.idl',
  403. ['//',
  404. 'namespace test {',
  405. ' interface {',
  406. ' static void function1();',
  407. ' };',
  408. '};'],
  409. 'Interface missing name.'),
  410. ]
  411. input_api.files = [MockFile(filename, contents)
  412. for (filename, contents, _) in test_data]
  413. for (filename, _, expected_error) in test_data:
  414. actual_error = PRESUBMIT._GetIDLParseError(input_api, filename)
  415. self.assertTrue(expected_error in str(actual_error),
  416. "'%s' not found in '%s'" % (expected_error, actual_error))
  417. class UserMetricsActionTest(unittest.TestCase):
  418. def testUserMetricsActionInActions(self):
  419. input_api = MockInputApi()
  420. file_with_user_action = 'file_with_user_action.cc'
  421. contents_with_user_action = [
  422. 'base::UserMetricsAction("AboutChrome")'
  423. ]
  424. input_api.files = [MockFile(file_with_user_action,
  425. contents_with_user_action)]
  426. self.assertEqual(
  427. [], PRESUBMIT.CheckUserActionUpdate(input_api, MockOutputApi()))
  428. def testUserMetricsActionNotAddedToActions(self):
  429. input_api = MockInputApi()
  430. file_with_user_action = 'file_with_user_action.cc'
  431. contents_with_user_action = [
  432. 'base::UserMetricsAction("NotInActionsXml")'
  433. ]
  434. input_api.files = [MockFile(file_with_user_action,
  435. contents_with_user_action)]
  436. output = PRESUBMIT.CheckUserActionUpdate(input_api, MockOutputApi())
  437. self.assertEqual(
  438. ('File %s line %d: %s is missing in '
  439. 'tools/metrics/actions/actions.xml. Please run '
  440. 'tools/metrics/actions/extract_actions.py to update.'
  441. % (file_with_user_action, 1, 'NotInActionsXml')),
  442. output[0].message)
  443. def testUserMetricsActionInTestFile(self):
  444. input_api = MockInputApi()
  445. file_with_user_action = 'file_with_user_action_unittest.cc'
  446. contents_with_user_action = [
  447. 'base::UserMetricsAction("NotInActionsXml")'
  448. ]
  449. input_api.files = [MockFile(file_with_user_action,
  450. contents_with_user_action)]
  451. self.assertEqual(
  452. [], PRESUBMIT.CheckUserActionUpdate(input_api, MockOutputApi()))
  453. class PydepsNeedsUpdatingTest(unittest.TestCase):
  454. class MockPopen:
  455. def __init__(self, stdout_func):
  456. self._stdout_func = stdout_func
  457. def wait(self):
  458. self.stdout = io.StringIO(self._stdout_func())
  459. return 0
  460. class MockSubprocess:
  461. CalledProcessError = subprocess.CalledProcessError
  462. PIPE = 0
  463. def __init__(self):
  464. self._popen_func = None
  465. def SetPopenCallback(self, func):
  466. self._popen_func = func
  467. def Popen(self, cmd, *args, **kwargs):
  468. return PydepsNeedsUpdatingTest.MockPopen(lambda: self._popen_func(cmd))
  469. def _MockParseGclientArgs(self, is_android=True):
  470. return lambda: {'checkout_android': 'true' if is_android else 'false' }
  471. def setUp(self):
  472. mock_all_pydeps = ['A.pydeps', 'B.pydeps', 'D.pydeps']
  473. self.old_ALL_PYDEPS_FILES = PRESUBMIT._ALL_PYDEPS_FILES
  474. PRESUBMIT._ALL_PYDEPS_FILES = mock_all_pydeps
  475. mock_android_pydeps = ['D.pydeps']
  476. self.old_ANDROID_SPECIFIC_PYDEPS_FILES = (
  477. PRESUBMIT._ANDROID_SPECIFIC_PYDEPS_FILES)
  478. PRESUBMIT._ANDROID_SPECIFIC_PYDEPS_FILES = mock_android_pydeps
  479. self.old_ParseGclientArgs = PRESUBMIT._ParseGclientArgs
  480. PRESUBMIT._ParseGclientArgs = self._MockParseGclientArgs()
  481. self.mock_input_api = MockInputApi()
  482. self.mock_output_api = MockOutputApi()
  483. self.mock_input_api.subprocess = PydepsNeedsUpdatingTest.MockSubprocess()
  484. self.checker = PRESUBMIT.PydepsChecker(self.mock_input_api, mock_all_pydeps)
  485. self.checker._file_cache = {
  486. 'A.pydeps': '# Generated by:\n# CMD --output A.pydeps A\nA.py\nC.py\n',
  487. 'B.pydeps': '# Generated by:\n# CMD --output B.pydeps B\nB.py\nC.py\n',
  488. 'D.pydeps': '# Generated by:\n# CMD --output D.pydeps D\nD.py\n',
  489. }
  490. def tearDown(self):
  491. PRESUBMIT._ALL_PYDEPS_FILES = self.old_ALL_PYDEPS_FILES
  492. PRESUBMIT._ANDROID_SPECIFIC_PYDEPS_FILES = (
  493. self.old_ANDROID_SPECIFIC_PYDEPS_FILES)
  494. PRESUBMIT._ParseGclientArgs = self.old_ParseGclientArgs
  495. def _RunCheck(self):
  496. return PRESUBMIT.CheckPydepsNeedsUpdating(self.mock_input_api,
  497. self.mock_output_api,
  498. checker_for_tests=self.checker)
  499. def testAddedPydep(self):
  500. # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
  501. if not self.mock_input_api.platform.startswith('linux'):
  502. return []
  503. self.mock_input_api.files = [
  504. MockAffectedFile('new.pydeps', [], action='A'),
  505. ]
  506. self.mock_input_api.CreateMockFileInPath(
  507. [x.LocalPath() for x in self.mock_input_api.AffectedFiles(
  508. include_deletes=True)])
  509. results = self._RunCheck()
  510. self.assertEqual(1, len(results))
  511. self.assertIn('PYDEPS_FILES', str(results[0]))
  512. def testPydepNotInSrc(self):
  513. self.mock_input_api.files = [
  514. MockAffectedFile('new.pydeps', [], action='A'),
  515. ]
  516. self.mock_input_api.CreateMockFileInPath([])
  517. results = self._RunCheck()
  518. self.assertEqual(0, len(results))
  519. def testRemovedPydep(self):
  520. # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
  521. if not self.mock_input_api.platform.startswith('linux'):
  522. return []
  523. self.mock_input_api.files = [
  524. MockAffectedFile(PRESUBMIT._ALL_PYDEPS_FILES[0], [], action='D'),
  525. ]
  526. self.mock_input_api.CreateMockFileInPath(
  527. [x.LocalPath() for x in self.mock_input_api.AffectedFiles(
  528. include_deletes=True)])
  529. results = self._RunCheck()
  530. self.assertEqual(1, len(results))
  531. self.assertIn('PYDEPS_FILES', str(results[0]))
  532. def testRandomPyIgnored(self):
  533. # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
  534. if not self.mock_input_api.platform.startswith('linux'):
  535. return []
  536. self.mock_input_api.files = [
  537. MockAffectedFile('random.py', []),
  538. ]
  539. results = self._RunCheck()
  540. self.assertEqual(0, len(results), 'Unexpected results: %r' % results)
  541. def testRelevantPyNoChange(self):
  542. # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
  543. if not self.mock_input_api.platform.startswith('linux'):
  544. return []
  545. self.mock_input_api.files = [
  546. MockAffectedFile('A.py', []),
  547. ]
  548. def popen_callback(cmd):
  549. self.assertEqual('CMD --output A.pydeps A --output ""', cmd)
  550. return self.checker._file_cache['A.pydeps']
  551. self.mock_input_api.subprocess.SetPopenCallback(popen_callback)
  552. results = self._RunCheck()
  553. self.assertEqual(0, len(results), 'Unexpected results: %r' % results)
  554. def testRelevantPyOneChange(self):
  555. # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
  556. if not self.mock_input_api.platform.startswith('linux'):
  557. return []
  558. self.mock_input_api.files = [
  559. MockAffectedFile('A.py', []),
  560. ]
  561. def popen_callback(cmd):
  562. self.assertEqual('CMD --output A.pydeps A --output ""', cmd)
  563. return 'changed data'
  564. self.mock_input_api.subprocess.SetPopenCallback(popen_callback)
  565. results = self._RunCheck()
  566. self.assertEqual(1, len(results))
  567. # Check that --output "" is not included.
  568. self.assertNotIn('""', str(results[0]))
  569. self.assertIn('File is stale', str(results[0]))
  570. def testRelevantPyTwoChanges(self):
  571. # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
  572. if not self.mock_input_api.platform.startswith('linux'):
  573. return []
  574. self.mock_input_api.files = [
  575. MockAffectedFile('C.py', []),
  576. ]
  577. def popen_callback(cmd):
  578. return 'changed data'
  579. self.mock_input_api.subprocess.SetPopenCallback(popen_callback)
  580. results = self._RunCheck()
  581. self.assertEqual(2, len(results))
  582. self.assertIn('File is stale', str(results[0]))
  583. self.assertIn('File is stale', str(results[1]))
  584. def testRelevantAndroidPyInNonAndroidCheckout(self):
  585. # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
  586. if not self.mock_input_api.platform.startswith('linux'):
  587. return []
  588. self.mock_input_api.files = [
  589. MockAffectedFile('D.py', []),
  590. ]
  591. def popen_callback(cmd):
  592. self.assertEqual('CMD --output D.pydeps D --output ""', cmd)
  593. return 'changed data'
  594. self.mock_input_api.subprocess.SetPopenCallback(popen_callback)
  595. PRESUBMIT._ParseGclientArgs = self._MockParseGclientArgs(is_android=False)
  596. results = self._RunCheck()
  597. self.assertEqual(1, len(results))
  598. self.assertIn('Android', str(results[0]))
  599. self.assertIn('D.pydeps', str(results[0]))
  600. def testGnPathsAndMissingOutputFlag(self):
  601. # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
  602. if not self.mock_input_api.platform.startswith('linux'):
  603. return []
  604. self.checker._file_cache = {
  605. 'A.pydeps': '# Generated by:\n# CMD --gn-paths A\n//A.py\n//C.py\n',
  606. 'B.pydeps': '# Generated by:\n# CMD --gn-paths B\n//B.py\n//C.py\n',
  607. 'D.pydeps': '# Generated by:\n# CMD --gn-paths D\n//D.py\n',
  608. }
  609. self.mock_input_api.files = [
  610. MockAffectedFile('A.py', []),
  611. ]
  612. def popen_callback(cmd):
  613. self.assertEqual('CMD --gn-paths A --output A.pydeps --output ""', cmd)
  614. return 'changed data'
  615. self.mock_input_api.subprocess.SetPopenCallback(popen_callback)
  616. results = self._RunCheck()
  617. self.assertEqual(1, len(results))
  618. self.assertIn('File is stale', str(results[0]))
  619. class IncludeGuardTest(unittest.TestCase):
  620. def testIncludeGuardChecks(self):
  621. mock_input_api = MockInputApi()
  622. mock_output_api = MockOutputApi()
  623. mock_input_api.files = [
  624. MockAffectedFile('content/browser/thing/foo.h', [
  625. '// Comment',
  626. '#ifndef CONTENT_BROWSER_THING_FOO_H_',
  627. '#define CONTENT_BROWSER_THING_FOO_H_',
  628. 'struct McBoatFace;',
  629. '#endif // CONTENT_BROWSER_THING_FOO_H_',
  630. ]),
  631. MockAffectedFile('content/browser/thing/bar.h', [
  632. '#ifndef CONTENT_BROWSER_THING_BAR_H_',
  633. '#define CONTENT_BROWSER_THING_BAR_H_',
  634. 'namespace content {',
  635. '#endif // CONTENT_BROWSER_THING_BAR_H_',
  636. '} // namespace content',
  637. ]),
  638. MockAffectedFile('content/browser/test1.h', [
  639. 'namespace content {',
  640. '} // namespace content',
  641. ]),
  642. MockAffectedFile('content\\browser\\win.h', [
  643. '#ifndef CONTENT_BROWSER_WIN_H_',
  644. '#define CONTENT_BROWSER_WIN_H_',
  645. 'struct McBoatFace;',
  646. '#endif // CONTENT_BROWSER_WIN_H_',
  647. ]),
  648. MockAffectedFile('content/browser/test2.h', [
  649. '// Comment',
  650. '#ifndef CONTENT_BROWSER_TEST2_H_',
  651. 'struct McBoatFace;',
  652. '#endif // CONTENT_BROWSER_TEST2_H_',
  653. ]),
  654. MockAffectedFile('content/browser/internal.h', [
  655. '// Comment',
  656. '#ifndef CONTENT_BROWSER_INTERNAL_H_',
  657. '#define CONTENT_BROWSER_INTERNAL_H_',
  658. '// Comment',
  659. '#ifndef INTERNAL_CONTENT_BROWSER_INTERNAL_H_',
  660. '#define INTERNAL_CONTENT_BROWSER_INTERNAL_H_',
  661. 'namespace internal {',
  662. '} // namespace internal',
  663. '#endif // INTERNAL_CONTENT_BROWSER_THING_BAR_H_',
  664. 'namespace content {',
  665. '} // namespace content',
  666. '#endif // CONTENT_BROWSER_THING_BAR_H_',
  667. ]),
  668. MockAffectedFile('content/browser/thing/foo.cc', [
  669. '// This is a non-header.',
  670. ]),
  671. MockAffectedFile('content/browser/disabled.h', [
  672. '// no-include-guard-because-multiply-included',
  673. 'struct McBoatFace;',
  674. ]),
  675. # New files don't allow misspelled include guards.
  676. MockAffectedFile('content/browser/spleling.h', [
  677. '#ifndef CONTENT_BROWSER_SPLLEING_H_',
  678. '#define CONTENT_BROWSER_SPLLEING_H_',
  679. 'struct McBoatFace;',
  680. '#endif // CONTENT_BROWSER_SPLLEING_H_',
  681. ]),
  682. # New files don't allow + in include guards.
  683. MockAffectedFile('content/browser/foo+bar.h', [
  684. '#ifndef CONTENT_BROWSER_FOO+BAR_H_',
  685. '#define CONTENT_BROWSER_FOO+BAR_H_',
  686. 'struct McBoatFace;',
  687. '#endif // CONTENT_BROWSER_FOO+BAR_H_',
  688. ]),
  689. # Old files allow misspelled include guards (for now).
  690. MockAffectedFile('chrome/old.h', [
  691. '// New contents',
  692. '#ifndef CHROME_ODL_H_',
  693. '#define CHROME_ODL_H_',
  694. '#endif // CHROME_ODL_H_',
  695. ], [
  696. '// Old contents',
  697. '#ifndef CHROME_ODL_H_',
  698. '#define CHROME_ODL_H_',
  699. '#endif // CHROME_ODL_H_',
  700. ]),
  701. # Using a Blink style include guard outside Blink is wrong.
  702. MockAffectedFile('content/NotInBlink.h', [
  703. '#ifndef NotInBlink_h',
  704. '#define NotInBlink_h',
  705. 'struct McBoatFace;',
  706. '#endif // NotInBlink_h',
  707. ]),
  708. # Using a Blink style include guard in Blink is no longer ok.
  709. MockAffectedFile('third_party/blink/InBlink.h', [
  710. '#ifndef InBlink_h',
  711. '#define InBlink_h',
  712. 'struct McBoatFace;',
  713. '#endif // InBlink_h',
  714. ]),
  715. # Using a bad include guard in Blink is not ok.
  716. MockAffectedFile('third_party/blink/AlsoInBlink.h', [
  717. '#ifndef WrongInBlink_h',
  718. '#define WrongInBlink_h',
  719. 'struct McBoatFace;',
  720. '#endif // WrongInBlink_h',
  721. ]),
  722. # Using a bad include guard in Blink is not accepted even if
  723. # it's an old file.
  724. MockAffectedFile('third_party/blink/StillInBlink.h', [
  725. '// New contents',
  726. '#ifndef AcceptedInBlink_h',
  727. '#define AcceptedInBlink_h',
  728. 'struct McBoatFace;',
  729. '#endif // AcceptedInBlink_h',
  730. ], [
  731. '// Old contents',
  732. '#ifndef AcceptedInBlink_h',
  733. '#define AcceptedInBlink_h',
  734. 'struct McBoatFace;',
  735. '#endif // AcceptedInBlink_h',
  736. ]),
  737. # Using a non-Chromium include guard in third_party
  738. # (outside blink) is accepted.
  739. MockAffectedFile('third_party/foo/some_file.h', [
  740. '#ifndef REQUIRED_RPCNDR_H_',
  741. '#define REQUIRED_RPCNDR_H_',
  742. 'struct SomeFileFoo;',
  743. '#endif // REQUIRED_RPCNDR_H_',
  744. ]),
  745. # Not having proper include guard in *_message_generator.h
  746. # for old IPC messages is allowed.
  747. MockAffectedFile('content/common/content_message_generator.h', [
  748. '#undef CONTENT_COMMON_FOO_MESSAGES_H_',
  749. '#include "content/common/foo_messages.h"',
  750. '#ifndef CONTENT_COMMON_FOO_MESSAGES_H_',
  751. '#error "Failed to include content/common/foo_messages.h"',
  752. '#endif',
  753. ]),
  754. ]
  755. msgs = PRESUBMIT.CheckForIncludeGuards(
  756. mock_input_api, mock_output_api)
  757. expected_fail_count = 8
  758. self.assertEqual(expected_fail_count, len(msgs),
  759. 'Expected %d items, found %d: %s'
  760. % (expected_fail_count, len(msgs), msgs))
  761. self.assertEqual(msgs[0].items, ['content/browser/thing/bar.h'])
  762. self.assertEqual(msgs[0].message,
  763. 'Include guard CONTENT_BROWSER_THING_BAR_H_ '
  764. 'not covering the whole file')
  765. self.assertIn('content/browser/test1.h', msgs[1].message)
  766. self.assertIn('Recommended name: CONTENT_BROWSER_TEST1_H_',
  767. msgs[1].message)
  768. self.assertEqual(msgs[2].items, ['content/browser/test2.h:3'])
  769. self.assertEqual(msgs[2].message,
  770. 'Missing "#define CONTENT_BROWSER_TEST2_H_" for '
  771. 'include guard')
  772. self.assertEqual(msgs[3].items, ['content/browser/spleling.h:1'])
  773. self.assertEqual(msgs[3].message,
  774. 'Header using the wrong include guard name '
  775. 'CONTENT_BROWSER_SPLLEING_H_')
  776. self.assertIn('content/browser/foo+bar.h', msgs[4].message)
  777. self.assertIn('Recommended name: CONTENT_BROWSER_FOO_BAR_H_',
  778. msgs[4].message)
  779. self.assertEqual(msgs[5].items, ['content/NotInBlink.h:1'])
  780. self.assertEqual(msgs[5].message,
  781. 'Header using the wrong include guard name '
  782. 'NotInBlink_h')
  783. self.assertEqual(msgs[6].items, ['third_party/blink/InBlink.h:1'])
  784. self.assertEqual(msgs[6].message,
  785. 'Header using the wrong include guard name '
  786. 'InBlink_h')
  787. self.assertEqual(msgs[7].items, ['third_party/blink/AlsoInBlink.h:1'])
  788. self.assertEqual(msgs[7].message,
  789. 'Header using the wrong include guard name '
  790. 'WrongInBlink_h')
  791. class AccessibilityRelnotesFieldTest(unittest.TestCase):
  792. def testRelnotesPresent(self):
  793. mock_input_api = MockInputApi()
  794. mock_output_api = MockOutputApi()
  795. mock_input_api.files = [MockAffectedFile('ui/accessibility/foo.bar', [''])]
  796. mock_input_api.change.DescriptionText = lambda : 'Commit description'
  797. mock_input_api.change.footers['AX-Relnotes'] = [
  798. 'Important user facing change']
  799. msgs = PRESUBMIT.CheckAccessibilityRelnotesField(
  800. mock_input_api, mock_output_api)
  801. self.assertEqual(0, len(msgs),
  802. 'Expected %d messages, found %d: %s'
  803. % (0, len(msgs), msgs))
  804. def testRelnotesMissingFromAccessibilityChange(self):
  805. mock_input_api = MockInputApi()
  806. mock_output_api = MockOutputApi()
  807. mock_input_api.files = [
  808. MockAffectedFile('some/file', ['']),
  809. MockAffectedFile('ui/accessibility/foo.bar', ['']),
  810. MockAffectedFile('some/other/file', [''])
  811. ]
  812. mock_input_api.change.DescriptionText = lambda : 'Commit description'
  813. msgs = PRESUBMIT.CheckAccessibilityRelnotesField(
  814. mock_input_api, mock_output_api)
  815. self.assertEqual(1, len(msgs),
  816. 'Expected %d messages, found %d: %s'
  817. % (1, len(msgs), msgs))
  818. self.assertTrue("Missing 'AX-Relnotes:' field" in msgs[0].message,
  819. 'Missing AX-Relnotes field message not found in errors')
  820. # The relnotes footer is not required for changes which do not touch any
  821. # accessibility directories.
  822. def testIgnoresNonAccessibilityCode(self):
  823. mock_input_api = MockInputApi()
  824. mock_output_api = MockOutputApi()
  825. mock_input_api.files = [
  826. MockAffectedFile('some/file', ['']),
  827. MockAffectedFile('some/other/file', [''])
  828. ]
  829. mock_input_api.change.DescriptionText = lambda : 'Commit description'
  830. msgs = PRESUBMIT.CheckAccessibilityRelnotesField(
  831. mock_input_api, mock_output_api)
  832. self.assertEqual(0, len(msgs),
  833. 'Expected %d messages, found %d: %s'
  834. % (0, len(msgs), msgs))
  835. # Test that our presubmit correctly raises an error for a set of known paths.
  836. def testExpectedPaths(self):
  837. filesToTest = [
  838. "chrome/browser/accessibility/foo.py",
  839. "chrome/browser/ash/arc/accessibility/foo.cc",
  840. "chrome/browser/ui/views/accessibility/foo.h",
  841. "chrome/browser/extensions/api/automation/foo.h",
  842. "chrome/browser/extensions/api/automation_internal/foo.cc",
  843. "chrome/renderer/extensions/accessibility_foo.h",
  844. "chrome/tests/data/accessibility/foo.html",
  845. "content/browser/accessibility/foo.cc",
  846. "content/renderer/accessibility/foo.h",
  847. "content/tests/data/accessibility/foo.cc",
  848. "extensions/renderer/api/automation/foo.h",
  849. "ui/accessibility/foo/bar/baz.cc",
  850. "ui/views/accessibility/foo/bar/baz.h",
  851. ]
  852. for testFile in filesToTest:
  853. mock_input_api = MockInputApi()
  854. mock_output_api = MockOutputApi()
  855. mock_input_api.files = [
  856. MockAffectedFile(testFile, [''])
  857. ]
  858. mock_input_api.change.DescriptionText = lambda : 'Commit description'
  859. msgs = PRESUBMIT.CheckAccessibilityRelnotesField(
  860. mock_input_api, mock_output_api)
  861. self.assertEqual(1, len(msgs),
  862. 'Expected %d messages, found %d: %s, for file %s'
  863. % (1, len(msgs), msgs, testFile))
  864. self.assertTrue("Missing 'AX-Relnotes:' field" in msgs[0].message,
  865. ('Missing AX-Relnotes field message not found in errors '
  866. ' for file %s' % (testFile)))
  867. # Test that AX-Relnotes field can appear in the commit description (as long
  868. # as it appears at the beginning of a line).
  869. def testRelnotesInCommitDescription(self):
  870. mock_input_api = MockInputApi()
  871. mock_output_api = MockOutputApi()
  872. mock_input_api.files = [
  873. MockAffectedFile('ui/accessibility/foo.bar', ['']),
  874. ]
  875. mock_input_api.change.DescriptionText = lambda : ('Description:\n' +
  876. 'AX-Relnotes: solves all accessibility issues forever')
  877. msgs = PRESUBMIT.CheckAccessibilityRelnotesField(
  878. mock_input_api, mock_output_api)
  879. self.assertEqual(0, len(msgs),
  880. 'Expected %d messages, found %d: %s'
  881. % (0, len(msgs), msgs))
  882. # Test that we don't match AX-Relnotes if it appears in the middle of a line.
  883. def testRelnotesMustAppearAtBeginningOfLine(self):
  884. mock_input_api = MockInputApi()
  885. mock_output_api = MockOutputApi()
  886. mock_input_api.files = [
  887. MockAffectedFile('ui/accessibility/foo.bar', ['']),
  888. ]
  889. mock_input_api.change.DescriptionText = lambda : ('Description:\n' +
  890. 'This change has no AX-Relnotes: we should print a warning')
  891. msgs = PRESUBMIT.CheckAccessibilityRelnotesField(
  892. mock_input_api, mock_output_api)
  893. self.assertTrue("Missing 'AX-Relnotes:' field" in msgs[0].message,
  894. 'Missing AX-Relnotes field message not found in errors')
  895. # Tests that the AX-Relnotes field can be lowercase and use a '=' in place
  896. # of a ':'.
  897. def testRelnotesLowercaseWithEqualSign(self):
  898. mock_input_api = MockInputApi()
  899. mock_output_api = MockOutputApi()
  900. mock_input_api.files = [
  901. MockAffectedFile('ui/accessibility/foo.bar', ['']),
  902. ]
  903. mock_input_api.change.DescriptionText = lambda : ('Description:\n' +
  904. 'ax-relnotes= this is a valid format for accessibility relnotes')
  905. msgs = PRESUBMIT.CheckAccessibilityRelnotesField(
  906. mock_input_api, mock_output_api)
  907. self.assertEqual(0, len(msgs),
  908. 'Expected %d messages, found %d: %s'
  909. % (0, len(msgs), msgs))
  910. class AccessibilityEventsTestsAreIncludedForAndroidTest(unittest.TestCase):
  911. # Test that no warning is raised when the Android file is also modified.
  912. def testAndroidChangeIncluded(self):
  913. mock_input_api = MockInputApi()
  914. mock_input_api.files = [
  915. MockAffectedFile('content/test/data/accessibility/event/foo.html',
  916. [''], action='A'),
  917. MockAffectedFile(
  918. 'accessibility/WebContentsAccessibilityEventsTest.java',
  919. [''], action='M')
  920. ]
  921. msgs = PRESUBMIT.CheckAccessibilityEventsTestsAreIncludedForAndroid(
  922. mock_input_api, MockOutputApi())
  923. self.assertEqual(0, len(msgs),
  924. 'Expected %d messages, found %d: %s'
  925. % (0, len(msgs), msgs))
  926. # Test that a warning is raised when the Android file is not modified.
  927. def testAndroidChangeMissing(self):
  928. mock_input_api = MockInputApi()
  929. mock_input_api.files = [
  930. MockAffectedFile('content/test/data/accessibility/event/foo.html',
  931. [''], action='A'),
  932. ]
  933. msgs = PRESUBMIT.CheckAccessibilityEventsTestsAreIncludedForAndroid(
  934. mock_input_api, MockOutputApi())
  935. self.assertEqual(1, len(msgs),
  936. 'Expected %d messages, found %d: %s'
  937. % (1, len(msgs), msgs))
  938. # Test that Android change is not required when no html file is added/removed.
  939. def testIgnoreNonHtmlFiles(self):
  940. mock_input_api = MockInputApi()
  941. mock_input_api.files = [
  942. MockAffectedFile('content/test/data/accessibility/event/foo.txt',
  943. [''], action='A'),
  944. MockAffectedFile('content/test/data/accessibility/event/foo.cc',
  945. [''], action='A'),
  946. MockAffectedFile('content/test/data/accessibility/event/foo.h',
  947. [''], action='A'),
  948. MockAffectedFile('content/test/data/accessibility/event/foo.py',
  949. [''], action='A')
  950. ]
  951. msgs = PRESUBMIT.CheckAccessibilityEventsTestsAreIncludedForAndroid(
  952. mock_input_api, MockOutputApi())
  953. self.assertEqual(0, len(msgs),
  954. 'Expected %d messages, found %d: %s'
  955. % (0, len(msgs), msgs))
  956. # Test that Android change is not required for unrelated html files.
  957. def testIgnoreNonRelatedHtmlFiles(self):
  958. mock_input_api = MockInputApi()
  959. mock_input_api.files = [
  960. MockAffectedFile('content/test/data/accessibility/aria/foo.html',
  961. [''], action='A'),
  962. MockAffectedFile('content/test/data/accessibility/html/foo.html',
  963. [''], action='A'),
  964. MockAffectedFile('chrome/tests/data/accessibility/foo.html',
  965. [''], action='A')
  966. ]
  967. msgs = PRESUBMIT.CheckAccessibilityEventsTestsAreIncludedForAndroid(
  968. mock_input_api, MockOutputApi())
  969. self.assertEqual(0, len(msgs),
  970. 'Expected %d messages, found %d: %s'
  971. % (0, len(msgs), msgs))
  972. # Test that only modifying an html file will not trigger the warning.
  973. def testIgnoreModifiedFiles(self):
  974. mock_input_api = MockInputApi()
  975. mock_input_api.files = [
  976. MockAffectedFile('content/test/data/accessibility/event/foo.html',
  977. [''], action='M')
  978. ]
  979. msgs = PRESUBMIT.CheckAccessibilityEventsTestsAreIncludedForAndroid(
  980. mock_input_api, MockOutputApi())
  981. self.assertEqual(0, len(msgs),
  982. 'Expected %d messages, found %d: %s'
  983. % (0, len(msgs), msgs))
  984. # Test that deleting an html file will trigger the warning.
  985. def testAndroidChangeMissingOnDeletedFile(self):
  986. mock_input_api = MockInputApi()
  987. mock_input_api.files = [
  988. MockAffectedFile('content/test/data/accessibility/event/foo.html',
  989. [], action='D')
  990. ]
  991. msgs = PRESUBMIT.CheckAccessibilityEventsTestsAreIncludedForAndroid(
  992. mock_input_api, MockOutputApi())
  993. self.assertEqual(1, len(msgs),
  994. 'Expected %d messages, found %d: %s'
  995. % (1, len(msgs), msgs))
  996. class AccessibilityTreeTestsAreIncludedForAndroidTest(unittest.TestCase):
  997. # Test that no warning is raised when the Android file is also modified.
  998. def testAndroidChangeIncluded(self):
  999. mock_input_api = MockInputApi()
  1000. mock_input_api.files = [
  1001. MockAffectedFile('content/test/data/accessibility/aria/foo.html',
  1002. [''], action='A'),
  1003. MockAffectedFile(
  1004. 'accessibility/WebContentsAccessibilityTreeTest.java',
  1005. [''], action='M')
  1006. ]
  1007. msgs = PRESUBMIT.CheckAccessibilityTreeTestsAreIncludedForAndroid(
  1008. mock_input_api, MockOutputApi())
  1009. self.assertEqual(0, len(msgs),
  1010. 'Expected %d messages, found %d: %s'
  1011. % (0, len(msgs), msgs))
  1012. # Test that no warning is raised when the Android file is also modified.
  1013. def testAndroidChangeIncludedManyFiles(self):
  1014. mock_input_api = MockInputApi()
  1015. mock_input_api.files = [
  1016. MockAffectedFile('content/test/data/accessibility/accname/foo.html',
  1017. [''], action='A'),
  1018. MockAffectedFile('content/test/data/accessibility/aria/foo.html',
  1019. [''], action='A'),
  1020. MockAffectedFile('content/test/data/accessibility/css/foo.html',
  1021. [''], action='A'),
  1022. MockAffectedFile('content/test/data/accessibility/html/foo.html',
  1023. [''], action='A'),
  1024. MockAffectedFile(
  1025. 'accessibility/WebContentsAccessibilityTreeTest.java',
  1026. [''], action='M')
  1027. ]
  1028. msgs = PRESUBMIT.CheckAccessibilityTreeTestsAreIncludedForAndroid(
  1029. mock_input_api, MockOutputApi())
  1030. self.assertEqual(0, len(msgs),
  1031. 'Expected %d messages, found %d: %s'
  1032. % (0, len(msgs), msgs))
  1033. # Test that a warning is raised when the Android file is not modified.
  1034. def testAndroidChangeMissing(self):
  1035. mock_input_api = MockInputApi()
  1036. mock_input_api.files = [
  1037. MockAffectedFile('content/test/data/accessibility/aria/foo.html',
  1038. [''], action='A'),
  1039. ]
  1040. msgs = PRESUBMIT.CheckAccessibilityTreeTestsAreIncludedForAndroid(
  1041. mock_input_api, MockOutputApi())
  1042. self.assertEqual(1, len(msgs),
  1043. 'Expected %d messages, found %d: %s'
  1044. % (1, len(msgs), msgs))
  1045. # Test that Android change is not required when no html file is added/removed.
  1046. def testIgnoreNonHtmlFiles(self):
  1047. mock_input_api = MockInputApi()
  1048. mock_input_api.files = [
  1049. MockAffectedFile('content/test/data/accessibility/accname/foo.txt',
  1050. [''], action='A'),
  1051. MockAffectedFile('content/test/data/accessibility/aria/foo.cc',
  1052. [''], action='A'),
  1053. MockAffectedFile('content/test/data/accessibility/css/foo.h',
  1054. [''], action='A'),
  1055. MockAffectedFile('content/test/data/accessibility/tree/foo.py',
  1056. [''], action='A')
  1057. ]
  1058. msgs = PRESUBMIT.CheckAccessibilityTreeTestsAreIncludedForAndroid(
  1059. mock_input_api, MockOutputApi())
  1060. self.assertEqual(0, len(msgs),
  1061. 'Expected %d messages, found %d: %s'
  1062. % (0, len(msgs), msgs))
  1063. # Test that Android change is not required for unrelated html files.
  1064. def testIgnoreNonRelatedHtmlFiles(self):
  1065. mock_input_api = MockInputApi()
  1066. mock_input_api.files = [
  1067. MockAffectedFile('content/test/data/accessibility/event/foo.html',
  1068. [''], action='A'),
  1069. ]
  1070. msgs = PRESUBMIT.CheckAccessibilityTreeTestsAreIncludedForAndroid(
  1071. mock_input_api, MockOutputApi())
  1072. self.assertEqual(0, len(msgs),
  1073. 'Expected %d messages, found %d: %s'
  1074. % (0, len(msgs), msgs))
  1075. # Test that only modifying an html file will not trigger the warning.
  1076. def testIgnoreModifiedFiles(self):
  1077. mock_input_api = MockInputApi()
  1078. mock_input_api.files = [
  1079. MockAffectedFile('content/test/data/accessibility/aria/foo.html',
  1080. [''], action='M')
  1081. ]
  1082. msgs = PRESUBMIT.CheckAccessibilityTreeTestsAreIncludedForAndroid(
  1083. mock_input_api, MockOutputApi())
  1084. self.assertEqual(0, len(msgs),
  1085. 'Expected %d messages, found %d: %s'
  1086. % (0, len(msgs), msgs))
  1087. # Test that deleting an html file will trigger the warning.
  1088. def testAndroidChangeMissingOnDeletedFile(self):
  1089. mock_input_api = MockInputApi()
  1090. mock_input_api.files = [
  1091. MockAffectedFile('content/test/data/accessibility/accname/foo.html',
  1092. [], action='D')
  1093. ]
  1094. msgs = PRESUBMIT.CheckAccessibilityTreeTestsAreIncludedForAndroid(
  1095. mock_input_api, MockOutputApi())
  1096. self.assertEqual(1, len(msgs),
  1097. 'Expected %d messages, found %d: %s'
  1098. % (1, len(msgs), msgs))
  1099. class AndroidDeprecatedTestAnnotationTest(unittest.TestCase):
  1100. def testCheckAndroidTestAnnotationUsage(self):
  1101. mock_input_api = MockInputApi()
  1102. mock_output_api = MockOutputApi()
  1103. mock_input_api.files = [
  1104. MockAffectedFile('LalaLand.java', [
  1105. 'random stuff'
  1106. ]),
  1107. MockAffectedFile('CorrectUsage.java', [
  1108. 'import android.support.test.filters.LargeTest;',
  1109. 'import android.support.test.filters.MediumTest;',
  1110. 'import android.support.test.filters.SmallTest;',
  1111. ]),
  1112. MockAffectedFile('UsedDeprecatedLargeTestAnnotation.java', [
  1113. 'import android.test.suitebuilder.annotation.LargeTest;',
  1114. ]),
  1115. MockAffectedFile('UsedDeprecatedMediumTestAnnotation.java', [
  1116. 'import android.test.suitebuilder.annotation.MediumTest;',
  1117. ]),
  1118. MockAffectedFile('UsedDeprecatedSmallTestAnnotation.java', [
  1119. 'import android.test.suitebuilder.annotation.SmallTest;',
  1120. ]),
  1121. MockAffectedFile('UsedDeprecatedSmokeAnnotation.java', [
  1122. 'import android.test.suitebuilder.annotation.Smoke;',
  1123. ])
  1124. ]
  1125. msgs = PRESUBMIT._CheckAndroidTestAnnotationUsage(
  1126. mock_input_api, mock_output_api)
  1127. self.assertEqual(1, len(msgs),
  1128. 'Expected %d items, found %d: %s'
  1129. % (1, len(msgs), msgs))
  1130. self.assertEqual(4, len(msgs[0].items),
  1131. 'Expected %d items, found %d: %s'
  1132. % (4, len(msgs[0].items), msgs[0].items))
  1133. self.assertTrue('UsedDeprecatedLargeTestAnnotation.java:1' in msgs[0].items,
  1134. 'UsedDeprecatedLargeTestAnnotation not found in errors')
  1135. self.assertTrue('UsedDeprecatedMediumTestAnnotation.java:1'
  1136. in msgs[0].items,
  1137. 'UsedDeprecatedMediumTestAnnotation not found in errors')
  1138. self.assertTrue('UsedDeprecatedSmallTestAnnotation.java:1' in msgs[0].items,
  1139. 'UsedDeprecatedSmallTestAnnotation not found in errors')
  1140. self.assertTrue('UsedDeprecatedSmokeAnnotation.java:1' in msgs[0].items,
  1141. 'UsedDeprecatedSmokeAnnotation not found in errors')
  1142. class CheckNoDownstreamDepsTest(unittest.TestCase):
  1143. def testInvalidDepFromUpstream(self):
  1144. mock_input_api = MockInputApi()
  1145. mock_output_api = MockOutputApi()
  1146. mock_input_api.files = [
  1147. MockAffectedFile('BUILD.gn', [
  1148. 'deps = [',
  1149. ' "//clank/target:test",',
  1150. ']'
  1151. ]),
  1152. MockAffectedFile('chrome/android/BUILD.gn', [
  1153. 'deps = [ "//clank/target:test" ]'
  1154. ]),
  1155. MockAffectedFile('chrome/chrome_java_deps.gni', [
  1156. 'java_deps = [',
  1157. ' "//clank/target:test",',
  1158. ']'
  1159. ]),
  1160. ]
  1161. mock_input_api.change.RepositoryRoot = lambda: 'chromium/src'
  1162. msgs = PRESUBMIT.CheckNoUpstreamDepsOnClank(
  1163. mock_input_api, mock_output_api)
  1164. self.assertEqual(1, len(msgs),
  1165. 'Expected %d items, found %d: %s'
  1166. % (1, len(msgs), msgs))
  1167. self.assertEqual(3, len(msgs[0].items),
  1168. 'Expected %d items, found %d: %s'
  1169. % (3, len(msgs[0].items), msgs[0].items))
  1170. self.assertTrue(any('BUILD.gn:2' in item for item in msgs[0].items),
  1171. 'BUILD.gn not found in errors')
  1172. self.assertTrue(
  1173. any('chrome/android/BUILD.gn:1' in item for item in msgs[0].items),
  1174. 'chrome/android/BUILD.gn:1 not found in errors')
  1175. self.assertTrue(
  1176. any('chrome/chrome_java_deps.gni:2' in item for item in msgs[0].items),
  1177. 'chrome/chrome_java_deps.gni:2 not found in errors')
  1178. def testAllowsComments(self):
  1179. mock_input_api = MockInputApi()
  1180. mock_output_api = MockOutputApi()
  1181. mock_input_api.files = [
  1182. MockAffectedFile('BUILD.gn', [
  1183. '# real implementation in //clank/target:test',
  1184. ]),
  1185. ]
  1186. mock_input_api.change.RepositoryRoot = lambda: 'chromium/src'
  1187. msgs = PRESUBMIT.CheckNoUpstreamDepsOnClank(
  1188. mock_input_api, mock_output_api)
  1189. self.assertEqual(0, len(msgs),
  1190. 'Expected %d items, found %d: %s'
  1191. % (0, len(msgs), msgs))
  1192. def testOnlyChecksBuildFiles(self):
  1193. mock_input_api = MockInputApi()
  1194. mock_output_api = MockOutputApi()
  1195. mock_input_api.files = [
  1196. MockAffectedFile('README.md', [
  1197. 'DEPS = [ "//clank/target:test" ]'
  1198. ]),
  1199. MockAffectedFile('chrome/android/java/file.java', [
  1200. '//clank/ only function'
  1201. ]),
  1202. ]
  1203. mock_input_api.change.RepositoryRoot = lambda: 'chromium/src'
  1204. msgs = PRESUBMIT.CheckNoUpstreamDepsOnClank(
  1205. mock_input_api, mock_output_api)
  1206. self.assertEqual(0, len(msgs),
  1207. 'Expected %d items, found %d: %s'
  1208. % (0, len(msgs), msgs))
  1209. def testValidDepFromDownstream(self):
  1210. mock_input_api = MockInputApi()
  1211. mock_output_api = MockOutputApi()
  1212. mock_input_api.files = [
  1213. MockAffectedFile('BUILD.gn', [
  1214. 'DEPS = [',
  1215. ' "//clank/target:test",',
  1216. ']'
  1217. ]),
  1218. MockAffectedFile('java/BUILD.gn', [
  1219. 'DEPS = [ "//clank/target:test" ]'
  1220. ]),
  1221. ]
  1222. mock_input_api.change.RepositoryRoot = lambda: 'chromium/src/clank'
  1223. msgs = PRESUBMIT.CheckNoUpstreamDepsOnClank(
  1224. mock_input_api, mock_output_api)
  1225. self.assertEqual(0, len(msgs),
  1226. 'Expected %d items, found %d: %s'
  1227. % (0, len(msgs), msgs))
  1228. class AndroidDeprecatedJUnitFrameworkTest(unittest.TestCase):
  1229. def testCheckAndroidTestJUnitFramework(self):
  1230. mock_input_api = MockInputApi()
  1231. mock_output_api = MockOutputApi()
  1232. mock_input_api.files = [
  1233. MockAffectedFile('LalaLand.java', [
  1234. 'random stuff'
  1235. ]),
  1236. MockAffectedFile('CorrectUsage.java', [
  1237. 'import org.junit.ABC',
  1238. 'import org.junit.XYZ;',
  1239. ]),
  1240. MockAffectedFile('UsedDeprecatedJUnit.java', [
  1241. 'import junit.framework.*;',
  1242. ]),
  1243. MockAffectedFile('UsedDeprecatedJUnitAssert.java', [
  1244. 'import junit.framework.Assert;',
  1245. ]),
  1246. ]
  1247. msgs = PRESUBMIT._CheckAndroidTestJUnitFrameworkImport(
  1248. mock_input_api, mock_output_api)
  1249. self.assertEqual(1, len(msgs),
  1250. 'Expected %d items, found %d: %s'
  1251. % (1, len(msgs), msgs))
  1252. self.assertEqual(2, len(msgs[0].items),
  1253. 'Expected %d items, found %d: %s'
  1254. % (2, len(msgs[0].items), msgs[0].items))
  1255. self.assertTrue('UsedDeprecatedJUnit.java:1' in msgs[0].items,
  1256. 'UsedDeprecatedJUnit.java not found in errors')
  1257. self.assertTrue('UsedDeprecatedJUnitAssert.java:1'
  1258. in msgs[0].items,
  1259. 'UsedDeprecatedJUnitAssert not found in errors')
  1260. class AndroidJUnitBaseClassTest(unittest.TestCase):
  1261. def testCheckAndroidTestJUnitBaseClass(self):
  1262. mock_input_api = MockInputApi()
  1263. mock_output_api = MockOutputApi()
  1264. mock_input_api.files = [
  1265. MockAffectedFile('LalaLand.java', [
  1266. 'random stuff'
  1267. ]),
  1268. MockAffectedFile('CorrectTest.java', [
  1269. '@RunWith(ABC.class);'
  1270. 'public class CorrectTest {',
  1271. '}',
  1272. ]),
  1273. MockAffectedFile('HistoricallyIncorrectTest.java', [
  1274. 'public class Test extends BaseCaseA {',
  1275. '}',
  1276. ], old_contents=[
  1277. 'public class Test extends BaseCaseB {',
  1278. '}',
  1279. ]),
  1280. MockAffectedFile('CorrectTestWithInterface.java', [
  1281. '@RunWith(ABC.class);'
  1282. 'public class CorrectTest implement Interface {',
  1283. '}',
  1284. ]),
  1285. MockAffectedFile('IncorrectTest.java', [
  1286. 'public class IncorrectTest extends TestCase {',
  1287. '}',
  1288. ]),
  1289. MockAffectedFile('IncorrectWithInterfaceTest.java', [
  1290. 'public class Test implements X extends BaseClass {',
  1291. '}',
  1292. ]),
  1293. MockAffectedFile('IncorrectMultiLineTest.java', [
  1294. 'public class Test implements X, Y, Z',
  1295. ' extends TestBase {',
  1296. '}',
  1297. ]),
  1298. ]
  1299. msgs = PRESUBMIT._CheckAndroidTestJUnitInheritance(
  1300. mock_input_api, mock_output_api)
  1301. self.assertEqual(1, len(msgs),
  1302. 'Expected %d items, found %d: %s'
  1303. % (1, len(msgs), msgs))
  1304. self.assertEqual(3, len(msgs[0].items),
  1305. 'Expected %d items, found %d: %s'
  1306. % (3, len(msgs[0].items), msgs[0].items))
  1307. self.assertTrue('IncorrectTest.java:1' in msgs[0].items,
  1308. 'IncorrectTest not found in errors')
  1309. self.assertTrue('IncorrectWithInterfaceTest.java:1'
  1310. in msgs[0].items,
  1311. 'IncorrectWithInterfaceTest not found in errors')
  1312. self.assertTrue('IncorrectMultiLineTest.java:2' in msgs[0].items,
  1313. 'IncorrectMultiLineTest not found in errors')
  1314. class AndroidDebuggableBuildTest(unittest.TestCase):
  1315. def testCheckAndroidDebuggableBuild(self):
  1316. mock_input_api = MockInputApi()
  1317. mock_output_api = MockOutputApi()
  1318. mock_input_api.files = [
  1319. MockAffectedFile('RandomStuff.java', [
  1320. 'random stuff'
  1321. ]),
  1322. MockAffectedFile('CorrectUsage.java', [
  1323. 'import org.chromium.base.BuildInfo;',
  1324. 'some random stuff',
  1325. 'boolean isOsDebuggable = BuildInfo.isDebugAndroid();',
  1326. ]),
  1327. MockAffectedFile('JustCheckUserdebugBuild.java', [
  1328. 'import android.os.Build;',
  1329. 'some random stuff',
  1330. 'boolean isOsDebuggable = Build.TYPE.equals("userdebug")',
  1331. ]),
  1332. MockAffectedFile('JustCheckEngineeringBuild.java', [
  1333. 'import android.os.Build;',
  1334. 'some random stuff',
  1335. 'boolean isOsDebuggable = "eng".equals(Build.TYPE)',
  1336. ]),
  1337. MockAffectedFile('UsedBuildType.java', [
  1338. 'import android.os.Build;',
  1339. 'some random stuff',
  1340. 'boolean isOsDebuggable = Build.TYPE.equals("userdebug")'
  1341. '|| "eng".equals(Build.TYPE)',
  1342. ]),
  1343. MockAffectedFile('UsedExplicitBuildType.java', [
  1344. 'some random stuff',
  1345. 'boolean isOsDebuggable = android.os.Build.TYPE.equals("userdebug")'
  1346. '|| "eng".equals(android.os.Build.TYPE)',
  1347. ]),
  1348. ]
  1349. msgs = PRESUBMIT._CheckAndroidDebuggableBuild(
  1350. mock_input_api, mock_output_api)
  1351. self.assertEqual(1, len(msgs),
  1352. 'Expected %d items, found %d: %s'
  1353. % (1, len(msgs), msgs))
  1354. self.assertEqual(4, len(msgs[0].items),
  1355. 'Expected %d items, found %d: %s'
  1356. % (4, len(msgs[0].items), msgs[0].items))
  1357. self.assertTrue('JustCheckUserdebugBuild.java:3' in msgs[0].items)
  1358. self.assertTrue('JustCheckEngineeringBuild.java:3' in msgs[0].items)
  1359. self.assertTrue('UsedBuildType.java:3' in msgs[0].items)
  1360. self.assertTrue('UsedExplicitBuildType.java:2' in msgs[0].items)
  1361. class LogUsageTest(unittest.TestCase):
  1362. def testCheckAndroidCrLogUsage(self):
  1363. mock_input_api = MockInputApi()
  1364. mock_output_api = MockOutputApi()
  1365. mock_input_api.files = [
  1366. MockAffectedFile('RandomStuff.java', [
  1367. 'random stuff'
  1368. ]),
  1369. MockAffectedFile('HasAndroidLog.java', [
  1370. 'import android.util.Log;',
  1371. 'some random stuff',
  1372. 'Log.d("TAG", "foo");',
  1373. ]),
  1374. MockAffectedFile('HasExplicitUtilLog.java', [
  1375. 'some random stuff',
  1376. 'android.util.Log.d("TAG", "foo");',
  1377. ]),
  1378. MockAffectedFile('IsInBasePackage.java', [
  1379. 'package org.chromium.base;',
  1380. 'private static final String TAG = "cr_Foo";',
  1381. 'Log.d(TAG, "foo");',
  1382. ]),
  1383. MockAffectedFile('IsInBasePackageButImportsLog.java', [
  1384. 'package org.chromium.base;',
  1385. 'import android.util.Log;',
  1386. 'private static final String TAG = "cr_Foo";',
  1387. 'Log.d(TAG, "foo");',
  1388. ]),
  1389. MockAffectedFile('HasBothLog.java', [
  1390. 'import org.chromium.base.Log;',
  1391. 'some random stuff',
  1392. 'private static final String TAG = "cr_Foo";',
  1393. 'Log.d(TAG, "foo");',
  1394. 'android.util.Log.d("TAG", "foo");',
  1395. ]),
  1396. MockAffectedFile('HasCorrectTag.java', [
  1397. 'import org.chromium.base.Log;',
  1398. 'some random stuff',
  1399. 'private static final String TAG = "cr_Foo";',
  1400. 'Log.d(TAG, "foo");',
  1401. ]),
  1402. MockAffectedFile('HasOldTag.java', [
  1403. 'import org.chromium.base.Log;',
  1404. 'some random stuff',
  1405. 'private static final String TAG = "cr.Foo";',
  1406. 'Log.d(TAG, "foo");',
  1407. ]),
  1408. MockAffectedFile('HasDottedTag.java', [
  1409. 'import org.chromium.base.Log;',
  1410. 'some random stuff',
  1411. 'private static final String TAG = "cr_foo.bar";',
  1412. 'Log.d(TAG, "foo");',
  1413. ]),
  1414. MockAffectedFile('HasDottedTagPublic.java', [
  1415. 'import org.chromium.base.Log;',
  1416. 'some random stuff',
  1417. 'public static final String TAG = "cr_foo.bar";',
  1418. 'Log.d(TAG, "foo");',
  1419. ]),
  1420. MockAffectedFile('HasNoTagDecl.java', [
  1421. 'import org.chromium.base.Log;',
  1422. 'some random stuff',
  1423. 'Log.d(TAG, "foo");',
  1424. ]),
  1425. MockAffectedFile('HasIncorrectTagDecl.java', [
  1426. 'import org.chromium.base.Log;',
  1427. 'private static final String TAHG = "cr_Foo";',
  1428. 'some random stuff',
  1429. 'Log.d(TAG, "foo");',
  1430. ]),
  1431. MockAffectedFile('HasInlineTag.java', [
  1432. 'import org.chromium.base.Log;',
  1433. 'some random stuff',
  1434. 'private static final String TAG = "cr_Foo";',
  1435. 'Log.d("TAG", "foo");',
  1436. ]),
  1437. MockAffectedFile('HasInlineTagWithSpace.java', [
  1438. 'import org.chromium.base.Log;',
  1439. 'some random stuff',
  1440. 'private static final String TAG = "cr_Foo";',
  1441. 'Log.d("log message", "foo");',
  1442. ]),
  1443. MockAffectedFile('HasUnprefixedTag.java', [
  1444. 'import org.chromium.base.Log;',
  1445. 'some random stuff',
  1446. 'private static final String TAG = "rubbish";',
  1447. 'Log.d(TAG, "foo");',
  1448. ]),
  1449. MockAffectedFile('HasTooLongTag.java', [
  1450. 'import org.chromium.base.Log;',
  1451. 'some random stuff',
  1452. 'private static final String TAG = "21_characters_long___";',
  1453. 'Log.d(TAG, "foo");',
  1454. ]),
  1455. MockAffectedFile('HasTooLongTagWithNoLogCallsInDiff.java', [
  1456. 'import org.chromium.base.Log;',
  1457. 'some random stuff',
  1458. 'private static final String TAG = "21_characters_long___";',
  1459. ]),
  1460. ]
  1461. msgs = PRESUBMIT._CheckAndroidCrLogUsage(
  1462. mock_input_api, mock_output_api)
  1463. self.assertEqual(5, len(msgs),
  1464. 'Expected %d items, found %d: %s' % (5, len(msgs), msgs))
  1465. # Declaration format
  1466. nb = len(msgs[0].items)
  1467. self.assertEqual(2, nb,
  1468. 'Expected %d items, found %d: %s' % (2, nb, msgs[0].items))
  1469. self.assertTrue('HasNoTagDecl.java' in msgs[0].items)
  1470. self.assertTrue('HasIncorrectTagDecl.java' in msgs[0].items)
  1471. # Tag length
  1472. nb = len(msgs[1].items)
  1473. self.assertEqual(2, nb,
  1474. 'Expected %d items, found %d: %s' % (2, nb, msgs[1].items))
  1475. self.assertTrue('HasTooLongTag.java' in msgs[1].items)
  1476. self.assertTrue('HasTooLongTagWithNoLogCallsInDiff.java' in msgs[1].items)
  1477. # Tag must be a variable named TAG
  1478. nb = len(msgs[2].items)
  1479. self.assertEqual(3, nb,
  1480. 'Expected %d items, found %d: %s' % (3, nb, msgs[2].items))
  1481. self.assertTrue('HasBothLog.java:5' in msgs[2].items)
  1482. self.assertTrue('HasInlineTag.java:4' in msgs[2].items)
  1483. self.assertTrue('HasInlineTagWithSpace.java:4' in msgs[2].items)
  1484. # Util Log usage
  1485. nb = len(msgs[3].items)
  1486. self.assertEqual(3, nb,
  1487. 'Expected %d items, found %d: %s' % (3, nb, msgs[3].items))
  1488. self.assertTrue('HasAndroidLog.java:3' in msgs[3].items)
  1489. self.assertTrue('HasExplicitUtilLog.java:2' in msgs[3].items)
  1490. self.assertTrue('IsInBasePackageButImportsLog.java:4' in msgs[3].items)
  1491. # Tag must not contain
  1492. nb = len(msgs[4].items)
  1493. self.assertEqual(3, nb,
  1494. 'Expected %d items, found %d: %s' % (2, nb, msgs[4].items))
  1495. self.assertTrue('HasDottedTag.java' in msgs[4].items)
  1496. self.assertTrue('HasDottedTagPublic.java' in msgs[4].items)
  1497. self.assertTrue('HasOldTag.java' in msgs[4].items)
  1498. class GoogleAnswerUrlFormatTest(unittest.TestCase):
  1499. def testCatchAnswerUrlId(self):
  1500. input_api = MockInputApi()
  1501. input_api.files = [
  1502. MockFile('somewhere/file.cc',
  1503. ['char* host = '
  1504. ' "https://support.google.com/chrome/answer/123456";']),
  1505. MockFile('somewhere_else/file.cc',
  1506. ['char* host = '
  1507. ' "https://support.google.com/chrome/a/answer/123456";']),
  1508. ]
  1509. warnings = PRESUBMIT.CheckGoogleSupportAnswerUrlOnUpload(
  1510. input_api, MockOutputApi())
  1511. self.assertEqual(1, len(warnings))
  1512. self.assertEqual(2, len(warnings[0].items))
  1513. def testAllowAnswerUrlParam(self):
  1514. input_api = MockInputApi()
  1515. input_api.files = [
  1516. MockFile('somewhere/file.cc',
  1517. ['char* host = '
  1518. ' "https://support.google.com/chrome/?p=cpn_crash_reports";']),
  1519. ]
  1520. warnings = PRESUBMIT.CheckGoogleSupportAnswerUrlOnUpload(
  1521. input_api, MockOutputApi())
  1522. self.assertEqual(0, len(warnings))
  1523. class HardcodedGoogleHostsTest(unittest.TestCase):
  1524. def testWarnOnAssignedLiterals(self):
  1525. input_api = MockInputApi()
  1526. input_api.files = [
  1527. MockFile('content/file.cc',
  1528. ['char* host = "https://www.google.com";']),
  1529. MockFile('content/file.cc',
  1530. ['char* host = "https://www.googleapis.com";']),
  1531. MockFile('content/file.cc',
  1532. ['char* host = "https://clients1.google.com";']),
  1533. ]
  1534. warnings = PRESUBMIT.CheckHardcodedGoogleHostsInLowerLayers(
  1535. input_api, MockOutputApi())
  1536. self.assertEqual(1, len(warnings))
  1537. self.assertEqual(3, len(warnings[0].items))
  1538. def testAllowInComment(self):
  1539. input_api = MockInputApi()
  1540. input_api.files = [
  1541. MockFile('content/file.cc',
  1542. ['char* host = "https://www.aol.com"; // google.com'])
  1543. ]
  1544. warnings = PRESUBMIT.CheckHardcodedGoogleHostsInLowerLayers(
  1545. input_api, MockOutputApi())
  1546. self.assertEqual(0, len(warnings))
  1547. class ChromeOsSyncedPrefRegistrationTest(unittest.TestCase):
  1548. def testWarnsOnChromeOsDirectories(self):
  1549. files = [
  1550. MockFile('ash/file.cc',
  1551. ['PrefRegistrySyncable::SYNCABLE_PREF']),
  1552. MockFile('chrome/browser/chromeos/file.cc',
  1553. ['PrefRegistrySyncable::SYNCABLE_PREF']),
  1554. MockFile('chromeos/file.cc',
  1555. ['PrefRegistrySyncable::SYNCABLE_PREF']),
  1556. MockFile('components/arc/file.cc',
  1557. ['PrefRegistrySyncable::SYNCABLE_PREF']),
  1558. MockFile('components/exo/file.cc',
  1559. ['PrefRegistrySyncable::SYNCABLE_PREF']),
  1560. ]
  1561. input_api = MockInputApi()
  1562. for file in files:
  1563. input_api.files = [file]
  1564. warnings = PRESUBMIT.CheckChromeOsSyncedPrefRegistration(
  1565. input_api, MockOutputApi())
  1566. self.assertEqual(1, len(warnings))
  1567. def testDoesNotWarnOnSyncOsPref(self):
  1568. input_api = MockInputApi()
  1569. input_api.files = [
  1570. MockFile('chromeos/file.cc',
  1571. ['PrefRegistrySyncable::SYNCABLE_OS_PREF']),
  1572. ]
  1573. warnings = PRESUBMIT.CheckChromeOsSyncedPrefRegistration(
  1574. input_api, MockOutputApi())
  1575. self.assertEqual(0, len(warnings))
  1576. def testDoesNotWarnOnOtherDirectories(self):
  1577. input_api = MockInputApi()
  1578. input_api.files = [
  1579. MockFile('chrome/browser/ui/file.cc',
  1580. ['PrefRegistrySyncable::SYNCABLE_PREF']),
  1581. MockFile('components/sync/file.cc',
  1582. ['PrefRegistrySyncable::SYNCABLE_PREF']),
  1583. MockFile('content/browser/file.cc',
  1584. ['PrefRegistrySyncable::SYNCABLE_PREF']),
  1585. MockFile('a/notchromeos/file.cc',
  1586. ['PrefRegistrySyncable::SYNCABLE_PREF']),
  1587. ]
  1588. warnings = PRESUBMIT.CheckChromeOsSyncedPrefRegistration(
  1589. input_api, MockOutputApi())
  1590. self.assertEqual(0, len(warnings))
  1591. def testSeparateWarningForPriorityPrefs(self):
  1592. input_api = MockInputApi()
  1593. input_api.files = [
  1594. MockFile('chromeos/file.cc',
  1595. ['PrefRegistrySyncable::SYNCABLE_PREF',
  1596. 'PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF']),
  1597. ]
  1598. warnings = PRESUBMIT.CheckChromeOsSyncedPrefRegistration(
  1599. input_api, MockOutputApi())
  1600. self.assertEqual(2, len(warnings))
  1601. class ForwardDeclarationTest(unittest.TestCase):
  1602. def testCheckHeadersOnlyOutsideThirdParty(self):
  1603. mock_input_api = MockInputApi()
  1604. mock_input_api.files = [
  1605. MockAffectedFile('somewhere/file.cc', [
  1606. 'class DummyClass;'
  1607. ]),
  1608. MockAffectedFile('third_party/header.h', [
  1609. 'class DummyClass;'
  1610. ])
  1611. ]
  1612. warnings = PRESUBMIT.CheckUselessForwardDeclarations(mock_input_api,
  1613. MockOutputApi())
  1614. self.assertEqual(0, len(warnings))
  1615. def testNoNestedDeclaration(self):
  1616. mock_input_api = MockInputApi()
  1617. mock_input_api.files = [
  1618. MockAffectedFile('somewhere/header.h', [
  1619. 'class SomeClass {',
  1620. ' protected:',
  1621. ' class NotAMatch;',
  1622. '};'
  1623. ])
  1624. ]
  1625. warnings = PRESUBMIT.CheckUselessForwardDeclarations(mock_input_api,
  1626. MockOutputApi())
  1627. self.assertEqual(0, len(warnings))
  1628. def testSubStrings(self):
  1629. mock_input_api = MockInputApi()
  1630. mock_input_api.files = [
  1631. MockAffectedFile('somewhere/header.h', [
  1632. 'class NotUsefulClass;',
  1633. 'struct SomeStruct;',
  1634. 'UsefulClass *p1;',
  1635. 'SomeStructPtr *p2;'
  1636. ])
  1637. ]
  1638. warnings = PRESUBMIT.CheckUselessForwardDeclarations(mock_input_api,
  1639. MockOutputApi())
  1640. self.assertEqual(2, len(warnings))
  1641. def testUselessForwardDeclaration(self):
  1642. mock_input_api = MockInputApi()
  1643. mock_input_api.files = [
  1644. MockAffectedFile('somewhere/header.h', [
  1645. 'class DummyClass;',
  1646. 'struct DummyStruct;',
  1647. 'class UsefulClass;',
  1648. 'std::unique_ptr<UsefulClass> p;'
  1649. ])
  1650. ]
  1651. warnings = PRESUBMIT.CheckUselessForwardDeclarations(mock_input_api,
  1652. MockOutputApi())
  1653. self.assertEqual(2, len(warnings))
  1654. def testBlinkHeaders(self):
  1655. mock_input_api = MockInputApi()
  1656. mock_input_api.files = [
  1657. MockAffectedFile('third_party/blink/header.h', [
  1658. 'class DummyClass;',
  1659. 'struct DummyStruct;',
  1660. ]),
  1661. MockAffectedFile('third_party\\blink\\header.h', [
  1662. 'class DummyClass;',
  1663. 'struct DummyStruct;',
  1664. ])
  1665. ]
  1666. warnings = PRESUBMIT.CheckUselessForwardDeclarations(mock_input_api,
  1667. MockOutputApi())
  1668. self.assertEqual(4, len(warnings))
  1669. class RelativeIncludesTest(unittest.TestCase):
  1670. def testThirdPartyNotWebKitIgnored(self):
  1671. mock_input_api = MockInputApi()
  1672. mock_input_api.files = [
  1673. MockAffectedFile('third_party/test.cpp', '#include "../header.h"'),
  1674. MockAffectedFile('third_party/test/test.cpp', '#include "../header.h"'),
  1675. ]
  1676. mock_output_api = MockOutputApi()
  1677. errors = PRESUBMIT.CheckForRelativeIncludes(
  1678. mock_input_api, mock_output_api)
  1679. self.assertEqual(0, len(errors))
  1680. def testNonCppFileIgnored(self):
  1681. mock_input_api = MockInputApi()
  1682. mock_input_api.files = [
  1683. MockAffectedFile('test.py', '#include "../header.h"'),
  1684. ]
  1685. mock_output_api = MockOutputApi()
  1686. errors = PRESUBMIT.CheckForRelativeIncludes(
  1687. mock_input_api, mock_output_api)
  1688. self.assertEqual(0, len(errors))
  1689. def testInnocuousChangesAllowed(self):
  1690. mock_input_api = MockInputApi()
  1691. mock_input_api.files = [
  1692. MockAffectedFile('test.cpp', '#include "header.h"'),
  1693. MockAffectedFile('test2.cpp', '../'),
  1694. ]
  1695. mock_output_api = MockOutputApi()
  1696. errors = PRESUBMIT.CheckForRelativeIncludes(
  1697. mock_input_api, mock_output_api)
  1698. self.assertEqual(0, len(errors))
  1699. def testRelativeIncludeNonWebKitProducesError(self):
  1700. mock_input_api = MockInputApi()
  1701. mock_input_api.files = [
  1702. MockAffectedFile('test.cpp', ['#include "../header.h"']),
  1703. ]
  1704. mock_output_api = MockOutputApi()
  1705. errors = PRESUBMIT.CheckForRelativeIncludes(
  1706. mock_input_api, mock_output_api)
  1707. self.assertEqual(1, len(errors))
  1708. def testRelativeIncludeWebKitProducesError(self):
  1709. mock_input_api = MockInputApi()
  1710. mock_input_api.files = [
  1711. MockAffectedFile('third_party/blink/test.cpp',
  1712. ['#include "../header.h']),
  1713. ]
  1714. mock_output_api = MockOutputApi()
  1715. errors = PRESUBMIT.CheckForRelativeIncludes(
  1716. mock_input_api, mock_output_api)
  1717. self.assertEqual(1, len(errors))
  1718. class CCIncludeTest(unittest.TestCase):
  1719. def testThirdPartyNotBlinkIgnored(self):
  1720. mock_input_api = MockInputApi()
  1721. mock_input_api.files = [
  1722. MockAffectedFile('third_party/test.cpp', '#include "file.cc"'),
  1723. ]
  1724. mock_output_api = MockOutputApi()
  1725. errors = PRESUBMIT.CheckForCcIncludes(
  1726. mock_input_api, mock_output_api)
  1727. self.assertEqual(0, len(errors))
  1728. def testPythonFileIgnored(self):
  1729. mock_input_api = MockInputApi()
  1730. mock_input_api.files = [
  1731. MockAffectedFile('test.py', '#include "file.cc"'),
  1732. ]
  1733. mock_output_api = MockOutputApi()
  1734. errors = PRESUBMIT.CheckForCcIncludes(
  1735. mock_input_api, mock_output_api)
  1736. self.assertEqual(0, len(errors))
  1737. def testIncFilesAccepted(self):
  1738. mock_input_api = MockInputApi()
  1739. mock_input_api.files = [
  1740. MockAffectedFile('test.py', '#include "file.inc"'),
  1741. ]
  1742. mock_output_api = MockOutputApi()
  1743. errors = PRESUBMIT.CheckForCcIncludes(
  1744. mock_input_api, mock_output_api)
  1745. self.assertEqual(0, len(errors))
  1746. def testInnocuousChangesAllowed(self):
  1747. mock_input_api = MockInputApi()
  1748. mock_input_api.files = [
  1749. MockAffectedFile('test.cpp', '#include "header.h"'),
  1750. MockAffectedFile('test2.cpp', 'Something "file.cc"'),
  1751. ]
  1752. mock_output_api = MockOutputApi()
  1753. errors = PRESUBMIT.CheckForCcIncludes(
  1754. mock_input_api, mock_output_api)
  1755. self.assertEqual(0, len(errors))
  1756. def testCcIncludeNonBlinkProducesError(self):
  1757. mock_input_api = MockInputApi()
  1758. mock_input_api.files = [
  1759. MockAffectedFile('test.cpp', ['#include "file.cc"']),
  1760. ]
  1761. mock_output_api = MockOutputApi()
  1762. errors = PRESUBMIT.CheckForCcIncludes(
  1763. mock_input_api, mock_output_api)
  1764. self.assertEqual(1, len(errors))
  1765. def testCppIncludeBlinkProducesError(self):
  1766. mock_input_api = MockInputApi()
  1767. mock_input_api.files = [
  1768. MockAffectedFile('third_party/blink/test.cpp',
  1769. ['#include "foo/file.cpp"']),
  1770. ]
  1771. mock_output_api = MockOutputApi()
  1772. errors = PRESUBMIT.CheckForCcIncludes(
  1773. mock_input_api, mock_output_api)
  1774. self.assertEqual(1, len(errors))
  1775. class GnGlobForwardTest(unittest.TestCase):
  1776. def testAddBareGlobs(self):
  1777. mock_input_api = MockInputApi()
  1778. mock_input_api.files = [
  1779. MockAffectedFile('base/stuff.gni', [
  1780. 'forward_variables_from(invoker, "*")']),
  1781. MockAffectedFile('base/BUILD.gn', [
  1782. 'forward_variables_from(invoker, "*")']),
  1783. ]
  1784. warnings = PRESUBMIT.CheckGnGlobForward(mock_input_api, MockOutputApi())
  1785. self.assertEqual(1, len(warnings))
  1786. msg = '\n'.join(warnings[0].items)
  1787. self.assertIn('base/stuff.gni', msg)
  1788. # Should not check .gn files. Local templates don't need to care about
  1789. # visibility / testonly.
  1790. self.assertNotIn('base/BUILD.gn', msg)
  1791. def testValidUses(self):
  1792. mock_input_api = MockInputApi()
  1793. mock_input_api.files = [
  1794. MockAffectedFile('base/stuff.gni', [
  1795. 'forward_variables_from(invoker, "*", [])']),
  1796. MockAffectedFile('base/stuff2.gni', [
  1797. 'forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY)']),
  1798. MockAffectedFile('base/stuff3.gni', [
  1799. 'forward_variables_from(invoker, [ "testonly" ])']),
  1800. ]
  1801. warnings = PRESUBMIT.CheckGnGlobForward(mock_input_api, MockOutputApi())
  1802. self.assertEqual([], warnings)
  1803. class NewHeaderWithoutGnChangeTest(unittest.TestCase):
  1804. def testAddHeaderWithoutGn(self):
  1805. mock_input_api = MockInputApi()
  1806. mock_input_api.files = [
  1807. MockAffectedFile('base/stuff.h', ''),
  1808. ]
  1809. warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
  1810. mock_input_api, MockOutputApi())
  1811. self.assertEqual(1, len(warnings))
  1812. self.assertTrue('base/stuff.h' in warnings[0].items)
  1813. def testModifyHeader(self):
  1814. mock_input_api = MockInputApi()
  1815. mock_input_api.files = [
  1816. MockAffectedFile('base/stuff.h', '', action='M'),
  1817. ]
  1818. warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
  1819. mock_input_api, MockOutputApi())
  1820. self.assertEqual(0, len(warnings))
  1821. def testDeleteHeader(self):
  1822. mock_input_api = MockInputApi()
  1823. mock_input_api.files = [
  1824. MockAffectedFile('base/stuff.h', '', action='D'),
  1825. ]
  1826. warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
  1827. mock_input_api, MockOutputApi())
  1828. self.assertEqual(0, len(warnings))
  1829. def testAddHeaderWithGn(self):
  1830. mock_input_api = MockInputApi()
  1831. mock_input_api.files = [
  1832. MockAffectedFile('base/stuff.h', ''),
  1833. MockAffectedFile('base/BUILD.gn', 'stuff.h'),
  1834. ]
  1835. warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
  1836. mock_input_api, MockOutputApi())
  1837. self.assertEqual(0, len(warnings))
  1838. def testAddHeaderWithGni(self):
  1839. mock_input_api = MockInputApi()
  1840. mock_input_api.files = [
  1841. MockAffectedFile('base/stuff.h', ''),
  1842. MockAffectedFile('base/files.gni', 'stuff.h'),
  1843. ]
  1844. warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
  1845. mock_input_api, MockOutputApi())
  1846. self.assertEqual(0, len(warnings))
  1847. def testAddHeaderWithOther(self):
  1848. mock_input_api = MockInputApi()
  1849. mock_input_api.files = [
  1850. MockAffectedFile('base/stuff.h', ''),
  1851. MockAffectedFile('base/stuff.cc', 'stuff.h'),
  1852. ]
  1853. warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
  1854. mock_input_api, MockOutputApi())
  1855. self.assertEqual(1, len(warnings))
  1856. def testAddHeaderWithWrongGn(self):
  1857. mock_input_api = MockInputApi()
  1858. mock_input_api.files = [
  1859. MockAffectedFile('base/stuff.h', ''),
  1860. MockAffectedFile('base/BUILD.gn', 'stuff_h'),
  1861. ]
  1862. warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
  1863. mock_input_api, MockOutputApi())
  1864. self.assertEqual(1, len(warnings))
  1865. def testAddHeadersWithGn(self):
  1866. mock_input_api = MockInputApi()
  1867. mock_input_api.files = [
  1868. MockAffectedFile('base/stuff.h', ''),
  1869. MockAffectedFile('base/another.h', ''),
  1870. MockAffectedFile('base/BUILD.gn', 'another.h\nstuff.h'),
  1871. ]
  1872. warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
  1873. mock_input_api, MockOutputApi())
  1874. self.assertEqual(0, len(warnings))
  1875. def testAddHeadersWithWrongGn(self):
  1876. mock_input_api = MockInputApi()
  1877. mock_input_api.files = [
  1878. MockAffectedFile('base/stuff.h', ''),
  1879. MockAffectedFile('base/another.h', ''),
  1880. MockAffectedFile('base/BUILD.gn', 'another_h\nstuff.h'),
  1881. ]
  1882. warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
  1883. mock_input_api, MockOutputApi())
  1884. self.assertEqual(1, len(warnings))
  1885. self.assertFalse('base/stuff.h' in warnings[0].items)
  1886. self.assertTrue('base/another.h' in warnings[0].items)
  1887. def testAddHeadersWithWrongGn2(self):
  1888. mock_input_api = MockInputApi()
  1889. mock_input_api.files = [
  1890. MockAffectedFile('base/stuff.h', ''),
  1891. MockAffectedFile('base/another.h', ''),
  1892. MockAffectedFile('base/BUILD.gn', 'another_h\nstuff_h'),
  1893. ]
  1894. warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
  1895. mock_input_api, MockOutputApi())
  1896. self.assertEqual(1, len(warnings))
  1897. self.assertTrue('base/stuff.h' in warnings[0].items)
  1898. self.assertTrue('base/another.h' in warnings[0].items)
  1899. class CorrectProductNameInMessagesTest(unittest.TestCase):
  1900. def testProductNameInDesc(self):
  1901. mock_input_api = MockInputApi()
  1902. mock_input_api.files = [
  1903. MockAffectedFile('chrome/app/google_chrome_strings.grd', [
  1904. '<message name="Foo" desc="Welcome to Chrome">',
  1905. ' Welcome to Chrome!',
  1906. '</message>',
  1907. ]),
  1908. MockAffectedFile('chrome/app/chromium_strings.grd', [
  1909. '<message name="Bar" desc="Welcome to Chrome">',
  1910. ' Welcome to Chromium!',
  1911. '</message>',
  1912. ]),
  1913. ]
  1914. warnings = PRESUBMIT.CheckCorrectProductNameInMessages(
  1915. mock_input_api, MockOutputApi())
  1916. self.assertEqual(0, len(warnings))
  1917. def testChromeInChromium(self):
  1918. mock_input_api = MockInputApi()
  1919. mock_input_api.files = [
  1920. MockAffectedFile('chrome/app/google_chrome_strings.grd', [
  1921. '<message name="Foo" desc="Welcome to Chrome">',
  1922. ' Welcome to Chrome!',
  1923. '</message>',
  1924. ]),
  1925. MockAffectedFile('chrome/app/chromium_strings.grd', [
  1926. '<message name="Bar" desc="Welcome to Chrome">',
  1927. ' Welcome to Chrome!',
  1928. '</message>',
  1929. ]),
  1930. ]
  1931. warnings = PRESUBMIT.CheckCorrectProductNameInMessages(
  1932. mock_input_api, MockOutputApi())
  1933. self.assertEqual(1, len(warnings))
  1934. self.assertTrue('chrome/app/chromium_strings.grd' in warnings[0].items[0])
  1935. def testChromiumInChrome(self):
  1936. mock_input_api = MockInputApi()
  1937. mock_input_api.files = [
  1938. MockAffectedFile('chrome/app/google_chrome_strings.grd', [
  1939. '<message name="Foo" desc="Welcome to Chrome">',
  1940. ' Welcome to Chromium!',
  1941. '</message>',
  1942. ]),
  1943. MockAffectedFile('chrome/app/chromium_strings.grd', [
  1944. '<message name="Bar" desc="Welcome to Chrome">',
  1945. ' Welcome to Chromium!',
  1946. '</message>',
  1947. ]),
  1948. ]
  1949. warnings = PRESUBMIT.CheckCorrectProductNameInMessages(
  1950. mock_input_api, MockOutputApi())
  1951. self.assertEqual(1, len(warnings))
  1952. self.assertTrue(
  1953. 'chrome/app/google_chrome_strings.grd:2' in warnings[0].items[0])
  1954. def testMultipleInstances(self):
  1955. mock_input_api = MockInputApi()
  1956. mock_input_api.files = [
  1957. MockAffectedFile('chrome/app/chromium_strings.grd', [
  1958. '<message name="Bar" desc="Welcome to Chrome">',
  1959. ' Welcome to Chrome!',
  1960. '</message>',
  1961. '<message name="Baz" desc="A correct message">',
  1962. ' Chromium is the software you are using.',
  1963. '</message>',
  1964. '<message name="Bat" desc="An incorrect message">',
  1965. ' Google Chrome is the software you are using.',
  1966. '</message>',
  1967. ]),
  1968. ]
  1969. warnings = PRESUBMIT.CheckCorrectProductNameInMessages(
  1970. mock_input_api, MockOutputApi())
  1971. self.assertEqual(1, len(warnings))
  1972. self.assertTrue(
  1973. 'chrome/app/chromium_strings.grd:2' in warnings[0].items[0])
  1974. self.assertTrue(
  1975. 'chrome/app/chromium_strings.grd:8' in warnings[0].items[1])
  1976. def testMultipleWarnings(self):
  1977. mock_input_api = MockInputApi()
  1978. mock_input_api.files = [
  1979. MockAffectedFile('chrome/app/chromium_strings.grd', [
  1980. '<message name="Bar" desc="Welcome to Chrome">',
  1981. ' Welcome to Chrome!',
  1982. '</message>',
  1983. '<message name="Baz" desc="A correct message">',
  1984. ' Chromium is the software you are using.',
  1985. '</message>',
  1986. '<message name="Bat" desc="An incorrect message">',
  1987. ' Google Chrome is the software you are using.',
  1988. '</message>',
  1989. ]),
  1990. MockAffectedFile('components/components_google_chrome_strings.grd', [
  1991. '<message name="Bar" desc="Welcome to Chrome">',
  1992. ' Welcome to Chrome!',
  1993. '</message>',
  1994. '<message name="Baz" desc="A correct message">',
  1995. ' Chromium is the software you are using.',
  1996. '</message>',
  1997. '<message name="Bat" desc="An incorrect message">',
  1998. ' Google Chrome is the software you are using.',
  1999. '</message>',
  2000. ]),
  2001. ]
  2002. warnings = PRESUBMIT.CheckCorrectProductNameInMessages(
  2003. mock_input_api, MockOutputApi())
  2004. self.assertEqual(2, len(warnings))
  2005. self.assertTrue(
  2006. 'components/components_google_chrome_strings.grd:5'
  2007. in warnings[0].items[0])
  2008. self.assertTrue(
  2009. 'chrome/app/chromium_strings.grd:2' in warnings[1].items[0])
  2010. self.assertTrue(
  2011. 'chrome/app/chromium_strings.grd:8' in warnings[1].items[1])
  2012. class _SecurityOwnersTestCase(unittest.TestCase):
  2013. def _createMockInputApi(self):
  2014. mock_input_api = MockInputApi()
  2015. def FakeRepositoryRoot():
  2016. return mock_input_api.os_path.join('chromium', 'src')
  2017. mock_input_api.change.RepositoryRoot = FakeRepositoryRoot
  2018. self._injectFakeOwnersClient(
  2019. mock_input_api,
  2020. ['apple@chromium.org', 'orange@chromium.org'])
  2021. return mock_input_api
  2022. def _setupFakeChange(self, input_api):
  2023. class FakeGerrit(object):
  2024. def IsOwnersOverrideApproved(self, issue):
  2025. return False
  2026. input_api.change.issue = 123
  2027. input_api.gerrit = FakeGerrit()
  2028. def _injectFakeOwnersClient(self, input_api, owners):
  2029. class FakeOwnersClient(object):
  2030. def ListOwners(self, f):
  2031. return owners
  2032. input_api.owners_client = FakeOwnersClient()
  2033. def _injectFakeChangeOwnerAndReviewers(self, input_api, owner, reviewers):
  2034. def MockOwnerAndReviewers(input_api, email_regexp, approval_needed=False):
  2035. return [owner, reviewers]
  2036. input_api.canned_checks.GetCodereviewOwnerAndReviewers = \
  2037. MockOwnerAndReviewers
  2038. class IpcSecurityOwnerTest(_SecurityOwnersTestCase):
  2039. _test_cases = [
  2040. ('*_messages.cc', 'scary_messages.cc'),
  2041. ('*_messages*.h', 'scary_messages.h'),
  2042. ('*_messages*.h', 'scary_messages_android.h'),
  2043. ('*_param_traits*.*', 'scary_param_traits.h'),
  2044. ('*_param_traits*.*', 'scary_param_traits_win.h'),
  2045. ('*.mojom', 'scary.mojom'),
  2046. ('*_mojom_traits*.*', 'scary_mojom_traits.h'),
  2047. ('*_mojom_traits*.*', 'scary_mojom_traits_mac.h'),
  2048. ('*_type_converter*.*', 'scary_type_converter.h'),
  2049. ('*_type_converter*.*', 'scary_type_converter_nacl.h'),
  2050. ('*.aidl', 'scary.aidl'),
  2051. ]
  2052. def testHasCorrectPerFileRulesAndSecurityReviewer(self):
  2053. mock_input_api = self._createMockInputApi()
  2054. new_owners_file_path = mock_input_api.os_path.join(
  2055. 'services', 'goat', 'public', 'OWNERS')
  2056. new_owners_file = [
  2057. 'per-file *.mojom=set noparent',
  2058. 'per-file *.mojom=file://ipc/SECURITY_OWNERS'
  2059. ]
  2060. def FakeReadFile(filename):
  2061. self.assertEqual(
  2062. mock_input_api.os_path.join('chromium', 'src', new_owners_file_path),
  2063. filename)
  2064. return '\n'.join(new_owners_file)
  2065. mock_input_api.ReadFile = FakeReadFile
  2066. mock_input_api.files = [
  2067. MockAffectedFile(
  2068. new_owners_file_path, new_owners_file),
  2069. MockAffectedFile(
  2070. mock_input_api.os_path.join(
  2071. 'services', 'goat', 'public', 'goat.mojom'),
  2072. ['// Scary contents.'])]
  2073. self._setupFakeChange(mock_input_api)
  2074. self._injectFakeChangeOwnerAndReviewers(
  2075. mock_input_api, 'owner@chromium.org', ['orange@chromium.org'])
  2076. mock_input_api.is_committing = True
  2077. mock_input_api.dry_run = False
  2078. mock_output_api = MockOutputApi()
  2079. results = PRESUBMIT.CheckSecurityOwners(
  2080. mock_input_api, mock_output_api)
  2081. self.assertEqual(0, len(results))
  2082. def testMissingSecurityReviewerAtUpload(self):
  2083. mock_input_api = self._createMockInputApi()
  2084. new_owners_file_path = mock_input_api.os_path.join(
  2085. 'services', 'goat', 'public', 'OWNERS')
  2086. new_owners_file = [
  2087. 'per-file *.mojom=set noparent',
  2088. 'per-file *.mojom=file://ipc/SECURITY_OWNERS'
  2089. ]
  2090. def FakeReadFile(filename):
  2091. self.assertEqual(
  2092. mock_input_api.os_path.join('chromium', 'src', new_owners_file_path),
  2093. filename)
  2094. return '\n'.join(new_owners_file)
  2095. mock_input_api.ReadFile = FakeReadFile
  2096. mock_input_api.files = [
  2097. MockAffectedFile(
  2098. new_owners_file_path, new_owners_file),
  2099. MockAffectedFile(
  2100. mock_input_api.os_path.join(
  2101. 'services', 'goat', 'public', 'goat.mojom'),
  2102. ['// Scary contents.'])]
  2103. self._setupFakeChange(mock_input_api)
  2104. self._injectFakeChangeOwnerAndReviewers(
  2105. mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
  2106. mock_input_api.is_committing = False
  2107. mock_input_api.dry_run = False
  2108. mock_output_api = MockOutputApi()
  2109. results = PRESUBMIT.CheckSecurityOwners(
  2110. mock_input_api, mock_output_api)
  2111. self.assertEqual(1, len(results))
  2112. self.assertEqual('notify', results[0].type)
  2113. self.assertEqual(
  2114. 'Review from an owner in ipc/SECURITY_OWNERS is required for the '
  2115. 'following newly-added files:', results[0].message)
  2116. def testMissingSecurityReviewerAtDryRunCommit(self):
  2117. mock_input_api = self._createMockInputApi()
  2118. new_owners_file_path = mock_input_api.os_path.join(
  2119. 'services', 'goat', 'public', 'OWNERS')
  2120. new_owners_file = [
  2121. 'per-file *.mojom=set noparent',
  2122. 'per-file *.mojom=file://ipc/SECURITY_OWNERS'
  2123. ]
  2124. def FakeReadFile(filename):
  2125. self.assertEqual(
  2126. mock_input_api.os_path.join('chromium', 'src', new_owners_file_path),
  2127. filename)
  2128. return '\n'.join(new_owners_file)
  2129. mock_input_api.ReadFile = FakeReadFile
  2130. mock_input_api.files = [
  2131. MockAffectedFile(
  2132. new_owners_file_path, new_owners_file),
  2133. MockAffectedFile(
  2134. mock_input_api.os_path.join(
  2135. 'services', 'goat', 'public', 'goat.mojom'),
  2136. ['// Scary contents.'])]
  2137. self._setupFakeChange(mock_input_api)
  2138. self._injectFakeChangeOwnerAndReviewers(
  2139. mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
  2140. mock_input_api.is_committing = True
  2141. mock_input_api.dry_run = True
  2142. mock_output_api = MockOutputApi()
  2143. results = PRESUBMIT.CheckSecurityOwners(
  2144. mock_input_api, mock_output_api)
  2145. self.assertEqual(1, len(results))
  2146. self.assertEqual('error', results[0].type)
  2147. self.assertEqual(
  2148. 'Review from an owner in ipc/SECURITY_OWNERS is required for the '
  2149. 'following newly-added files:', results[0].message)
  2150. def testMissingSecurityApprovalAtRealCommit(self):
  2151. mock_input_api = self._createMockInputApi()
  2152. new_owners_file_path = mock_input_api.os_path.join(
  2153. 'services', 'goat', 'public', 'OWNERS')
  2154. new_owners_file = [
  2155. 'per-file *.mojom=set noparent',
  2156. 'per-file *.mojom=file://ipc/SECURITY_OWNERS'
  2157. ]
  2158. def FakeReadFile(filename):
  2159. self.assertEqual(
  2160. mock_input_api.os_path.join('chromium', 'src', new_owners_file_path),
  2161. filename)
  2162. return '\n'.join(new_owners_file)
  2163. mock_input_api.ReadFile = FakeReadFile
  2164. mock_input_api.files = [
  2165. MockAffectedFile(
  2166. new_owners_file_path, new_owners_file),
  2167. MockAffectedFile(
  2168. mock_input_api.os_path.join(
  2169. 'services', 'goat', 'public', 'goat.mojom'),
  2170. ['// Scary contents.'])]
  2171. self._setupFakeChange(mock_input_api)
  2172. self._injectFakeChangeOwnerAndReviewers(
  2173. mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
  2174. mock_input_api.is_committing = True
  2175. mock_input_api.dry_run = False
  2176. mock_output_api = MockOutputApi()
  2177. results = PRESUBMIT.CheckSecurityOwners(
  2178. mock_input_api, mock_output_api)
  2179. self.assertEqual('error', results[0].type)
  2180. self.assertEqual(
  2181. 'Review from an owner in ipc/SECURITY_OWNERS is required for the '
  2182. 'following newly-added files:', results[0].message)
  2183. def testIpcChangeNeedsSecurityOwner(self):
  2184. for is_committing in [True, False]:
  2185. for pattern, filename in self._test_cases:
  2186. with self.subTest(
  2187. line=f'is_committing={is_committing}, filename={filename}'):
  2188. mock_input_api = self._createMockInputApi()
  2189. mock_input_api.files = [
  2190. MockAffectedFile(
  2191. mock_input_api.os_path.join(
  2192. 'services', 'goat', 'public', filename),
  2193. ['// Scary contents.'])]
  2194. self._setupFakeChange(mock_input_api)
  2195. self._injectFakeChangeOwnerAndReviewers(
  2196. mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
  2197. mock_input_api.is_committing = is_committing
  2198. mock_input_api.dry_run = False
  2199. mock_output_api = MockOutputApi()
  2200. results = PRESUBMIT.CheckSecurityOwners(
  2201. mock_input_api, mock_output_api)
  2202. self.assertEqual(1, len(results))
  2203. self.assertEqual('error', results[0].type)
  2204. self.assertTrue(results[0].message.replace('\\', '/').startswith(
  2205. 'Found missing OWNERS lines for security-sensitive files. '
  2206. 'Please add the following lines to services/goat/public/OWNERS:'))
  2207. self.assertEqual(['ipc-security-reviews@chromium.org'],
  2208. mock_output_api.more_cc)
  2209. def testServiceManifestChangeNeedsSecurityOwner(self):
  2210. mock_input_api = self._createMockInputApi()
  2211. mock_input_api.files = [
  2212. MockAffectedFile(
  2213. mock_input_api.os_path.join(
  2214. 'services', 'goat', 'public', 'cpp', 'manifest.cc'),
  2215. [
  2216. '#include "services/goat/public/cpp/manifest.h"',
  2217. 'const service_manager::Manifest& GetManifest() {}',
  2218. ])]
  2219. self._setupFakeChange(mock_input_api)
  2220. self._injectFakeChangeOwnerAndReviewers(
  2221. mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
  2222. mock_output_api = MockOutputApi()
  2223. errors = PRESUBMIT.CheckSecurityOwners(
  2224. mock_input_api, mock_output_api)
  2225. self.assertEqual(1, len(errors))
  2226. self.assertTrue(errors[0].message.replace('\\', '/').startswith(
  2227. 'Found missing OWNERS lines for security-sensitive files. '
  2228. 'Please add the following lines to services/goat/public/cpp/OWNERS:'))
  2229. self.assertEqual(['ipc-security-reviews@chromium.org'], mock_output_api.more_cc)
  2230. def testNonServiceManifestSourceChangesDoNotRequireSecurityOwner(self):
  2231. mock_input_api = self._createMockInputApi()
  2232. self._injectFakeChangeOwnerAndReviewers(
  2233. mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
  2234. mock_input_api.files = [
  2235. MockAffectedFile('some/non/service/thing/foo_manifest.cc',
  2236. [
  2237. 'const char kNoEnforcement[] = "not a manifest!";',
  2238. ])]
  2239. mock_output_api = MockOutputApi()
  2240. errors = PRESUBMIT.CheckSecurityOwners(
  2241. mock_input_api, mock_output_api)
  2242. self.assertEqual([], errors)
  2243. self.assertEqual([], mock_output_api.more_cc)
  2244. class FuchsiaSecurityOwnerTest(_SecurityOwnersTestCase):
  2245. def testFidlChangeNeedsSecurityOwner(self):
  2246. mock_input_api = self._createMockInputApi()
  2247. mock_input_api.files = [
  2248. MockAffectedFile('potentially/scary/ipc.fidl',
  2249. [
  2250. 'library test.fidl'
  2251. ])]
  2252. self._setupFakeChange(mock_input_api)
  2253. self._injectFakeChangeOwnerAndReviewers(
  2254. mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
  2255. mock_output_api = MockOutputApi()
  2256. errors = PRESUBMIT.CheckSecurityOwners(
  2257. mock_input_api, mock_output_api)
  2258. self.assertEqual(1, len(errors))
  2259. self.assertTrue(errors[0].message.replace('\\', '/').startswith(
  2260. 'Found missing OWNERS lines for security-sensitive files. '
  2261. 'Please add the following lines to potentially/scary/OWNERS:'))
  2262. def testComponentManifestV1ChangeNeedsSecurityOwner(self):
  2263. mock_input_api = self._createMockInputApi()
  2264. mock_input_api.files = [
  2265. MockAffectedFile('potentially/scary/v2_manifest.cmx',
  2266. [
  2267. '{ "that is no": "manifest!" }'
  2268. ])]
  2269. self._setupFakeChange(mock_input_api)
  2270. self._injectFakeChangeOwnerAndReviewers(
  2271. mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
  2272. mock_output_api = MockOutputApi()
  2273. errors = PRESUBMIT.CheckSecurityOwners(
  2274. mock_input_api, mock_output_api)
  2275. self.assertEqual(1, len(errors))
  2276. self.assertTrue(errors[0].message.replace('\\', '/').startswith(
  2277. 'Found missing OWNERS lines for security-sensitive files. '
  2278. 'Please add the following lines to potentially/scary/OWNERS:'))
  2279. def testComponentManifestV2NeedsSecurityOwner(self):
  2280. mock_input_api = self._createMockInputApi()
  2281. mock_input_api.files = [
  2282. MockAffectedFile('potentially/scary/v2_manifest.cml',
  2283. [
  2284. '{ "that is no": "manifest!" }'
  2285. ])]
  2286. self._setupFakeChange(mock_input_api)
  2287. self._injectFakeChangeOwnerAndReviewers(
  2288. mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
  2289. mock_output_api = MockOutputApi()
  2290. errors = PRESUBMIT.CheckSecurityOwners(
  2291. mock_input_api, mock_output_api)
  2292. self.assertEqual(1, len(errors))
  2293. self.assertTrue(errors[0].message.replace('\\', '/').startswith(
  2294. 'Found missing OWNERS lines for security-sensitive files. '
  2295. 'Please add the following lines to potentially/scary/OWNERS:'))
  2296. def testThirdPartyTestsDoNotRequireSecurityOwner(self):
  2297. mock_input_api = MockInputApi()
  2298. self._injectFakeChangeOwnerAndReviewers(
  2299. mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
  2300. mock_input_api.files = [
  2301. MockAffectedFile('third_party/crashpad/test/tests.cmx',
  2302. [
  2303. 'const char kNoEnforcement[] = "Security?!? Pah!";',
  2304. ])]
  2305. mock_output_api = MockOutputApi()
  2306. errors = PRESUBMIT.CheckSecurityOwners(
  2307. mock_input_api, mock_output_api)
  2308. self.assertEqual([], errors)
  2309. def testOtherFuchsiaChangesDoNotRequireSecurityOwner(self):
  2310. mock_input_api = MockInputApi()
  2311. self._injectFakeChangeOwnerAndReviewers(
  2312. mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
  2313. mock_input_api.files = [
  2314. MockAffectedFile('some/non/service/thing/fuchsia_fidl_cml_cmx_magic.cc',
  2315. [
  2316. 'const char kNoEnforcement[] = "Security?!? Pah!";',
  2317. ])]
  2318. mock_output_api = MockOutputApi()
  2319. errors = PRESUBMIT.CheckSecurityOwners(
  2320. mock_input_api, mock_output_api)
  2321. self.assertEqual([], errors)
  2322. class SecurityChangeTest(_SecurityOwnersTestCase):
  2323. def testDiffGetServiceSandboxType(self):
  2324. mock_input_api = MockInputApi()
  2325. mock_input_api.files = [
  2326. MockAffectedFile(
  2327. 'services/goat/teleporter_host.cc',
  2328. [
  2329. 'template <>',
  2330. 'inline content::SandboxType',
  2331. 'content::GetServiceSandboxType<chrome::mojom::GoatTeleporter>() {',
  2332. '#if defined(OS_WIN)',
  2333. ' return SandboxType::kGoaty;',
  2334. '#else',
  2335. ' return SandboxType::kNoSandbox;',
  2336. '#endif // !defined(OS_WIN)',
  2337. '}'
  2338. ]
  2339. ),
  2340. ]
  2341. files_to_functions = PRESUBMIT._GetFilesUsingSecurityCriticalFunctions(
  2342. mock_input_api)
  2343. self.assertEqual({
  2344. 'services/goat/teleporter_host.cc': set([
  2345. 'content::GetServiceSandboxType<>()'
  2346. ])},
  2347. files_to_functions)
  2348. def testDiffRemovingLine(self):
  2349. mock_input_api = MockInputApi()
  2350. mock_file = MockAffectedFile('services/goat/teleporter_host.cc', '')
  2351. mock_file._scm_diff = """--- old 2020-05-04 14:08:25.000000000 -0400
  2352. +++ new 2020-05-04 14:08:32.000000000 -0400
  2353. @@ -1,5 +1,4 @@
  2354. template <>
  2355. inline content::SandboxType
  2356. -content::GetServiceSandboxType<chrome::mojom::GoatTeleporter>() {
  2357. #if defined(OS_WIN)
  2358. return SandboxType::kGoaty;
  2359. """
  2360. mock_input_api.files = [mock_file]
  2361. files_to_functions = PRESUBMIT._GetFilesUsingSecurityCriticalFunctions(
  2362. mock_input_api)
  2363. self.assertEqual({
  2364. 'services/goat/teleporter_host.cc': set([
  2365. 'content::GetServiceSandboxType<>()'
  2366. ])},
  2367. files_to_functions)
  2368. def testChangeOwnersMissing(self):
  2369. mock_input_api = self._createMockInputApi()
  2370. self._setupFakeChange(mock_input_api)
  2371. self._injectFakeChangeOwnerAndReviewers(
  2372. mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
  2373. mock_input_api.is_committing = False
  2374. mock_input_api.files = [
  2375. MockAffectedFile('file.cc', ['GetServiceSandboxType<Goat>(Sandbox)'])
  2376. ]
  2377. mock_output_api = MockOutputApi()
  2378. result = PRESUBMIT.CheckSecurityChanges(mock_input_api, mock_output_api)
  2379. self.assertEqual(1, len(result))
  2380. self.assertEqual(result[0].type, 'notify')
  2381. self.assertEqual(result[0].message,
  2382. 'The following files change calls to security-sensitive functions\n' \
  2383. 'that need to be reviewed by ipc/SECURITY_OWNERS.\n'
  2384. ' file.cc\n'
  2385. ' content::GetServiceSandboxType<>()\n\n')
  2386. def testChangeOwnersMissingAtCommit(self):
  2387. mock_input_api = self._createMockInputApi()
  2388. self._setupFakeChange(mock_input_api)
  2389. self._injectFakeChangeOwnerAndReviewers(
  2390. mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
  2391. mock_input_api.is_committing = True
  2392. mock_input_api.dry_run = False
  2393. mock_input_api.files = [
  2394. MockAffectedFile('file.cc', ['GetServiceSandboxType<mojom::Goat>()'])
  2395. ]
  2396. mock_output_api = MockOutputApi()
  2397. result = PRESUBMIT.CheckSecurityChanges(mock_input_api, mock_output_api)
  2398. self.assertEqual(1, len(result))
  2399. self.assertEqual(result[0].type, 'error')
  2400. self.assertEqual(result[0].message,
  2401. 'The following files change calls to security-sensitive functions\n' \
  2402. 'that need to be reviewed by ipc/SECURITY_OWNERS.\n'
  2403. ' file.cc\n'
  2404. ' content::GetServiceSandboxType<>()\n\n')
  2405. def testChangeOwnersPresent(self):
  2406. mock_input_api = self._createMockInputApi()
  2407. self._injectFakeChangeOwnerAndReviewers(
  2408. mock_input_api, 'owner@chromium.org',
  2409. ['apple@chromium.org', 'banana@chromium.org'])
  2410. mock_input_api.files = [
  2411. MockAffectedFile('file.cc', ['WithSandboxType(Sandbox)'])
  2412. ]
  2413. mock_output_api = MockOutputApi()
  2414. result = PRESUBMIT.CheckSecurityChanges(mock_input_api, mock_output_api)
  2415. self.assertEqual(0, len(result))
  2416. def testChangeOwnerIsSecurityOwner(self):
  2417. mock_input_api = self._createMockInputApi()
  2418. self._setupFakeChange(mock_input_api)
  2419. self._injectFakeChangeOwnerAndReviewers(
  2420. mock_input_api, 'orange@chromium.org', ['pear@chromium.org'])
  2421. mock_input_api.files = [
  2422. MockAffectedFile('file.cc', ['GetServiceSandboxType<T>(Sandbox)'])
  2423. ]
  2424. mock_output_api = MockOutputApi()
  2425. result = PRESUBMIT.CheckSecurityChanges(mock_input_api, mock_output_api)
  2426. self.assertEqual(1, len(result))
  2427. class BannedTypeCheckTest(unittest.TestCase):
  2428. def testBannedCppFunctions(self):
  2429. input_api = MockInputApi()
  2430. input_api.files = [
  2431. MockFile('some/cpp/problematic/file.cc',
  2432. ['using namespace std;']),
  2433. MockFile('third_party/blink/problematic/file.cc',
  2434. ['GetInterfaceProvider()']),
  2435. MockFile('some/cpp/ok/file.cc',
  2436. ['using std::string;']),
  2437. MockFile('some/cpp/problematic/file2.cc',
  2438. ['set_owned_by_client()']),
  2439. MockFile('some/cpp/nocheck/file.cc',
  2440. ['using namespace std; // nocheck']),
  2441. MockFile('some/cpp/comment/file.cc',
  2442. [' // A comment about `using namespace std;`']),
  2443. ]
  2444. results = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
  2445. # warnings are results[0], errors are results[1]
  2446. self.assertEqual(2, len(results))
  2447. self.assertTrue('some/cpp/problematic/file.cc' in results[1].message)
  2448. self.assertTrue(
  2449. 'third_party/blink/problematic/file.cc' in results[0].message)
  2450. self.assertTrue('some/cpp/ok/file.cc' not in results[1].message)
  2451. self.assertTrue('some/cpp/problematic/file2.cc' in results[0].message)
  2452. self.assertFalse('some/cpp/nocheck/file.cc' in results[0].message)
  2453. self.assertFalse('some/cpp/nocheck/file.cc' in results[1].message)
  2454. self.assertFalse('some/cpp/comment/file.cc' in results[0].message)
  2455. self.assertFalse('some/cpp/comment/file.cc' in results[1].message)
  2456. def testBannedIosObjcFunctions(self):
  2457. input_api = MockInputApi()
  2458. input_api.files = [
  2459. MockFile('some/ios/file.mm',
  2460. ['TEST(SomeClassTest, SomeInteraction) {',
  2461. '}']),
  2462. MockFile('some/mac/file.mm',
  2463. ['TEST(SomeClassTest, SomeInteraction) {',
  2464. '}']),
  2465. MockFile('another/ios_file.mm',
  2466. ['class SomeTest : public testing::Test {};']),
  2467. MockFile('some/ios/file_egtest.mm',
  2468. ['- (void)testSomething { EXPECT_OCMOCK_VERIFY(aMock); }']),
  2469. MockFile('some/ios/file_unittest.mm',
  2470. ['TEST_F(SomeTest, TestThis) { EXPECT_OCMOCK_VERIFY(aMock); }']),
  2471. ]
  2472. errors = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
  2473. self.assertEqual(1, len(errors))
  2474. self.assertTrue('some/ios/file.mm' in errors[0].message)
  2475. self.assertTrue('another/ios_file.mm' in errors[0].message)
  2476. self.assertTrue('some/mac/file.mm' not in errors[0].message)
  2477. self.assertTrue('some/ios/file_egtest.mm' in errors[0].message)
  2478. self.assertTrue('some/ios/file_unittest.mm' not in errors[0].message)
  2479. def testBannedMojoFunctions(self):
  2480. input_api = MockInputApi()
  2481. input_api.files = [
  2482. MockFile('some/cpp/problematic/file2.cc',
  2483. ['mojo::ConvertTo<>']),
  2484. MockFile('third_party/blink/ok/file3.cc',
  2485. ['mojo::ConvertTo<>']),
  2486. MockFile('content/renderer/ok/file3.cc',
  2487. ['mojo::ConvertTo<>']),
  2488. ]
  2489. results = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
  2490. # warnings are results[0], errors are results[1]
  2491. self.assertEqual(1, len(results))
  2492. self.assertTrue('some/cpp/problematic/file2.cc' in results[0].message)
  2493. self.assertTrue('third_party/blink/ok/file3.cc' not in results[0].message)
  2494. self.assertTrue('content/renderer/ok/file3.cc' not in results[0].message)
  2495. def testBannedMojomPatterns(self):
  2496. input_api = MockInputApi()
  2497. input_api.files = [
  2498. MockFile('bad.mojom',
  2499. ['struct Bad {',
  2500. ' handle<shared_buffer> buffer;',
  2501. '};']),
  2502. MockFile('good.mojom',
  2503. ['struct Good {',
  2504. ' mojo_base.mojom.ReadOnlySharedMemoryRegion region1;',
  2505. ' mojo_base.mojom.WritableSharedMemoryRegion region2;',
  2506. ' mojo_base.mojom.UnsafeSharedMemoryRegion region3;',
  2507. '};']),
  2508. ]
  2509. results = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
  2510. # warnings are results[0], errors are results[1]
  2511. self.assertEqual(1, len(results))
  2512. self.assertTrue('bad.mojom' in results[0].message)
  2513. self.assertTrue('good.mojom' not in results[0].message)
  2514. class NoProductionCodeUsingTestOnlyFunctionsTest(unittest.TestCase):
  2515. def testTruePositives(self):
  2516. mock_input_api = MockInputApi()
  2517. mock_input_api.files = [
  2518. MockFile('some/path/foo.cc', ['foo_for_testing();']),
  2519. MockFile('some/path/foo.mm', ['FooForTesting();']),
  2520. MockFile('some/path/foo.cxx', ['FooForTests();']),
  2521. MockFile('some/path/foo.cpp', ['foo_for_test();']),
  2522. ]
  2523. results = PRESUBMIT.CheckNoProductionCodeUsingTestOnlyFunctions(
  2524. mock_input_api, MockOutputApi())
  2525. self.assertEqual(1, len(results))
  2526. self.assertEqual(4, len(results[0].items))
  2527. self.assertTrue('foo.cc' in results[0].items[0])
  2528. self.assertTrue('foo.mm' in results[0].items[1])
  2529. self.assertTrue('foo.cxx' in results[0].items[2])
  2530. self.assertTrue('foo.cpp' in results[0].items[3])
  2531. def testFalsePositives(self):
  2532. mock_input_api = MockInputApi()
  2533. mock_input_api.files = [
  2534. MockFile('some/path/foo.h', ['foo_for_testing();']),
  2535. MockFile('some/path/foo.mm', ['FooForTesting() {']),
  2536. MockFile('some/path/foo.cc', ['::FooForTests();']),
  2537. MockFile('some/path/foo.cpp', ['// foo_for_test();']),
  2538. ]
  2539. results = PRESUBMIT.CheckNoProductionCodeUsingTestOnlyFunctions(
  2540. mock_input_api, MockOutputApi())
  2541. self.assertEqual(0, len(results))
  2542. def testAllowedFiles(self):
  2543. mock_input_api = MockInputApi()
  2544. mock_input_api.files = [
  2545. MockFile('path/foo_unittest.cc', ['foo_for_testing();']),
  2546. MockFile('path/bar_unittest_mac.cc', ['foo_for_testing();']),
  2547. MockFile('path/baz_unittests.cc', ['foo_for_testing();']),
  2548. ]
  2549. results = PRESUBMIT.CheckNoProductionCodeUsingTestOnlyFunctions(
  2550. mock_input_api, MockOutputApi())
  2551. self.assertEqual(0, len(results))
  2552. class NoProductionJavaCodeUsingTestOnlyFunctionsTest(unittest.TestCase):
  2553. def testTruePositives(self):
  2554. mock_input_api = MockInputApi()
  2555. mock_input_api.files = [
  2556. MockFile('dir/java/src/foo.java', ['FooForTesting();']),
  2557. MockFile('dir/java/src/bar.java', ['FooForTests(x);']),
  2558. MockFile('dir/java/src/baz.java', ['FooForTest(', 'y', ');']),
  2559. MockFile('dir/java/src/mult.java', [
  2560. 'int x = SomethingLongHere()',
  2561. ' * SomethingLongHereForTesting();'
  2562. ])
  2563. ]
  2564. results = PRESUBMIT.CheckNoProductionCodeUsingTestOnlyFunctionsJava(
  2565. mock_input_api, MockOutputApi())
  2566. self.assertEqual(1, len(results))
  2567. self.assertEqual(4, len(results[0].items))
  2568. self.assertTrue('foo.java' in results[0].items[0])
  2569. self.assertTrue('bar.java' in results[0].items[1])
  2570. self.assertTrue('baz.java' in results[0].items[2])
  2571. self.assertTrue('mult.java' in results[0].items[3])
  2572. def testFalsePositives(self):
  2573. mock_input_api = MockInputApi()
  2574. mock_input_api.files = [
  2575. MockFile('dir/java/src/foo.xml', ['FooForTesting();']),
  2576. MockFile('dir/java/src/foo.java', ['FooForTests() {']),
  2577. MockFile('dir/java/src/bar.java', ['// FooForTest();']),
  2578. MockFile('dir/java/src/bar2.java', ['x = 1; // FooForTest();']),
  2579. MockFile('dir/java/src/bar3.java', ['@VisibleForTesting']),
  2580. MockFile('dir/java/src/bar4.java', ['@VisibleForTesting()']),
  2581. MockFile('dir/java/src/bar5.java', [
  2582. '@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)'
  2583. ]),
  2584. MockFile('dir/javatests/src/baz.java', ['FooForTest(', 'y', ');']),
  2585. MockFile('dir/junit/src/baz.java', ['FooForTest(', 'y', ');']),
  2586. MockFile('dir/junit/src/javadoc.java', [
  2587. '/** Use FooForTest(); to obtain foo in tests.'
  2588. ' */'
  2589. ]),
  2590. MockFile('dir/junit/src/javadoc2.java', [
  2591. '/** ',
  2592. ' * Use FooForTest(); to obtain foo in tests.'
  2593. ' */'
  2594. ]),
  2595. ]
  2596. results = PRESUBMIT.CheckNoProductionCodeUsingTestOnlyFunctionsJava(
  2597. mock_input_api, MockOutputApi())
  2598. self.assertEqual(0, len(results))
  2599. class NewImagesWarningTest(unittest.TestCase):
  2600. def testTruePositives(self):
  2601. mock_input_api = MockInputApi()
  2602. mock_input_api.files = [
  2603. MockFile('dir/android/res/drawable/foo.png', []),
  2604. MockFile('dir/android/res/drawable-v21/bar.svg', []),
  2605. MockFile('dir/android/res/mipmap-v21-en/baz.webp', []),
  2606. MockFile('dir/android/res_gshoe/drawable-mdpi/foobar.png', []),
  2607. ]
  2608. results = PRESUBMIT._CheckNewImagesWarning(mock_input_api, MockOutputApi())
  2609. self.assertEqual(1, len(results))
  2610. self.assertEqual(4, len(results[0].items))
  2611. self.assertTrue('foo.png' in results[0].items[0].LocalPath())
  2612. self.assertTrue('bar.svg' in results[0].items[1].LocalPath())
  2613. self.assertTrue('baz.webp' in results[0].items[2].LocalPath())
  2614. self.assertTrue('foobar.png' in results[0].items[3].LocalPath())
  2615. def testFalsePositives(self):
  2616. mock_input_api = MockInputApi()
  2617. mock_input_api.files = [
  2618. MockFile('dir/pngs/README.md', []),
  2619. MockFile('java/test/res/drawable/foo.png', []),
  2620. MockFile('third_party/blink/foo.png', []),
  2621. MockFile('dir/third_party/libpng/src/foo.cc', ['foobar']),
  2622. MockFile('dir/resources.webp/.gitignore', ['foo.png']),
  2623. ]
  2624. results = PRESUBMIT._CheckNewImagesWarning(mock_input_api, MockOutputApi())
  2625. self.assertEqual(0, len(results))
  2626. class ProductIconsTest(unittest.TestCase):
  2627. def test(self):
  2628. mock_input_api = MockInputApi()
  2629. mock_input_api.files = [
  2630. MockFile('components/vector_icons/google_jetpack.icon', []),
  2631. MockFile('components/vector_icons/generic_jetpack.icon', []),
  2632. ]
  2633. results = PRESUBMIT.CheckNoProductIconsAddedToPublicRepo(mock_input_api, MockOutputApi())
  2634. self.assertEqual(1, len(results))
  2635. self.assertEqual(1, len(results[0].items))
  2636. self.assertTrue('google_jetpack.icon' in results[0].items[0])
  2637. class CheckUniquePtrTest(unittest.TestCase):
  2638. def testTruePositivesNullptr(self):
  2639. mock_input_api = MockInputApi()
  2640. mock_input_api.files = [
  2641. MockFile('dir/baz.cc', ['std::unique_ptr<T>()']),
  2642. MockFile('dir/baz-p.cc', ['std::unique_ptr<T<P>>()']),
  2643. ]
  2644. results = PRESUBMIT.CheckUniquePtrOnUpload(mock_input_api, MockOutputApi())
  2645. self.assertEqual(1, len(results))
  2646. self.assertTrue('nullptr' in results[0].message)
  2647. self.assertEqual(2, len(results[0].items))
  2648. self.assertTrue('baz.cc' in results[0].items[0])
  2649. self.assertTrue('baz-p.cc' in results[0].items[1])
  2650. def testTruePositivesConstructor(self):
  2651. mock_input_api = MockInputApi()
  2652. mock_input_api.files = [
  2653. MockFile('dir/foo.cc', ['return std::unique_ptr<T>(foo);']),
  2654. MockFile('dir/bar.mm', ['bar = std::unique_ptr<T>(foo)']),
  2655. MockFile('dir/mult.cc', [
  2656. 'return',
  2657. ' std::unique_ptr<T>(barVeryVeryLongFooSoThatItWouldNotFitAbove);'
  2658. ]),
  2659. MockFile('dir/mult2.cc', [
  2660. 'barVeryVeryLongLongBaaaaaarSoThatTheLineLimitIsAlmostReached =',
  2661. ' std::unique_ptr<T>(foo);'
  2662. ]),
  2663. MockFile('dir/mult3.cc', [
  2664. 'bar = std::unique_ptr<T>(',
  2665. ' fooVeryVeryVeryLongStillGoingWellThisWillTakeAWhileFinallyThere);'
  2666. ]),
  2667. MockFile('dir/multi_arg.cc', [
  2668. 'auto p = std::unique_ptr<std::pair<T, D>>(new std::pair(T, D));']),
  2669. ]
  2670. results = PRESUBMIT.CheckUniquePtrOnUpload(mock_input_api, MockOutputApi())
  2671. self.assertEqual(1, len(results))
  2672. self.assertTrue('std::make_unique' in results[0].message)
  2673. self.assertEqual(6, len(results[0].items))
  2674. self.assertTrue('foo.cc' in results[0].items[0])
  2675. self.assertTrue('bar.mm' in results[0].items[1])
  2676. self.assertTrue('mult.cc' in results[0].items[2])
  2677. self.assertTrue('mult2.cc' in results[0].items[3])
  2678. self.assertTrue('mult3.cc' in results[0].items[4])
  2679. self.assertTrue('multi_arg.cc' in results[0].items[5])
  2680. def testFalsePositives(self):
  2681. mock_input_api = MockInputApi()
  2682. mock_input_api.files = [
  2683. MockFile('dir/foo.cc', ['return std::unique_ptr<T[]>(foo);']),
  2684. MockFile('dir/bar.mm', ['bar = std::unique_ptr<T[]>(foo)']),
  2685. MockFile('dir/file.cc', ['std::unique_ptr<T> p = Foo();']),
  2686. MockFile('dir/baz.cc', [
  2687. 'std::unique_ptr<T> result = std::make_unique<T>();'
  2688. ]),
  2689. MockFile('dir/baz2.cc', [
  2690. 'std::unique_ptr<T> result = std::make_unique<T>('
  2691. ]),
  2692. MockFile('dir/nested.cc', ['set<std::unique_ptr<T>>();']),
  2693. MockFile('dir/nested2.cc', ['map<U, std::unique_ptr<T>>();']),
  2694. # Two-argument invocation of std::unique_ptr is exempt because there is
  2695. # no equivalent using std::make_unique.
  2696. MockFile('dir/multi_arg.cc', [
  2697. 'auto p = std::unique_ptr<T, D>(new T(), D());']),
  2698. ]
  2699. results = PRESUBMIT.CheckUniquePtrOnUpload(mock_input_api, MockOutputApi())
  2700. self.assertEqual(0, len(results))
  2701. class CheckNoDirectIncludesHeadersWhichRedefineStrCat(unittest.TestCase):
  2702. def testBlocksDirectIncludes(self):
  2703. mock_input_api = MockInputApi()
  2704. mock_input_api.files = [
  2705. MockFile('dir/foo_win.cc', ['#include "shlwapi.h"']),
  2706. MockFile('dir/bar.h', ['#include <propvarutil.h>']),
  2707. MockFile('dir/baz.h', ['#include <atlbase.h>']),
  2708. MockFile('dir/jumbo.h', ['#include "sphelper.h"']),
  2709. ]
  2710. results = PRESUBMIT.CheckNoStrCatRedefines(mock_input_api, MockOutputApi())
  2711. self.assertEqual(1, len(results))
  2712. self.assertEqual(4, len(results[0].items))
  2713. self.assertTrue('StrCat' in results[0].message)
  2714. self.assertTrue('foo_win.cc' in results[0].items[0])
  2715. self.assertTrue('bar.h' in results[0].items[1])
  2716. self.assertTrue('baz.h' in results[0].items[2])
  2717. self.assertTrue('jumbo.h' in results[0].items[3])
  2718. def testAllowsToIncludeWrapper(self):
  2719. mock_input_api = MockInputApi()
  2720. mock_input_api.files = [
  2721. MockFile('dir/baz_win.cc', ['#include "base/win/shlwapi.h"']),
  2722. MockFile('dir/baz-win.h', ['#include "base/win/atl.h"']),
  2723. ]
  2724. results = PRESUBMIT.CheckNoStrCatRedefines(mock_input_api, MockOutputApi())
  2725. self.assertEqual(0, len(results))
  2726. def testAllowsToCreateWrapper(self):
  2727. mock_input_api = MockInputApi()
  2728. mock_input_api.files = [
  2729. MockFile('base/win/shlwapi.h', [
  2730. '#include <shlwapi.h>',
  2731. '#include "base/win/windows_defines.inc"']),
  2732. ]
  2733. results = PRESUBMIT.CheckNoStrCatRedefines(mock_input_api, MockOutputApi())
  2734. self.assertEqual(0, len(results))
  2735. def testIgnoresNonImplAndHeaders(self):
  2736. mock_input_api = MockInputApi()
  2737. mock_input_api.files = [
  2738. MockFile('dir/foo_win.txt', ['#include "shlwapi.h"']),
  2739. MockFile('dir/bar.asm', ['#include <propvarutil.h>']),
  2740. ]
  2741. results = PRESUBMIT.CheckNoStrCatRedefines(mock_input_api, MockOutputApi())
  2742. self.assertEqual(0, len(results))
  2743. class StringTest(unittest.TestCase):
  2744. """Tests ICU syntax check and translation screenshots check."""
  2745. # An empty grd file.
  2746. OLD_GRD_CONTENTS = """<?xml version="1.0" encoding="UTF-8"?>
  2747. <grit latest_public_release="1" current_release="1">
  2748. <release seq="1">
  2749. <messages></messages>
  2750. </release>
  2751. </grit>
  2752. """.splitlines()
  2753. # A grd file with a single message.
  2754. NEW_GRD_CONTENTS1 = """<?xml version="1.0" encoding="UTF-8"?>
  2755. <grit latest_public_release="1" current_release="1">
  2756. <release seq="1">
  2757. <messages>
  2758. <message name="IDS_TEST1">
  2759. Test string 1
  2760. </message>
  2761. <message name="IDS_TEST_STRING_NON_TRANSLATEABLE1"
  2762. translateable="false">
  2763. Non translateable message 1, should be ignored
  2764. </message>
  2765. <message name="IDS_TEST_STRING_ACCESSIBILITY"
  2766. is_accessibility_with_no_ui="true">
  2767. Accessibility label 1, should be ignored
  2768. </message>
  2769. </messages>
  2770. </release>
  2771. </grit>
  2772. """.splitlines()
  2773. # A grd file with two messages.
  2774. NEW_GRD_CONTENTS2 = """<?xml version="1.0" encoding="UTF-8"?>
  2775. <grit latest_public_release="1" current_release="1">
  2776. <release seq="1">
  2777. <messages>
  2778. <message name="IDS_TEST1">
  2779. Test string 1
  2780. </message>
  2781. <message name="IDS_TEST2">
  2782. Test string 2
  2783. </message>
  2784. <message name="IDS_TEST_STRING_NON_TRANSLATEABLE2"
  2785. translateable="false">
  2786. Non translateable message 2, should be ignored
  2787. </message>
  2788. </messages>
  2789. </release>
  2790. </grit>
  2791. """.splitlines()
  2792. # A grd file with one ICU syntax message without syntax errors.
  2793. NEW_GRD_CONTENTS_ICU_SYNTAX_OK1 = """<?xml version="1.0" encoding="UTF-8"?>
  2794. <grit latest_public_release="1" current_release="1">
  2795. <release seq="1">
  2796. <messages>
  2797. <message name="IDS_TEST1">
  2798. {NUM, plural,
  2799. =1 {Test text for numeric one}
  2800. other {Test text for plural with {NUM} as number}}
  2801. </message>
  2802. </messages>
  2803. </release>
  2804. </grit>
  2805. """.splitlines()
  2806. # A grd file with one ICU syntax message without syntax errors.
  2807. NEW_GRD_CONTENTS_ICU_SYNTAX_OK2 = """<?xml version="1.0" encoding="UTF-8"?>
  2808. <grit latest_public_release="1" current_release="1">
  2809. <release seq="1">
  2810. <messages>
  2811. <message name="IDS_TEST1">
  2812. {NUM, plural,
  2813. =1 {Different test text for numeric one}
  2814. other {Different test text for plural with {NUM} as number}}
  2815. </message>
  2816. </messages>
  2817. </release>
  2818. </grit>
  2819. """.splitlines()
  2820. # A grd file with one ICU syntax message with syntax errors (misses a comma).
  2821. NEW_GRD_CONTENTS_ICU_SYNTAX_ERROR = """<?xml version="1.0" encoding="UTF-8"?>
  2822. <grit latest_public_release="1" current_release="1">
  2823. <release seq="1">
  2824. <messages>
  2825. <message name="IDS_TEST1">
  2826. {NUM, plural
  2827. =1 {Test text for numeric one}
  2828. other {Test text for plural with {NUM} as number}}
  2829. </message>
  2830. </messages>
  2831. </release>
  2832. </grit>
  2833. """.splitlines()
  2834. OLD_GRDP_CONTENTS = (
  2835. '<?xml version="1.0" encoding="utf-8"?>',
  2836. '<grit-part>',
  2837. '</grit-part>'
  2838. )
  2839. NEW_GRDP_CONTENTS1 = (
  2840. '<?xml version="1.0" encoding="utf-8"?>',
  2841. '<grit-part>',
  2842. '<message name="IDS_PART_TEST1">',
  2843. 'Part string 1',
  2844. '</message>',
  2845. '</grit-part>')
  2846. NEW_GRDP_CONTENTS2 = (
  2847. '<?xml version="1.0" encoding="utf-8"?>',
  2848. '<grit-part>',
  2849. '<message name="IDS_PART_TEST1">',
  2850. 'Part string 1',
  2851. '</message>',
  2852. '<message name="IDS_PART_TEST2">',
  2853. 'Part string 2',
  2854. '</message>',
  2855. '</grit-part>')
  2856. NEW_GRDP_CONTENTS3 = (
  2857. '<?xml version="1.0" encoding="utf-8"?>',
  2858. '<grit-part>',
  2859. '<message name="IDS_PART_TEST1" desc="Description with typo.">',
  2860. 'Part string 1',
  2861. '</message>',
  2862. '</grit-part>')
  2863. NEW_GRDP_CONTENTS4 = (
  2864. '<?xml version="1.0" encoding="utf-8"?>',
  2865. '<grit-part>',
  2866. '<message name="IDS_PART_TEST1" desc="Description with typo fixed.">',
  2867. 'Part string 1',
  2868. '</message>',
  2869. '</grit-part>')
  2870. NEW_GRDP_CONTENTS5 = (
  2871. '<?xml version="1.0" encoding="utf-8"?>',
  2872. '<grit-part>',
  2873. '<message name="IDS_PART_TEST1" meaning="Meaning with typo.">',
  2874. 'Part string 1',
  2875. '</message>',
  2876. '</grit-part>')
  2877. NEW_GRDP_CONTENTS6 = (
  2878. '<?xml version="1.0" encoding="utf-8"?>',
  2879. '<grit-part>',
  2880. '<message name="IDS_PART_TEST1" meaning="Meaning with typo fixed.">',
  2881. 'Part string 1',
  2882. '</message>',
  2883. '</grit-part>')
  2884. # A grdp file with one ICU syntax message without syntax errors.
  2885. NEW_GRDP_CONTENTS_ICU_SYNTAX_OK1 = (
  2886. '<?xml version="1.0" encoding="utf-8"?>',
  2887. '<grit-part>',
  2888. '<message name="IDS_PART_TEST1">',
  2889. '{NUM, plural,',
  2890. '=1 {Test text for numeric one}',
  2891. 'other {Test text for plural with {NUM} as number}}',
  2892. '</message>',
  2893. '</grit-part>')
  2894. # A grdp file with one ICU syntax message without syntax errors.
  2895. NEW_GRDP_CONTENTS_ICU_SYNTAX_OK2 = (
  2896. '<?xml version="1.0" encoding="utf-8"?>',
  2897. '<grit-part>',
  2898. '<message name="IDS_PART_TEST1">',
  2899. '{NUM, plural,',
  2900. '=1 {Different test text for numeric one}',
  2901. 'other {Different test text for plural with {NUM} as number}}',
  2902. '</message>',
  2903. '</grit-part>')
  2904. # A grdp file with one ICU syntax message with syntax errors (superfluent
  2905. # whitespace).
  2906. NEW_GRDP_CONTENTS_ICU_SYNTAX_ERROR = (
  2907. '<?xml version="1.0" encoding="utf-8"?>',
  2908. '<grit-part>',
  2909. '<message name="IDS_PART_TEST1">',
  2910. '{NUM, plural,',
  2911. '= 1 {Test text for numeric one}',
  2912. 'other {Test text for plural with {NUM} as number}}',
  2913. '</message>',
  2914. '</grit-part>')
  2915. DO_NOT_UPLOAD_PNG_MESSAGE = ('Do not include actual screenshots in the '
  2916. 'changelist. Run '
  2917. 'tools/translate/upload_screenshots.py to '
  2918. 'upload them instead:')
  2919. GENERATE_SIGNATURES_MESSAGE = ('You are adding or modifying UI strings.\n'
  2920. 'To ensure the best translations, take '
  2921. 'screenshots of the relevant UI '
  2922. '(https://g.co/chrome/translation) and add '
  2923. 'these files to your changelist:')
  2924. REMOVE_SIGNATURES_MESSAGE = ('You removed strings associated with these '
  2925. 'files. Remove:')
  2926. ICU_SYNTAX_ERROR_MESSAGE = ('ICU syntax errors were found in the following '
  2927. 'strings (problems or feedback? Contact '
  2928. 'rainhard@chromium.org):')
  2929. def makeInputApi(self, files):
  2930. input_api = MockInputApi()
  2931. input_api.files = files
  2932. # Override os_path.exists because the presubmit uses the actual
  2933. # os.path.exists.
  2934. input_api.CreateMockFileInPath(
  2935. [x.LocalPath() for x in input_api.AffectedFiles(include_deletes=True)])
  2936. return input_api
  2937. """ CL modified and added messages, but didn't add any screenshots."""
  2938. def testNoScreenshots(self):
  2939. # No new strings (file contents same). Should not warn.
  2940. input_api = self.makeInputApi([
  2941. MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS1,
  2942. self.NEW_GRD_CONTENTS1, action='M'),
  2943. MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS1,
  2944. self.NEW_GRDP_CONTENTS1, action='M')])
  2945. warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  2946. self.assertEqual(0, len(warnings))
  2947. # Add two new strings. Should have two warnings.
  2948. input_api = self.makeInputApi([
  2949. MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS2,
  2950. self.NEW_GRD_CONTENTS1, action='M'),
  2951. MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS2,
  2952. self.NEW_GRDP_CONTENTS1, action='M')])
  2953. warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  2954. self.assertEqual(1, len(warnings))
  2955. self.assertEqual(self.GENERATE_SIGNATURES_MESSAGE, warnings[0].message)
  2956. self.assertEqual('error', warnings[0].type)
  2957. self.assertEqual([
  2958. os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
  2959. os.path.join('test_grd', 'IDS_TEST2.png.sha1')],
  2960. warnings[0].items)
  2961. # Add four new strings. Should have four warnings.
  2962. input_api = self.makeInputApi([
  2963. MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS2,
  2964. self.OLD_GRD_CONTENTS, action='M'),
  2965. MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS2,
  2966. self.OLD_GRDP_CONTENTS, action='M')])
  2967. warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  2968. self.assertEqual(1, len(warnings))
  2969. self.assertEqual('error', warnings[0].type)
  2970. self.assertEqual(self.GENERATE_SIGNATURES_MESSAGE, warnings[0].message)
  2971. self.assertEqual([
  2972. os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
  2973. os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
  2974. os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
  2975. os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
  2976. ], warnings[0].items)
  2977. def testModifiedMessageDescription(self):
  2978. # CL modified a message description for a message that does not yet have a
  2979. # screenshot. Should not warn.
  2980. input_api = self.makeInputApi([
  2981. MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS3,
  2982. self.NEW_GRDP_CONTENTS4, action='M')])
  2983. warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  2984. self.assertEqual(0, len(warnings))
  2985. # CL modified a message description for a message that already has a
  2986. # screenshot. Should not warn.
  2987. input_api = self.makeInputApi([
  2988. MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS3,
  2989. self.NEW_GRDP_CONTENTS4, action='M'),
  2990. MockFile(os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
  2991. 'binary', action='A')])
  2992. warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  2993. self.assertEqual(0, len(warnings))
  2994. def testModifiedMessageMeaning(self):
  2995. # CL modified a message meaning for a message that does not yet have a
  2996. # screenshot. Should warn.
  2997. input_api = self.makeInputApi([
  2998. MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS5,
  2999. self.NEW_GRDP_CONTENTS6, action='M')])
  3000. warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  3001. self.assertEqual(1, len(warnings))
  3002. # CL modified a message meaning for a message that already has a
  3003. # screenshot. Should not warn.
  3004. input_api = self.makeInputApi([
  3005. MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS5,
  3006. self.NEW_GRDP_CONTENTS6, action='M'),
  3007. MockFile(os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
  3008. 'binary', action='A')])
  3009. warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  3010. self.assertEqual(0, len(warnings))
  3011. def testPngAddedSha1NotAdded(self):
  3012. # CL added one new message in a grd file and added the png file associated
  3013. # with it, but did not add the corresponding sha1 file. This should warn
  3014. # twice:
  3015. # - Once for the added png file (because we don't want developers to upload
  3016. # actual images)
  3017. # - Once for the missing .sha1 file
  3018. input_api = self.makeInputApi([
  3019. MockAffectedFile(
  3020. 'test.grd',
  3021. self.NEW_GRD_CONTENTS1,
  3022. self.OLD_GRD_CONTENTS,
  3023. action='M'),
  3024. MockAffectedFile(
  3025. os.path.join('test_grd', 'IDS_TEST1.png'), 'binary', action='A')
  3026. ])
  3027. warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  3028. self.assertEqual(2, len(warnings))
  3029. self.assertEqual('error', warnings[0].type)
  3030. self.assertEqual(self.DO_NOT_UPLOAD_PNG_MESSAGE, warnings[0].message)
  3031. self.assertEqual([os.path.join('test_grd', 'IDS_TEST1.png')],
  3032. warnings[0].items)
  3033. self.assertEqual('error', warnings[1].type)
  3034. self.assertEqual(self.GENERATE_SIGNATURES_MESSAGE, warnings[1].message)
  3035. self.assertEqual([os.path.join('test_grd', 'IDS_TEST1.png.sha1')],
  3036. warnings[1].items)
  3037. # CL added two messages (one in grd, one in grdp) and added the png files
  3038. # associated with the messages, but did not add the corresponding sha1
  3039. # files. This should warn twice:
  3040. # - Once for the added png files (because we don't want developers to upload
  3041. # actual images)
  3042. # - Once for the missing .sha1 files
  3043. input_api = self.makeInputApi([
  3044. # Modified files:
  3045. MockAffectedFile(
  3046. 'test.grd',
  3047. self.NEW_GRD_CONTENTS1,
  3048. self.OLD_GRD_CONTENTS,
  3049. action='M'),
  3050. MockAffectedFile(
  3051. 'part.grdp',
  3052. self.NEW_GRDP_CONTENTS1,
  3053. self.OLD_GRDP_CONTENTS,
  3054. action='M'),
  3055. # Added files:
  3056. MockAffectedFile(
  3057. os.path.join('test_grd', 'IDS_TEST1.png'), 'binary', action='A'),
  3058. MockAffectedFile(
  3059. os.path.join('part_grdp', 'IDS_PART_TEST1.png'), 'binary',
  3060. action='A')
  3061. ])
  3062. warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  3063. self.assertEqual(2, len(warnings))
  3064. self.assertEqual('error', warnings[0].type)
  3065. self.assertEqual(self.DO_NOT_UPLOAD_PNG_MESSAGE, warnings[0].message)
  3066. self.assertEqual([os.path.join('part_grdp', 'IDS_PART_TEST1.png'),
  3067. os.path.join('test_grd', 'IDS_TEST1.png')],
  3068. warnings[0].items)
  3069. self.assertEqual('error', warnings[0].type)
  3070. self.assertEqual(self.GENERATE_SIGNATURES_MESSAGE, warnings[1].message)
  3071. self.assertEqual([os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
  3072. os.path.join('test_grd', 'IDS_TEST1.png.sha1')],
  3073. warnings[1].items)
  3074. def testScreenshotsWithSha1(self):
  3075. # CL added four messages (two each in a grd and grdp) and their
  3076. # corresponding .sha1 files. No warnings.
  3077. input_api = self.makeInputApi([
  3078. # Modified files:
  3079. MockAffectedFile(
  3080. 'test.grd',
  3081. self.NEW_GRD_CONTENTS2,
  3082. self.OLD_GRD_CONTENTS,
  3083. action='M'),
  3084. MockAffectedFile(
  3085. 'part.grdp',
  3086. self.NEW_GRDP_CONTENTS2,
  3087. self.OLD_GRDP_CONTENTS,
  3088. action='M'),
  3089. # Added files:
  3090. MockFile(
  3091. os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
  3092. 'binary',
  3093. action='A'),
  3094. MockFile(
  3095. os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
  3096. 'binary',
  3097. action='A'),
  3098. MockFile(
  3099. os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
  3100. 'binary',
  3101. action='A'),
  3102. MockFile(
  3103. os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
  3104. 'binary',
  3105. action='A'),
  3106. ])
  3107. warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  3108. self.assertEqual([], warnings)
  3109. def testScreenshotsRemovedWithSha1(self):
  3110. # Replace new contents with old contents in grd and grp files, removing
  3111. # IDS_TEST1, IDS_TEST2, IDS_PART_TEST1 and IDS_PART_TEST2.
  3112. # Should warn to remove the sha1 files associated with these strings.
  3113. input_api = self.makeInputApi([
  3114. # Modified files:
  3115. MockAffectedFile(
  3116. 'test.grd',
  3117. self.OLD_GRD_CONTENTS, # new_contents
  3118. self.NEW_GRD_CONTENTS2, # old_contents
  3119. action='M'),
  3120. MockAffectedFile(
  3121. 'part.grdp',
  3122. self.OLD_GRDP_CONTENTS, # new_contents
  3123. self.NEW_GRDP_CONTENTS2, # old_contents
  3124. action='M'),
  3125. # Unmodified files:
  3126. MockFile(os.path.join('test_grd', 'IDS_TEST1.png.sha1'), 'binary', ''),
  3127. MockFile(os.path.join('test_grd', 'IDS_TEST2.png.sha1'), 'binary', ''),
  3128. MockFile(os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
  3129. 'binary', ''),
  3130. MockFile(os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
  3131. 'binary', '')
  3132. ])
  3133. warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  3134. self.assertEqual(1, len(warnings))
  3135. self.assertEqual('error', warnings[0].type)
  3136. self.assertEqual(self.REMOVE_SIGNATURES_MESSAGE, warnings[0].message)
  3137. self.assertEqual([
  3138. os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
  3139. os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
  3140. os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
  3141. os.path.join('test_grd', 'IDS_TEST2.png.sha1')
  3142. ], warnings[0].items)
  3143. # Same as above, but this time one of the .sha1 files is also removed.
  3144. input_api = self.makeInputApi([
  3145. # Modified files:
  3146. MockAffectedFile(
  3147. 'test.grd',
  3148. self.OLD_GRD_CONTENTS, # new_contents
  3149. self.NEW_GRD_CONTENTS2, # old_contents
  3150. action='M'),
  3151. MockAffectedFile(
  3152. 'part.grdp',
  3153. self.OLD_GRDP_CONTENTS, # new_contents
  3154. self.NEW_GRDP_CONTENTS2, # old_contents
  3155. action='M'),
  3156. # Unmodified files:
  3157. MockFile(os.path.join('test_grd', 'IDS_TEST1.png.sha1'), 'binary', ''),
  3158. MockFile(os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
  3159. 'binary', ''),
  3160. # Deleted files:
  3161. MockAffectedFile(
  3162. os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
  3163. '',
  3164. 'old_contents',
  3165. action='D'),
  3166. MockAffectedFile(
  3167. os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
  3168. '',
  3169. 'old_contents',
  3170. action='D')
  3171. ])
  3172. warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  3173. self.assertEqual(1, len(warnings))
  3174. self.assertEqual('error', warnings[0].type)
  3175. self.assertEqual(self.REMOVE_SIGNATURES_MESSAGE, warnings[0].message)
  3176. self.assertEqual([os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
  3177. os.path.join('test_grd', 'IDS_TEST1.png.sha1')
  3178. ], warnings[0].items)
  3179. # Remove all sha1 files. There should be no warnings.
  3180. input_api = self.makeInputApi([
  3181. # Modified files:
  3182. MockAffectedFile(
  3183. 'test.grd',
  3184. self.OLD_GRD_CONTENTS,
  3185. self.NEW_GRD_CONTENTS2,
  3186. action='M'),
  3187. MockAffectedFile(
  3188. 'part.grdp',
  3189. self.OLD_GRDP_CONTENTS,
  3190. self.NEW_GRDP_CONTENTS2,
  3191. action='M'),
  3192. # Deleted files:
  3193. MockFile(
  3194. os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
  3195. 'binary',
  3196. action='D'),
  3197. MockFile(
  3198. os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
  3199. 'binary',
  3200. action='D'),
  3201. MockFile(
  3202. os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
  3203. 'binary',
  3204. action='D'),
  3205. MockFile(
  3206. os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
  3207. 'binary',
  3208. action='D')
  3209. ])
  3210. warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  3211. self.assertEqual([], warnings)
  3212. def testIcuSyntax(self):
  3213. # Add valid ICU syntax string. Should not raise an error.
  3214. input_api = self.makeInputApi([
  3215. MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS_ICU_SYNTAX_OK2,
  3216. self.NEW_GRD_CONTENTS1, action='M'),
  3217. MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS_ICU_SYNTAX_OK2,
  3218. self.NEW_GRDP_CONTENTS1, action='M')])
  3219. results = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  3220. # We expect no ICU syntax errors.
  3221. icu_errors = [e for e in results
  3222. if e.message == self.ICU_SYNTAX_ERROR_MESSAGE]
  3223. self.assertEqual(0, len(icu_errors))
  3224. # Valid changes in ICU syntax. Should not raise an error.
  3225. input_api = self.makeInputApi([
  3226. MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS_ICU_SYNTAX_OK2,
  3227. self.NEW_GRD_CONTENTS_ICU_SYNTAX_OK1, action='M'),
  3228. MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS_ICU_SYNTAX_OK2,
  3229. self.NEW_GRDP_CONTENTS_ICU_SYNTAX_OK1, action='M')])
  3230. results = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  3231. # We expect no ICU syntax errors.
  3232. icu_errors = [e for e in results
  3233. if e.message == self.ICU_SYNTAX_ERROR_MESSAGE]
  3234. self.assertEqual(0, len(icu_errors))
  3235. # Add invalid ICU syntax strings. Should raise two errors.
  3236. input_api = self.makeInputApi([
  3237. MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS_ICU_SYNTAX_ERROR,
  3238. self.NEW_GRD_CONTENTS1, action='M'),
  3239. MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS_ICU_SYNTAX_ERROR,
  3240. self.NEW_GRD_CONTENTS1, action='M')])
  3241. results = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  3242. # We expect 2 ICU syntax errors.
  3243. icu_errors = [e for e in results
  3244. if e.message == self.ICU_SYNTAX_ERROR_MESSAGE]
  3245. self.assertEqual(1, len(icu_errors))
  3246. self.assertEqual([
  3247. 'IDS_TEST1: This message looks like an ICU plural, but does not follow '
  3248. 'ICU syntax.',
  3249. 'IDS_PART_TEST1: Variant "= 1" is not valid for plural message'
  3250. ], icu_errors[0].items)
  3251. # Change two strings to have ICU syntax errors. Should raise two errors.
  3252. input_api = self.makeInputApi([
  3253. MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS_ICU_SYNTAX_ERROR,
  3254. self.NEW_GRD_CONTENTS_ICU_SYNTAX_OK1, action='M'),
  3255. MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS_ICU_SYNTAX_ERROR,
  3256. self.NEW_GRDP_CONTENTS_ICU_SYNTAX_OK1, action='M')])
  3257. results = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
  3258. # We expect 2 ICU syntax errors.
  3259. icu_errors = [e for e in results
  3260. if e.message == self.ICU_SYNTAX_ERROR_MESSAGE]
  3261. self.assertEqual(1, len(icu_errors))
  3262. self.assertEqual([
  3263. 'IDS_TEST1: This message looks like an ICU plural, but does not follow '
  3264. 'ICU syntax.',
  3265. 'IDS_PART_TEST1: Variant "= 1" is not valid for plural message'
  3266. ], icu_errors[0].items)
  3267. class TranslationExpectationsTest(unittest.TestCase):
  3268. ERROR_MESSAGE_FORMAT = (
  3269. "Failed to get a list of translatable grd files. "
  3270. "This happens when:\n"
  3271. " - One of the modified grd or grdp files cannot be parsed or\n"
  3272. " - %s is not updated.\n"
  3273. "Stack:\n"
  3274. )
  3275. REPO_ROOT = os.path.join('tools', 'translation', 'testdata')
  3276. # This lists all .grd files under REPO_ROOT.
  3277. EXPECTATIONS = os.path.join(REPO_ROOT,
  3278. "translation_expectations.pyl")
  3279. # This lists all .grd files under REPO_ROOT except unlisted.grd.
  3280. EXPECTATIONS_WITHOUT_UNLISTED_FILE = os.path.join(
  3281. REPO_ROOT, "translation_expectations_without_unlisted_file.pyl")
  3282. # Tests that the presubmit doesn't return when no grd or grdp files are
  3283. # modified.
  3284. def testExpectationsNoModifiedGrd(self):
  3285. input_api = MockInputApi()
  3286. input_api.files = [
  3287. MockAffectedFile('not_used.txt', 'not used', 'not used', action='M')
  3288. ]
  3289. # Fake list of all grd files in the repo. This list is missing all grd/grdps
  3290. # under tools/translation/testdata. This is OK because the presubmit won't
  3291. # run in the first place since there are no modified grd/grps in input_api.
  3292. grd_files = ['doesnt_exist_doesnt_matter.grd']
  3293. warnings = PRESUBMIT.CheckTranslationExpectations(
  3294. input_api, MockOutputApi(), self.REPO_ROOT, self.EXPECTATIONS,
  3295. grd_files)
  3296. self.assertEqual(0, len(warnings))
  3297. # Tests that the list of files passed to the presubmit matches the list of
  3298. # files in the expectations.
  3299. def testExpectationsSuccess(self):
  3300. # Mock input file list needs a grd or grdp file in order to run the
  3301. # presubmit. The file itself doesn't matter.
  3302. input_api = MockInputApi()
  3303. input_api.files = [
  3304. MockAffectedFile('dummy.grd', 'not used', 'not used', action='M')
  3305. ]
  3306. # List of all grd files in the repo.
  3307. grd_files = ['test.grd', 'unlisted.grd', 'not_translated.grd',
  3308. 'internal.grd']
  3309. warnings = PRESUBMIT.CheckTranslationExpectations(
  3310. input_api, MockOutputApi(), self.REPO_ROOT, self.EXPECTATIONS,
  3311. grd_files)
  3312. self.assertEqual(0, len(warnings))
  3313. # Tests that the presubmit warns when a file is listed in expectations, but
  3314. # does not actually exist.
  3315. def testExpectationsMissingFile(self):
  3316. # Mock input file list needs a grd or grdp file in order to run the
  3317. # presubmit.
  3318. input_api = MockInputApi()
  3319. input_api.files = [
  3320. MockAffectedFile('dummy.grd', 'not used', 'not used', action='M')
  3321. ]
  3322. # unlisted.grd is listed under tools/translation/testdata but is not
  3323. # included in translation expectations.
  3324. grd_files = ['unlisted.grd', 'not_translated.grd', 'internal.grd']
  3325. warnings = PRESUBMIT.CheckTranslationExpectations(
  3326. input_api, MockOutputApi(), self.REPO_ROOT, self.EXPECTATIONS,
  3327. grd_files)
  3328. self.assertEqual(1, len(warnings))
  3329. self.assertTrue(warnings[0].message.startswith(
  3330. self.ERROR_MESSAGE_FORMAT % self.EXPECTATIONS))
  3331. self.assertTrue(
  3332. ("test.grd is listed in the translation expectations, "
  3333. "but this grd file does not exist")
  3334. in warnings[0].message)
  3335. # Tests that the presubmit warns when a file is not listed in expectations but
  3336. # does actually exist.
  3337. def testExpectationsUnlistedFile(self):
  3338. # Mock input file list needs a grd or grdp file in order to run the
  3339. # presubmit.
  3340. input_api = MockInputApi()
  3341. input_api.files = [
  3342. MockAffectedFile('dummy.grd', 'not used', 'not used', action='M')
  3343. ]
  3344. # unlisted.grd is listed under tools/translation/testdata but is not
  3345. # included in translation expectations.
  3346. grd_files = ['test.grd', 'unlisted.grd', 'not_translated.grd',
  3347. 'internal.grd']
  3348. warnings = PRESUBMIT.CheckTranslationExpectations(
  3349. input_api, MockOutputApi(), self.REPO_ROOT,
  3350. self.EXPECTATIONS_WITHOUT_UNLISTED_FILE, grd_files)
  3351. self.assertEqual(1, len(warnings))
  3352. self.assertTrue(warnings[0].message.startswith(
  3353. self.ERROR_MESSAGE_FORMAT % self.EXPECTATIONS_WITHOUT_UNLISTED_FILE))
  3354. self.assertTrue(
  3355. ("unlisted.grd appears to be translatable "
  3356. "(because it contains <file> or <message> elements), "
  3357. "but is not listed in the translation expectations.")
  3358. in warnings[0].message)
  3359. # Tests that the presubmit warns twice:
  3360. # - for a non-existing file listed in expectations
  3361. # - for an existing file not listed in expectations
  3362. def testMultipleWarnings(self):
  3363. # Mock input file list needs a grd or grdp file in order to run the
  3364. # presubmit.
  3365. input_api = MockInputApi()
  3366. input_api.files = [
  3367. MockAffectedFile('dummy.grd', 'not used', 'not used', action='M')
  3368. ]
  3369. # unlisted.grd is listed under tools/translation/testdata but is not
  3370. # included in translation expectations.
  3371. # test.grd is not listed under tools/translation/testdata but is included
  3372. # in translation expectations.
  3373. grd_files = ['unlisted.grd', 'not_translated.grd', 'internal.grd']
  3374. warnings = PRESUBMIT.CheckTranslationExpectations(
  3375. input_api, MockOutputApi(), self.REPO_ROOT,
  3376. self.EXPECTATIONS_WITHOUT_UNLISTED_FILE, grd_files)
  3377. self.assertEqual(1, len(warnings))
  3378. self.assertTrue(warnings[0].message.startswith(
  3379. self.ERROR_MESSAGE_FORMAT % self.EXPECTATIONS_WITHOUT_UNLISTED_FILE))
  3380. self.assertTrue(
  3381. ("unlisted.grd appears to be translatable "
  3382. "(because it contains <file> or <message> elements), "
  3383. "but is not listed in the translation expectations.")
  3384. in warnings[0].message)
  3385. self.assertTrue(
  3386. ("test.grd is listed in the translation expectations, "
  3387. "but this grd file does not exist")
  3388. in warnings[0].message)
  3389. class DISABLETypoInTest(unittest.TestCase):
  3390. def testPositive(self):
  3391. # Verify the typo "DISABLE_" instead of "DISABLED_" in various contexts
  3392. # where the desire is to disable a test.
  3393. tests = [
  3394. # Disabled on one platform:
  3395. '#if defined(OS_WIN)\n'
  3396. '#define MAYBE_FoobarTest DISABLE_FoobarTest\n'
  3397. '#else\n'
  3398. '#define MAYBE_FoobarTest FoobarTest\n'
  3399. '#endif\n',
  3400. # Disabled on one platform spread cross lines:
  3401. '#if defined(OS_WIN)\n'
  3402. '#define MAYBE_FoobarTest \\\n'
  3403. ' DISABLE_FoobarTest\n'
  3404. '#else\n'
  3405. '#define MAYBE_FoobarTest FoobarTest\n'
  3406. '#endif\n',
  3407. # Disabled on all platforms:
  3408. ' TEST_F(FoobarTest, DISABLE_Foo)\n{\n}',
  3409. # Disabled on all platforms but multiple lines
  3410. ' TEST_F(FoobarTest,\n DISABLE_foo){\n}\n',
  3411. ]
  3412. for test in tests:
  3413. mock_input_api = MockInputApi()
  3414. mock_input_api.files = [
  3415. MockFile('some/path/foo_unittest.cc', test.splitlines()),
  3416. ]
  3417. results = PRESUBMIT.CheckNoDISABLETypoInTests(mock_input_api,
  3418. MockOutputApi())
  3419. self.assertEqual(
  3420. 1,
  3421. len(results),
  3422. msg=('expected len(results) == 1 but got %d in test: %s' %
  3423. (len(results), test)))
  3424. self.assertTrue(
  3425. 'foo_unittest.cc' in results[0].message,
  3426. msg=('expected foo_unittest.cc in message but got %s in test %s' %
  3427. (results[0].message, test)))
  3428. def testIgnoreNotTestFiles(self):
  3429. mock_input_api = MockInputApi()
  3430. mock_input_api.files = [
  3431. MockFile('some/path/foo.cc', 'TEST_F(FoobarTest, DISABLE_Foo)'),
  3432. ]
  3433. results = PRESUBMIT.CheckNoDISABLETypoInTests(mock_input_api,
  3434. MockOutputApi())
  3435. self.assertEqual(0, len(results))
  3436. def testIgnoreDeletedFiles(self):
  3437. mock_input_api = MockInputApi()
  3438. mock_input_api.files = [
  3439. MockFile('some/path/foo.cc', 'TEST_F(FoobarTest, Foo)', action='D'),
  3440. ]
  3441. results = PRESUBMIT.CheckNoDISABLETypoInTests(mock_input_api,
  3442. MockOutputApi())
  3443. self.assertEqual(0, len(results))
  3444. class ForgettingMAYBEInTests(unittest.TestCase):
  3445. def testPositive(self):
  3446. test = (
  3447. '#if defined(HAS_ENERGY)\n'
  3448. '#define MAYBE_CastExplosion DISABLED_CastExplosion\n'
  3449. '#else\n'
  3450. '#define MAYBE_CastExplosion CastExplosion\n'
  3451. '#endif\n'
  3452. 'TEST_F(ArchWizard, CastExplosion) {\n'
  3453. '#if defined(ARCH_PRIEST_IN_PARTY)\n'
  3454. '#define MAYBE_ArchPriest ArchPriest\n'
  3455. '#else\n'
  3456. '#define MAYBE_ArchPriest DISABLED_ArchPriest\n'
  3457. '#endif\n'
  3458. 'TEST_F(ArchPriest, CastNaturesBounty) {\n'
  3459. '#if !defined(CRUSADER_IN_PARTY)\n'
  3460. '#define MAYBE_Crusader \\\n'
  3461. ' DISABLED_Crusader \n'
  3462. '#else\n'
  3463. '#define MAYBE_Crusader \\\n'
  3464. ' Crusader\n'
  3465. '#endif\n'
  3466. ' TEST_F(\n'
  3467. ' Crusader,\n'
  3468. ' CastTaunt) { }\n'
  3469. '#if defined(LEARNED_BASIC_SKILLS)\n'
  3470. '#define MAYBE_CastSteal \\\n'
  3471. ' DISABLED_CastSteal \n'
  3472. '#else\n'
  3473. '#define MAYBE_CastSteal \\\n'
  3474. ' CastSteal\n'
  3475. '#endif\n'
  3476. ' TEST_F(\n'
  3477. ' ThiefClass,\n'
  3478. ' CastSteal) { }\n'
  3479. )
  3480. mock_input_api = MockInputApi()
  3481. mock_input_api.files = [
  3482. MockFile('fantasyworld/classes_unittest.cc', test.splitlines()),
  3483. ]
  3484. results = PRESUBMIT.CheckForgettingMAYBEInTests(mock_input_api,
  3485. MockOutputApi())
  3486. self.assertEqual(4, len(results))
  3487. self.assertTrue('CastExplosion' in results[0].message)
  3488. self.assertTrue('fantasyworld/classes_unittest.cc:2' in results[0].message)
  3489. self.assertTrue('ArchPriest' in results[1].message)
  3490. self.assertTrue('fantasyworld/classes_unittest.cc:8' in results[1].message)
  3491. self.assertTrue('Crusader' in results[2].message)
  3492. self.assertTrue('fantasyworld/classes_unittest.cc:14' in results[2].message)
  3493. self.assertTrue('CastSteal' in results[3].message)
  3494. self.assertTrue('fantasyworld/classes_unittest.cc:24' in results[3].message)
  3495. def testNegative(self):
  3496. test = (
  3497. '#if defined(HAS_ENERGY)\n'
  3498. '#define MAYBE_CastExplosion DISABLED_CastExplosion\n'
  3499. '#else\n'
  3500. '#define MAYBE_CastExplosion CastExplosion\n'
  3501. '#endif\n'
  3502. 'TEST_F(ArchWizard, MAYBE_CastExplosion) {\n'
  3503. '#if defined(ARCH_PRIEST_IN_PARTY)\n'
  3504. '#define MAYBE_ArchPriest ArchPriest\n'
  3505. '#else\n'
  3506. '#define MAYBE_ArchPriest DISABLED_ArchPriest\n'
  3507. '#endif\n'
  3508. 'TEST_F(MAYBE_ArchPriest, CastNaturesBounty) {\n'
  3509. '#if !defined(CRUSADER_IN_PARTY)\n'
  3510. '#define MAYBE_Crusader \\\n'
  3511. ' DISABLED_Crusader \n'
  3512. '#else\n'
  3513. '#define MAYBE_Crusader \\\n'
  3514. ' Crusader\n'
  3515. '#endif\n'
  3516. ' TEST_F(\n'
  3517. ' MAYBE_Crusader,\n'
  3518. ' CastTaunt) { }\n'
  3519. '#if defined(LEARNED_BASIC_SKILLS)\n'
  3520. '#define MAYBE_CastSteal \\\n'
  3521. ' DISABLED_CastSteal \n'
  3522. '#else\n'
  3523. '#define MAYBE_CastSteal \\\n'
  3524. ' CastSteal\n'
  3525. '#endif\n'
  3526. ' TEST_F(\n'
  3527. ' ThiefClass,\n'
  3528. ' MAYBE_CastSteal) { }\n'
  3529. )
  3530. mock_input_api = MockInputApi()
  3531. mock_input_api.files = [
  3532. MockFile('fantasyworld/classes_unittest.cc', test.splitlines()),
  3533. ]
  3534. results = PRESUBMIT.CheckForgettingMAYBEInTests(mock_input_api,
  3535. MockOutputApi())
  3536. self.assertEqual(0, len(results))
  3537. class CheckFuzzTargetsTest(unittest.TestCase):
  3538. def _check(self, files):
  3539. mock_input_api = MockInputApi()
  3540. mock_input_api.files = []
  3541. for fname, contents in files.items():
  3542. mock_input_api.files.append(MockFile(fname, contents.splitlines()))
  3543. return PRESUBMIT.CheckFuzzTargetsOnUpload(mock_input_api, MockOutputApi())
  3544. def testLibFuzzerSourcesIgnored(self):
  3545. results = self._check({
  3546. "third_party/lib/Fuzzer/FuzzerDriver.cpp": "LLVMFuzzerInitialize",
  3547. })
  3548. self.assertEqual(results, [])
  3549. def testNonCodeFilesIgnored(self):
  3550. results = self._check({
  3551. "README.md": "LLVMFuzzerInitialize",
  3552. })
  3553. self.assertEqual(results, [])
  3554. def testNoErrorHeaderPresent(self):
  3555. results = self._check({
  3556. "fuzzer.cc": (
  3557. "#include \"testing/libfuzzer/libfuzzer_exports.h\"\n" +
  3558. "LLVMFuzzerInitialize"
  3559. )
  3560. })
  3561. self.assertEqual(results, [])
  3562. def testErrorMissingHeader(self):
  3563. results = self._check({
  3564. "fuzzer.cc": "LLVMFuzzerInitialize"
  3565. })
  3566. self.assertEqual(len(results), 1)
  3567. self.assertEqual(results[0].items, ['fuzzer.cc'])
  3568. class SetNoParentTest(unittest.TestCase):
  3569. def testSetNoParentTopLevelAllowed(self):
  3570. mock_input_api = MockInputApi()
  3571. mock_input_api.files = [
  3572. MockAffectedFile('goat/OWNERS',
  3573. [
  3574. 'set noparent',
  3575. 'jochen@chromium.org',
  3576. ])
  3577. ]
  3578. mock_output_api = MockOutputApi()
  3579. errors = PRESUBMIT.CheckSetNoParent(mock_input_api, mock_output_api)
  3580. self.assertEqual([], errors)
  3581. def testSetNoParentMissing(self):
  3582. mock_input_api = MockInputApi()
  3583. mock_input_api.files = [
  3584. MockAffectedFile('services/goat/OWNERS',
  3585. [
  3586. 'set noparent',
  3587. 'jochen@chromium.org',
  3588. 'per-file *.json=set noparent',
  3589. 'per-file *.json=jochen@chromium.org',
  3590. ])
  3591. ]
  3592. mock_output_api = MockOutputApi()
  3593. errors = PRESUBMIT.CheckSetNoParent(mock_input_api, mock_output_api)
  3594. self.assertEqual(1, len(errors))
  3595. self.assertTrue('goat/OWNERS:1' in errors[0].long_text)
  3596. self.assertTrue('goat/OWNERS:3' in errors[0].long_text)
  3597. def testSetNoParentWithCorrectRule(self):
  3598. mock_input_api = MockInputApi()
  3599. mock_input_api.files = [
  3600. MockAffectedFile('services/goat/OWNERS',
  3601. [
  3602. 'set noparent',
  3603. 'file://ipc/SECURITY_OWNERS',
  3604. 'per-file *.json=set noparent',
  3605. 'per-file *.json=file://ipc/SECURITY_OWNERS',
  3606. ])
  3607. ]
  3608. mock_output_api = MockOutputApi()
  3609. errors = PRESUBMIT.CheckSetNoParent(mock_input_api, mock_output_api)
  3610. self.assertEqual([], errors)
  3611. class MojomStabilityCheckTest(unittest.TestCase):
  3612. def runTestWithAffectedFiles(self, affected_files):
  3613. mock_input_api = MockInputApi()
  3614. mock_input_api.files = affected_files
  3615. mock_output_api = MockOutputApi()
  3616. return PRESUBMIT.CheckStableMojomChanges(
  3617. mock_input_api, mock_output_api)
  3618. def testSafeChangePasses(self):
  3619. errors = self.runTestWithAffectedFiles([
  3620. MockAffectedFile('foo/foo.mojom',
  3621. ['[Stable] struct S { [MinVersion=1] int32 x; };'],
  3622. old_contents=['[Stable] struct S {};'])
  3623. ])
  3624. self.assertEqual([], errors)
  3625. def testBadChangeFails(self):
  3626. errors = self.runTestWithAffectedFiles([
  3627. MockAffectedFile('foo/foo.mojom',
  3628. ['[Stable] struct S { int32 x; };'],
  3629. old_contents=['[Stable] struct S {};'])
  3630. ])
  3631. self.assertEqual(1, len(errors))
  3632. self.assertTrue('not backward-compatible' in errors[0].message)
  3633. def testDeletedFile(self):
  3634. """Regression test for https://crbug.com/1091407."""
  3635. errors = self.runTestWithAffectedFiles([
  3636. MockAffectedFile('a.mojom', [], old_contents=['struct S {};'],
  3637. action='D'),
  3638. MockAffectedFile('b.mojom',
  3639. ['struct S {}; struct T { S s; };'],
  3640. old_contents=['import "a.mojom"; struct T { S s; };'])
  3641. ])
  3642. self.assertEqual([], errors)
  3643. class CheckForUseOfChromeAppsDeprecationsTest(unittest.TestCase):
  3644. ERROR_MSG_PIECE = 'technologies which will soon be deprecated'
  3645. # Each positive test is also a naive negative test for the other cases.
  3646. def testWarningNMF(self):
  3647. mock_input_api = MockInputApi()
  3648. mock_input_api.files = [
  3649. MockAffectedFile(
  3650. 'foo.NMF',
  3651. ['"program"', '"Z":"content"', 'B'],
  3652. ['"program"', 'B'],
  3653. scm_diff='\n'.join([
  3654. '--- foo.NMF.old 2020-12-02 20:40:54.430676385 +0100',
  3655. '+++ foo.NMF.new 2020-12-02 20:41:02.086700197 +0100',
  3656. '@@ -1,2 +1,3 @@',
  3657. ' "program"',
  3658. '+"Z":"content"',
  3659. ' B']),
  3660. action='M')
  3661. ]
  3662. mock_output_api = MockOutputApi()
  3663. errors = PRESUBMIT.CheckForUseOfChromeAppsDeprecations(mock_input_api,
  3664. mock_output_api)
  3665. self.assertEqual(1, len(errors))
  3666. self.assertTrue( self.ERROR_MSG_PIECE in errors[0].message)
  3667. self.assertTrue( 'foo.NMF' in errors[0].message)
  3668. def testWarningManifest(self):
  3669. mock_input_api = MockInputApi()
  3670. mock_input_api.files = [
  3671. MockAffectedFile(
  3672. 'manifest.json',
  3673. ['"app":', '"Z":"content"', 'B'],
  3674. ['"app":"', 'B'],
  3675. scm_diff='\n'.join([
  3676. '--- manifest.json.old 2020-12-02 20:40:54.430676385 +0100',
  3677. '+++ manifest.json.new 2020-12-02 20:41:02.086700197 +0100',
  3678. '@@ -1,2 +1,3 @@',
  3679. ' "app"',
  3680. '+"Z":"content"',
  3681. ' B']),
  3682. action='M')
  3683. ]
  3684. mock_output_api = MockOutputApi()
  3685. errors = PRESUBMIT.CheckForUseOfChromeAppsDeprecations(mock_input_api,
  3686. mock_output_api)
  3687. self.assertEqual(1, len(errors))
  3688. self.assertTrue( self.ERROR_MSG_PIECE in errors[0].message)
  3689. self.assertTrue( 'manifest.json' in errors[0].message)
  3690. def testOKWarningManifestWithoutApp(self):
  3691. mock_input_api = MockInputApi()
  3692. mock_input_api.files = [
  3693. MockAffectedFile(
  3694. 'manifest.json',
  3695. ['"name":', '"Z":"content"', 'B'],
  3696. ['"name":"', 'B'],
  3697. scm_diff='\n'.join([
  3698. '--- manifest.json.old 2020-12-02 20:40:54.430676385 +0100',
  3699. '+++ manifest.json.new 2020-12-02 20:41:02.086700197 +0100',
  3700. '@@ -1,2 +1,3 @@',
  3701. ' "app"',
  3702. '+"Z":"content"',
  3703. ' B']),
  3704. action='M')
  3705. ]
  3706. mock_output_api = MockOutputApi()
  3707. errors = PRESUBMIT.CheckForUseOfChromeAppsDeprecations(mock_input_api,
  3708. mock_output_api)
  3709. self.assertEqual(0, len(errors))
  3710. def testWarningPPAPI(self):
  3711. mock_input_api = MockInputApi()
  3712. mock_input_api.files = [
  3713. MockAffectedFile(
  3714. 'foo.hpp',
  3715. ['A', '#include <ppapi.h>', 'B'],
  3716. ['A', 'B'],
  3717. scm_diff='\n'.join([
  3718. '--- foo.hpp.old 2020-12-02 20:40:54.430676385 +0100',
  3719. '+++ foo.hpp.new 2020-12-02 20:41:02.086700197 +0100',
  3720. '@@ -1,2 +1,3 @@',
  3721. ' A',
  3722. '+#include <ppapi.h>',
  3723. ' B']),
  3724. action='M')
  3725. ]
  3726. mock_output_api = MockOutputApi()
  3727. errors = PRESUBMIT.CheckForUseOfChromeAppsDeprecations(mock_input_api,
  3728. mock_output_api)
  3729. self.assertEqual(1, len(errors))
  3730. self.assertTrue( self.ERROR_MSG_PIECE in errors[0].message)
  3731. self.assertTrue( 'foo.hpp' in errors[0].message)
  3732. def testNoWarningPPAPI(self):
  3733. mock_input_api = MockInputApi()
  3734. mock_input_api.files = [
  3735. MockAffectedFile(
  3736. 'foo.txt',
  3737. ['A', 'Peppapig', 'B'],
  3738. ['A', 'B'],
  3739. scm_diff='\n'.join([
  3740. '--- foo.txt.old 2020-12-02 20:40:54.430676385 +0100',
  3741. '+++ foo.txt.new 2020-12-02 20:41:02.086700197 +0100',
  3742. '@@ -1,2 +1,3 @@',
  3743. ' A',
  3744. '+Peppapig',
  3745. ' B']),
  3746. action='M')
  3747. ]
  3748. mock_output_api = MockOutputApi()
  3749. errors = PRESUBMIT.CheckForUseOfChromeAppsDeprecations(mock_input_api,
  3750. mock_output_api)
  3751. self.assertEqual(0, len(errors))
  3752. class CheckDeprecationOfPreferencesTest(unittest.TestCase):
  3753. # Test that a warning is generated if a preference registration is removed
  3754. # from a random file.
  3755. def testWarning(self):
  3756. mock_input_api = MockInputApi()
  3757. mock_input_api.files = [
  3758. MockAffectedFile(
  3759. 'foo.cc',
  3760. ['A', 'B'],
  3761. ['A', 'prefs->RegisterStringPref("foo", "default");', 'B'],
  3762. scm_diff='\n'.join([
  3763. '--- foo.cc.old 2020-12-02 20:40:54.430676385 +0100',
  3764. '+++ foo.cc.new 2020-12-02 20:41:02.086700197 +0100',
  3765. '@@ -1,3 +1,2 @@',
  3766. ' A',
  3767. '-prefs->RegisterStringPref("foo", "default");',
  3768. ' B']),
  3769. action='M')
  3770. ]
  3771. mock_output_api = MockOutputApi()
  3772. errors = PRESUBMIT.CheckDeprecationOfPreferences(mock_input_api,
  3773. mock_output_api)
  3774. self.assertEqual(1, len(errors))
  3775. self.assertTrue(
  3776. 'Discovered possible removal of preference registrations' in
  3777. errors[0].message)
  3778. # Test that a warning is inhibited if the preference registration was moved
  3779. # to the deprecation functions in browser prefs.
  3780. def testNoWarningForMigration(self):
  3781. mock_input_api = MockInputApi()
  3782. mock_input_api.files = [
  3783. # RegisterStringPref was removed from foo.cc.
  3784. MockAffectedFile(
  3785. 'foo.cc',
  3786. ['A', 'B'],
  3787. ['A', 'prefs->RegisterStringPref("foo", "default");', 'B'],
  3788. scm_diff='\n'.join([
  3789. '--- foo.cc.old 2020-12-02 20:40:54.430676385 +0100',
  3790. '+++ foo.cc.new 2020-12-02 20:41:02.086700197 +0100',
  3791. '@@ -1,3 +1,2 @@',
  3792. ' A',
  3793. '-prefs->RegisterStringPref("foo", "default");',
  3794. ' B']),
  3795. action='M'),
  3796. # But the preference was properly migrated.
  3797. MockAffectedFile(
  3798. 'chrome/browser/prefs/browser_prefs.cc',
  3799. [
  3800. '// BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
  3801. '// END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
  3802. '// BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS',
  3803. 'prefs->RegisterStringPref("foo", "default");',
  3804. '// END_MIGRATE_OBSOLETE_PROFILE_PREFS',
  3805. ],
  3806. [
  3807. '// BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
  3808. '// END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
  3809. '// BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS',
  3810. '// END_MIGRATE_OBSOLETE_PROFILE_PREFS',
  3811. ],
  3812. scm_diff='\n'.join([
  3813. '--- browser_prefs.cc.old 2020-12-02 20:51:40.812686731 +0100',
  3814. '+++ browser_prefs.cc.new 2020-12-02 20:52:02.936755539 +0100',
  3815. '@@ -2,3 +2,4 @@',
  3816. ' // END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
  3817. ' // BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS',
  3818. '+prefs->RegisterStringPref("foo", "default");',
  3819. ' // END_MIGRATE_OBSOLETE_PROFILE_PREFS']),
  3820. action='M'),
  3821. ]
  3822. mock_output_api = MockOutputApi()
  3823. errors = PRESUBMIT.CheckDeprecationOfPreferences(mock_input_api,
  3824. mock_output_api)
  3825. self.assertEqual(0, len(errors))
  3826. # Test that a warning is NOT inhibited if the preference registration was
  3827. # moved to a place outside of the migration functions in browser_prefs.cc
  3828. def testWarningForImproperMigration(self):
  3829. mock_input_api = MockInputApi()
  3830. mock_input_api.files = [
  3831. # RegisterStringPref was removed from foo.cc.
  3832. MockAffectedFile(
  3833. 'foo.cc',
  3834. ['A', 'B'],
  3835. ['A', 'prefs->RegisterStringPref("foo", "default");', 'B'],
  3836. scm_diff='\n'.join([
  3837. '--- foo.cc.old 2020-12-02 20:40:54.430676385 +0100',
  3838. '+++ foo.cc.new 2020-12-02 20:41:02.086700197 +0100',
  3839. '@@ -1,3 +1,2 @@',
  3840. ' A',
  3841. '-prefs->RegisterStringPref("foo", "default");',
  3842. ' B']),
  3843. action='M'),
  3844. # The registration call was moved to a place in browser_prefs.cc that
  3845. # is outside the migration functions.
  3846. MockAffectedFile(
  3847. 'chrome/browser/prefs/browser_prefs.cc',
  3848. [
  3849. 'prefs->RegisterStringPref("foo", "default");',
  3850. '// BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
  3851. '// END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
  3852. '// BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS',
  3853. '// END_MIGRATE_OBSOLETE_PROFILE_PREFS',
  3854. ],
  3855. [
  3856. '// BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
  3857. '// END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
  3858. '// BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS',
  3859. '// END_MIGRATE_OBSOLETE_PROFILE_PREFS',
  3860. ],
  3861. scm_diff='\n'.join([
  3862. '--- browser_prefs.cc.old 2020-12-02 20:51:40.812686731 +0100',
  3863. '+++ browser_prefs.cc.new 2020-12-02 20:52:02.936755539 +0100',
  3864. '@@ -1,2 +1,3 @@',
  3865. '+prefs->RegisterStringPref("foo", "default");',
  3866. ' // BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
  3867. ' // END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS']),
  3868. action='M'),
  3869. ]
  3870. mock_output_api = MockOutputApi()
  3871. errors = PRESUBMIT.CheckDeprecationOfPreferences(mock_input_api,
  3872. mock_output_api)
  3873. self.assertEqual(1, len(errors))
  3874. self.assertTrue(
  3875. 'Discovered possible removal of preference registrations' in
  3876. errors[0].message)
  3877. # Check that the presubmit fails if a marker line in browser_prefs.cc is
  3878. # deleted.
  3879. def testDeletedMarkerRaisesError(self):
  3880. mock_input_api = MockInputApi()
  3881. mock_input_api.files = [
  3882. MockAffectedFile('chrome/browser/prefs/browser_prefs.cc',
  3883. [
  3884. '// BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
  3885. '// END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
  3886. '// BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS',
  3887. # The following line is deleted for this test
  3888. # '// END_MIGRATE_OBSOLETE_PROFILE_PREFS',
  3889. ])
  3890. ]
  3891. mock_output_api = MockOutputApi()
  3892. errors = PRESUBMIT.CheckDeprecationOfPreferences(mock_input_api,
  3893. mock_output_api)
  3894. self.assertEqual(1, len(errors))
  3895. self.assertEqual(
  3896. 'Broken .*MIGRATE_OBSOLETE_.*_PREFS markers in browser_prefs.cc.',
  3897. errors[0].message)
  3898. class MPArchApiUsage(unittest.TestCase):
  3899. def _assert_notify(
  3900. self, expected_uses, expect_fyi, msg, local_path, new_contents):
  3901. mock_input_api = MockInputApi()
  3902. mock_output_api = MockOutputApi()
  3903. mock_input_api.files = [
  3904. MockFile(local_path, new_contents),
  3905. ]
  3906. result = PRESUBMIT.CheckMPArchApiUsage(mock_input_api, mock_output_api)
  3907. watchlist_email = ('mparch-reviews+watchfyi@chromium.org'
  3908. if expect_fyi else 'mparch-reviews+watch@chromium.org')
  3909. self.assertEqual(
  3910. bool(expected_uses or expect_fyi),
  3911. watchlist_email in mock_output_api.more_cc,
  3912. msg)
  3913. if expected_uses:
  3914. self.assertEqual(1, len(result), msg)
  3915. self.assertEqual(result[0].type, 'notify', msg)
  3916. self.assertEqual(sorted(result[0].items), sorted(expected_uses), msg)
  3917. else:
  3918. self.assertEqual(0, len(result), msg)
  3919. def testNotify(self):
  3920. self._assert_notify(
  3921. ['IsInMainFrame'],
  3922. False,
  3923. 'Introduce IsInMainFrame',
  3924. 'chrome/my_feature.cc',
  3925. ['void DoSomething(content::NavigationHandle* navigation_handle) {',
  3926. ' if (navigation_handle->IsInMainFrame())',
  3927. ' all_of_our_page_state.reset();',
  3928. '}',
  3929. ])
  3930. self._assert_notify(
  3931. ['FromRenderFrameHost'],
  3932. False,
  3933. 'Introduce WC::FromRenderFrameHost',
  3934. 'chrome/my_feature.cc',
  3935. ['void DoSomething(content::RenderFrameHost* rfh) {',
  3936. ' auto* wc = content::WebContents::FromRenderFrameHost(rfh);',
  3937. ' ChangeTabState(wc);',
  3938. '}',
  3939. ])
  3940. def testFyi(self):
  3941. self._assert_notify(
  3942. [],
  3943. True,
  3944. 'Introduce WCO and WCUD',
  3945. 'chrome/my_feature.h',
  3946. ['class MyFeature',
  3947. ' : public content::WebContentsObserver,',
  3948. ' public content::WebContentsUserData<MyFeature> {};',
  3949. ])
  3950. self._assert_notify(
  3951. [],
  3952. True,
  3953. 'Introduce WCO override',
  3954. 'chrome/my_feature.h',
  3955. ['void DidFinishNavigation(',
  3956. ' content::NavigationHandle* navigation_handle) override;',
  3957. ])
  3958. def testNoNotify(self):
  3959. self._assert_notify(
  3960. [],
  3961. False,
  3962. 'No API usage',
  3963. 'chrome/my_feature.cc',
  3964. ['void DoSomething() {',
  3965. ' // TODO: Something',
  3966. '}',
  3967. ])
  3968. # Something under a top level directory we're not concerned about happens
  3969. # to share a name with a content API.
  3970. self._assert_notify(
  3971. [],
  3972. False,
  3973. 'Uninteresting top level directory',
  3974. 'third_party/my_dep/my_code.cc',
  3975. ['bool HasParent(Node* node) {',
  3976. ' return node->GetParent();',
  3977. '}',
  3978. ])
  3979. # We're not concerned with usage in test code.
  3980. self._assert_notify(
  3981. [],
  3982. False,
  3983. 'Usage in test code',
  3984. 'chrome/my_feature_unittest.cc',
  3985. ['TEST_F(MyFeatureTest, DoesSomething) {',
  3986. ' EXPECT_TRUE(rfh()->GetMainFrame());',
  3987. '}',
  3988. ])
  3989. class AssertAshOnlyCodeTest(unittest.TestCase):
  3990. def testErrorsOnlyOnAshDirectories(self):
  3991. files_in_ash = [
  3992. MockFile('ash/BUILD.gn', []),
  3993. MockFile('chrome/browser/ash/BUILD.gn', []),
  3994. ]
  3995. other_files = [
  3996. MockFile('chrome/browser/BUILD.gn', []),
  3997. MockFile('chrome/browser/BUILD.gn', ['assert(is_chromeos_ash)']),
  3998. ]
  3999. input_api = MockInputApi()
  4000. input_api.files = files_in_ash
  4001. errors = PRESUBMIT.CheckAssertAshOnlyCode(input_api, MockOutputApi())
  4002. self.assertEqual(2, len(errors))
  4003. input_api.files = other_files
  4004. errors = PRESUBMIT.CheckAssertAshOnlyCode(input_api, MockOutputApi())
  4005. self.assertEqual(0, len(errors))
  4006. def testDoesNotErrorOnNonGNFiles(self):
  4007. input_api = MockInputApi()
  4008. input_api.files = [
  4009. MockFile('ash/test.h', ['assert(is_chromeos_ash)']),
  4010. MockFile('chrome/browser/ash/test.cc',
  4011. ['assert(is_chromeos_ash)']),
  4012. ]
  4013. errors = PRESUBMIT.CheckAssertAshOnlyCode(input_api, MockOutputApi())
  4014. self.assertEqual(0, len(errors))
  4015. def testDeletedFile(self):
  4016. input_api = MockInputApi()
  4017. input_api.files = [
  4018. MockFile('ash/BUILD.gn', []),
  4019. MockFile('ash/foo/BUILD.gn', [], action='D'),
  4020. ]
  4021. errors = PRESUBMIT.CheckAssertAshOnlyCode(input_api, MockOutputApi())
  4022. self.assertEqual(1, len(errors))
  4023. def testDoesNotErrorWithAssertion(self):
  4024. input_api = MockInputApi()
  4025. input_api.files = [
  4026. MockFile('ash/BUILD.gn', ['assert(is_chromeos_ash)']),
  4027. MockFile('chrome/browser/ash/BUILD.gn',
  4028. ['assert(is_chromeos_ash)']),
  4029. MockFile('chrome/browser/ash/BUILD.gn',
  4030. ['assert(is_chromeos_ash, "test")']),
  4031. ]
  4032. errors = PRESUBMIT.CheckAssertAshOnlyCode(input_api, MockOutputApi())
  4033. self.assertEqual(0, len(errors))
  4034. class CheckRawPtrUsageTest(unittest.TestCase):
  4035. def testAllowedCases(self):
  4036. mock_input_api = MockInputApi()
  4037. mock_input_api.files = [
  4038. # Browser-side files are allowed.
  4039. MockAffectedFile('test10/browser/foo.h', ['raw_ptr<int>']),
  4040. MockAffectedFile('test11/browser/foo.cc', ['raw_ptr<int>']),
  4041. MockAffectedFile('test12/blink/common/foo.cc', ['raw_ptr<int>']),
  4042. MockAffectedFile('test13/blink/public/common/foo.cc', ['raw_ptr<int>']),
  4043. MockAffectedFile('test14/blink/public/platform/foo.cc',
  4044. ['raw_ptr<int>']),
  4045. # Non-C++ files are allowed.
  4046. MockAffectedFile('test20/renderer/foo.md', ['raw_ptr<int>']),
  4047. # Mentions in a comment are allowed.
  4048. MockAffectedFile('test30/renderer/foo.cc', ['//raw_ptr<int>']),
  4049. ]
  4050. mock_output_api = MockOutputApi()
  4051. errors = PRESUBMIT.CheckRawPtrUsage(mock_input_api, mock_output_api)
  4052. self.assertFalse(errors)
  4053. def testDisallowedCases(self):
  4054. mock_input_api = MockInputApi()
  4055. mock_input_api.files = [
  4056. MockAffectedFile('test1/renderer/foo.h', ['raw_ptr<int>']),
  4057. MockAffectedFile('test2/renderer/foo.cc', ['raw_ptr<int>']),
  4058. MockAffectedFile('test3/blink/public/web/foo.cc', ['raw_ptr<int>']),
  4059. ]
  4060. mock_output_api = MockOutputApi()
  4061. errors = PRESUBMIT.CheckRawPtrUsage(mock_input_api, mock_output_api)
  4062. self.assertEqual(len(mock_input_api.files), len(errors))
  4063. for error in errors:
  4064. self.assertTrue(
  4065. 'raw_ptr<T> should not be used in Renderer-only code' in
  4066. error.message)
  4067. class AssertPythonShebangTest(unittest.TestCase):
  4068. def testError(self):
  4069. input_api = MockInputApi()
  4070. input_api.files = [
  4071. MockFile('ash/test.py', ['#!/usr/bin/python']),
  4072. MockFile('chrome/test.py', ['#!/usr/bin/python2']),
  4073. MockFile('third_party/blink/test.py', ['#!/usr/bin/python3']),
  4074. MockFile('empty.py', []),
  4075. ]
  4076. errors = PRESUBMIT.CheckPythonShebang(input_api, MockOutputApi())
  4077. self.assertEqual(3, len(errors))
  4078. def testNonError(self):
  4079. input_api = MockInputApi()
  4080. input_api.files = [
  4081. MockFile('chrome/browser/BUILD.gn', ['#!/usr/bin/python']),
  4082. MockFile('third_party/blink/web_tests/external/test.py',
  4083. ['#!/usr/bin/python2']),
  4084. MockFile('third_party/test/test.py', ['#!/usr/bin/python3']),
  4085. ]
  4086. errors = PRESUBMIT.CheckPythonShebang(input_api, MockOutputApi())
  4087. self.assertEqual(0, len(errors))
  4088. class VerifyDcheckParentheses(unittest.TestCase):
  4089. def testPermissibleUsage(self):
  4090. input_api = MockInputApi()
  4091. input_api.files = [
  4092. MockFile('okay1.cc', ['DCHECK_IS_ON()']),
  4093. MockFile('okay2.cc', ['#if DCHECK_IS_ON()']),
  4094. # Other constructs that aren't exactly `DCHECK_IS_ON()` do their
  4095. # own thing at their own risk.
  4096. MockFile('okay3.cc', ['PA_DCHECK_IS_ON']),
  4097. MockFile('okay4.cc', ['#if PA_DCHECK_IS_ON']),
  4098. MockFile('okay6.cc', ['BUILDFLAG(PA_DCHECK_IS_ON)']),
  4099. ]
  4100. errors = PRESUBMIT.CheckDCHECK_IS_ONHasBraces(input_api, MockOutputApi())
  4101. self.assertEqual(0, len(errors))
  4102. def testMissingParentheses(self):
  4103. input_api = MockInputApi()
  4104. input_api.files = [
  4105. MockFile('bad1.cc', ['DCHECK_IS_ON']),
  4106. MockFile('bad2.cc', ['#if DCHECK_IS_ON']),
  4107. MockFile('bad3.cc', ['DCHECK_IS_ON && foo']),
  4108. ]
  4109. errors = PRESUBMIT.CheckDCHECK_IS_ONHasBraces(input_api, MockOutputApi())
  4110. self.assertEqual(3, len(errors))
  4111. for error in errors:
  4112. self.assertRegex(error.message, r'DCHECK_IS_ON().+parentheses')
  4113. class CheckBatchAnnotation(unittest.TestCase):
  4114. """Test the CheckBatchAnnotation presubmit check."""
  4115. def testTruePositives(self):
  4116. """Examples of when there is no @Batch or @DoNotBatch is correctly flagged.
  4117. """
  4118. mock_input = MockInputApi()
  4119. mock_input.files = [
  4120. MockFile('path/OneTest.java', ['public class OneTest']),
  4121. MockFile('path/TwoTest.java', ['public class TwoTest']),
  4122. MockFile('path/ThreeTest.java',
  4123. ['@Batch(Batch.PER_CLASS)',
  4124. 'import org.chromium.base.test.BaseRobolectricTestRunner;',
  4125. 'public class Three {']),
  4126. MockFile('path/FourTest.java',
  4127. ['@DoNotBatch(reason = "dummy reason 1")',
  4128. 'import org.chromium.base.test.BaseRobolectricTestRunner;',
  4129. 'public class Four {']),
  4130. ]
  4131. errors = PRESUBMIT.CheckBatchAnnotation(mock_input, MockOutputApi())
  4132. self.assertEqual(2, len(errors))
  4133. self.assertEqual(2, len(errors[0].items))
  4134. self.assertIn('OneTest.java', errors[0].items[0])
  4135. self.assertIn('TwoTest.java', errors[0].items[1])
  4136. self.assertEqual(2, len(errors[1].items))
  4137. self.assertIn('ThreeTest.java', errors[1].items[0])
  4138. self.assertIn('FourTest.java', errors[1].items[1])
  4139. def testAnnotationsPresent(self):
  4140. """Examples of when there is @Batch or @DoNotBatch is correctly flagged."""
  4141. mock_input = MockInputApi()
  4142. mock_input.files = [
  4143. MockFile('path/OneTest.java',
  4144. ['@Batch(Batch.PER_CLASS)', 'public class One {']),
  4145. MockFile('path/TwoTest.java',
  4146. ['@DoNotBatch(reason = "dummy reasons.")', 'public class Two {'
  4147. ]),
  4148. MockFile('path/ThreeTest.java',
  4149. ['@Batch(Batch.PER_CLASS)',
  4150. 'public class Three extends BaseTestA {'],
  4151. ['@Batch(Batch.PER_CLASS)',
  4152. 'public class Three extends BaseTestB {']),
  4153. MockFile('path/FourTest.java',
  4154. ['@DoNotBatch(reason = "dummy reason 1")',
  4155. 'public class Four extends BaseTestA {'],
  4156. ['@DoNotBatch(reason = "dummy reason 2")',
  4157. 'public class Four extends BaseTestB {']),
  4158. MockFile('path/FiveTest.java',
  4159. ['import androidx.test.uiautomator.UiDevice;',
  4160. 'public class Five extends BaseTestA {'],
  4161. ['import androidx.test.uiautomator.UiDevice;',
  4162. 'public class Five extends BaseTestB {']),
  4163. MockFile('path/SixTest.java',
  4164. ['import org.chromium.base.test.BaseRobolectricTestRunner;',
  4165. 'public class Six extends BaseTestA {'],
  4166. ['import org.chromium.base.test.BaseRobolectricTestRunner;',
  4167. 'public class Six extends BaseTestB {']),
  4168. MockFile('path/SevenTest.java',
  4169. ['import org.robolectric.annotation.Config;',
  4170. 'public class Seven extends BaseTestA {'],
  4171. ['import org.robolectric.annotation.Config;',
  4172. 'public class Seven extends BaseTestB {']),
  4173. MockFile(
  4174. 'path/OtherClass.java',
  4175. ['public class OtherClass {'],
  4176. ),
  4177. MockFile('path/PRESUBMIT.py',
  4178. ['@Batch(Batch.PER_CLASS)',
  4179. '@DoNotBatch(reason = "dummy reason)']),
  4180. ]
  4181. errors = PRESUBMIT.CheckBatchAnnotation(mock_input, MockOutputApi())
  4182. self.assertEqual(0, len(errors))
  4183. if __name__ == '__main__':
  4184. unittest.main()