<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="/feeds.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:base="https://chameth.com/">
    <title>Chameth.com - posts like android-espresso-spoon, debugging-beyond-the-debugger, g15-ram-upgrade, why-you-should-be-using-https but not migrating-from-github-to-forgejo, travel-toolkit</title>
    <subtitle>Personal homepage of Chris Smith</subtitle>
    <link href="https://chameth.com/feeds/posts/like/android-espresso-spoon,debugging-beyond-the-debugger,g15-ram-upgrade,why-you-should-be-using-https/unlike/migrating-from-github-to-forgejo,travel-toolkit/" rel="self"/>
    <link href="https://chameth.com/"/>
    <icon>https://chameth.com/favicon.png</icon>
    <updated>2023-07-29T00:00:00Z</updated>
    <id>https://chameth.com/</id>
    <author>
        <name>Chris Smith</name>
    </author>
    <entry>
        <title>Upgrading the RAM in a Dell G15 laptop</title>
        <link href="https://chameth.com/g15-ram-upgrade/"/>
        <updated>2023-07-29T00:00:00Z</updated>
        <id>https://chameth.com/g15-ram-upgrade/</id>
        <content xml:lang="en" type="html">&lt;figure class=&#34;image left&#34;&gt;
  &lt;picture&gt;
      &lt;source srcset=&#34;https://chameth.com/g15-ram-upgrade/g15.avif&#34; type=&#34;image/avif&#34;/&gt;
      &lt;source srcset=&#34;https://chameth.com/g15-ram-upgrade/g15.webp&#34; type=&#34;image/webp&#34;/&gt;
      &lt;img src=&#34;https://chameth.com/g15-ram-upgrade/g15.png&#34; alt=&#34;An open Dell G15 laptop&#34; loading=&#34;lazy&#34; width=&#34;580&#34; height=&#34;402&#34;/&gt;
  &lt;/picture&gt;
  &lt;figcaption&gt;&lt;p&gt;The Dell G15&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I currently use a Dell G15 laptop for work. It has served me well for a little
over a year, but recently it has been struggling a little with my day-to-day
workload. It came with 32GB of RAM — the highest possible specification at the
time&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a class=&#34;footnote-ref&#34; href=&#34;#fn:1&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; — but that is apparently no longer enough for me.&lt;/p&gt;
&lt;p&gt;For a recent project, I was working on a Rust library used in an Android app.
That meant running the usual glut of Android tools (Android Studio, an emulator
and at least one Gradle daemon) alongside a normal IDE (IntelliJ IDEA). Throw
in a web browser and a couple of electron apps, and I often managed to
use all 32GB.&lt;/p&gt;
&lt;p&gt;When you start swapping memory out to an encrypted disk — even an SSD — it
doesn’t make for great performance. At first, I tried to work around this
by enabling the Linux out-of-memory (OOM) killer, but it turns out that it’s not
too good with Electron apps: it will kill the large browser process, but then
the small Electron wrapper will just respawn it.&lt;/p&gt;
&lt;!--more--&gt;
&lt;h3 id=&#34;can-it-be-upgraded-or-not&#34;&gt;Can it be upgraded or not?&lt;/h3&gt;
&lt;p&gt;The obvious solution to not having enough RAM is to add more RAM. A quick look
in the manual showed this might not be possible, though. The manual includes
the following “Memory specifications” table:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Values&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Memory slots&lt;/td&gt;
&lt;td&gt;Two SODIMM slots&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory type&lt;/td&gt;
&lt;td&gt;DDR5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory speed&lt;/td&gt;
&lt;td&gt;4800&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maximum memory configuration&lt;/td&gt;
&lt;td&gt;32GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Minimum memory configuration&lt;/td&gt;
&lt;td&gt;8GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory size per slot&lt;/td&gt;
&lt;td&gt;8GB or 16GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory configurations supported&lt;/td&gt;
&lt;td&gt;&lt;ul&gt;&lt;li&gt;8 GB, 1 x 8 GB, DDR5, 4800 MHz&lt;/li&gt;&lt;li&gt;16 GB, 1 x 16 GB, DDR5, 4800 MHz&lt;/li&gt;&lt;li&gt;16 GB, 2 x 8 GB, DDR5, 4800 MHz, dual-channel&lt;/li&gt;&lt;li&gt;32 GB, 2 x 16 GB, DDR5, 4800 MHz, dual-channel&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;That unambiguously says that an upgrade from 32GB is not possible. I gave up.&lt;/p&gt;
&lt;p&gt;Later though, I was complaining about memory issues to a friend, and he pointed
out a Dell forum thread where a couple of people claim to have successfully
installed dual-channel 32GB modules. Since the alternative was getting an
entire new PC after only a year, I decided to give it a go.&lt;/p&gt;
&lt;h3 id=&#34;the-upgrade-attempt&#34;&gt;The upgrade attempt&lt;/h3&gt;
&lt;p&gt;I ordered a pair of Crucial 32GB DDR5-4800 SODIMMs, and after they turned up
dismantled the laptop. The G15 comes apart pretty normally: there are uncovered
screws on the bottom holding the lower part of the case on. With those
removed and some gentle prying, it pops off, and you get access to the battery,
GPU and motherboard.&lt;/p&gt;
&lt;p&gt;The first thing I saw was this:&lt;/p&gt;
&lt;figure class=&#34;image full&#34;&gt;
  &lt;picture&gt;
      &lt;source srcset=&#34;https://chameth.com/g15-ram-upgrade/motherboard.avif&#34; type=&#34;image/avif&#34;/&gt;
      &lt;source srcset=&#34;https://chameth.com/g15-ram-upgrade/motherboard.webp&#34; type=&#34;image/webp&#34;/&gt;
      &lt;img src=&#34;https://chameth.com/g15-ram-upgrade/motherboard.jpg&#34; alt=&#34;The G15 motherboard, with a large &amp;#34;DDR5 8G/16G Only&amp;#34; label, and a smaller &amp;#34;DIMM B DDR5 8G/16G&amp;#34; label next to a SODIMM slot&#34; loading=&#34;lazy&#34; width=&#34;1536&#34; height=&#34;1300&#34;/&gt;
  &lt;/picture&gt;
  &lt;figcaption&gt;&lt;p&gt;The G15 motherboard adamantly proclaiming that it doesn’t want 32GB SODIMMs&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Not one but two labels that indicate it will only accept 8GB or 16GB modules.
Oh well, what’s the worst that can happen?&lt;/p&gt;
&lt;h3 id=&#34;oops&#34;&gt;Oops?&lt;/h3&gt;
&lt;p&gt;I dutifully installed the new modules, reconnected the battery and put the
case back together. I pressed the power button, and… nothing. None of the
usual garish lights that immediately come on, no screen output, just a dead
laptop. After reading some more of the user manual, I found that there is a tiny
status LED on the side next to the ethernet port. Forcing the laptop to power
off and back on again, the status LED blinked a distress code at me: 2 amber
blinks, 4 white blinks. The manual says that is — unsurprisingly — a memory
fault.&lt;/p&gt;
&lt;p&gt;I figured at this point that the manual and labels on the motherboard were
probably right. I took the laptop apart again, reinstalled the original 2x16GB
modules, reassembled it, and pressed the power button. It didn’t boot. I don’t
spend a lot of time fiddling inside computers, but I’ve done it enough that
I’m reasonably confident I can’t entirely break a computer while swapping some
RAM modules. I took to Googling&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a class=&#34;footnote-ref&#34; href=&#34;#fn:2&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;, and found an interesting article that said
Dell laptops don’t like to boot after RAM changes unless you clear the CMOS by
popping out the battery for 15 minutes.&lt;/p&gt;
&lt;p&gt;I opened the laptop up, and looked around for the CMOS battery. There wasn’t
one. Turns out they don’t exist any more. I left the main battery disconnected
for a while to see if it would help, and it didn’t.&lt;/p&gt;
&lt;h3 id=&#34;unexpected-success&#34;&gt;Unexpected success&lt;/h3&gt;
&lt;p&gt;I started to get worried: if I couldn’t fix this, I wouldn’t be able to
work until I got a new PC, and that wasn’t really in my budget at the minute.
I sat reading old forum threads and help guides&lt;sup id=&#34;fnref:3&#34;&gt;&lt;a class=&#34;footnote-ref&#34; href=&#34;#fn:3&#34; role=&#34;doc-noteref&#34;&gt;3&lt;/a&gt;&lt;/sup&gt;, none of which were actually
useful. Out of nowhere, though, the laptop booted up.&lt;/p&gt;
&lt;p&gt;Naturally, I immediately shut the laptop down again, opened it up, and switched
back to the new RAM modules. Then I turned it on again and sat waiting. After
about 15 minutes of it looking totally dead, it turned on and showed a BIOS
warning about the hardware configuration being changed. It then booted perfectly
normally, and all 64GB of RAM was visible and usable.&lt;/p&gt;
&lt;p&gt;My theory is that the forum threads were right: Dell laptops are funny about
RAM upgrades. But somehow in removing the physical CMOS battery, they’ve kept
the same “you have to wait 15 minutes” behaviour just without any indication
that’s what’s happening. Regardless, I now have enough RAM even for the
greediest of IDEs and Electron apps.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr/&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Bizarrely, the maximum spec has &lt;em&gt;decreased&lt;/em&gt; to 16GB since then. &lt;a class=&#34;footnote-backref&#34; href=&#34;#fnref:1&#34; role=&#34;doc-backlink&#34;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;In the generic sense. I use &lt;a href=&#34;https://kagi.com/&#34;&gt;Kagi&lt;/a&gt; these days. &lt;a class=&#34;footnote-backref&#34; href=&#34;#fnref:2&#34; role=&#34;doc-backlink&#34;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:3&#34;&gt;
&lt;p&gt;On my phone because, y’know, the laptop was busted. &lt;a class=&#34;footnote-backref&#34; href=&#34;#fnref:3&#34; role=&#34;doc-backlink&#34;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</content>
    </entry>
    <entry>
        <title>Debugging beyond the debugger</title>
        <link href="https://chameth.com/debugging-beyond-the-debugger/"/>
        <updated>2019-05-08T00:00:00Z</updated>
        <id>https://chameth.com/debugging-beyond-the-debugger/</id>
        <content xml:lang="en" type="html">&lt;figure class=&#34;image right&#34;&gt;
  &lt;picture&gt;
      &lt;source srcset=&#34;https://chameth.com/debugging-beyond-the-debugger/tools.avif&#34; type=&#34;image/avif&#34;/&gt;
      &lt;source srcset=&#34;https://chameth.com/debugging-beyond-the-debugger/tools.webp&#34; type=&#34;image/webp&#34;/&gt;
      &lt;img src=&#34;https://chameth.com/debugging-beyond-the-debugger/tools.jpg&#34; alt=&#34;Collection of tools hanging on a wall&#34; loading=&#34;lazy&#34; width=&#34;300&#34; height=&#34;396&#34;/&gt;
  &lt;/picture&gt;
  &lt;figcaption&gt;&lt;p&gt;Real-life debugging tools&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Most programming — and sysadmin — problems can be debugged in a
fairly straight forward manner using logs, print statements,
educated guesses, or an actual debugger. Sometimes, though, the
problem is more elusive. There’s a wider box of tricks that can
be employed in these cases but I’ve not managed to find a nice
overview of them, so here’s mine. I’m mainly focusing on Linux
and similar systems, but there tend to be alternatives available
for other Operating Systems or VMs if you seek them out.&lt;/p&gt;
&lt;h3 id=&#34;networking&#34;&gt;Networking&lt;/h3&gt;
&lt;h4 id=&#34;tcpdump&#34;&gt;tcpdump&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;tcpdump&lt;/code&gt; prints out descriptions of packets on a network interface. You can
apply filters to limit which packets are displayed, chose to dump the entire
content of the packet, and so forth.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;Typical usage might look something like:&lt;/p&gt;
&lt;pre class=&#34;chroma-chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;# tcpdump -nSi eth0 port 80
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;16:03:35.577781 IP6 2001:db8::1.54742 &amp;gt; 2001:db8::2.80: Flags [S], seq 2815779044, win 64800, options [mss 1440,sackOK,TS val 2378811665 ecr 0,nop,wscale 7], length 0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;16:03:35.586853 IP6 2001:db8::2.80 &amp;gt; 2001:db8::1.54742: Flags [S.], seq 1522609102, ack 2815779045, win 28560, options [mss 1440,sackOK,TS val 3063610173 ecr 2378811665,nop,wscale 7], length 0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;16:03:35.586877 IP6 2001:db8::1.54742 &amp;gt; 2001:db8::2.80: Flags [.], ack 1522609103, win 507, options [nop,nop,TS val 2378811674 ecr 3063610173], length 0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;16:03:35.620678 IP6 2001:db8::1.54742 &amp;gt; 2001:db8::2.80: Flags [P.], seq 2815779045:2815779399, ack 1522609103, win 507, options [nop,nop,TS val 2378811708 ecr 3063610173], length 354: HTTP: GET / HTTP/1.1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here you can see the start of a plaintext HTTP request: the three-way
handshake as the TCP connection is established followed by a GET request.
Even if the data is encrypted as it will be in most cases, it’s often useful
to see the “shape” of the transmissions: did the client start sending data
when it connected, did the server ever respond, etc.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://danielmiessler.com/study/tcpdump/&#34;&gt;Daniel Miessler has a good tutorial on tcpdump&lt;/a&gt;
if you’re not familiar with it and don’t want to jump straight into the man
page.&lt;/p&gt;
&lt;h5 id=&#34;-with-docker&#34;&gt;… with Docker&lt;/h5&gt;
&lt;p&gt;Docker sets up separate network namespaces for each container. To see the
traffic across the interfaces of a single container you can &lt;code&gt;nsenter&lt;/code&gt; the
container’s network namespace:&lt;/p&gt;
&lt;pre class=&#34;chroma-chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;# nsenter -t $(docker inspect --format &amp;#39;{{.State.Pid}}&amp;#39; my_container) -n tcpdump -nS port 80
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This retrieves the PID for the container, and tells &lt;code&gt;nsenter&lt;/code&gt; to enter the
network (&lt;code&gt;-n&lt;/code&gt;) namespace from the given target (&lt;code&gt;-t&lt;/code&gt;) PID, and then run the
given command (in this case &lt;code&gt;tcpdump ...&lt;/code&gt;).&lt;/p&gt;
&lt;h4 id=&#34;openssl-s-client--s-server&#34;&gt;openssl s_client / s_server&lt;/h4&gt;
&lt;p&gt;When a connection is using TLS it’s often useful to try connecting to the
server and see what certificate it presents, algorithms it negotiates, and
so forth. OpenSSL offers two useful subcommands which can help with this:
&lt;code&gt;s_client&lt;/code&gt; for connecting as a client, and &lt;code&gt;s_server&lt;/code&gt; for listening to
connections.&lt;/p&gt;
&lt;p&gt;For example, using &lt;code&gt;s_client&lt;/code&gt; to connect to &lt;code&gt;google.com&lt;/code&gt; on the standard
HTTPS port shows us details about the server cert and its verification
status:&lt;/p&gt;
&lt;pre class=&#34;chroma-chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;$ openssl s_client -connect google.com:443
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;CONNECTED(00000003)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;depth=2 OU = GlobalSign Root CA - R2, O = GlobalSign, CN = GlobalSign
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;verify return:1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;depth=1 C = US, O = Google Trust Services, CN = Google Internet Authority G3
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;verify return:1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;depth=0 C = US, ST = California, L = Mountain View, O = Google LLC, CN = *.google.com
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;verify return:1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;Certificate chain
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt; 0 s:C = US, ST = California, L = Mountain View, O = Google LLC, CN = *.google.com
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;   i:C = US, O = Google Trust Services, CN = Google Internet Authority G3
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt; 1 s:C = US, O = Google Trust Services, CN = Google Internet Authority G3
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;   i:OU = GlobalSign Root CA - R2, O = GlobalSign, CN = GlobalSign
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;# ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Whereas connecting to my webserver and providing an unknown host in the SNI
field results in an SSL alert 112 (“The server name sent was not recognized”)
and no server certificate is sent:&lt;/p&gt;
&lt;pre class=&#34;chroma-chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;$ openssl s_client -connect chameth.com:443 -servername example.com
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;CONNECTED(00000003)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;140384831313024:error:14094458:SSL routines:ssl3_read_bytes:tlsv1 unrecognized name:../ssl/record/rec_layer_s3.c:1536:SSL alert number 112
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;no peer certificate available
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;# ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Often if you hit this kind of alert in an application the exact error will be
lost somewhere in the many layers between the SSL library and the logs, so
being able to directly connect and test can help diagnose a lot of issues.&lt;/p&gt;
&lt;p&gt;Once a connection is established you can read and write plain text and it
will be encrypted and decrypted automatically.&lt;/p&gt;
&lt;h4 id=&#34;java-apps&#34;&gt;Java apps&lt;/h4&gt;
&lt;p&gt;If a Java app is involved in the connection, you can enable a lot of built-in
debugging with a simple JVM property: &lt;code&gt;javax.net.debug&lt;/code&gt;. You can tweak
what exactly gets logged, but the easiest thing to do is just set the property
to &lt;code&gt;all&lt;/code&gt; and you’ll see information about certificate chains, verification,
and packet dumps:&lt;/p&gt;
&lt;pre class=&#34;chroma-chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;$ java -Djavax.net.debug=all -jar ....
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;# ...
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;found key for : duke
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;chain [0] = [
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;[
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;  Version: V1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;  Subject: CN=Duke, OU=Java Software, O=&amp;#34;Sun Microsystems, Inc.&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;  L=Cupertino, ST=CA, C=US
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;# ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;More information about Java’s debugging options is available on
&lt;a href=&#34;https://docs.oracle.com/javase/7/docs/technotes/guides/security/jsse/ReadDebug.html&#34;&gt;docs.oracle.com&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;thread-and-core-dumps&#34;&gt;Thread and core dumps&lt;/h3&gt;
&lt;p&gt;Higher-level languages frequently provide an interactive way to dump the
current execution state of all of their threads (a “thread dump”). This
is useful to spot deadlocks, some types of race conditions, and as a
quick and dirty method of investigating hangs or excessive CPU usage.&lt;/p&gt;
&lt;p&gt;With both Java and Go applications you can send a QUIT signal to have a
thread dump printed out; Go applications will quit after doing so, Java
ones will carry on running. At most terminals you can hit &lt;code&gt;Ctrl&lt;/code&gt; and &lt;code&gt;\&lt;/code&gt; to
send a QUIT signal.&lt;/p&gt;
&lt;p&gt;For Java you can also use the &lt;code&gt;jstack&lt;/code&gt; tool from the JDK to dump threads
by PID; this can be useful if the application is running in the background
or has redirected sysout:&lt;/p&gt;
&lt;pre class=&#34;chroma-chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;$ jstack 8321
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;Attaching to process ID 8321, please wait...
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;Debugger attached successfully.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;Client compiler detected.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;Thread t@5: (state = BLOCKED)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt; - java.lang.Object.wait(long) @bci=-1107318896 (Interpreted frame)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt; - java.lang.Object.wait(long) @bci=0 (Interpreted frame)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt; - java.lang.ref.ReferenceQueue.remove(long) @bci=44, line=116 (Interpreted frame)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt; - java.lang.ref.ReferenceQueue.remove() @bci=2, line=132 (Interpreted frame)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt; - java.lang.ref.Finalizer$FinalizerThread.run() @bci=3, line=159 (Interpreted frame)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;# ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A core dump provides more complete information about the state of a process,
but is often more complex to interpret. The &lt;code&gt;gcore&lt;/code&gt; utility from GDB will
create a core dump of a process with a given PID. You can then generally
load the core file using your normal debugger, depending on the language
in question.&lt;/p&gt;
&lt;h3 id=&#34;system-calls&#34;&gt;System calls&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;strace&lt;/code&gt; is the swiss army knife for seeing what a process is doing. It
details each system call made by a program (you can filter them down, of
course). For example:&lt;/p&gt;
&lt;pre class=&#34;chroma-chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;$ strace -e read curl https://google.com/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;read(3, &amp;#34;\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0&amp;gt;\0\1\0\0\0 \236\0\0\0\0\0\0&amp;#34;..., 832) = 832
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;read(3, &amp;#34;\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0&amp;gt;\0\1\0\0\0P!\0\0\0\0\0\0&amp;#34;..., 832) = 832
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;read(3, &amp;#34;\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0&amp;gt;\0\1\0\0\0\200l\2\0\0\0\0\0&amp;#34;..., 832) = 832
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;read(3, &amp;#34;\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0&amp;gt;\0\1\0\0\0\20Q\0\0\0\0\0\0&amp;#34;..., 832) = 832
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;# ...
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;read(3, &amp;#34;\0\0\0\0\0\0\0\4\25\345\366\302\273sE6\365wI\225\321|\3435Z\362\216\372\215\251aO&amp;#34;..., 253) = 253
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&amp;lt;HTML&amp;gt;&amp;lt;HEAD&amp;gt;&amp;lt;meta http-equiv=&amp;#34;content-type&amp;#34; content=&amp;#34;text/html;charset=utf-8&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&amp;lt;TITLE&amp;gt;301 Moved&amp;lt;/TITLE&amp;gt;&amp;lt;/HEAD&amp;gt;&amp;lt;BODY&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&amp;lt;H1&amp;gt;301 Moved&amp;lt;/H1&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;The document has moved
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&amp;lt;A HREF=&amp;#34;https://www.google.com/&amp;#34;&amp;gt;here&amp;lt;/A&amp;gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&amp;lt;/BODY&amp;gt;&amp;lt;/HTML&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;read(3, &amp;#34;\27\3\3\0!&amp;#34;, 5)                = 5
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;# ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&#34;http://www.brendangregg.com/blog/2014-05-12/strace-wow-much-syscall.html&#34;&gt;Brendan Gregg&lt;/a&gt;
has a nice guide on &lt;code&gt;strace&lt;/code&gt; and alternatives.&lt;/p&gt;
&lt;h4 id=&#34;-with-docker-1&#34;&gt;… with docker&lt;/h4&gt;
&lt;p&gt;When the application is running in docker you can usually just &lt;code&gt;strace&lt;/code&gt; it
from the host with the correct PID
(from e.g. &lt;code&gt;docker inspect --format &amp;#39;{{.State.Pid}}&amp;#39; my_container&lt;/code&gt;).
Sometimes you may need to trace the startup of an application though, which is
a bit trickier. Instead you can run a new container using the same PID
namespace as your target, and the permissions needed to &lt;code&gt;strace&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;chroma-chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;$ docker run --rm -it --pid=container:my_container \
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;  --net=container:my_container \
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;  --cap-add sys_admin \
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;  --cap-add sys_ptrace \
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;  alpine
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;From within the new container you can install strace, and trace any running
program within the target container using &lt;code&gt;strace -p&lt;/code&gt; as normal. To start a
new program you need access to the target container’s filesystem, which you
can get to via &lt;code&gt;/proc/1/root&lt;/code&gt; (PID &lt;code&gt;1&lt;/code&gt; being the main process that docker
started in the target container).&lt;/p&gt;
&lt;h3 id=&#34;files&#34;&gt;Files&lt;/h3&gt;
&lt;p&gt;Sometimes the problem might relate to file access. There are a couple of
straight forward — but nonetheless useful — tools which might help here.
&lt;code&gt;inotifywait&lt;/code&gt; uses the Linux &lt;code&gt;inotify&lt;/code&gt; subsystem to watch files or directories
for operations. For example:&lt;/p&gt;
&lt;pre class=&#34;chroma-chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;$ inotifywait -mr site/content
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;Setting up watches.  Beware: since -r was given, this may take a while!
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;Watches established.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;site/content/post/ MODIFY 2019-05-08-debugging-beyond-the-debugger.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;site/content/post/ OPEN 2019-05-08-debugging-beyond-the-debugger.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;site/content/post/ MODIFY 2019-05-08-debugging-beyond-the-debugger.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;site/content/post/ MODIFY 2019-05-08-debugging-beyond-the-debugger.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;site/content/post/ CLOSE_WRITE,CLOSE 2019-05-08-debugging-beyond-the-debugger.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;# ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here the &lt;code&gt;-m&lt;/code&gt; switch makes &lt;code&gt;inotifywait&lt;/code&gt; monitor the files forever (instead
of exiting on the first modification, which is the normal behaviour) and &lt;code&gt;r&lt;/code&gt;
makes it recurse into the directory and monitor each file and subdirectory in
there.&lt;/p&gt;
&lt;p&gt;If you want to see what processes currently have a file open, &lt;code&gt;fuser&lt;/code&gt; is the
go-to tool. For example:&lt;/p&gt;
&lt;pre class=&#34;chroma-chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;$ fuser -v /
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;                     USER PID ACCESS COMMAND
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;/:                   root     kernel mount /
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;                     chris      2961 .rc.. systemd
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;                     chris      2986 .r... gdm-x-session
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;                     chris      2994 .r... dbus-daemon
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;                     chris      3001 .r... gnome-session-b
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;# ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;honourable-mentions&#34;&gt;Honourable mentions&lt;/h3&gt;
&lt;p&gt;These aren’t really debugging tools, but I feel it’s worth mentioning as
they often feature somewhere along the debugging-of-weird-problems journey.&lt;/p&gt;
&lt;p&gt;I’ve seen some weird and wonderful problems happen
because a disk is full, so a quick &lt;code&gt;df&lt;/code&gt; early on in the debugging process
never hurts. Some apps may hang, some may corrupt their config, some may
fall over and die; sometimes the manner in which they fail doesn’t obviously
point to a disk space issue.&lt;/p&gt;
&lt;p&gt;Another issue that comes up now and then — especially inside VMs or
other environment that don’t have a decent amount of “noise” happening —
is entropy exhaustion. A quick look at &lt;code&gt;/proc/sys/kernel/random/entropy_avail&lt;/code&gt;
should be enough to confirm that everything is ticking along nicely. If it’s
exceedingly low then you may find that anything involving random number
generation stalls (TLS connections for example).&lt;/p&gt;
</content>
    </entry>
    <entry>
        <title>Android testing with Espresso and Spoon</title>
        <link href="https://chameth.com/android-espresso-spoon/"/>
        <updated>2017-05-16T00:00:00Z</updated>
        <id>https://chameth.com/android-espresso-spoon/</id>
        <content xml:lang="en" type="html">&lt;p&gt;I’ve been spending some time recently setting up automated testing for our
collection of Android apps and libraries at work. We have a mixture of unit
tests, integration tests, and UI tests for most projects, and getting them all
to run reliably and automatically has posed some interesting challenges.&lt;/p&gt;
&lt;h3 id=&#34;running-tests-on-multiple-devices-using-spoon&#34;&gt;Running tests on multiple devices using Spoon&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/square/spoon&#34;&gt;Spoon&lt;/a&gt; is a tool developed by Square that
handles distributing instrumentation tests to multiple connected devices,
aggregating the results, and making reports.&lt;/p&gt;
&lt;p&gt;As part of our continuous integration we build both application and test APKs,
and these are pushed to the build server as build artefacts. A separate build
job then pulls these artefacts down to a Mac Mini we have in the office,
and executes Spoon with a few arguments:&lt;/p&gt;
&lt;pre class=&#34;chroma-chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;java -jar spoon-runner.jar &lt;span class=&#34;chroma-se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    --apk application.apk &lt;span class=&#34;chroma-se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    --test-apk applicationTests.apk &lt;span class=&#34;chroma-se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    --fail-on-failure &lt;span class=&#34;chroma-se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    --fail-if-no-device-connected
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Spoon finds all devices, deploys both APKs on them, and then begins the
instrumentation tests. We use two physical devices and an emulator to cover
the form factors and API versions that are important to us; if any test fails
on any of those devices, Spoon will return an error code and the build will
fail.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;For library projects, you only have a single APK containing both the tests
and the library itself. The current version of Spoon requires both &lt;code&gt;--apk&lt;/code&gt; and
&lt;code&gt;--test-apk&lt;/code&gt; to be specified, so we simply pass in the same APK to both. It
looks like future versions of Spoon will be
&lt;a href=&#34;https://github.com/square/spoon/pull/453&#34;&gt;more flexible&lt;/a&gt; in this regard.&lt;/p&gt;
&lt;p&gt;Spoon produces HTML reports, showing the status of each test run on each device.
We have the report output folder collected as a build artefact, so the reports
can be seen right from the build server:&lt;/p&gt;
&lt;figure class=&#34;image full&#34;&gt;
  &lt;picture&gt;
      &lt;source srcset=&#34;https://chameth.com/android-espresso-spoon/spoon.avif&#34; type=&#34;image/avif&#34;/&gt;
      &lt;source srcset=&#34;https://chameth.com/android-espresso-spoon/spoon.webp&#34; type=&#34;image/webp&#34;/&gt;
      &lt;img src=&#34;https://chameth.com/android-espresso-spoon/spoon.png&#34; alt=&#34;Spoon output summary, showing results of 171 tests run on 3 devices&#34; loading=&#34;lazy&#34; width=&#34;671&#34; height=&#34;262&#34;/&gt;
  &lt;/picture&gt;
  &lt;figcaption&gt;&lt;p&gt;Spoon output summary, showing results of 171 tests run on 3 devices&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id=&#34;flake-free-ui-testing-with-espresso&#34;&gt;Flake-free UI testing with Espresso&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://developer.android.com/topic/libraries/testing-support-library/index.html#Espresso&#34;&gt;Espresso&lt;/a&gt;
is an Android library that provides an API for interacting with and making
assertions about the UI of Android applications. Espresso has a very simple
interface, and does lots of clever things under the hood to ensure that your
tests only execute code when the UI is idle (and hence stable). You shouldn’t
ever need to make your code sleep or wait.&lt;/p&gt;
&lt;p&gt;In order for Espresso’s magic to work, it needs to know whenever some background
activity is going on that it needs to wait for. By default, it hooks in to
Android’s &lt;code&gt;AsyncTask&lt;/code&gt; executor so it can wait for those to finish. In our apps,
there were a few cases where we used an explicit &lt;code&gt;Thread&lt;/code&gt; to do some background
work, which caused tests to fail intermittently (depending on whether the thread
performed its UI update before or after Espresso executed the test code).
Rewriting these cases to use an &lt;code&gt;AsyncTask&lt;/code&gt; enabled Espresso to figure out what
was happening and the tests started passing reliably.&lt;/p&gt;
&lt;p&gt;Another, similar, problem occurred where we were using RxJava to load some data.
There are two possible ways to deal with this… Espresso has a concept of an
&lt;a href=&#34;https://developer.android.com/reference/android/support/test/espresso/IdlingResource.html&#34;&gt;idling resource&lt;/a&gt;,
which provides a way of telling Espresso when a resource is busy so it can
belay interacting with or testing the UI until the resource is finished. In our
case, the code in question was going to be rewritten soon, so we went for a
quicker and dirtier option: force RxJava to use the same executor as AsyncTask.&lt;/p&gt;
&lt;p&gt;To do this, we added a simple test utility class that registers an &lt;code&gt;RxJavaPlugin&lt;/code&gt;
that overrides the Schedulers used by Rx:&lt;/p&gt;
&lt;pre class=&#34;chroma-chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-cm&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-cm&#34;&gt; * Hooks in to the RxJava plugins API to force Rx work to be scheduled on the
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-cm&#34;&gt; * AsyncTask&amp;#39;s thread pool executor. This is a quick and dirty hack to make
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-cm&#34;&gt; * Espresso aware of when Rx is doing work (and wait for it).
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-cm&#34;&gt; */&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-kd&#34;&gt;final&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;chroma-nc&#34;&gt;RxSchedulerHook&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;chroma-kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-kd&#34;&gt;static&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-kd&#34;&gt;final&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;RxJavaSchedulersHook&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;javaHook&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;chroma-k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;RxJavaTestSchedulerHook&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;chroma-kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-nf&#34;&gt;RxSchedulerHook&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;chroma-c1&#34;&gt;// Should not be insantiated&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;chroma-kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-kd&#34;&gt;static&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-nf&#34;&gt;registerHooksForTesting&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;chroma-k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;RxJavaPlugins&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;getInstance&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;getSchedulersHook&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;!=&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;javaHook&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;RxJavaPlugins&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;getInstance&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;reset&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;RxJavaPlugins&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;getInstance&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;registerSchedulersHook&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;javaHook&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;chroma-kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-kd&#34;&gt;static&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;chroma-nc&#34;&gt;RxJavaTestSchedulerHook&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-kd&#34;&gt;extends&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;RxJavaSchedulersHook&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;chroma-nd&#34;&gt;@Override&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;chroma-kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;Scheduler&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-nf&#34;&gt;getComputationScheduler&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;chroma-k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;Schedulers&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;from&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;AsyncTask&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;THREAD_POOL_EXECUTOR&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;chroma-nd&#34;&gt;@Override&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;chroma-kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;Scheduler&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-nf&#34;&gt;getIOScheduler&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;chroma-k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;Schedulers&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;from&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;AsyncTask&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;THREAD_POOL_EXECUTOR&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;chroma-nd&#34;&gt;@Override&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;chroma-kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;Scheduler&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-nf&#34;&gt;getNewThreadScheduler&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;chroma-k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;Schedulers&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;from&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;AsyncTask&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;THREAD_POOL_EXECUTOR&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;With the hook registered Rx does all of its work on the same thread pool as
&lt;code&gt;AsyncTask&lt;/code&gt;, which Espresso already knows about. It’s not the best long-term
solution, but it means we don’t have to spend time integrating IdlingResource
for code that doesn’t have long to live. With the hook in place, the tests
that were flaking because of Rx started passing reliably as well.&lt;/p&gt;
&lt;h3 id=&#34;getting-automatic-screenshots-of-failures&#34;&gt;Getting automatic screenshots of failures&lt;/h3&gt;
&lt;p&gt;Spoon provides a client library to, among other things, take a screenshot of
the device. Espresso provides a hook that can be used to change how errors
are handled. Putting the two together is very simple:&lt;/p&gt;
&lt;pre class=&#34;chroma-chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-kd&#34;&gt;final&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;FailureHandler&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;defaultHandler&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;chroma-k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;DefaultFailureHandler&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;                &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;InstrumentationRegistry&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;getTargetContext&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;());&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-n&#34;&gt;Espresso&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;setFailureHandler&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;FailureHandler&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;chroma-nd&#34;&gt;@Override&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;chroma-kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-nf&#34;&gt;handle&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;Throwable&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;throwable&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;Matcher&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;View&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;matcher&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;chroma-k&#34;&gt;try&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;Spoon&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;screenshot&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;                    &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;getActivity&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(),&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;                    &lt;/span&gt;&lt;span class=&#34;chroma-s&#34;&gt;&amp;#34;espresso-failure&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;                    &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;getClassName&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(),&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;                    &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;getMethodName&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;());&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-k&#34;&gt;catch&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;Exception&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;ex&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;Log&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;TAG&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-s&#34;&gt;&amp;#34;Error capturing screenshot&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;ex&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;defaultHandler&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;handle&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;throwable&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;matcher&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-p&#34;&gt;});&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In our new error handler we simply ask Spoon to take a screenshot, then call
Espresso’s original handler so that it can output its debugging information
and fail the test. The Spoon runner automatically picks up the screenshot and
adds it to the report:&lt;/p&gt;
&lt;figure class=&#34;image full&#34;&gt;
  &lt;picture&gt;
      &lt;source srcset=&#34;https://chameth.com/android-espresso-spoon/spoon-espresso.avif&#34; type=&#34;image/avif&#34;/&gt;
      &lt;source srcset=&#34;https://chameth.com/android-espresso-spoon/spoon-espresso.webp&#34; type=&#34;image/webp&#34;/&gt;
      &lt;img src=&#34;https://chameth.com/android-espresso-spoon/spoon-espresso.png&#34; alt=&#34;Spoon output details, showing a screenshot captured of the failure&#34; loading=&#34;lazy&#34; width=&#34;593&#34; height=&#34;197&#34;/&gt;
  &lt;/picture&gt;
  &lt;figcaption&gt;&lt;p&gt;Spoon output details, showing a screenshot captured of the failure&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Having the screenshot, error message and logs all presented in a clean UI
makes debugging failures much, much easier than searching through a huge build
log to try and find the exception.&lt;/p&gt;
&lt;h3 id=&#34;filtering-tests-based-on-device-capabilities&#34;&gt;Filtering tests based on device capabilities&lt;/h3&gt;
&lt;p&gt;Some tests won’t work on every device you throw at them. We had two problems:
some tests require a higher API version than some of our devices, and some
of the UIs under test were designed to run only on certain resolutions.&lt;/p&gt;
&lt;p&gt;The main cause for our dependence on newer API versions was the use of
&lt;a href=&#34;http://wiremock.org/&#34;&gt;WireMock&lt;/a&gt;, a brilliant library for stubbing out
web services. WireMock requires API 19, while our physical devices tend
to run versions older than that. Stopping these tests running is simply a case
of applying an annotation to the class:&lt;/p&gt;
&lt;pre class=&#34;chroma-chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-nd&#34;&gt;@SdkSuppress&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;minSdkVersion&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;19&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Screen resolution is a bit more complicated. One of our apps is designed for
a specific tablet device (and will never be used on anything else), and trying
to render the UI on smaller screens results in items overlapping, important
parts ending up below the fold, and other problems.&lt;/p&gt;
&lt;p&gt;We’d still like all the other tests for that app to run on all of the devices,
though, so we can test them on a variety of API versions and in other
conditions. We just need to suppress the UI tests. To do this, we subclassed
the Android &lt;code&gt;ActivityTestRule&lt;/code&gt; and overrode the apply method:&lt;/p&gt;
&lt;pre class=&#34;chroma-chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-nd&#34;&gt;@Override&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;Statement&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-nf&#34;&gt;apply&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;Statement&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;base&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-kd&#34;&gt;final&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;Description&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;chroma-k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;canRunUiTests&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;InstrumentationRegistry&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;getContext&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;()))&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;chroma-c1&#34;&gt;// If we can&amp;#39;t run UI tests, then return a statement that does nothing&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;chroma-c1&#34;&gt;// at all.  With normal JUnit tests we&amp;#39;d just throw an assumption&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;chroma-c1&#34;&gt;// failure and the test would be ignored, but that makes the Android&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;chroma-c1&#34;&gt;// runner angry.&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;chroma-k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;Statement&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;chroma-nd&#34;&gt;@Override&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;chroma-kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-nf&#34;&gt;evaluate&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-kd&#34;&gt;throws&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;Throwable&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;                &lt;/span&gt;&lt;span class=&#34;chroma-c1&#34;&gt;// Do nothing&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;};&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;chroma-k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-kd&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;apply&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;base&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-cm&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-cm&#34;&gt; * Checks that the screen size is close enough to that of our tablet device.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-cm&#34;&gt; */&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-nd&#34;&gt;@TargetApi&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;Build&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;VERSION_CODES&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;HONEYCOMB_MR2&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-kt&#34;&gt;boolean&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-nf&#34;&gt;canRunUiTests&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;Context&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;chroma-k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;Build&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;VERSION&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;SDK_INT&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;Build&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;VERSION_CODES&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;HONEYCOMB_MR2&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;chroma-k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;chroma-kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;screenWidth&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;dpToPx&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;getResources&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;getConfiguration&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;screenWidthDp&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;chroma-k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;screenWidth&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;&amp;gt;=&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;600&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-nf&#34;&gt;dpToPx&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;Context&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;dp&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;DisplayMetrics&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;displayMetrics&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;getResources&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;getDisplayMetrics&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;chroma-k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;Math&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;round&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;dp&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;displayMetrics&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;density&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;chroma-w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When the rule is run, we check if the screen width meets a minimum number of
pixels. If it doesn’t, an empty &lt;code&gt;Statement&lt;/code&gt; is returned that turns the test
into a no-op. These show up as passed rather than skipped in the output, but
there doesn’t seem to be a nice way to signal to the Android JUnit runner that
the test is being ignored programatically.&lt;/p&gt;
</content>
    </entry>
    <entry>
        <title>Why you should be using HTTPS</title>
        <link href="https://chameth.com/why-you-should-be-using-https/"/>
        <updated>2016-06-17T00:00:00Z</updated>
        <id>https://chameth.com/why-you-should-be-using-https/</id>
        <content xml:lang="en" type="html">&lt;figure class=&#34;image left&#34;&gt;
  &lt;picture&gt;
      &lt;source srcset=&#34;https://chameth.com/why-you-should-be-using-https/https-everywhere.avif&#34; type=&#34;image/avif&#34;/&gt;
      &lt;source srcset=&#34;https://chameth.com/why-you-should-be-using-https/https-everywhere.webp&#34; type=&#34;image/webp&#34;/&gt;
      &lt;img src=&#34;https://chameth.com/why-you-should-be-using-https/https-everywhere.jpg&#34; alt=&#34;The EFF&amp;#39;s HTTPS Everywhere logo&#34; loading=&#34;lazy&#34; width=&#34;300&#34; height=&#34;260&#34;/&gt;
  &lt;/picture&gt;
  &lt;figcaption&gt;&lt;p&gt;The EFF’s HTTPS Everywhere logo&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;One of my favourite hobbyhorses recently has been the use of HTTPS, or lack thereof. HTTPS is the
thing that makes the little padlock appear in your browser, and has existed for over 20 years.
In the past, that little padlock was the exclusive preserve of banks and other ‘high security’
establishments; over time its use has gradually expanded to most (but not all) websites
that handle user information, and the time is now right for it to become ubiquitous.&lt;/p&gt;
&lt;h3 id=&#34;why-use-https&#34;&gt;Why use HTTPS?&lt;/h3&gt;
&lt;p&gt;There are numerous advantages to using HTTPS, both for the users of a website and for the
operator:&lt;/p&gt;
&lt;h4 id=&#34;privacy&#34;&gt;Privacy&lt;/h4&gt;
&lt;p&gt;The most obvious advantage is that HTTPS gives your users additional privacy. An insecure (HTTP)
request can potentially be read by anyone on the same network, or the network operators, or anyone
who happens to operate a network along the path between the user and the server.&lt;/p&gt;
&lt;p&gt;Users on shared WiFi networks (such as those in coffee shops, hotels, or offices) are particularly
vulnerable to passive sniffing by anyone else on that network. If the network is open (as is
frequently the case) then anyone in radio range can see exactly what the user is up to.&lt;/p&gt;
&lt;!--more--&gt;
&lt;h4 id=&#34;integrity&#34;&gt;Integrity&lt;/h4&gt;
&lt;p&gt;HTTPS also helps to maintain the integrity of your site. With a plain HTTP request, there’s nothing
to stop anyone in between the server and the user from modifying the content of the request or the
response. This is a frequent tactic used by annoying WiFi gateways (such as the ones &lt;a href=&#34;http://justinsomnia.org/2012/04/hotel-wifi-javascript-injection/&#34;&gt;you’d find in
a hotel&lt;/a&gt;), dubious ISPs who want
to serve you extra adverts, or just plain old nefarious attackers.&lt;/p&gt;
&lt;p&gt;If you’re trying to convey some kind of information to users (and if you aren’t, why exactly are
you running a website again?) it seems beneficial to both you and them if the information arrives
as you intended, rather than in a modified form due to someone or something tampering with it.&lt;/p&gt;
&lt;h4 id=&#34;security&#34;&gt;Security&lt;/h4&gt;
&lt;p&gt;If your website has any kind of authentication, or session identifiers, it becomes extremely
vulnerable to an attacker monitoring the traffic and stealing the credentials. This was
starkly demonstrated in 2010 when &lt;a href=&#34;https://en.wikipedia.org/wiki/Firesheep&#34;&gt;Firesheep&lt;/a&gt; was
released. This tool allowed anyone to quickly and automatically hijack social media accounts of
anyone on the same network who was using HTTP to access them.&lt;/p&gt;
&lt;p&gt;Even if your login pages are served over HTTPS, if you send a single session ID cookie over HTTP
(such as a page you decided wasn’t particularly ‘important’) then an attacker can probably spoof
the user’s session and gain full access to their account.  Again, in the case of open WiFi networks
that could be anyone in radio range.&lt;/p&gt;
&lt;h4 id=&#34;search-engine-rankings&#34;&gt;Search engine rankings&lt;/h4&gt;
&lt;p&gt;Some search engines use HTTPS as a signal in their ranking algorithms. &lt;a href=&#34;https://security.googleblog.com/2014/08/https-as-ranking-signal_6.html&#34;&gt;Google announced in
2004&lt;/a&gt; that it was using
the presence of HTTPS as a small positive signal, but that it may strengthen that signal over time
as more and more websites switch to using a secure transport. It’s not unthinkable that at some
point in the future there will be HTTPS-only search engines.&lt;/p&gt;
&lt;h3 id=&#34;but-but-but&#34;&gt;But… But… But…&lt;/h3&gt;
&lt;p&gt;There are lots of excuses for not implementing HTTPS. Most of them are either misguided or outdated.&lt;/p&gt;
&lt;h4 id=&#34;its-too-expensive-andor-complicated&#34;&gt;It’s too expensive and/or complicated&lt;/h4&gt;
&lt;p&gt;In the past, getting HTTPS certificates was a pain. A number of free suppliers have existed for
a while but the process for getting their certificates wasn’t particularly straight forward, and
many imposed arbitrary restrictions on the certificate parameters. Even once you had the
certificate, you had to fiddle about with your HTTP server configuration to make it work, remember
to manually get a new certificate when the old one expired, and lots of other annoying busywork.&lt;/p&gt;
&lt;p&gt;With the arrival of &lt;a href=&#34;https://letsencrypt.org/&#34;&gt;Let’s Encrypt&lt;/a&gt;, all that changed. You can retrieve
and deploy a free HTTPS certificate with two or three commands. Renewal can be handled completely
automatically with a single command executed by cron.&lt;/p&gt;
&lt;h4 id=&#34;theres-no-point-nothing-on-my-site-is-sensitive&#34;&gt;There’s no point; nothing on my site is sensitive&lt;/h4&gt;
&lt;p&gt;You might not think your content warrants privacy, but can you speak for everyone who accesses it?
Even content that seems mundane to you — such as travel advice, or technical writing — could be
used to build up a profile of a user. If an attacker is monitoring traffic in a coffee shop and
sees a user looking at travel advice and weather forecasts for a foreign country, he could use that
information to plan a burglary knowing that the user will be away. Similarly, some content which
is perfectly mundane to you may actually be very sensitive in other countries with repressive
governments. HTTPS makes it much harder for these people to snoop on traffic.&lt;/p&gt;
&lt;p&gt;From another angle, if you’re offering any kind of information, instructions, or especially file
downloads, there’s a severe risk to users if the content is modified on its way to them. An evil
sysadmin could rewrite your travel advice to suggest visiting the local drug dealer’s hangout, or
replace your download with a malware-infested version.&lt;/p&gt;
&lt;h4 id=&#34;https-is-slower-uses-more-resources-etc&#34;&gt;HTTPS is slower, uses more resources, etc&lt;/h4&gt;
&lt;p&gt;Back in 1995 this might have been a valid argument. Enabling HTTPS on a modern server will make
an almost negligible difference to performance. If you also enable HTTP/2 (which most
implementations only support over HTTPS), it’s likely to actually use fewer resources, and result
in a faster, smoother experience for your users. HTTP/2 was designed to work with HTTPS, and
designed with modern requirements and networking techniques in mind.&lt;/p&gt;
&lt;p&gt;CloudFlare have an &lt;a href=&#34;https://www.cloudflare.com/http2/&#34;&gt;excellent demonstration&lt;/a&gt; of the benefits of
HTTP/2, and it can show speed improvements of 2-3x in a typical environment. On top of being faster,
HTTP/2 uses fewer connections which results in less resource overhead on both the server and the
client.&lt;/p&gt;
&lt;h3 id=&#34;so-what-are-you-waiting-for&#34;&gt;So what are you waiting for?&lt;/h3&gt;
&lt;p&gt;If you run a website and aren’t using HTTPS, &lt;a href=&#34;https://certbot.eff.org/&#34;&gt;give it a try&lt;/a&gt;.&lt;/p&gt;
</content>
    </entry>
</feed>
