Do you have a development task that is mostly a series of commands? Do you have to perform that task on a regular basis? If so, a script might be just what you need. Over the years I’ve wanted to learn bash scripting several times. Yet there’s always something else that takes priority and my desire to script takes a back seat. Not any more!

I finally decided to start learning how to write bash scripts and I want to share it with you in case you’d like to do the same. At the bottom of this post, I’ve listed several resources that I’m using to help me on this journey. If you’re an Android developer, you can use Gradle to handle many automated tasks. If you want to learn about Gradle on Android or how to create a Gradle plugin, check out my course here.

On to the script. It does the following:

Runs the unit and widget tests for a Flutter application

Generates a code coverage report

Removes the unwanted files from the coverage output

Converts the coverage data to HTML

Opens the HTML page in your browser

I’m going to break down what each section of the script does so that you can start having fun creating your own scripts! (This post assumes you are already familiar with programming and related concepts.)

View the code on Gist.

The Breakdown

#! /usr/bin/env bash

The first line specifies that we want to use bash to run our script. There are other shells available for writing scripts like zsh, ksh, or fish. But for this script, we’re using bash.

red=$(tput setaf 1) none=$(tput sgr0)

These lines create two global variables that we will use later in our script for displaying error messages. They both use the tput command which helps you to format text on the command line. First, we use it to set the foreground color of text to red. The number 1 represents the color red in tput ‘s world. Then later we use it to clear our changes and go back to the default text settings.

Displaying Help

show_help() { printf "usage: $0 [--help] [--report][--test] [<path to package>] Script for running all unit and widget tests with code coverage. (run from root of repo) where: <path to package> runs all tests with coverage and reports -t, --test runs all tests with coverage, but no report -r, --report generate a coverage report (requires lcov, install with Homebrew) -h, --help print this message requires lcov command, install with Homebrew " }

Next, we create our first function, show_help . This function prints to the screen the help documentation for the script. There is one string that contains information about the options for running the script. One thing that to note here is the use of the $0 positional variable. It represents the name of the script. When this command is executed, the name of the script will be included in the output string. See below for an example.

Running Tests

run_tests() { if [[ -f "pubspec.yaml" ]]; then rm -f coverage/lcov.info rm -f coverage/lcov-final.info flutter test --coverage else printf "

${red}Error: this is not a Flutter project${none}" exit 1 fi }

Next up, the run_tests function. If we find a pubspec.yaml file in the current directory we remove the existing coverage files and then execute the tests. If you’re not familiar with Flutter, the pubspec.yaml file is where we specify the dependencies for our app.

However, if we don’t find the pubspec.yaml file, we assume that you are not inside a Flutter project and print an error message to the screen. This is where we use our red and none variables that we created earlier to set the text of the error message to red. You can see that below.

Generating a Report

Moving on to the run_report function. We first do a check for if the code coverage file exists, with if [[ -f "coverage/lcov.info" ]]; then . If it does then we do a little bit of fancy lcov work.

lcov -r coverage/lcov.info lib/resources/l10n/\* lib/\*/fake_\*.dart \ -o coverage/lcov-final.info genhtml -o coverage coverage/lcov-final.info open coverage/index-sort-l.html

We take the generated lcov.info file and remove files that we don’t want to be included in the final code coverage report. In my case, I don’t want the localized strings ( lib/resources/l10n/\* ) or any of the fakes ( lib/\*/fake_\*.dart ) that I’ve created for debugging. Then we create a new file lcov-final.info to include the paired down coverage information.

With our new coverage file, we use the genhtml command to turn it into an HTML page. And finally, we open that index page that has been created in the coverage directory with our default browser. Here’s an example of a generated report:

I’m still working on adding more tests, so don’t judge me on the current coverage numbers 😉

Giving the User Options

The final thing we do in our script is handle the user’s input. To do that we use a bash case statement. If you’re familiar with any of the popular programming languages the bash case statement is comparable to a switch statement. It has the following format:

case $input in pattern) commands ;; esac

You open with the case keyword and then close with the esac keyword, which is just case spelled backward. Then you include the cases by providing a pattern followed by a parenthesis, the commands you want to execute, and then a closing ;; . The ;; is like a break statement in a switch .

Below is the case statement for our script. It contains four different cases:

Display the help information (-h or —help)

Run the tests (-t or —test)

Generate the report (-r or —report)

Run the tests and generate the report (nothing or anything else)

case $1 in -h|--help) show_help ;; -t|--test) run_tests ;; -r|--report) run_report ;; *) run_tests run_report ;; esac

The input to the case statement is the $1 positional variable. This is the first argument that is directly after the name of the script on the command line. If you have the following on the command line:

./tests_with_coverage.sh --help

then the value of $1 will be --help . Now when the user calls our script we can handle the various options that they send in. By default, if they simply call ./tests_with_coverage.sh we will fall into the *) case and run the tests along with the report.

It was cool to investigate bash scripting finally. Thanks to @nocnoc for the inspiration (his blog post is below). I hope this helps you to get started as well!

Resources: