Skip to main content

My Favorite Script - Being Lazy for Fun and Profit

Success is a journey, not a destination. So stop running.

As a tester, one of my most repeated routines includes the following:

  • downloading a new build from our CI server
  • uninstalling the build from my test devices (note the plural)
  • launching the app for a specific set of tests or investigations. 
This happens many times a day, each day, all year. This routine also has many variations based on whether I just want to clear app data and relaunch it, whether I have a different method to launch the app in mind (different starting activity, different intent extras, etc). On top of all of this, there are details about the build which are relevant to any resultant actions I take in our task/bug tracking suite such as build number, device information, etc. This kind of jumble is cumbersome and time-consuming to manage manually. The whole reason to manage all of that process and information is to maintain a consistent practice of reliable reproducible tests and rich environment and setup data for defect reports. Yes, if you're thinking that looks like a lot of work, you're right. It is. And as we all know, good engineers are lazy.

When it comes to bug reporting, for example, junior engineers may be inspired by their laziness to build templates, which isn't a bad start. People use templates pre-loaded with details about their builds, about their test devices, and other relevant bug reporting stats and then just copy and paste from those templates into their reports. After a few years of that you might start to realize that dumb text files you have to manually update with configuration changes, device data, et al, become work to maintain. Being a good engineer (i.e. productively lazy), you may want to use a better template tool than just a text file from which you copy and paste. I use a series of snippets from the very excellent tool, Text Expander, which I describe in detail in the following post. The short version is that they are brilliant little macros that I use for collating all of that repetitive static and situational data neatly into applied Jira every time I open a bug, close a ticket, comment, etc. But as I mentioned in that post, my laziness goes deeper to the point where I'm not even thinking about configs, device trivia, formatting, or any of the rest of that stuff much anymore. And it all starts with my favorite script ever.

My favorite script ever takes the most common, repetitive tasks I have, automates them across all connected test devices my box can reach by adb, and makes me look not only competent but maybe even smart. The script has evolved over time from a simple three-liner that had hard-coded device IDs, package names, and file names. Now it is a 300+ liner that calls a config script. I've shared it across teams, rebuilt it and improved it across multiple employers, and now I'm finally sharing it with the world.

Here's a taste of what it does:

  • Scan connected android devices/emulators for specific, useful trivia
  • Downloads a build from a build server using cURL
  • Parses the build for package and version names which it stores for bug report data (useful given that we update the version name with build stats and you should too)
  • Installs the build on all connected devices after uninstalling previous build
  • Optionally just clears the current install instead of downloading or reinstalling
  • Optionally launches the app with a desired launch string supplying the launch string to a reference file for automatic entry in a ticket template (useful for various intent-based entry-points, launch activities, config-manager endpoints, etc).
In order to take advantage of this script you'll first need to ensure you've got the Android SDK's aapt and adb tools in your $PATH environment variable. You're also going to want to be on a Mac running OSX or you'll need to potentially update the syntax on the calls I'm making to sed and awk if you're on Linux. If you're on Windows, please do yourself a favor and move to a more script-friendly OS or download Cygwin at least. Next, put a copy of it in a convenient working directory. Additionally, chmod it to add execute permission to your user. Finally you'll want to go through the script and update the resource URLs, FILEPATHs, and launch options. Keep in mind I lean heavily on TextExpander for bug entry so every time you see this script saving content to a text file, it is specifically consumed by my snippets in TextExpander.

Okay now that we've gotten through the boring stuff, let's talk about using the script.

DISCLAIMER: Unfortunately for you, I have had to rewrite the whole of my script for external publication in order to comply with company policy. That means I have put much less usage through these patterns so it may take some tuning on your part to really hit your stride. On the other hand the scripts I will provide here represent an unfiltered version of my ideas which may prompt better ideas from you when you start using them or your own versions of them (which is my hope).

Now, before I just paste a GitHub gist of my script in here, let's orient ourselves with some of the major functions I'm using:

  • source - This is how one script can call another script. In my case I try to separate the project-specific data from the logic so that A) the large bash script retains some easy debuggability and B) to make it more portable between projects. Yes, in my case, the script I'm calling really just supplies variables rather than other more complex functions so you can think of it like a config file.
  • sed & awk - These parsers work in conjunction in several places in the script in order to pull versionName and packageName from the aapt dump badging command. sed and awk are far more powerful than I need and are the equivalent of my using a Hanzo sword to open the mail.
  • cURL - Like sed and awk, I'm barely scraping the surface of what is possible with cURL in this script. Just remember to be careful with the file permissions on the script if you're ever silly enough to hard-code your user credentials in the script if your build server requires HTTP auth for access.
  • getopts - You know you're a nerd when you remember the first time you used a specific shell command as clearly as you remember your first kiss. This little beauty is the heart and soul of what makes the main script flexible in the first place.
  • functions and, loops and, arrays, oh my - I've got a lot of functions baked into the main script, many loops and conditionals, I have to handle arrays in places and all of this without a debugger or formal training. That means they're relatively easy to figure out (i.e. "if *I* can do it..."). The main thing to notice is that they can make the flow of the script a little muddy so I tried to comment clearly and name things well both to keep my own thoughts straight and to help others along.
Now I could go on and on with an exhaustive tutorial about how to use these scripts with screenshots and other hooplah but that sure sounds like a lot of work. I'm honestly amazed you're still reading at this point and not passed out from induced narcolepsy. The bottom line is that these are fun experiments for me. I update mine constantly with small tweaks and ideas. I've built something like 10 versions of this core functionality (including some embarrassing ANT integrations back in the day which I prefer not to talk about). You should think of them the same way. Play with them and see what works best for your work flow. Read through them and study them for the thoughts I'm playing with to improve my own quality of life. Look for things you do repeatedly that take more habit than thought. With a couple tweaks to your specific versions of these generic scripts, you can be well and happily on your lazy way too.

Config script example: "generified_apk_installer_settings.config"

Main deployment script example: ""


Popular posts from this blog

UiAutomator and Watchers: Adding Async Robustness to UI Automation

"I'm looking over your shoulder... only because I've got your back." ~ Stephen Colbert
After my recent UiAutomator review a user brought up an important question about the use of UiWatcher. The watchers serve as async guardians of the test flow, making sure the odd dialog window doesn't completely frustrate your tests. Having a tool that automatically watches your back when you're focused on the functional flow of your tests is awesome. 100% pure awesomesauce. Since the API documentation on watchers is scant and the UI Testing tutorial on the Android dev guide doesn't cover their use in depth, I figured I should add a post here that goes over a simple scenario demonstrating how to use this fundamentally important UI automation tool.

In my example code below, I'm using uiautomator to launch the API Demo app (meaning run this against an Emulator built in API level 17 - I used the Galaxy Nexus image included in the latest ADT and platform tools). The te…

Jenkins + Devices + AndroidJUnitRunner

New Android build system and test runner, same goose chase using undocumented features and hacks

As I've posted before, I am a big fan of Jenkins. It is extremely flexible, open source, and supported by a staggering array of plugins actively developed by engineers running over 100,000 instances of the server worldwide. With it's distributed node model, you can even build your own device cloud for hosting enterprise-scale automation, economizing hardware investments by sharing resources across multiple projects as well as speeding up automation by parallelizing test runs. I had been using a Jenkins-based system in the past to support instrumentation automation with Robotium quite happily. For the last couple years however, my work hasn't required that as much and I've found myself doing a lot more manual testing and using UiAutomator which didn't require a tight integration between the product codebase and the test code. As a result, I've been slow to adopt the…

Run-As Like the Wind: Getting private app data off non-rooted devices using adb run-as and a debuggable app

"You're some kind of big, fat, smart-bug aren't you?"
~Johnny Rico, Starship Troopers (1997) One of the most important things about writing bugs is making them descriptive but concise. Screenshots, video, debug logging, and hardware snapshots are all fairly standard or available to Android testers these days and can save you enormously on text when communicating what constitutes the bug. Sometimes though, the app gets into a weird state due to some transient data issue where you may not own the API or the complexity with forcing the app data into a certain state is prohibitively high. In those cases it is very handy to directly inspect the data the app has in its own directories.

Getting at this data is trivial on emulators and rooted devices but due to file system permissions, this data is otherwise completely private to the app itself. If you're like me, you tend to test using devices rather than emulators and you probably prefer not to root your devices since t…