Sunday, August 30, 2015

MyNotes: Jacoco Round 2

 

The testing landscape has changed since my first jacoco post.  A bug was introduced in v22 of the tools.  If you are interested you can find the details at https://code.google.com/p/android/issues/detail?id=170607.  The gist of the bug is the coverage report file is empty which interferes with JaCoCo working. Basically, it shows you 0% coverage across the board.

The good news is that there is a workaround!  You have to create a custom test runner and then everything is golden.   Since some the last post is invalid now, I am including the full steps to getting JaCoCo to work for the android tests again.

1. Creating the Workaround Test Runner

  • Copy the following class into the androidTest folder:
  •    1:  
       2: /**
       3:  * An annoying work around for
       4:  * <a href="https://code.google.com/p/android/issues/detail?id=170607">issue 170607</a>
       5:  */
       6: public class JacocoTestRunner extends AndroidJUnitRunner {
       7:  
       8:         static {
       9:             System.setProperty("jacoco-agent.destfile",
      10:                                "/data/data/"+BuildConfig.APPLICATION_ID+"/coverage.ec");
      11:         }
      12:  
      13:         @Override
      14:         public void finish(int resultCode, Bundle results) {
      15:             try {
      16:                 Class rt = Class.forName("org.jacoco.agent.rt.RT");
      17:                 Method getAgent = rt.getMethod("getAgent");
      18:                 Method dump = getAgent.getReturnType().getMethod("dump", boolean.class);
      19:                 Object agent = getAgent.invoke(null);
      20:                 dump.invoke(agent, false);
      21:             }
      22:             catch (Throwable e) {
      23:                 e.printStackTrace();
      24:             }
      25:             super.finish(resultCode, results);
      26:         }
      27: }

2. Update Gradle




   1: ...
   2: apply plugin: "jacoco"
   3:  
   4:  
   5: android {
   6:     ...    
   7:     jacoco {
   8:         version "0.7.1.201405082137"
   9:     }
  10:  
  11:     defaultConfig {
  12:         ...
  13:         //workaround for issue 170607.
  14:         testInstrumentationRunner "com.fsk.mynotes.JacocoTestRunner"
  15:         //testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
  16:     }
  17:  
  18:     buildTypes {
  19:         debug {
  20:             testCoverageEnabled = true
  21:         }
  22:     }
  23:  
  24: //
  25: //    dexOptions {
  26: //        incremental true
  27: //    }
  28: ...
  29: }
  30:  
  31: task jacocoTestReport(type: JacocoReport) {
  32:  
  33:     def coverageSourceDirs = [
  34:             'src/main/java'
  35:     ]
  36:  
  37:     group = "Reporting"
  38:     description = "Generates Jacoco coverage reports"
  39:     reports {
  40:         xml {
  41:             enabled = true
  42:             destination "${buildDir}/reports/jacoco/jacoco.xml"
  43:         }
  44:         csv.enabled false
  45:         html {
  46:             enabled true
  47:             destination "${buildDir}/jacocoHtml"
  48:         }
  49:     }
  50:  
  51:     classDirectories = fileTree(
  52:             dir: 'build/intermediates/classes/debug',
  53:             excludes: ['**/R.class',
  54:                        '**/R$*.class',
  55:                        '**/BuildConfig.*',
  56:                        '**/Manifest*.*',
  57:                        '**/*Activity*.*',
  58:                        '**/*Fragment*.*'
  59:             ]
  60:     )
  61:  
  62:     sourceDirectories = files(coverageSourceDirs)
  63:     additionalSourceDirs = files(coverageSourceDirs)
  64:     executionData = files('build/outputs/code-coverage/connected/coverage.ec')
  65:  
  66:     doFirst {
  67:         new File("$buildDir/intermediates/classes/").eachFileRecurse { file ->
  68:             if (file.name.contains('$$')) {
  69:                 file.renameTo(file.path.replace('$$', '$'))
  70:             }
  71:         }
  72:     }
  73: }
  74:  
  75: dependencies {
  76:     ...
  77:  
  78:     //Unit test dependencies
  79:     testCompile 'junit:junit:4.12'
  80:     testCompile 'org.mockito:mockito-core:1.10.19'
  81:     testCompile 'org.powermock:powermock-module-junit4:1.6.2'
  82:     testCompile 'org.powermock:powermock-api-mockito:1.6.2'
  83:  
  84:     androidTestCompile 'org.hamcrest:hamcrest-library:1.1'
  85:     androidTestCompile 'com.android.support.test:runner:0.3'
  86:     androidTestCompile 'com.android.support.test:rules:0.3'
  87:     androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2'
  88:     androidTestCompile "com.google.dexmaker:dexmaker:1.2"
  89:     androidTestCompile "com.google.dexmaker:dexmaker-mockito:1.2"
  90: }


  • Sync the project to the gradle file. This should happen automatically, but if it doesn’t go to Tools=>Android=>Sync Project with Gradle Files.

3. Running the tests for Coverage



  • Start an emulator or a device that can run the tests.

  • Open the Android Studio Terminal and enter the following commands:


   1: gradlew :MyNotes:createDebugCoverageReport
   2: gradlew :MyNotes:jacocoTestReport

(substitute the MyNotes with your module name)



  • Assuming that everything succeeded, open the module’s build\jacocoHtml folder.

  • Right-Click on the index.html file and open it in a browser.

image




Caveats



  • The androidTests only contain the tests that I either can’t get working as local tests or that will take more work than it is worth.  As a result, I have two test categories, Android Tests and Local Tests, that give me coverage for different areas of the code.  Unfortunately, this means that I will likely never get a single tool to give me 100% coverage.

  • Sometimes the terminal commands will fail or will not produce a coverage report.  When that happens just clean the build and repeat the terminal commands.

  • I created a special gradle file, build_coverage.gradle, to contain the changes for the coverage.  I didn’t want it in my main gradle file because I do not want to deliver the changes in the release version.  When I want to run coverage, I copy the special gradle file into the main gradle contents and then revert the change when I am done.

Commits


The majority of the  changes can be found on github at https://github.com/fsk-software/mynotes/commit/ed209572c5e1302ac03288a84b4e2003cb5894e7.

Tuesday, August 18, 2015

Android: Smore Worthy

Android 6.0 finally got its name: Marshmallow.  Personally, I was hoping for Milkshake. It beats a marshmallow any day of the week and is the ultimate frozen treat(no offense FroYo and  Ice-Cream-Sandwich).

The final preview version was released yesterday and is now available for download in the SDK manager.  

At some point, I will add voice integration to my apps and will have to play with the on-demand permissions soon.

PS.  I am still working on the MyNotes app.  I have the local tests updated and just need to work on the Android tests.  It is summer and even with a messed up ankle, it is too tempting to be outside instead of inside…

Sunday, August 9, 2015

MyNotes: Decisions, Decisions, Decisions

 

I have finally reached feature parity (gratuitous buzzword usage) with the last released version of MyNotes. That leaves me at a big decision point.  I can either keep working on new features:

  • Table UI designs
  • Share Features
  • Voice control
  • Photo Notes
  • Android M updates

or I can begin prepping to release.

I still have to do a lot of unit test updates (the gift that keeps on giving…) before I make my choice.  So that gives me some time, but I will have to decide soon.  It would be nice to have it back on the market, but I am really tempted to get the tablet design working first.

I haven’t pushed the latest round of changes yet.  I want to get those pesky unit tests whipped back into shape first.  I should be able to do that within a couple of days(hopefully/maybe?).

Which leads to my next decision : what should my next blog entry be about: Observer\Obsversable pattern, Compound Custom Views, or ViewAnimators(ViewFlipper/ImageSwitcher)… 

We will all know that when I finish the unit tests and find time to write it.

I think I also need to find a new Plugin for formatting code on the blog.  I am really not happy with the current one I am using,  Maybe I will figure that out by my next post also…