<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>JaCoCo on Trevor's Blog</title><link>http://mings.in/en/tags/jacoco/</link><description>Recent content in JaCoCo on Trevor's Blog</description><generator>Hugo</generator><language>en-US</language><lastBuildDate>Thu, 20 Jun 2019 09:00:00 +0800</lastBuildDate><atom:link href="http://mings.in/en/tags/jacoco/index.xml" rel="self" type="application/rss+xml"/><item><title>Using JaCoCo to Check Test Coverage</title><link>http://mings.in/en/posts/enable-jacoco-for-manual-test/</link><pubDate>Thu, 20 Jun 2019 09:00:00 +0800</pubDate><guid>http://mings.in/en/posts/enable-jacoco-for-manual-test/</guid><description>Enabling jacoco during manual testing to check test coverage</description><content:encoded><![CDATA[<p>​    The team had no automated test coverage at the time, so the QA team wanted a sense of what manual testing actually covered. That is how this post came about. Plenty of articles online use Android&rsquo;s instrumentation test framework and start the app from a command. The report is also generated after the launched activity finishes, which in complex scenarios cannot capture function calls on every screen.</p>
<p>The approach here overrides the Application code under a new <code>build type</code>, used only during manual testing, with no impact on existing code. Hope it helps. Sample code for this post is available here: <a href="https://github.com/trevorwang/jacoco-manual-coverage">trevorwang/jacoco-manual-coverage</a></p>
<ol>
<li>
<p>In your project&rsquo;s <code>buildscripts</code> directory, create a <code>jacoco.gradle</code> file and add the following code</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-groovy" data-lang="groovy"><span class="line"><span class="cl"><span class="n">apply</span> <span class="nl">plugin:</span> <span class="s1">&#39;jacoco&#39;</span>  <span class="c1">// enable jacoco
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">jacoco</span> <span class="o">{</span>	
</span></span><span class="line"><span class="cl">    <span class="n">toolVersion</span> <span class="o">=</span> <span class="s2">&#34;0.8.3&#34;</span>  <span class="c1">// set the jacoco version
</span></span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// If you use Robolectric, you must add the following
</span></span></span><span class="line"><span class="cl"><span class="n">tasks</span><span class="o">.</span><span class="na">withType</span><span class="o">(</span><span class="n">Test</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">jacoco</span><span class="o">.</span><span class="na">includeNoLocationClasses</span> <span class="o">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></div></li>
<li>
<p>In the <code>build.gradle</code> under the app directory, add code to apply the script</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-groovy" data-lang="groovy"><span class="line"><span class="cl"><span class="n">apply</span> <span class="nl">from:</span> <span class="s1">&#39;../buildscripts/jacoco.gradle&#39;</span>
</span></span></code></pre></div></li>
<li>
<p>After running <code>testDebugUnitTest</code>, you will see <code>testDebugUnitTest.exec</code> under <code>app/build/jococo/</code>. Remember that file — we will use it later to generate the report.</p>
<p><img alt="jacoco-executedata" loading="lazy" src="/images/jacoco-executedata.png"></p>
</li>
<li>
<p>Create a <code>jacoco</code> task</p>
<p>The Android gradle plugin generates different <code>variant</code>s, so we need a different task per <code>variant</code> to generate reports.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-groovy" data-lang="groovy"><span class="line"><span class="cl"><span class="n">project</span><span class="o">.</span><span class="na">afterEvaluate</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">android</span><span class="o">.</span><span class="na">applicationVariants</span><span class="o">.</span><span class="na">all</span> <span class="o">{</span> <span class="n">variant</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="kt">def</span> <span class="n">variantName</span> <span class="o">=</span> <span class="n">variant</span><span class="o">.</span><span class="na">name</span>
</span></span><span class="line"><span class="cl">        <span class="kt">def</span> <span class="n">testTaskName</span> <span class="o">=</span> <span class="s2">&#34;test${variantName.capitalize()}UnitTest&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="n">tasks</span><span class="o">.</span><span class="na">create</span><span class="o">(</span><span class="nl">name:</span> <span class="s2">&#34;${testTaskName}Coverage&#34;</span><span class="o">,</span> <span class="nl">type:</span> <span class="n">JacocoReport</span><span class="o">,</span> <span class="nl">dependsOn:</span> <span class="s2">&#34;$testTaskName&#34;</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">          <span class="c1">// TODO implement later
</span></span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></div><p>After clicking <code>Sync Gradle</code>, the gradle tasks gain two entries, <code> testDebugUnitTestCoverage</code>, <code>testReleaseUnitTestCoverage</code>. Next we add the implementation that generates the report.</p>
</li>
<li>
<p>Replace the <code>TODO</code> from the previous step with this code</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-groovy" data-lang="groovy"><span class="line"><span class="cl"> <span class="n">group</span> <span class="o">=</span> <span class="s2">&#34;Reporting&#34;</span>
</span></span><span class="line"><span class="cl">   <span class="n">description</span> <span class="o">=</span> <span class="s2">&#34;Generate Jacoco coverage reports for the ${variantName.capitalize()} build.&#34;</span>
</span></span><span class="line"><span class="cl"> 	<span class="c1">// set report formats
</span></span></span><span class="line"><span class="cl">   <span class="n">reports</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">       <span class="n">html</span><span class="o">.</span><span class="na">enabled</span> <span class="o">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">       <span class="n">xml</span><span class="o">.</span><span class="na">enabled</span> <span class="o">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">   <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// exclude classes we do not want counted
</span></span></span><span class="line"><span class="cl">   <span class="kt">def</span> <span class="n">excludes</span> <span class="o">=</span> <span class="o">[</span>
</span></span><span class="line"><span class="cl">           <span class="s1">&#39;**/R.class&#39;</span><span class="o">,</span>
</span></span><span class="line"><span class="cl">           <span class="s1">&#39;**/R$*.class&#39;</span><span class="o">,</span>
</span></span><span class="line"><span class="cl">           <span class="s1">&#39;**/BuildConfig.*&#39;</span><span class="o">,</span>
</span></span><span class="line"><span class="cl">           <span class="s1">&#39;**/Manifest*.*&#39;</span><span class="o">,</span>
</span></span><span class="line"><span class="cl">           <span class="s1">&#39;**/*Test*.*&#39;</span><span class="o">,</span>
</span></span><span class="line"><span class="cl">           <span class="s1">&#39;android/**/*.*&#39;</span><span class="o">,</span>
</span></span><span class="line"><span class="cl">     	  <span class="s1">&#39;androidx/**/*.*&#39;</span>
</span></span><span class="line"><span class="cl">   <span class="o">]</span>
</span></span><span class="line"><span class="cl">   <span class="c1">// Java class files
</span></span></span><span class="line"><span class="cl">   <span class="kt">def</span> <span class="n">javaClasses</span> <span class="o">=</span> <span class="n">fileTree</span><span class="o">(</span><span class="nl">dir:</span> <span class="n">variant</span><span class="o">.</span><span class="na">javaCompiler</span><span class="o">.</span><span class="na">destinationDir</span><span class="o">,</span> <span class="nl">excludes:</span> <span class="n">excludes</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">   <span class="c1">// Kotlin files
</span></span></span><span class="line"><span class="cl">   <span class="kt">def</span> <span class="n">kotlinClasses</span> <span class="o">=</span> <span class="n">fileTree</span><span class="o">(</span><span class="nl">dir:</span> <span class="s2">&#34;${buildDir}/tmp/kotlin-classes/${variantName}&#34;</span><span class="o">,</span> <span class="nl">excludes:</span> <span class="n">excludes</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">   <span class="n">classDirectories</span> <span class="o">=</span> <span class="n">files</span><span class="o">([</span><span class="n">javaClasses</span><span class="o">,</span> <span class="n">kotlinClasses</span><span class="o">])</span>
</span></span><span class="line"><span class="cl">   <span class="c1">// source files
</span></span></span><span class="line"><span class="cl">   <span class="n">sourceDirectories</span> <span class="o">=</span> <span class="n">files</span><span class="o">([</span>
</span></span><span class="line"><span class="cl">           <span class="s2">&#34;$project.projectDir/src/main/java&#34;</span><span class="o">,</span>
</span></span><span class="line"><span class="cl">           <span class="s2">&#34;$project.projectDir/src/${variantName}/java&#34;</span><span class="o">,</span>
</span></span><span class="line"><span class="cl">           <span class="s2">&#34;$project.projectDir/src/main/kotlin&#34;</span><span class="o">,</span>
</span></span><span class="line"><span class="cl">           <span class="s2">&#34;$project.projectDir/src/${variantName}/kotlin&#34;</span>
</span></span><span class="line"><span class="cl">   <span class="o">])</span>
</span></span><span class="line"><span class="cl"> 	<span class="c1">// the file we generated at the beginning
</span></span></span><span class="line"><span class="cl">   <span class="n">executionData</span> <span class="o">=</span> <span class="n">files</span><span class="o">(</span><span class="s2">&#34;${project.buildDir}/jacoco/${testTaskName}.exec&#34;</span><span class="o">)</span>
</span></span></code></pre></div></li>
<li>
<p>Run the <code>testDebugUnitTestCoverage</code> task and the report shows up in the build directory</p>
<p><img loading="lazy" src="/images/jacoco-reports.png"></p>
<p>With that, we have finished generating a jacoco report.</p>
</li>
</ol>
<p>Now the key step: how do you enable jacoco in the packaged app?</p>
<ol start="7">
<li>
<p>Create a new <code>staging</code> build type</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-groovy" data-lang="groovy"><span class="line"><span class="cl">    <span class="n">buildTypes</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">release</span> <span class="o">{...}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">staging</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">initWith</span><span class="o">(</span><span class="n">debug</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">matchingFallbacks</span> <span class="o">=</span> <span class="o">[</span><span class="s2">&#34;debug&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">            <span class="n">testCoverageEnabled</span> <span class="kc">true</span>    <span class="c1">// packages the jacoco runtime into the app
</span></span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span></code></pre></div></li>
<li>
<p>Under <code>src</code>, at the same level as <code>main</code>, create a <code>staging</code> directory</p>
</li>
<li>
<p>Under <code>staging</code>, create a <code>java</code> directory, and add a <code>StagingApp.kt</code> file in the <code>com.example.staging</code> package, with this code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-kotlin" data-lang="kotlin"><span class="line"><span class="cl"><span class="k">package</span> <span class="nn">com.example.staging</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">import</span> <span class="nn">android.Manifest</span>
</span></span><span class="line"><span class="cl"><span class="k">import</span> <span class="nn">android.app.Activity</span>
</span></span><span class="line"><span class="cl"><span class="k">import</span> <span class="nn">android.app.Application</span>
</span></span><span class="line"><span class="cl"><span class="k">import</span> <span class="nn">android.os.Bundle</span>
</span></span><span class="line"><span class="cl"><span class="k">import</span> <span class="nn">android.os.Environment</span>
</span></span><span class="line"><span class="cl"><span class="k">import</span> <span class="nn">android.util.Log</span>
</span></span><span class="line"><span class="cl"><span class="k">import</span> <span class="nn">android.widget.Toast</span>
</span></span><span class="line"><span class="cl"><span class="k">import</span> <span class="nn">androidx.fragment.app.FragmentActivity</span>
</span></span><span class="line"><span class="cl"><span class="k">import</span> <span class="nn">com.tbruyelle.rxpermissions2.RxPermissions</span>
</span></span><span class="line"><span class="cl"><span class="k">import</span> <span class="nn">java.io.File</span>
</span></span><span class="line"><span class="cl"><span class="k">import</span> <span class="nn">java.io.FileOutputStream</span>
</span></span><span class="line"><span class="cl"><span class="k">import</span> <span class="nn">java.io.IOException</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">StagingApp</span> <span class="p">:</span> <span class="n">Application</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">override</span> <span class="k">fun</span> <span class="nf">onCreate</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">super</span><span class="p">.</span><span class="n">onCreate</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="nc">Log</span><span class="p">.</span><span class="n">d</span><span class="p">(</span><span class="n">TAG</span><span class="p">,</span> <span class="s2">&#34;StagingApp&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">registerActivityLifecycleCallbacks</span><span class="p">(</span><span class="k">object</span> <span class="err">: </span><span class="nc">ActivityLifecycleCallbacks</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">var</span> <span class="py">activitySize</span> <span class="p">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">            <span class="k">override</span> <span class="k">fun</span> <span class="nf">onActivityPaused</span><span class="p">(</span><span class="n">activity</span><span class="p">:</span> <span class="n">Activity</span><span class="p">?)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="k">override</span> <span class="k">fun</span> <span class="nf">onActivityResumed</span><span class="p">(</span><span class="n">activity</span><span class="p">:</span> <span class="n">Activity</span><span class="p">?)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="c1">// ask the first activity for SD card directory access
</span></span></span><span class="line"><span class="cl">                <span class="k">if</span> <span class="p">(</span><span class="n">activitySize</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="p">(</span><span class="n">activity</span> <span class="k">as</span><span class="p">?</span> <span class="n">FragmentActivity</span><span class="p">)</span><span class="o">?.</span><span class="n">let</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                        <span class="k">val</span> <span class="py">rxPerm</span> <span class="p">=</span> <span class="n">RxPermissions</span><span class="p">(</span><span class="k">it</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                        <span class="n">rxPerm</span><span class="p">.</span><span class="n">request</span><span class="p">(</span><span class="nc">Manifest</span><span class="p">.</span><span class="n">permission</span><span class="p">.</span><span class="n">WRITE_EXTERNAL_STORAGE</span><span class="p">).</span><span class="n">subscribe</span><span class="p">({</span> <span class="n">result</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">                            <span class="k">if</span> <span class="p">(!</span><span class="n">result</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="nc">Toast</span><span class="p">.</span><span class="n">makeText</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                                    <span class="k">it</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                    <span class="s2">&#34;You have to grant the permission to save coverage file&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                    <span class="nc">Toast</span><span class="p">.</span><span class="n">LENGTH_SHORT</span>
</span></span><span class="line"><span class="cl">                                <span class="p">).</span><span class="n">show</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">                            <span class="p">}</span>
</span></span><span class="line"><span class="cl">                        <span class="p">},</span> <span class="p">{</span> <span class="n">e</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">                            <span class="n">e</span><span class="p">.</span><span class="n">printStackTrace</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">                        <span class="p">})</span>
</span></span><span class="line"><span class="cl">                    <span class="p">}</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="k">override</span> <span class="k">fun</span> <span class="nf">onActivityStarted</span><span class="p">(</span><span class="n">activity</span><span class="p">:</span> <span class="n">Activity</span><span class="p">?)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="k">override</span> <span class="k">fun</span> <span class="nf">onActivityDestroyed</span><span class="p">(</span><span class="n">activity</span><span class="p">:</span> <span class="n">Activity</span><span class="p">?)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">activitySize</span> <span class="o">-=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                <span class="k">if</span> <span class="p">(</span><span class="n">activitySize</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                  <span class="c1">//once all activities are destroyed, write the report file
</span></span></span><span class="line"><span class="cl">                    <span class="n">generateCoverageReport</span><span class="p">(</span><span class="n">createFile</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="k">override</span> <span class="k">fun</span> <span class="nf">onActivitySaveInstanceState</span><span class="p">(</span><span class="n">activity</span><span class="p">:</span> <span class="n">Activity</span><span class="p">?,</span> <span class="n">outState</span><span class="p">:</span> <span class="n">Bundle</span><span class="p">?)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="k">override</span> <span class="k">fun</span> <span class="nf">onActivityStopped</span><span class="p">(</span><span class="n">activity</span><span class="p">:</span> <span class="n">Activity</span><span class="p">?)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="k">override</span> <span class="k">fun</span> <span class="nf">onActivityCreated</span><span class="p">(</span><span class="n">activity</span><span class="p">:</span> <span class="n">Activity</span><span class="p">?,</span> <span class="n">savedInstanceState</span><span class="p">:</span> <span class="n">Bundle</span><span class="p">?)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">activitySize</span> <span class="o">+=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">private</span> <span class="k">fun</span> <span class="nf">generateCoverageReport</span><span class="p">(</span><span class="k">file</span><span class="p">:</span> <span class="n">File</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nc">Log</span><span class="p">.</span><span class="n">d</span><span class="p">(</span><span class="n">TAG</span><span class="p">,</span> <span class="s2">&#34;generateCoverageReport():</span><span class="si">${file.absolutePath}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">FileOutputStream</span><span class="p">(</span><span class="k">file</span><span class="p">,</span> <span class="k">false</span><span class="p">).</span><span class="n">use</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">val</span> <span class="py">agent</span> <span class="p">=</span> <span class="nc">Class</span><span class="p">.</span><span class="n">forName</span><span class="p">(</span><span class="s2">&#34;org.jacoco.agent.rt.RT&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="p">.</span><span class="n">getMethod</span><span class="p">(</span><span class="s2">&#34;getAgent&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="p">.</span><span class="n">invoke</span><span class="p">(</span><span class="k">null</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="nc">Log</span><span class="p">.</span><span class="n">d</span><span class="p">(</span><span class="n">TAG</span><span class="p">,</span> <span class="n">agent</span><span class="p">.</span><span class="n">toString</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">            <span class="k">it</span><span class="p">.</span><span class="n">write</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="n">agent</span><span class="p">.</span><span class="n">javaClass</span><span class="p">.</span><span class="n">getMethod</span><span class="p">(</span><span class="s2">&#34;getExecutionData&#34;</span><span class="p">,</span> <span class="n">Boolean</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">javaPrimitiveType</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                    <span class="p">.</span><span class="n">invoke</span><span class="p">(</span><span class="n">agent</span><span class="p">,</span> <span class="k">false</span><span class="p">)</span> <span class="k">as</span> <span class="n">ByteArray</span>
</span></span><span class="line"><span class="cl">            <span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">fun</span> <span class="nf">createFile</span><span class="p">():</span> <span class="n">File</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// under the SD card
</span></span></span><span class="line"><span class="cl">        <span class="k">val</span> <span class="py">file</span> <span class="p">=</span> <span class="n">File</span><span class="p">(</span><span class="nc">Environment</span><span class="p">.</span><span class="n">getExternalStorageDirectory</span><span class="p">(),</span> <span class="s2">&#34;jacoco/</span><span class="si">$DEFAULT</span><span class="s2">_COVERAGE_FILE_PATH&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(!</span><span class="k">file</span><span class="p">.</span><span class="n">exists</span><span class="p">())</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="k">file</span><span class="p">.</span><span class="n">parentFile</span><span class="o">?.</span><span class="n">mkdirs</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">                <span class="k">file</span><span class="p">.</span><span class="n">createNewFile</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="n">e</span><span class="p">:</span> <span class="n">IOException</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nc">Log</span><span class="p">.</span><span class="n">d</span><span class="p">(</span><span class="n">TAG</span><span class="p">,</span> <span class="s2">&#34;Exception : </span><span class="si">$e</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="n">e</span><span class="p">.</span><span class="n">printStackTrace</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">file</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">companion</span> <span class="k">object</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">const</span> <span class="k">val</span> <span class="py">DEFAULT</span><span class="n">_COVERAGE_FILE_PATH</span> <span class="p">=</span> <span class="s2">&#34;jacoco-coverage.ec&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="k">const</span> <span class="k">val</span> <span class="py">TAG</span> <span class="p">=</span> <span class="s2">&#34;StagingApp&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
<li>
<p>Create an <code>AndroidManifest.xml</code> file in the <code>staging</code> directory with this content</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="cp">&lt;?xml version=&#34;1.0&#34; encoding=&#34;utf-8&#34;?&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;manifest</span>
</span></span><span class="line"><span class="cl">        <span class="na">xmlns:android=</span><span class="s">&#34;http://schemas.android.com/apk/res/android&#34;</span> <span class="na">package=</span><span class="s">&#34;com.example.jacocomanual&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;uses-permission</span> <span class="na">android:name=</span><span class="s">&#34;android.permission.WRITE_EXTERNAL_STORAGE&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;application</span> <span class="na">android:name=</span><span class="s">&#34;com.example.staging.StagingApp&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/manifest&gt;</span>
</span></span></code></pre></div><p>The result looks like this:</p>
<p><img alt="jacoco-staging-build-type" loading="lazy" src="/images/jacoco-staging-build-type.png"></p>
</li>
<li>
<p>In the IDE, select <code>staging</code> under <code>Build Variants</code></p>
<p><img alt="jacoco-build-variants" loading="lazy" src="/images/jacoco-build-variants.png"></p>
</li>
<li>
<p>Run the app and install it on a device or emulator, click around a bit, then press back to close every screen. This generates a <code>jacoco/jacoco-coverage.ec</code> file under the SD card directory.</p>
<p><img alt="jacoco-sd-location" loading="lazy" src="/images/jacoco-sd-location.png"></p>
</li>
<li>
<p>Copy <code>jacoco-coverage.ec</code> into the <code>jacoco</code> folder at the project root</p>
</li>
<li>
<p>Now modify the jacoco task to generate the final report</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-groovy" data-lang="groovy"><span class="line"><span class="cl">            <span class="c1">// the file we generated at the beginning
</span></span></span><span class="line"><span class="cl">            <span class="n">executionData</span> <span class="o">=</span> <span class="n">files</span><span class="o">([</span>
</span></span><span class="line"><span class="cl">                    <span class="s2">&#34;${project.buildDir}/jacoco/${testTaskName}.exec&#34;</span><span class="o">,</span>
</span></span><span class="line"><span class="cl">                    <span class="s2">&#34;${rootDir}/jacoco/jacoco-coverage.ec&#34;</span>   <span class="c1">// add a second data source
</span></span></span><span class="line"><span class="cl">            <span class="o">])</span>
</span></span></code></pre></div></li>
<li>
<p>Run <code>testStagingUnitTest</code> and the report is there</p>
<p><img alt="jacoco-report-html" loading="lazy" src="/images/jacoco-report-html.png"></p>
</li>
</ol>
<p>Good stuff, honestly.~~</p>
]]></content:encoded></item></channel></rss>