From 5c1cf39e0fc36910cd733eb10dfb6591d6d237d3 Mon Sep 17 00:00:00 2001 From: Ryan Harter Date: Fri, 1 Nov 2024 13:59:44 -0500 Subject: [PATCH] Moves emulator setup into gradle tasks so they always run, and updates tests. --- .github/scripts/demo_mode.sh | 33 ----- .github/workflows/publish.yml | 16 ++- dropshots/build.gradle.kts | 60 +++++++-- .../assets/MatchesFullScreenshot.png | Bin 30427 -> 29146 bytes .../src/androidTest/assets/static/50x50.png | Bin 0 -> 172 bytes .../com/dropbox/dropshots/DropshotsTest.kt | 118 ++++++++++-------- .../dropbox/dropshots/EmulatorConfigRule.kt | 42 ------- .../java/com/dropbox/dropshots/Dropshots.kt | 2 - 8 files changed, 129 insertions(+), 142 deletions(-) delete mode 100755 .github/scripts/demo_mode.sh create mode 100644 dropshots/src/androidTest/assets/static/50x50.png delete mode 100644 dropshots/src/androidTest/kotlin/com/dropbox/dropshots/EmulatorConfigRule.kt diff --git a/.github/scripts/demo_mode.sh b/.github/scripts/demo_mode.sh deleted file mode 100755 index 79486fb..0000000 --- a/.github/scripts/demo_mode.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash -CMD=$1 - -if [[ $ADB == "" ]]; then - ADB=adb -fi - -if [[ $CMD != "on" && $CMD != "off" ]]; then - echo "Usage: $0 [on|off] [hhmm]" >&2 - exit -fi - -if [[ "$2" != "" ]]; then - HHMM="$2" -fi - -$ADB root || exit -$ADB wait-for-device -$ADB shell settings put global sysui_demo_allowed 1 - -if [ $CMD == "on" ]; then - $ADB shell am broadcast -a com.android.systemui.demo -e command enter || exit - if [[ "$HHMM" != "" ]]; then - $ADB shell am broadcast -a com.android.systemui.demo -e command clock -e hhmm ${HHMM} - fi - $ADB shell am broadcast -a com.android.systemui.demo -e command battery -e plugged false - $ADB shell am broadcast -a com.android.systemui.demo -e command battery -e level 100 - $ADB shell am broadcast -a com.android.systemui.demo -e command network -e wifi show -e level 4 - $ADB shell am broadcast -a com.android.systemui.demo -e command network -e mobile show -e datatype none -e level 4 - $ADB shell am broadcast -a com.android.systemui.demo -e command notifications -e visible false -elif [ $CMD == "off" ]; then - $ADB shell am broadcast -a com.android.systemui.demo -e command exit -fi diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 640adcf..48cf84b 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -127,10 +127,7 @@ jobs: api-level: 31 arch: x86_64 profile: pixel_5 - script: | - ./.github/scripts/demo_mode.sh on 1234 - ./gradlew connectedCheck --stacktrace - ./.github/scripts/demo_mode.sh off + script: ./gradlew connectedCheck --stacktrace - name: Prevent pushing new screenshots if this is a fork id: checkfork_screenshots @@ -145,6 +142,17 @@ jobs: if: steps.screenshotsverify.outcome == 'failure' && github.event_name == 'pull_request' run: cp dropshots/build/reports/androidTests/dropshots/reference/*.png dropshots/src/androidTest/assets/ + # Since commits from actions don't trigger new actions, we validate the new screenshots here + # before we commit them to ensure there isn't flakiness in the tests. + - name: Validate updated screenshots + uses: reactivecircus/android-emulator-runner@62dbb605bba737720e10b196cb4220d374026a6d # v2.33.0 + if: steps.screenshotsverify.outcome == 'failure' && steps.screenshotspull.outcome == 'success' + with: + api-level: 31 + arch: x86_64 + profile: pixel_5 + script: ./gradlew connectedCheck --stacktrace + - name: Push new screenshots if available uses: stefanzweifel/git-auto-commit-action@4b8a201e31cadd9829df349894b28c54e6c19fe6 if: steps.screenshotspull.outcome == 'success' diff --git a/dropshots/build.gradle.kts b/dropshots/build.gradle.kts index 211ef8f..b90b627 100644 --- a/dropshots/build.gradle.kts +++ b/dropshots/build.gradle.kts @@ -92,6 +92,43 @@ android.testVariants.all { } } + val setupEmulatorTask = tasks.register("setup${name.capitalize()}ScreenshotEmulator") { + description = "Configures the test device for screenshots." + group = "verification" + doLast { + val adb = adbExecutablePath.get() + fun adbCommand(cmd: String): ExecResult { + return project.exec { + executable = adb + args = cmd.split(" ") + } + } + + adbCommand("root") + adbCommand("wait-for-device") + adbCommand("shell cmd overlay enable com.android.internal.systemui.navbar.gestural") + adbCommand("shell settings put global sysui_demo_allowed 1") + adbCommand("shell am broadcast -a com.android.systemui.demo -e command enter") + .assertNormalExitValue() + adbCommand("shell am broadcast -a com.android.systemui.demo -e command clock -e hhmm 1234") + adbCommand("shell am broadcast -a com.android.systemui.demo -e command battery -e plugged false") + adbCommand("shell am broadcast -a com.android.systemui.demo -e command battery -e level 100") + adbCommand("shell am broadcast -a com.android.systemui.demo -e command network -e wifi show -e level 4") + adbCommand("shell am broadcast -a com.android.systemui.demo -e command network -e mobile show -e datatype none -e level 4") + adbCommand("shell am broadcast -a com.android.systemui.demo -e command notifications -e visible false") + } + } + val restoreEmulatorTask = tasks.register("restore${name.capitalize()}ScreenshotEmulator") { + description = "Restores the test device from screenshot mode." + group = "verification" + doLast { + project.exec { + executable = adbExecutablePath.get() + args = "shell am broadcast -a com.android.systemui.demo -e command exit".split(" ") + } + } + } + val pullScreenshotsTask = tasks.register("pull${name.capitalize()}Screenshots") { description = "Pull screenshots from the test device." group = "verification" @@ -111,24 +148,29 @@ android.testVariants.all { if (checkResult.exitValue == 0) { val output = ByteArrayOutputStream() - project.exec { + val pullResult = project.exec { executable = adb args = listOf("pull", "$screenshotDir/.", outputDir.path) standardOutput = output + isIgnoreExitValue = true } - val fileCount = """^$screenshotDir/?\./: ([0-9]*) files pulled,.*$""".toRegex() - val matchResult = fileCount.find(output.toString(Charsets.UTF_8)) - if (matchResult != null && matchResult.groups.size > 1) { - println("${matchResult.groupValues[1]} screenshots saved at ${outputDir.path}") + if (pullResult.exitValue == 0) { + val fileCount = """^$screenshotDir/?\./: ([0-9]*) files pulled,.*$""".toRegex() + val matchResult = fileCount.find(output.toString(Charsets.UTF_8)) + if (matchResult != null && matchResult.groups.size > 1) { + println("${matchResult.groupValues[1]} screenshots saved at ${outputDir.path}") + } else { + println("Unknown result executing adb: $adb pull $screenshotDir/. ${outputDir.path}") + print(output.toString(Charsets.UTF_8)) + } } else { - println("Unknown result executing adb: $adb pull $screenshotDir/. ${outputDir.path}") - print(output.toString(Charsets.UTF_8)) + println("Failed to pull screenshots.") } project.exec { executable = adb - args = listOf("shell", "rm", "-r", screenshotDir) + args = listOf("shell", "rm", "-r", "/storage/emulated/0/Download/screenshots") } } } @@ -137,5 +179,7 @@ android.testVariants.all { connectedAndroidTest.configure { dependsOn(pushMarkerFileTask) finalizedBy(pullScreenshotsTask) + dependsOn(setupEmulatorTask) + finalizedBy(restoreEmulatorTask) } } diff --git a/dropshots/src/androidTest/assets/MatchesFullScreenshot.png b/dropshots/src/androidTest/assets/MatchesFullScreenshot.png index 566a48bab06c5143c8bc5f7ad415784600305751..97f69fd3bac0c2bd1899fe6a0ac1439253b177a8 100644 GIT binary patch literal 29146 zcmeIbXH=7I_dST+M?h?VNK@%udM6?RhALgU(tGb!K|n!@fzTl$CA5I_4oVRzp@ky7 z_ugxgxp>~+J8R8a^I`ro>%Z1~fG?4lP__#RS_rCzIJ02d{=^V!tR{`?;V zf9|TEKimxYc7BJ?g|3pG|JwK@`~2a~ZOLok;l0C6*7ILKO1-`W9v)K#oPR%p={py1 z{ogfA8vmJ|EyKfOB!Z0n&GS{nLT8fwLipW}LFSU1YZKSO1e}ozi;AAcvckC#A$eh8 zB_+-Z3Z1)4OGsrp`|*0}`}d>Ab&<+x0oQ0HyiA%&?Xa%$uLp8<@N*j3Kkgbb-etwHaVTgl+KZ5GZ&TpM zo7X0yNT~i@8jOqj**%L0{{zb!x9UeiZ61|1-u(soNH<$3j7I!qaR{YbVRJnHt0{zW zqI1u6V{gB|En2fsJ87J9XF7eE99&<4`Pt*QTi-4bQ}X>e$-PY_CglI?JDXOanXg6y zrV2BphE#~9x7<%G)2>Qa%~DwYe4kOBuy0~eJ`!X8f>9yaWA-;%p~2;LW47TRB2m$r zdo5w!VjuF)=YNcwAu5?&46sZd{849!y-hC8oW>X1LaKH&=e5*3Mk1(rgCZ1r5DmQXS_%~JhQ0X8jEs)(=5f`BEiR(QSx9t|HI9)=SVv6DaiTO@V* z&!pl=k+ZqXkeEr&PZ?s$EF5oz+J=G+4Tuui6XM=qX!!fu+uL8R9`5Xn9ISGwAr%el zo#3AhUPL>p-6DGV`nb@+#pP%(T3LtBUNgr?TI2etHJn|=s3R-mqM`ShHh?uR`@Ev77aBuSiHvO=8MzQ?<_4@psviWwn^{-lC4&Bmc`-Ixs+TpZ<<$6^=KLmy_ zGBPG^1ifMz4*Iq;ou{8SxQOsotx%!{AKZz0_v@@GNAfA(-dTWjSj1IY4Lqjib=oED zsqb;i;!5mWYSB`N=Su&ExHqxk@k|h*s+J!ky~)JcH74a0O*{taFfCE%eg0ZaO-=Ac zqU)0!&W`@MVt)w=FMc; zMSiMs0^fPDA@*bOy(rzXJ_QyMTJg|NpPp?kr-=HdjLfXQ+Uix7I2GduOLA{&y6Ih$ zEW38K0#=!jFpOrrnxfuzyxNULe?%F3^z&EXx&-#$SGsKCu+wz1s3W{?wdd}_dhd7- z+=xgxqgd< z4MaYJOo&o7$fxiyN&%ndnhg4R9*kLMt@~!`iR1J4{#N!!F8p@=udq}5t3&C(zhJ66 z&w>cm7PC-RZ@PImUjzjcJ<9g;!JZIb5d`tdr1xl(k&&K0{#g3{y?YAWhciEMSSZVI z7?>UTR|-O&JN?M4}mv`{R==yOX$lSh>WC#Y|pjb*O#Xeb$o&S`TKX4 z$F|Y+f`*r$XI5@#73yQkb}B1}D(ogTA)M;j-=5^9@HqyEuwy+Jy9&g;iN+oh5JX7} z+Ko~1nPevzy7WY)$sn{$mZE;q1(Wb@h)nlwRPxhkSh*yT< z=vi1;EN3!Uauhp2_M})FD;ou2px)OjX>mT5UBBFU$Zod48C+iK1_=+i{6AOWB!*e% z8=K3pRSVhu8o9@wGkjxVdzO#-Evr^Re~z)NtNrPdz#F*IcTy`E%uT_O&rEo! zs8jk$W@cyixt2E;1ia>M?ptHaU3Is5)6X+`r_IQh1V2B&{gpxPeM0JgBt&d~E6f%| z$2`3qaBHt>cgRpJOHt4SI;qMqK=M6RJbK)_ZFVhR={AgBTUS?9Sj5cYKn83V9z3l; zfk91{|MWkztvRiCyw8rd=W;Ma{@@yK*}=E1JKY9p=+`tGjFtmJH zrO&(>B*FSD;KemUO~PBM5DBln__adWNSYdlZP>|nYTYS~m`{8fJn|8@*uEP&I2^L7 zUF?`H1GYm9JqrVaDfj{jshuDOnW)(4=^;7w4O@OET3`!}j743SA5oH6&G>biTQm0H>G>o zeJ169B)s^r^F%UockfGH(^f6FNoc#@pyn=lyjoZj&8UoSAGv%5OUZA;%m%jE{$4`K zV8N+m_kC;i!h?Z0!(7^K!%F6K)cGp>oUc=tbjFe68PiW;cVJ@>Kk@;U^}wuj})43O0=63L%--jCuJu_53%I zSdVcMNP%_@=jy7M-%Rs19x-izdU3on*_}AOxXK1q4O>8S8m!eD4X~oG0c23^ww&)* zYlVJ<5;p-^(M&lFJB^Kv3tcJp-vJCE&~^cMMN%r* z>|{AGn5fQiHmgX$?5Ls%eClk(EgqY_KX(DR$T`{me4pubZ{55v&UxShvlM;2jC(R7hbm18sc`I_2)I=e2V_dbtkW;^Ytj@g9 z{kJu1ZbxQ^mMZOyqf_K{czvCBW~~2skyQ1n3X-~vy2kB3fd9`*EP2gNj*kcObX&KO zRR0Mu#C`#@fG z?3Zv~_=%8Kf&XZ^h_JBf$-pIo_s5wT8G9QT_stFcmQYfDlj9oqChx=LBpA#nMvEzP zj{Pd>!(6)*5niNnNqKoJ{ql6WY)Xxr|09Y7IB9itbp)-TCe~y6dy7{W1np<5jaGD+#u}*ehf~fMY{L*ILA~MhZ?f`HKYIx4KjjyKz}QQh~@|rOw46a`x+<02 zRK0jk0}gV2@`y|tAF0stnf;&iV>zvhrR zWZ&cTU>$60QTKHThpnjQ=9kVkAnHF$Us_rM(FH&wP)0;W_2kG8foEJ0aL!+9a+-`D zf7+d$$q2gxCWh;XkG4Rh9LUv>fAvbpdqb}}jFe+$Hb&Il$LDOeHBvcMY=82Y?=ry_ zVxU+$Y+j!Ha0O$(SfHMzKRQKl6!+3NWf$a6R*yt~kyAj*Z|N(ub8 zoR7k?YBD26uF|oEl5-8_ns($D$~J_NbD27J1zJgJ1O^1~oaFPGcO^^Qhrx%?lK*7} zXfJq!3y$Q15Bk65gVNhKh@W?1vfo`{V`D?ZK7C?}<239lSijpWtC3q-U2PHD+~DcF zw^Xz?l1f6;nl{{@ZNTBaF_y?sXiz8Ox#HqxOF}RO3HTO3M9FVdiAqT^YnyJ86_Q;0 zo93G-N6Ih%^y$-^tXWNt76Rint{fVSFEpS}jH!q~*b#Q3-(EaT`% z5CrE>9y@dM9dW~T`q~4f(Akag>Vu!hbpL%1_A>>q%Jn~{*tL(l9i((iEeMt z^~7Y+9>~uIwo;Q4{0zyL312COeQLey-qciLY8<5^GwEAqHTarLWYN)XB%|mx3eI!# zdsj%*nx!*6vuBCtq|b-`oe~$;9sl0-^0@o=_$HujgZXC+W$|(O_ z{C4Y$Yw$1i+gn>(Rs#VhLGGK0vuluaF0|~u;C~6NBV~*JA87>;I2-w!A8#d{?Y=l- zEB%_Vr-%IVG?0ilhfR_H=baLK{NXZu3vdP4j=j~<)|M7&O(A<+z~8@bu*yU=u59v@ zal698!oGVdr276EIqDp`e1@Pr6c%jzWMphy~M23 zBsevWW3Uuz%xm}Hz02H}EJHvn5fTyoK|mVP0|VjkfuxTg7rRrjq%?jj{mbAY3Wb9h z3~%;|ELdHfp1V4_x()Jjp`Sl{?k)9J_4G90>&>U+#`N@^hqDr132|~Nl~|g6eJUYI ztHCj+&s-6$CMPFnqc_$RLel;5|nDqL7!PZ@24!ZQ9=Rw9*Clf}usc-h(2 z?D>oXa{V4Wd>9WnQd`bZ_st1u85x5*H|$;lFQ^+RbRvt*3>y4WMD`0qLS)l}iEG8g zokc}kTYeiNRNgJ%to-sa`jSM@9y-4aU2>hsM$9kr5GY?rlqxxw6$EsTW;l zdv$C%>bZ~jbK^JwiP|Jpx09WnO(|%fD&{J}#@20AmMtiwc{I}!c68(+-tFY*SX5FH zO=`8Fmp&!LH{&CLsVu_omAx7?^A3j*O4%7QV1+2fKdFeMcj&@lRdlr4##ogpp@rY^ z(s$LIo%s$XHlN)EML_p`;7X~g$U4jAa|i^yUJ5B4qqW^Lr#oqZq2Nt%_96p$T3m(_ zBq&09dU}9WCu`^92+6t3EYRVU{8mHdihuxumq_G{*Lk0Z3`+lrn2lUU<&y%Rc5C*SkesiBO*6ZvPB-r7dxGJ^ z9D1m@_rXmPlF8!vAyCI;)&Qb84XcGHwN8Tg`^_Vs($V=J?^h60`V08j&bEf5R5Ods z+Gzc$Xhi)2P^fjlAFK}H|7y+>W}CF#t5&)bURhF~^sHVrGc=JQ53t)MZYgq^D+B4? zpvX)q3qPeHB`q^`is&%J)k|>uRsMCYN`$Mn0oE68Kn7KGcC)YXF@bMY$NwqrMeA! zPW1GP4F*cg6n1xaG5cCjSR$X@SjKh(c~>{0p~wE`bCi5y+U*j3R3d-F$qu1Ez8G+w z?G21zCXUm%Iwb#+G*`K{IS$Xup={?aa41WPNUW`xI7?r?p)=0(uX2|1f){u{iXZ<2sztHQYoXqV>^L_n#AYrn*-fKVc$ts^w86XsEbzVoUscELm>;oAmMa)%aRn5z7 zlTn@pMxP?#`pP&}_>QLqrqrEH#4!@ecl2e-$8qXc0|KkWjMD3B)(WO( z2yoP`V8c?4$$!6v z{5ogh!qSqE^GRuz;-ghLmK>+~c7BKFcpZN0T=m?XXr?mDaXDo<=sB$Ub>)VMMfBe8 z&Q7wpbyjLglR7DD41(Xd8m_^(3!vBdk)tu(JOoCcnej*Y4!We|7Ad=s^>03L^N<`L zUwL#Qcio%b@45T;hg%OR>hzs2g_n=ZrJ<{){8Q({+Td{&T5m4m}DTq_jGFGGu z_CE!Qzgks}HAznZtTfBl4Sv;Ze99V>UFS2@9?MeV@aqiVlf8@>b+BoPDFxyo?&Y{i z?RRV_l@aEr&_0m1=;-JepeM6icZU&YSgfzlEprPjqlhJUbySJDDMQdL(*6_EY?f!w zab-TM2OLe5HD)9Gl!A?s^@%iTI#Tl9`5x ziIn;ci$7XOMhIkbSq-_Q{q0@gNi?J1;?h!l+5i}J9QM?0y>EC6*`KYV-~DR6eV>Ge z@9oi#v@+`A#Qaft(0U^5RzkmOBCUQ4zX%~Re)gJxh&HpeP?BHSJaG`I1IdQ(MvOG`^@YwORT-37F$@5lpegKk!KY~ASrLfl(P^G&fy z8)VA2?4acRnKGW+sDS{_JUshAdwQ}$iZ1N zZTPaoGU4-6l2`V$(uZ?Lo||Pit&!fDg1C1bUQd04+}Yht+Qlm9llOl_@d4VL6+TaU z3|QP;S^&geq`XlDi?z&VqH$|KX(^-S!ELPL8%?8k?^FtvY{ivx^m4xMaGrdQMk*8x zRQ}fs@TJyN#Gd7Q18mB|6$T?ZrepmmN@%00CI-ds<%b z`m?$_H2fCCO4abz8OM7BjJr3>+5S|D3nhFFc_=B zj$(&c4s9rWI_rDIxVJQ@G2}W1(4MTACoC!|ibU1&|`g~$1>%&v`S+s{mn z`ENbusJRx-Wu`&FlGBogRUEA*C_No`)b?|Hz1!re=aI20wBA`%P$Jzj{|i2 zvK6OIwUu_I#kLimv^eY=^=QRpi92H9fm8S+fv9~EIQ?>0O2c5jKGHRi_v7p{4YJ6l zo%wdl@H=29ir>Gt(+!E7Z16{LA!OlzaMmiI7oBx+aCj(Y@pfoKoTu~`(7#B)l|4zL z+2T-fNMv48Z2cQfPEK2$GMxs;^g;fIb-DIqMBdnw$*!Ja8cC}~ey^@E+^#YjlF)D) z-Pzmg2*}%ZzX2G%xbGuRJPtclXjoHSO?GggNdrU}KTwC?!^d}|`8UI;1^X?Syvu@+U%La)-Aeh8t zXjG@F=9wvKL&0dBl2CQ_teTymKx!+LIxRG*L&h|`Ok~x_f6N9|Q0Rxq-FnHW`ML^<#gNJQ4lObcZ6`oO_rn?|ChEKnvH^CmX)CwYu^%nh7f$~3=WY|+u+q`d(Rv8G zH9BaUh>cI;v+J+XQYMMygM-1M4x3hvy6tG`#za}( zVC8qRM_zu*#q)`~H^^CLcwM%>$-(DnJ6PdtKx{EH?cs&OKq9HIX4}kRgsQ5m_k%=p zxQ4BeL;@V%dV3xC1`?J#{_+$WHrys5@jvES02S21!9k)LJi*_*rS{A4)^7Kx|KwL4 zgn)p+`DI6{N}SBgtOlG;BdCKX_mvbB#&D+*2tnJyS_`d0yK%51SnVJ4+4h;N9!%o< z4|a{Kt*R(#>gR_H8;tCpdyBkFonw}gR()T$Myn8g9Vx0NNpylH}C(BfTOAu68iY9`=4#C%qATP0#OL?hXUv# z7hpE%DYU0`cXuCdbX^@BBvtNX0#>!ujp5eyux`j`g+Crpy!?dWZ!ERPeC75ZAaBKS8TL|TZuO)| z6@OFx@%9(M5D10M`VWrEI$v17uBM!h#;`!ahZv3%1;?a>zszmtOa9-pZlK?1b;tc(hjLmrgcyD2K*D?STut z&eF{Tbi{%0+RkmyK)6ykvYR=(Mrf)?7;*K+;nDz;*qQzC&Y-|x@uWPznpOv3RpH25 zjzmE=%{6~_;c6G0%<-_8!_-t&&ubK0=bZ?tHjmacr;Z41J+qeh$pA$^KiDRxs$}ysHxL-k0E9pA3+T%IPUJ*CC7R0McPoHC9_?) zd!b=aY9WVb3=E2tTGo`-`r$c126aKR$@Q7~?HH1@{pDZ?(SOWKEwUKrx57`;s z8j zE8rSL<;&gUv*Fzq`8Z2`T^UCzIT?}L>+9?MH*_>a{S6kE)&WN!D{TQ{6uCe)<#zt& zqF$Ak#wwz~pe}2z#M<1R{LWON-rq9~6he6QnQke5MSh;R4V0RoOx0T50WtZegqwoq zn+l~w(JN22bO>AaSU@~&SGlDe^6`(HM6RmvxRTmA*veTIv}5LyUtTE(F^YQl$8(T| zC~Obm(%uK_YJL-WOWAy;<{2Q9(2AKPr|o{_xO$5f)C}wzauR#ZX-y$;L-QOM3>HHh z-xyTKk6&J1&Z*sXINpH=hTwWixZhRSQLC~~EiVuE{74OGr?phnDzG?qCAlq;WzHB5 zgqS~^A6~&SlDnCufC_r+0sCV%F?R`d{M+NalEEkM4w60HNdrAH+GA5P@j4w~qs1U2 zCxYtane8~O_nRUi%g3t#6UI;v*VT$)J7Ndexs}V3K>;FsqPV{@VAY>JQQ^)tPjZmx zy86Y5H)ZxUnz=O%g+dj}7M7LOGuF!nM5YSg|9(7~BD!;0p#u=n1e+EH)FtpByU?7E z#hJPLyynzqVIyU8f}D+wcGNxIE5{vN4efF}^SeD~iTA4dtOl~5uvK|%!<-~!O^Jm> zM9M8VY3E)?e^%1zoZ7SWoU8(6KT1%HjV&?7u-a$16P*oK34D^t&#%6i@f(bOcyyFa z*0j=L+K7y@jn4a@e%VZ?%+PlGYRCgR^*?&`KGK>qARf=mz)mD@<(_=qk(dH;DPzxO z6z5$6;#`tC07KY@Z^57ZwqIy_>?a;rN8`QaTwEHHu4?Z-^!7f@w+SB1OZxR|hs~Tv zJGns`pK;^z20IiMK?}*Ds_UI>SW9WZwV$*rr;2FkJL=bhECOP)G8Q?UYuXFT)L_*b zT;q8A?j5&D&*Sd19pTS_Hz-x?4SwP*beq`ZkK;pXYAhozd@bxduuh1&ZS?UQH-V7F zTg081e%oSyZJ5_IEj_&!8U9|wwp^r>49=>l?sh}2LKF?p(*k9H>0;1_50`Qb)j$Bj zMy}4Q`S=1SLav^wFCy*LS3Z6Bl>t_PSRudLZ?d$4!W@x%! zzY@a$J;vI78fh-en>&@>nwv$vte&aLpN%iF862v1U18R}Ydt>xNAhZQ`iyRFO#U1o ze&P!MK>B#V8sz}4rUS!b8N%3NOD-jO*q*>PYNx%Z5B=jAI>42w^%l z7PV>x?s3EY^SvEdGd*2l*TEEyA_MfRDsV*(d;06?B`1u6Xe;FL>_8(w|Ftl2<_<@5 zZGTnGSGAiivYF3Ews+{EUMDXlJmOTrO`Z^s$ih^1In`fj?g-5keK@?dIqCm+l}YQ{ z``Yl(K}8;ud2RD2rZ)X<6!W$ib721Cd7)xtWK>;JYZ`IvJ~-+wk~ErmvfB-TlM}+r zkPnX4&9A&yS5Z;nGOUl`-#mCFY6KI`n_omHtq$bn`NP_Q$!>K;@#{ZSAuyH5CTdZ4 zP)bX>rBwj86>pYe_@_^mF30VlOam^Ej-_wnJi#9wI37&CStzuUibviq2^#byl85*8 zeT2$*u09_;c=J=PBbq6)c)qX7Ijvep8{Ga>9#=z;4`HlkEJ}xOh@YnJe6P+nx2XY? zoXBu)P2@-y+XPJ2_u_$M3c%hZgQzUPo!#KC&BVln=HBbLcD+5-gHwHRLq_Sz>(`Dh zQGx;fY203K7qnKF9SDA4OyNT zA#*bqkMuL>kwCM2eX_m*BgG_J@6WUlYQHX0_U5r#0M!NvD?x;=;rP?3Uric$Ll2Ku zf&pKf{G@d{4T8IQD|e*ybL^!sG4b;sKqe)_l6^LDfk}Y(6?gAqM)&b3cII|zyT(7M zN+~6ZfcTC5Rmhh+Xe0g67lT0pIp0TpaePG7AM3iJq(cr+qk!Hnsn>Y=qaxZrFzLkLb+xGa-xPS%<4Gq` zQQh=>?LJKoH#bWOLbHO{Jgvg@kviQ~1fXwXId#p(WsLV zD1HhJ?c-lzbh`6lBl`^-W3oUy|0~nksEW-AiX%Ogp@@$8F8Z^L&78j81ce?ix|qW}wwPUIV|@;;nlkVg+XX(;~9jdfC@Hv6=5TPjOi+p?;MMJe<6Bz8yR z%hcw|f{0zGg?P8>#1x!92bwM;TQ(kY4!VY^Ab!PqBMSzk{0lCH`tlN1XTv=`YrcWz zT_67}B(sn|v@kYKw;Rh$Zd+9NRKKkS;=lv-7;`3_8yy|lADgc-v7pg*lr3>#oK-eu zdMF>uEip`hn!uR4-+E%g57CYh9X}C^X0t+pP#Mk7mRfcTR-o9^P>@F&8i~ki^{A~u@i5p5M1b3AFrpTz>)1#021F>+xbK2aY?ZR<- zLG-=!y_oPY@AcGj>*DG36V}#jC{)RBXAU}8A$PTqRFU1L47-&S4ZiK5rXO>?pKa;w zp!TpC`-$4#Gh{+urdH%edsVC&f3_k1=P#-DX-A%Luvim>Zo99IRT(cT32F_Elv!W9 ze*GktUHjYHOY@(8S^<=iDz;q%s_%!U*2GV2G_3E{-sYv}RGKhQg3-^v@elUE=@}Xp z+Z2{Q3JrdqU}kPVRP4FCPdP6>r3eU20T4iIeU3vHel)PcR{*goXge{pysRy@PNfLK z>##(_DbOHj#{8#J+(r@-`EzMcW+Nk42jKI4zyO&7TyvY(8|eX_Yv<|_3!R?eEz>zB z;Gv<}(1}yuQRj2_qozfaY^<-O*${4IEq%&@+x*a>yXR(JRaMm{XV7YWTo>27pU0k_ z+$)SzOw>2-vfAF=x5Z?YrIPCCXdi429VTy&5I9gh;&g6Jqlb2vwbT-#TDe&j_4N|1!;2VZv3snFjFRTzRS~r{uSxGgMI?*Pn8ze1+2wq1I`*& zlqB|~7)PqDUT8pV`ucov3sZ|TA`ls1JtPU(=V(<#tR1qw`>_$Ngirjw;=ShCo1Q^6 zRaF!`qjk%ik3hMvo!4h?4$S|S^ZdA0{2y!Owx}mx^!4;&B&7v@XA0+;PQAx1Z%jUw z_NjJVw)vAs88G{rXWNl^ckOuBcGREtWPNL+&dV}lE4rO|w}z5A#1BHK5kj$};7HjF zHaZ||7d$OXG!lDPCx?cHw7BIX&lA$}R2syDobU|WG4l5>bBL=1{qG((0L!Dp50@4; z4jk*M>ofcIWaXexZj*^Ob2l$vgX_MlFk$HOOvaL8KD1TV)O08D>K-5#{+R*VkEwDS zoNcIee1Zo}wV z%sbkX6Zx%}DOK@sHv#byjSi;ukyth++Q=PngEV7t2t_~Ku=#J1s%fE^FYZ*a%m#V~ zh}38tsF6q>@$79IAJ>vCP}Rx(++3v63ZZb4=H{rThXQ%6bH*wtmyevBe16{*3Imlm z-xg%vnS-5OhtHbPpDs(-k{fw+bhNw3Wf2Q-^&m=DRscQ-iNxK_pcojhafd_d#l82U z#?wzvHyg@=-_50aql#73y9OBg ziK)b1;;%mpnC*GKOasWP6(sFoyG}EQL9t8IZ-gs~S3uyPe;I@D>r*P}X;bM?r(}Gp zqp{d5(ivsUq~s%N^NDr+)3xwN{AZ5X>rxBDF3zqR6VZ23eW1<3EbpO+Cn!g{Fx=Fk zu#%G9h;?04!DrTk`P6zQv=@(ZxBeb#?Q* zzf&b_OATWFxLqDs0jZ{d=aOK)A;e$8_fR#7A09ciz0^yG{^|fk$!^vW%~&=qB|PdK z#L#g5GG6RtF|vuaYfiGlZXA$gaip3#>Q!JXdZi9D*wbQcRm<1%WxNU*1*}Ih0C+2I zOcHGBpZ%VZ=U$lVgmiTD+v@K9t*tku4!>fc_f6-s3iz7b5jx`{JIiVSVH|9BKbQIj z2qXEpIr7(HVq%Z_m7V(mL}1X6netNfX}#g3ICn;jYwyfcyRERnRzt!a zaG2G0y$juO_A?*1+au8%b>6PFRj<`qHR8G^^L2`BFu#ez$h(F_G=AtfY^i8}2GswO z3SBK>TcL9HdFKZ@sl(o%G7ezu_kO-tE}xa*asd5)i#z?OSe28k zhH^E^6)2#B^N%ySQZyzZKm&X0&(ZoO z_dNmr+U^1w%ZyqBt`*p0qsOoEE`Nwi8`m1?E>zEf6*mGaZIhj8zV_7ieVzI_fC3(~ zoriLr;0cc`;BaK&BC|L--SlqHc~Zj6X20HsK?2M)Q1TNsT7k!%8ZcH6&D>?nUi(`m z@9CkDt8Up+%uw8s2OI|vfshF_6;pS8(BR=WTCP|g45Xy)maTArE{|hX(;IM{?u2B5IyW^T-Vqm2TKXoiiZ99NVm`d*7OZ83QU4;3v|3PYa(uO|A9VZtUtB z?C9ti&^Ucub-uK_&N4$X;ij{{fy!gCC3fNoEG3zl@R}su)LPF{&RW;ggBv0;oILtv zF3V?%D$?CY7-TnS(6$e>`^loFEn9(GYFhi)(?io% z6!10mJPGtZTsdZ6Nl@gZgu-+xurIMp_XpkuOWe&8@LkCNE*;9u6nS{aB?{C*00$#! zWo!MgBW-QH425A@m$>G08h|m{&aNPbdPdP}%GBa;04Sjcdvk|v-iMn*hEL>xe-5mc z{1i8J5@2W`Lnon5w~_t#Fe#<4D12tM9f6$-Se+I+3Nu@P?PEv6xZ)I6d^UKn4(Z7= zCjpi3JOVI-2c{Vbw9QPKufePVUo;8itiR0Kd|uG_L%_8d!~Db-FS$oD&{M) zdpdmocccc*Fi=|B-`(Ip*Ey=J${tG@WaI%-s{MtmtSrbqsJd7|n=@h1K^98NX5kwP ztzln<>5F#s_NojMKVO(>kq2n7t+|~8#XdehZt6VR23+oi`iFr94T;%0z$xLseQaLb z_)*ZFj7y)D?MqmgE@+BWp$oAl1Jdv%?1?W%S|VD!^_?4aNQqf@uh;FB#l_Xd#k_r% z#6i?)?-`0wL4V-M6Z1Uv-oE*BK;Y{vBfwP6vhpk)aWfh;ZvflXNar#p#(jkzDtDN~ z%E@WhUTRtVu13LqilY7$=rP~Mc1Hcpil4l8*eV(coxTD57U>k~^wH;|3$ZJi=^QQr zO-SmolhxS8@zV6ykEF>abpY!QNZuH4Sy;yEP@(M0KR05eA{aF>Tx9^@0Y|%>fVx|? zQi_PU)80dILZJE@^)D518JgJGguB~MqEkhcg)z}T3>pR{E{%0OOtJTwTjDOB81)*f zblh$J9Gx&+{N>qbSEw(k^-i%aH(u;w)0ztp^!1fz(f#2-Wj5sakd&0)IZlWW{cm8>!PZh3~F- z(%ys07DA-X9FKk!L#;jo#?99rI5=RvvjS``!~zbiC1ps%!O4-YbiZ^uB9n0Lm&_QA zzX2=*Xgaz-d9@PX(Qfgc{M7(XEAHZO6jd;K+xh zWMr`$Fi=esow|7hU;pX@8fLb>(!tT+V+!W1UP@7uTie-`IleX^AF9snAM=#??5bhg$E2H+U5h;Mg4`v6u&uON) zjZ?oSBrC;}4*vPJ*cEp~SXkIjtc|K_U;USgQ#s#)9i^0@AZbEdI?wo-42Juywpsf1 zPSOcF0Aw8`n9ZASVgKs|7-`OLC!?WUJK% zGR)9euMLN!M%{3T!-9q?`cpF0P&^9$gZafb2inv^%P`-b+Uy~Q6)LEQWkPby-pls zm9Fts${%y2Wn}?7yPCwQ52hB0EK*#{`#Uw2?t5~8cWZuy4MBjpk#aN4BSV|=B3{8j zkCx(mCoEXh=VizHAz-?U+kbV84+{71gMFSAz$mc=Q&j@T3j}{=+#J^v(^3%|6O$ok z#HC+v)4bqt=e$S%c#GvSt70zDsB9~t0?}UXt-rk}Vh+(}>nbkr^T(_|M#@yVTR-wT-@OI3tRb^*0 zW0m>jsLkf@zq|h7(xpuKQ(ztM27y`*`Wf&%pc5SIc?I`gzLO7e%j4!|G7!r)7JIS# zJlH{j0=$ZV(?b5Hx&0`vhDDHU__6s_(x1$I;*wW8-_PD5C~joQRg--}k*#uh*-( zz1~nfTuKeeA;ziwYk9h6QK;vqOx76Dgd{}TJ??1uRVDKR5 z=ES0f+&v_yOk<9Qef-!gAID`rbTHNM8H^3l-2D7-xvMpp zFZ0E*wl)^Bi(o}Ujw@Se*3e_C=x|I?fV+N95)Z~V{69~URMTpV+WM&M!uE=J&D1pfaq0_SeG|8GBA_P%T4 z1XB-VO{>Q3kQmF&CscnQ8tV_W&K4csd4J_-#GByfCH4-dhDoI5i1*D_0Y%KdSIVO2 zXn9=)LI1JJC6CR0uVeY?iZ9$Q2SBBjUKYG~8}NSsWL&h7 literal 30427 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 diff --git a/dropshots/src/androidTest/assets/static/50x50.png b/dropshots/src/androidTest/assets/static/50x50.png new file mode 100644 index 0000000000000000000000000000000000000000..d7da4f60d704762bcdabaf47626b0c652c58130f GIT binary patch literal 172 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2nC-#^NA%Cx&(BWL^R}EX7WqAsj$Z z!;#VfIEG|2zCGW_$)G53V8iF{_amnZ*m)Q`?oLZP&%AFxb6%#)<&I^7zRI)Q jOnNh @@ -82,27 +86,7 @@ class DropshotsTest { @After fun after() { if (imageDirectory.exists()) { - Files.walkFileTree(imageDirectory.toPath(), object : FileVisitor { - override fun preVisitDirectory(dir: Path?, attrs: BasicFileAttributes?): FileVisitResult { - return FileVisitResult.CONTINUE - } - - override fun visitFile(file: Path?, attrs: BasicFileAttributes?): FileVisitResult { - requireNotNull(file) - Files.delete(file) - return FileVisitResult.CONTINUE - } - - override fun visitFileFailed(file: Path?, exc: IOException?): FileVisitResult { - throw exc!! - } - - override fun postVisitDirectory(dir: Path?, exc: IOException?): FileVisitResult { - requireNotNull(dir) - Files.delete(dir) - return FileVisitResult.CONTINUE - } - }) + imageDirectory.deleteRecursively() } } @@ -131,7 +115,7 @@ class DropshotsTest { } @Test - fun testWritesReferenceImageOnFailureWhenRecording() { + fun testWritesReferenceImageForMissingImages() { val dropshots = Dropshots( rootScreenshotDirectory = imageDirectory, filenameFunc = filenameFunc, @@ -181,40 +165,49 @@ class DropshotsTest { @Test fun testFailsForDifferences() { - assumeFalse(isRecordingScreenshots) + val dropshots = Dropshots( + resultValidator = CountValidator(0), + rootScreenshotDirectory = imageDirectory, + filenameFunc = filenameFunc, + recordScreenshots = false, + imageComparator = SimpleImageComparator(), + ) - var failed = false + var caughtError: AssertionError? = null activityScenarioRule.scenario.onActivity { try { - Log.d("!!! TEST !!!", "Asserting snapshot...") dropshots.assertSnapshot( view = it.findViewById(android.R.id.content), name = "MatchesViewScreenshotBad", filePath = "static" ) - Log.d("!!! TEST !!!", "Snapshot asserted") - failed = true } catch (e: AssertionError) { - Log.d("!!! TEST !!!", "Snapshot assertion failed as expected.") - // pass + caughtError = e } } - Log.d("!!! TEST !!!", "Validating thrown error") - if (failed) { - fail("Expected error when screenshots differ.") - } + assertNotNull("Expected error when screenshots differ.", caughtError) } @Test fun testPassesWhenValidatorPasses() { - assumeFalse(isRecordingScreenshots) + val dropshots = Dropshots( + resultValidator = FakeResultValidator { true }, + rootScreenshotDirectory = imageDirectory, + filenameFunc = filenameFunc, + recordScreenshots = false, + imageComparator = SimpleImageComparator(), + ) - fakeValidator.validator = { true } activityScenarioRule.scenario.onActivity { + val image = Bitmap.createBitmap(50, 50, Bitmap.Config.ARGB_8888) + with(Canvas(image)) { + drawColor(Color.BLACK) + } + dropshots.assertSnapshot( - view = it.findViewById(android.R.id.content), - name = "MatchesViewScreenshotBad", + bitmap = image, + name = "50x50", filePath = "static" ) } @@ -222,16 +215,25 @@ class DropshotsTest { @Test fun testFailsWhenValidatorFails() { - assumeFalse(isRecordingScreenshots) - - fakeValidator.validator = { false } + val dropshots = Dropshots( + resultValidator = FakeResultValidator { false }, + rootScreenshotDirectory = imageDirectory, + filenameFunc = filenameFunc, + recordScreenshots = false, + imageComparator = SimpleImageComparator(), + ) var caughtError: AssertionError? = null activityScenarioRule.scenario.onActivity { + val image = Bitmap.createBitmap(50, 50, Bitmap.Config.ARGB_8888) + with(Canvas(image)) { + drawColor(Color.BLACK) + } + try { dropshots.assertSnapshot( - view = it.findViewById(android.R.id.content), - name = "MatchesViewScreenshotBad", + bitmap = image, + name = "50x50", filePath = "static" ) } catch (e: AssertionError) { @@ -244,23 +246,33 @@ class DropshotsTest { @Test fun fastFailsForMismatchedSize() { - assumeFalse(isRecordingScreenshots) + val dropshots = Dropshots( + resultValidator = CountValidator(0), + rootScreenshotDirectory = imageDirectory, + filenameFunc = filenameFunc, + recordScreenshots = false, + imageComparator = SimpleImageComparator(), + ) - var failed = false + var caughtError: AssertionError? = null activityScenarioRule.scenario.onActivity { + val image = Bitmap.createBitmap(50, 60, Bitmap.Config.ARGB_8888) + with(Canvas(image)) { + drawColor(Color.BLACK) + } + try { dropshots.assertSnapshot( - view = it.findViewById(android.R.id.content), - name = "MatchesViewScreenshotBadSize", + bitmap = image, + name = "50x50", filePath = "static" ) - failed = true - } catch (e: Throwable) { - // no op + } catch (e: AssertionError) { + caughtError = e } } - assertFalse("Mismatched size screenshot test expected to fail, but passed.", failed) + assertNotNull("Mismatched size screenshot test expected to fail, but passed.", caughtError) } } diff --git a/dropshots/src/androidTest/kotlin/com/dropbox/dropshots/EmulatorConfigRule.kt b/dropshots/src/androidTest/kotlin/com/dropbox/dropshots/EmulatorConfigRule.kt deleted file mode 100644 index fb21b45..0000000 --- a/dropshots/src/androidTest/kotlin/com/dropbox/dropshots/EmulatorConfigRule.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.dropbox.dropshots - -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.uiautomator.UiDevice -import org.junit.rules.TestRule -import org.junit.runner.Description -import org.junit.runners.model.Statement - -class EmulatorConfigRule : TestRule { - override fun apply(base: Statement, description: Description): Statement = - EmulatorConfigStatement(base) - - private class EmulatorConfigStatement( - private val base: Statement, - ) : Statement() { - override fun evaluate() { - enableDemoMode() - base.evaluate() - disableDemoMode() - } - - private fun enableDemoMode() { - UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) - .apply { - executeShellCommand("cmd overlay enable com.android.internal.systemui.navbar.gestural") - executeShellCommand("settings put global sysui_demo_allowed 1") - executeShellCommand("am broadcast -a com.android.systemui.demo -e command enter") - executeShellCommand("am broadcast -a com.android.systemui.demo -e command clock -e hhmm 1234") - executeShellCommand("am broadcast -a com.android.systemui.demo -e command battery -e plugged false") - executeShellCommand("am broadcast -a com.android.systemui.demo -e command battery -e level 100") - executeShellCommand("am broadcast -a com.android.systemui.demo -e command network -e wifi show -e level 4") - executeShellCommand("am broadcast -a com.android.systemui.demo -e command network -e mobile show -e datatype none -e level 4") - executeShellCommand("am broadcast -a com.android.systemui.demo -e command notifications -e visible false") - } - } - - private fun disableDemoMode() { - UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) - .executeShellCommand("am broadcast -a com.android.systemui.demo -e command exit") - } - } -} diff --git a/dropshots/src/main/java/com/dropbox/dropshots/Dropshots.kt b/dropshots/src/main/java/com/dropbox/dropshots/Dropshots.kt index 75fc991..abdfa05 100644 --- a/dropshots/src/main/java/com/dropbox/dropshots/Dropshots.kt +++ b/dropshots/src/main/java/com/dropbox/dropshots/Dropshots.kt @@ -1,7 +1,6 @@ package com.dropbox.dropshots import android.app.Activity -import android.content.Context import android.graphics.Bitmap import android.graphics.BitmapFactory import android.graphics.Canvas @@ -30,7 +29,6 @@ public class Dropshots internal constructor( private val resultValidator: ResultValidator, ) : TestRule { private val context = InstrumentationRegistry.getInstrumentation().context - private val targetContext = InstrumentationRegistry.getInstrumentation().targetContext private var fqName: String = "" private var packageName: String = "" private var className: String = ""