1#!/bin/bash
2# Copyright (c) 2019 Intel Corporation
3# SPDX-License-Identifier: Apache-2.0
4
5image=net-tools
6name=net-tools
7network=net-tools0
8zephyr_pid=0
9docker_pid=0
10configuration=""
11result=0
12dirs=""
13zephyr_overlay=""
14docker_test_script_name=docker-test.sh
15scan_dirs=0
16
17RUNNING_FROM_MAIN_SCRIPT=1
18
19check_dirs ()
20{
21    local ret_zephyr=0
22    local ret_net_tools=0
23
24    if [ -z "$ZEPHYR_BASE" ]; then
25	    echo '$ZEPHYR_BASE is unset' >&2
26	    ret_zephyr=1
27    elif [ ! -d "$ZEPHYR_BASE" ]; then
28	    echo '$ZEPHYR_BASE is set, but it is not a directory' >&2
29	    ret_zephyr=1
30    fi
31
32    if [ -z "$NET_TOOLS_BASE" ]; then
33	    local d
34
35	    for d in "$ZEPHYR_BASE/../.." "$ZEPHYR_BASE/.."
36	    do
37	        local l
38
39	        l="$d/tools/net-tools"
40	        if [ -d "$l" ]; then
41		        NET_TOOLS_BASE="$l"
42		        break
43	        fi
44	    done
45    fi
46
47    if [ $ret_zephyr -eq 0 ]; then
48	    echo "\$ZEPHYR_BASE $ZEPHYR_BASE"
49    fi
50
51    if [ -z "$NET_TOOLS_BASE" ]; then
52	    echo '$NET_TOOLS_BASE is unset, no net-tools found' >&2
53	    ret_net_tools=1
54    elif [ ! -d "$NET_TOOLS_BASE" ]; then
55	    echo '$NET_TOOLS_BASE set, but it is not a directory' >&2
56	    ret_net_tools=1
57    fi
58
59    if [ $ret_net_tools -eq 0 ]; then
60	    echo "\$NET_TOOLS_BASE $NET_TOOLS_BASE"
61    fi
62
63    if [ $ret_zephyr -ne 0 -o $ret_net_tools -ne 0 ]; then
64	    return 1
65    fi
66
67    return 0
68}
69
70scan_dirs ()
71{
72    echo
73    echo "Following directories under $ZEPHYR_BASE can be used by this script:"
74    find "$ZEPHYR_BASE" -type f -name $docker_test_script_name | \
75        awk -F "$ZEPHYR_BASE/" '{ print $2 }' | \
76        sed "s/\/$docker_test_script_name//"
77}
78
79start_configuration ()
80{
81    local bridge_interface=""
82    local addresses="--ip=192.0.2.2 --ip6=2001:db8::2"
83
84    if ! docker image ls | grep "$image" > /dev/null; then
85	    echo "Docker image '$image' not found" >&2
86	    return 1
87    fi
88
89    if ! docker network ls | grep "$network" > /dev/null; then
90	    bridge_interface=$("$NET_TOOLS_BASE/net-setup.sh" \
91		                   --config "$NET_TOOLS_BASE/docker.conf" \
92			               start 2>/dev/null | tail -1)
93	    if [ $? != 0 ]; then
94	        echo "Could not start Docker network '$network'" >&2
95	        return 1
96	    fi
97
98	    echo "Started Docker network '$network' bridge interface" \
99	         "'$bridge_interface'..."
100    fi
101
102    if [ -n "$*" ]; then
103	    addresses="$*"
104    fi
105
106    if docker ps | grep "$name" > /dev/null; then
107	    docker stop "$name"
108    fi
109
110    if docker run --hostname=$name --name=$name $addresses \
111              --rm -dit --network=$network $image > /dev/null; then
112	    echo -n "Started Docker container '$name'"
113	    if [ -n "$*" ]; then
114	        echo -n " with extra arguments '$*'"
115	    fi
116
117	    echo "..."
118    else
119	    echo "Could not start Docker container '$image'"
120	    return 1
121    fi
122}
123
124stop_configuration ()
125{
126    local bridge_interface=""
127
128    if docker ps | grep "$name" > /dev/null; then
129	    docker stop "$name" > /dev/null
130	    if [ $? -eq 0 ]; then
131	        echo "Stopped Docker container '$name'..."
132	    else
133	        echo "Could not stop Docker container '$name'" >&2
134	    fi
135    fi
136
137    bridge_interface=$(docker network ls | grep "$network" | cut -d " " -f 1)
138    if [ -n "$bridge_interface" ]; then
139	    docker network rm "$network" > /dev/null
140	    if [ $? -eq 0 ]; then
141	        echo "Stopped Docker network '$network' bridge interface" \
142		         "'br-$bridge_interface'..."
143	    else
144	        echo "Could not stop Docker network '$network'" >&2
145	    fi
146    fi
147
148    # No need to keep the zephyr eth interface around
149    "$NET_TOOLS_BASE/net-setup.sh" --config "$NET_TOOLS_BASE/docker.conf" \
150			               stop > /dev/null 2>&1
151}
152
153start_zephyr ()
154{
155    if [ -n "$*" ]; then
156	    echo "Building Zephyr with additional arguments '$@'..." >&2
157    fi
158
159    rm -rf build && mkdir build && \
160	cmake -GNinja -DBOARD=native_posix -B build "$@" . && \
161	ninja -C build
162
163    # Run the binary directly so that ninja does not print errors that
164    # could be confusing.
165    #ninja -C build run &
166    build/zephyr/zephyr.exe &
167    zephyr_pid=$!
168
169    sleep 3
170    echo "Zephyr PID $zephyr_pid"
171}
172
173list_children () {
174    local pid="$(ps -o pid= --ppid "$1")"
175
176    for p in $pid
177    do
178	    list_children $p
179    done
180
181    if [ -n "$pid" ]; then
182	    echo -n "$pid "
183    fi
184}
185
186stop_zephyr ()
187{
188    if [ "$zephyr_pid" -ne 0 ]; then
189	    local zephyrs="$zephyr_pid $(list_children "$zephyr_pid")"
190
191	    echo "Stopping Zephyr PIDs $zephyrs"
192	    kill $zephyrs 2> /dev/null
193    fi
194
195    zephyr_pid=0
196}
197
198wait_zephyr ()
199{
200    local result=""
201
202    echo "Waiting for Zephyr $zephyr_pid..."
203    wait $zephyr_pid
204    result=$?
205
206    zephyr_pid=0
207
208    return $result
209}
210
211
212docker_run ()
213{
214    local test=""
215    local result=0
216
217    for test in "$@"
218    do
219	    echo "Running '$test' in the container..."
220	    docker container exec net-tools $test || return $?
221    done
222}
223
224start_docker ()
225{
226    docker_run "$@" &
227    docker_pid=$!
228
229    echo "Docker PID $docker_pid"
230}
231
232stop_docker ()
233{
234    if [ "$docker_pid" -ne 0 -a "$configuration" != "keep" ]; then
235	    local dockers="$docker_pid $(list_children "$docker_pid")"
236
237	    echo "Stopping Docker PIDs $dockers"
238	    kill $dockers 2> /dev/null
239    fi
240
241    docker_pid=0
242}
243
244wait_docker ()
245{
246    local result=""
247
248    echo "Waiting for Docker PID $docker_pid..."
249    wait $docker_pid
250    result=$?
251
252    docker_pid=0
253
254    echo "Docker returned '$result'"
255    return $result
256}
257
258run_test ()
259{
260    local test="$(basename $(pwd))"
261    local result=0
262    local overlay=""
263
264    if [ -n "$zephyr_overlay" ]; then
265        overlay="-DOVERLAY_CONFIG=$zephyr_overlay"
266    fi
267
268	source "$1"
269	result=$?
270
271    if [ $result -eq 0 ]; then
272	    echo "Sample '$test' successful"
273    else
274	    echo "Sample '$test' failed with return value '$result'"
275    fi
276
277    return $result
278}
279
280usage ()
281{
282    BASENAME=`basename $0`
283
284    cat <<EOF
285
286$BASENAME [-Z <zephyr base directory>] [-N <net-tools base directory>] [<list of test directories>]
287
288This script runs Zephyr sample tests using Docker container and
289network implemented by the 'net-tools' subproject.
290
291-Z|--zephyr-dir <dir>
292	set Zephyr base directory
293-N|--net-tools-dir <dir>
294	set net-tools directory
295--start
296	only start Docker container and network and exit
297--stop
298	only stop Docker container and network
299--keep
300	keep Docker container and network after test
301--overlay <config files>
302	additional configuration/overlay files for the Zephyr build process
303--scan
304    find out the directories that can be used for this testing
305<list of test directories>
306	run the tests in these directories instead of current directory
307
308The automatically detected directories are:
309EOF
310    check_dirs
311}
312
313stop_sample_test () {
314    echo "Interrupted..." >&2
315
316    stop_zephyr
317    stop_docker
318
319    stop_configuration
320    exit 2
321}
322
323trap stop_sample_test ABRT INT HUP TERM
324
325while test -n "$1"
326do
327    case "$1" in
328	-Z|--zephyr-dir)
329	    shift
330	    ZEPHYR_BASE="$1"
331	    ;;
332
333	-N|--net-tools-dir)
334	    shift
335	    NET_TOOLS_BASE="$1"
336	    ;;
337
338	-h|--help)
339	    usage
340	    exit
341	    ;;
342	--start)
343	    if [ -n "$configuration" ]; then
344		    echo "--start or --stop specified multiple times" >&2
345		    exit 1
346	    fi
347	    configuration=start_only
348	    ;;
349	--stop)
350	    if [ -n "$configuration" ]; then
351		    echo "--start or --stop specified multiple times" >&2
352		    exit 1
353	    fi
354	    configuration=stop_only
355	    ;;
356	--keep)
357	    configuration=keep
358	    ;;
359
360	--overlay)
361	    shift
362	    if [ -n "$zephyr_overlay" ]; then
363		    zephyr_overlay="$zephyr_overlay $1"
364	    else
365		    zephyr_overlay="$1"
366	    fi
367	    ;;
368
369    --scan)
370        scan_dirs=1
371        ;;
372	-*)
373	    echo "Argument '$1' not recognised" >&2
374	    usage
375	    return 0
376	    ;;
377	*)
378	    dirs="$dirs $1"
379	    ;;
380    esac
381
382    shift
383done
384
385check_dirs || exit $?
386
387if [ $scan_dirs -eq 1 ]; then
388    scan_dirs
389    exit 0
390fi
391
392if [ -z "$configuration" -o "$configuration" = "start_only" -o \
393	"$configuration" = "keep" ]; then
394    if [ "$configuration" = start_only ]; then
395	    start_configuration
396	    result=$?
397    else
398        result=0
399        found=0
400
401        if [ -z "$dirs" ]; then
402            dirs="."
403        fi
404
405        for d in $dirs; do
406            if [ ! -d "$d" ]; then
407                echo "No such directory $d, skipping it"
408                continue
409            fi
410
411            if [ ! -f "$d/$docker_test_script_name" ]; then
412                echo "No such file $d/$docker_test_script_name, skipping directory"
413                continue
414            fi
415
416            found=$(expr $found + 1)
417
418            CURR_DIR=`pwd`
419            cd "$d"
420	        run_test "./$docker_test_script_name"
421	        test_result=$?
422            cd "$CURR_DIR"
423
424            if [ $test_result -ne 0 ]; then
425                result=1
426            fi
427        done
428
429        if [ $found -eq 0 ]; then
430            exit 1
431        fi
432    fi
433fi
434
435if [ -z "$configuration" -o "$configuration" = stop_only ]; then
436    stop_configuration
437fi
438
439exit $result
440