1#!/bin/sh
2
3# Common unit test commands
4set -e
5
6export LC_ALL=C
7
8# Linux command line tools that may be different on other OSes
9READLINK=readlink
10BASE64_DECODE=-d
11TIMEOUT=timeout
12
13if [ -d "/mnt/c/Users" ]; then
14    # Windows 10 bash mode
15    HOST_OS=Windows
16    HOST_ARCH=amd64
17else
18    HOST_OS=$(uname -s)
19    HOST_ARCH=$(uname -m)
20fi
21
22case "$HOST_OS" in
23    Darwin)
24	# BSD stat
25        STAT_FILESIZE_FLAGS="-f %z"
26
27	# Not -d?
28        BASE64_DECODE=-D
29
30        BREW_PREFIX=$(brew --prefix)
31        READLINK="$BREW_PREFIX"/bin/greadlink
32        [ -e "$READLINK" ] || ( echo "Please run 'brew install coreutils' to install greadlink"; exit 1 )
33        [ -e "$BREW_PREFIX/bin/mdir" ] || ( echo "Please run 'brew install mtools' to install mdir"; exit 1 )
34
35        FSCK_FAT="$(brew --prefix dosfstools)/sbin/fsck.fat"
36        if [ -f "$FSCK_FAT" ]; then
37            FSCK_FAT="$FSCK_FAT -n"
38        else
39            # Fall back to the OSX fsck_msdos which isn't as picky, but still catches issues.
40            FSCK_FAT=fsck_msdos
41        fi
42
43        TIMEOUT=gtimeout
44        ;;
45    FreeBSD|NetBSD|OpenBSD|DragonFly)
46	# BSD stat
47        STAT_FILESIZE_FLAGS="-f %z"
48
49        # fsck_msdosfs fails on BSD, but the failure doesn't look like a
50        # problem, so ignore.
51        #FSCK_FAT=fsck_msdosfs
52        FSCK_FAT=true
53
54        # The BSDs don't have the timeout command
55        TIMEOUT=
56        ;;
57    *)
58	# GNU stat
59	STAT_FILESIZE_FLAGS=-c%s
60
61        # dosfstools
62        FSCK_FAT="fsck.fat -n"
63
64        # Check for Busybox timeout which is a symlink
65	if [ -L $(which $TIMEOUT) ]; then
66		TIMEOUT=
67	fi
68        ;;
69esac
70
71base64_decode() {
72    base64 $BASE64_DECODE
73}
74
75base64_decodez() {
76    base64 $BASE64_DECODE | zcat
77}
78
79filesize() {
80    stat $STAT_FILESIZE_FLAGS $1
81}
82
83TESTS_SRC_DIR=$(dirname $($READLINK -f $0))
84TESTS_DIR=$(pwd)
85
86# Default to testing the fwup built in the src directory,
87# but it is possible to define the version of fwup used for
88# the create and apply steps separately.
89FWUP_DEFAULT=$TESTS_DIR/../src/fwup
90FWUP_BINARY=$TESTS_DIR/../src/fwup
91FRAMING_HELPER=$TESTS_DIR/fixture/framing-helper
92if [ ! -e $FWUP_DEFAULT ]; then
93    if [ -e $FWUP_DEFAULT.exe ]; then
94        EXEEXT=.exe
95        FWUP_BINARY="$FWUP_DEFAULT.exe"
96        FWUP_DEFAULT="wine $FWUP_BINARY"
97        FRAMING_HELPER="wine $TESTS_DIR/fixture/framing-helper.exe"
98    fi
99fi
100
101if [ -z $FWUP_CREATE ]; then FWUP_CREATE="$FWUP_DEFAULT"; fi
102if [ -z $FWUP_APPLY ]; then FWUP_APPLY="$FWUP_DEFAULT"; fi
103if [ -z $FWUP_APPLY_NO_CHECK ]; then FWUP_APPLY_NO_CHECK="$FWUP_APPLY"; fi
104if [ -z $FWUP_VERIFY ]; then FWUP_VERIFY="$FWUP_DEFAULT"; fi
105
106if [ -z $EXEEXT ]; then
107    # No sanity check for windows builds.
108    [ -e $FWUP_CREATE ] || ( echo "Can't find $FWUP_CREATE"; exit 1 )
109    [ -e $FWUP_APPLY ] || ( echo "Can't find $FWUP_APPLY"; exit 1 )
110    [ -e $FWUP_VERIFY ] || ( echo "Can't find $FWUP_VERIFY"; exit 1 )
111    [ -e $FRAMING_HELPER ] || ( echo "Can't find $FRAMING_HELPER"; exit 1 )
112fi
113
114if [ -n "$VALGRIND" ]; then
115    # Example:
116    #  VALGRIND="valgrind -s --leak-check=full --error-exitcode=1 --track-origins=yes
117    FWUP_CREATE="$VALGRIND $FWUP_CREATE"
118    FWUP_APPLY="$VALGRIND $FWUP_APPLY"
119    FWUP_VERIFY="$VALGRIND $FWUP_VERIFY"
120else
121# The syscall verification code only runs on a subset of
122# platforms. Let autoconf figure out which ones and run it
123# if the verify-syscalls program built.
124if [ -e $TESTS_DIR/fixture/verify-syscalls ]; then
125    export VERIFY_SYSCALLS_CMD=$FWUP_APPLY
126    FWUP_APPLY=$TESTS_DIR/fixture/verify-syscalls
127fi
128fi
129
130# The write fault simulator only runs only runs on a subset of
131# platforms. Let autoconf figure out which ones.
132WRITE_SHIM="$TESTS_DIR/fixture/.libs/libwrite_shim.so"
133if [ -e "$WRITE_SHIM" ]; then
134    export HAS_WRITE_SHIM=true
135    export LD_PRELOAD="$WRITE_SHIM"
136    export DYLD_INSERT_LIBRARIES="$WRITE_SHIM"
137else
138    export HAS_WRITE_SHIM=false
139fi
140
141WORK=$TESTS_DIR/work-$(basename "$0")
142RESULTS=$WORK/results
143
144CONFIG=$WORK/fwup.conf
145FWFILE=$WORK/fwup.fw
146IMGFILE=$WORK/fwup.img
147UNZIPDIR=$WORK/unzip
148EXPECTED_META_CONF=$WORK/meta.conf.expected
149TRIMMED_META_CONF=$WORK/meta.conf.trimmed
150
151# Setup the directories
152rm -fr $WORK
153mkdir -p $WORK
154
155unzip_fw() {
156    mkdir -p $UNZIPDIR
157    unzip -q $FWFILE -d $UNZIPDIR
158}
159
160check_meta_conf() {
161    # Need to unzip the fw file to check the meta.conf
162    if ! [ -e $UNZIPDIR/meta.conf ]; then
163        unzip_fw
164    fi
165
166    # Trim the results of known lines that vary between runs
167    cat $UNZIPDIR/meta.conf \
168        > $TRIMMED_META_CONF
169    diff -w $EXPECTED_META_CONF $TRIMMED_META_CONF
170}
171
172cleanup() {
173    rc=$?
174    if [ $rc = "0" ]; then
175        echo "Test succeeded"
176        rm -fr $WORK
177    elif [ $rc = "77" ]; then
178        echo "Test skipped"
179        rm -fr $WORK
180    else
181        echo
182        echo "Test failed!"
183        echo
184        echo "Leaving test work files in '$WORK'"
185    fi
186}
187
188trap cleanup EXIT
189
190# Test input files
191
192# These files contain random data so that it is possible to
193# verify that fwup copied things correctly. Previously fwup
194# unit tests used files filled with 1s, and it possible for
195# a test to pass even though the data had been reordered.
196# (This never actually happened to my knowledge.)
197
198TESTFILE_1K="$TESTS_SRC_DIR/1K.bin"
199TESTFILE_1K_CORRUPT="$TESTS_SRC_DIR/1K-corrupt.bin"
200TESTFILE_150K="$TESTS_SRC_DIR/150K.bin"
201
202# Generated test data
203create_15M_file() {
204    if [ ! -e $TESTFILE_15M ]; then
205        i=0
206        while [ $i -lt 100 ]; do
207            cat $TESTFILE_150K >> $TESTFILE_15M
208            i=$(expr $i + 1)
209        done
210    fi
211}
212TESTFILE_15M=$WORK/15M.bin
213
214# GNU and BSD friendly cmp --bytes
215# Invoke as: cmp_bytes <byte count> <file1> <file2> [offset1] [offset2]
216#
217# NOTE: This implementation has the unfortunate constraint that bytes and
218#       offsets need to be multiples of the block size. The block size
219#       could be set to 1, but that makes some large comparisons VERY slow.
220cmp_bytes() {
221    BYTE_COUNT=$1
222    FILE1=$2
223    FILE2=$3
224    BEGIN1=$4
225    BEGIN2=$5
226
227    if [ -z $BEGIN1 ]; then BEGIN1=0; fi
228    if [ -z $BEGIN2 ]; then BEGIN2=0; fi
229
230    BEGIN1=$(expr $BEGIN1 / 512) || true
231    BEGIN2=$(expr $BEGIN2 / 512) || true
232    BLOCK_COUNT=$(expr $BYTE_COUNT / 512)
233
234    dd if=$FILE1 of=$WORK/cmp_bytes1 count=$BLOCK_COUNT skip=$BEGIN1 2> /dev/null
235    dd if=$FILE2 of=$WORK/cmp_bytes2 count=$BLOCK_COUNT skip=$BEGIN2 2> /dev/null
236
237    cmp $WORK/cmp_bytes1 $WORK/cmp_bytes2
238}
239