Running Tests On A target Connected With NFS

When working on an embedded system a usual setup is to export the rootfs via NFS. If you run the unit tests on the target or the functional tests while developing, then maybe you run them manually on the target console. Today I will show how to easily run them from the host so that you can retrieve the tests results directly in your code editor.

We need two things to run our tests on the target:

  • Find its IP address.
  • A way to run the tests and retrieve the results on the host.

Finding The Target IP Address

There are many possible ways to automatically retrieve the ip address of the target when it is assigned via DHCP: If you know its mac address your can retrieve the IP address by looking at the arp cache. If your use mdns or ssdp you can declare your own service to retrieve your device via a dedicated id.

However if you use NFS and your development machine is the NFS server, then this can be done completely automatically. By looking at the connected NFS clients, and comparing the volume they are connected to with the rootfs path, we can find our target without knowing any information about it. Here is a shell function that does this:

get_nfs_ip() {
    local output_dir=$1
    local clients=$(showmount -a --no-headers)
    while read -r client || [[ -n $client ]] ; do
        arr=$(echo $client | tr ":" "\")
        path_match=$(echo $nfs_path | grep $output_dir | cat)
        if [ "$path_match" == "$nfs_path" ]; then
            echo $ip
    done <<< "$clients"

It simply parses the output of the showmount command. showmount lists the clients connected to the NFS server and the associated volume path. The first client that has mounted the directory provided as a parameter is returned. If you have several targets connected and you want to use all of them, you can return a list instead of the first entry.

Running Tests Via SSH

The simplest way to run the tests is to use ssh. If there is no ssh server on your target, you can use dropbear. Executing a command on the remote machine (i.e. the target) is simply done by adding the command to run at the end of the ssh command:

ssh_exec() {
    local target=$1
    local exe=$2
    echo "executing $exe on $target via ssh"
    ssh root@$1 $2

This example considers that the root account is available without password, which is common on development boards. If you use a more restricted setup, you can authenticate with a password-less certificate so that you do not have to enter a password when running the tests. You can also use ssh-agent to avoid typing the password each time you run your tests.

Putting All Together

With these 2 shell functions, it is very easy to run tests from the host via a shell script. Here is an excerpt of such a script:

#! /bin/sh

set -e
usage() {
    echo "execute a program on the target"
    echo "Usage : "
    echo "run-target-exe [output dir] [exe path]"

# get_nfs_ip
# your config_is_pc here
# your localhost_exec here

if [ $# -ne 2 ] ; then
    exit -1


if [ "$(config_is_pc $output_dir)" == "yes" ]; then
    localhost_exec $output_dir $exe
    ip=$(get_nfs_ip $output_dir)
    ssh_exec $ip $exe

exit 0

You must implement your own config_is_pc and localhost_exec functions. config_is_pc returns yes if the current build is for the host. This allows to either run the tests locally - by calling localhost_exec - or on the target. This script can then be used on a make test target to run the tests either on the host or on the target. I use this to easily run my unit tests the same way if my environment is a PC build or a cross-compilation build. With Atom and the build package, this allows to navigate directly in the code where the unit-tests failed.

If your target operating system does not allow you to upload and run applications or binaries, this is a simple way to run your tests, or anything you need to do during development or automated testing.