1#! /bin/bash
2
3YELLOW="\033[1;33m"
4NOCOLOR="\033[0m"
5BLUE="\033[1;34m"
6RED="\033[1;91m"
7
8function happy_hedgehog {
9    echo -e "\t${BLUE}"
10    echo -e "\t       ___------__"
11    echo -e "\t |\__-- /\       _-"
12    echo -e "\t |/_   __      -"
13    echo -e "\t // \ /  \    /__"
14    echo -e "\t | 0 |  0 |__     --_        Gotta go fast!"
15    echo -e "\t \\____-- __ \   ___-"
16    echo -e "\t ( @    __/  / /_"
17    echo -e "\t    -_____---   --_"
18    echo -e "\t     //  \ \\   ___-"
19    echo -e "\t   //|\__/  \\  \\"
20    echo -e "\t   \_-\_____/  \-\\"
21    echo -e "\t        // \\--\|"
22    echo -e "\t   ${RED}____${BLUE}//  ||${RED}_"
23    echo -e "\t${RED}  /_____\ /___\\"
24    echo -e "${NOCOLOR}"
25}
26
27function sad_hedgehog {
28    echo -e "\t${BLUE}"
29    echo -e "\t       ___------__"
30    echo -e "\t |\__-- /\       _-"
31    echo -e "\t |/_    __      -"
32    echo -e "\t // \  /  \    /__"
33    echo -e "\t | 0 |  0 |__     --_        Gotta go sllloowwww!"
34    echo -e "\t \\____-- __ \   ___-"
35    echo -e "\t ( @    __   / /_"
36    echo -e "\t    -_____---   --_"
37    echo -e "\t     //  \ \\   ___-"
38    echo -e "\t   //|\__/  \\  \\"
39    echo -e "\t   \_-\_____/  \-\\"
40    echo -e "\t        // \\--\|"
41    echo -e "\t  ${RED} ____${BLUE}//  ||${RED}_"
42    echo -e "\t${RED}  /_____\ /___\\"
43    echo -e "{$NOCOLOR}"
44}
45
46function check_environment {
47    if [[ -z "${ANDROID_BUILD_TOP}" ]] || [[ -z "${ANDROID_HOST_OUT}" ]] ; then
48      echo -e "${RED}ANDROID_BUILD_TOP${NOCOLOR} or ${RED}ANDROID_HOST_OUT${NOCOLOR} is not set for host run"
49      echo -e "Navigate to android root and run:"
50      echo -e "${YELLOW}"
51      echo -e ". build/envsetup.sh"
52      echo -e "lunch <fish>"
53      echo -e "${NOCOLOR}"
54      echo
55      exit 1
56    fi
57    if ! [ -x "$(command -v python3.9)" ] ; then
58      echo -e "${RED}You must have python 3.9 installed${NOCOLOR}"
59      exit 1
60    fi
61    python3.9 -m virtualenv --version
62    if [[ $? -ne 0 ]] ; then
63        echo "${RED}virtualenv not installed for python3.9${NOCOLOR}"
64        echo "${RED}Please run 'python3.9 -m pip install virtualenv' to install it${NOCOLOR}"
65        exit 1
66    fi
67}
68
69ASHMEM_OUT="/dev/shm/out"
70ASHMEM_DIST="${ASHMEM_OUT}/dist"
71ASHMEM_VENV="${ASHMEM_DIST}/bluetooth_venv"
72ASHMEM_GOTTA_GO_FAST="/dev/shm/gottagofast"
73ASHMEM_HOST_LOGS="${ASHMEM_GOTTA_GO_FAST}/logs"
74ASHMEM_OUT_TARGET="${ASHMEM_GOTTA_GO_FAST}/target"
75ASHMEM_SOONG="${ASHMEM_GOTTA_GO_FAST}/out/soong"
76CERT_HOST_LOGS="/tmp/logs/HostOnlyCert"
77CERT_DEVICE_LOGS="TODO: Add this"
78CERT_TEST_VENV=${ANDROID_BUILD_TOP}/out/dist/bluetooth_venv
79OUT_TARGET="${ANDROID_BUILD_TOP}/out/target"
80TEST_CONFIG="${ANDROID_BUILD_TOP}/system/bt/gd/cert/host_config.json"
81TEST_FILTER="-tf ${ANDROID_BUILD_TOP}/system/bt/gd/cert/all_cert_testcases"
82CPP_BUILD_TARGET="bluetooth_stack_with_facade root-canal bluetooth_packets_python3"
83RUST_BUILD_TARGET="bluetooth_with_facades root-canal bluetooth_packets_python3"
84BUILD_TARGET=$CPP_BUILD_TARGET
85
86CLEAN_VENV=false
87GOTTA_GO_FAST=false
88NUM_REPETITIONS="1"
89SKIP_SOONG_BUILD=false
90USE_ASHMEM_VENV=true
91VERBOSE_MODE=false
92DEVICE_TEST=false
93
94# Directory for test configs to modify
95CONFIG_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
96
97## Verify devices connected and valid
98DUT_SERIAL="DUT Not Set"
99DUT_ADB="DUT Not Set"
100DUT_NAME="DUT Not Set"
101
102# Used for position arguments needed for later
103POSITIONAL=()
104function parse_options {
105    while [[ $# -gt 0 ]]
106    do
107    key="$1"
108    case $key in
109        # This will delete the existing venv before running the test
110        # If you updated external libraries such as ACTS, you need to add this flag
111        --clean)
112        CLEAN_VENV=true
113        shift # past argument
114        ;;
115        --help)
116        echo
117        echo -e "${YELLOW}Help menu${NOCOLOR}"
118        echo -e "==================================="
119        echo -e "${BLUE}  --clean${NOCOLOR}"
120        echo -e "    Clean the virtul environment; use if ACTS has been updated."
121        echo -e "${BLUE}  --disable-ashmem-venv${NOCOLOR}"
122        echo -e "    Places the virtual environment on disk rather than in ashmem which is default."
123        echo -e "${BLUE}  --gotta-go-fast${NOCOLOR}"
124        echo -e "    Makes use of ashmem as best as possible for targeted speed increases."
125        echo -e "${BLUE}  --device${NOCOLOR}"
126        echo -e "    Run the test on the 2 real devices."
127        echo -e "${BLUE}  --rust${NOCOLOR}"
128        echo -e "    Run the test using the rust implementation on the 2 real devices."
129        echo -e "${BLUE}  --rhost${NOCOLOR}"
130        echo -e "    Run the test using the rust implementation on the host."
131        echo -e "${BLUE}  --repeat=<N>${NOCOLOR}"
132        echo -e "    Repeat the test sequence N (int) number of times."
133        echo -e "${BLUE}  --skip-soong-build${NOCOLOR}"
134        echo -e "    Skips building soong targets. Use when you are just modifying simple python files."
135        echo -e "${BLUE}  --test_config=<configfile>${NOCOLOR}"
136        echo -e "    Override default test configuration."
137        echo -e "${BLUE}  --verbose${NOCOLOR}"
138        echo -e "    Displays device logs and test logs to output."
139        echo
140        echo -e "Usage: $0 [--clean|--host|--repeat=<N>|--test_config=<config>] [TestGroupName[:IndividualTestName]]"
141        echo -e "        ${YELLOW}e.g."
142        echo -e "         $0 --host --clean SecurityTest"
143        echo -e "         $0 --host --verbose SecurityTest:test_dut_initiated_display_only_display_only ${NOCOLOR}"
144        echo
145        shift
146        exit 0
147        ;;
148        # This will cause the bluetooth_venv to NOT be created in ashmem
149        # Using ashmem increases --clean build times by 40% (~21 seconds on my machine)
150        --disable-ashmem-venv)
151        USE_ASHMEM_VENV=false
152        shift # past argument
153        ;;
154        --gotta-go-fast)
155        GOTTA_GO_FAST=true
156        shift # past argument
157        ;;
158        --device)
159        TEST_CONFIG="${ANDROID_BUILD_TOP}/system/bt/gd/cert/devices_config.json"
160        DEVICE_TEST=true
161        shift # past argument
162        ;;
163        # Repeat running the specified test cases by N times in one single setup
164        --repeat=*)
165        NUM_REPETITIONS="${key#*=}"
166        shift # past argument
167        ;;
168        --skip-soong-build)
169        SKIP_SOONG_BUILD=true
170        shift
171        ;;
172        --test_config=*)
173        TEST_CONFIG="${key#*=}"
174        shift # past argument
175        ;;
176        --rust)
177        BUILD_TARGET=$RUST_BUILD_TARGET
178        export RUST_BACKTRACE=1
179        TEST_CONFIG=$ANDROID_BUILD_TOP/system/bt/gd/cert/rust_android_devices_config.json
180        DEVICE_TEST=true
181        shift # past argument
182        ;;
183        --rhost)
184        export RUST_BACKTRACE=1
185        BUILD_TARGET=$RUST_BUILD_TARGET
186        TEST_CONFIG=$ANDROID_BUILD_TOP/system/bt/gd/cert/rust_host_config.json
187        shift # past argument
188        ;;
189        # This will log everything to both log file and stdout
190        --verbose)
191        VERBOSE_MODE=true
192        shift # past argument
193        ;;
194        *)    # unknown option
195        POSITIONAL+=("$1") # save it in an array for later
196        shift # past argument
197        ;;
198    esac
199    done
200    set -- "${POSITIONAL[@]}" # restore positional parameters
201
202    # Set the test filter
203    if [[ -n "$1" ]] ; then
204      TEST_FILTER="-tc $1"
205    fi
206
207    INSTALL_ARGS="--reuse-acts"
208    if [ "$CLEAN_VENV" == true ] ; then
209      echo -e "${YELLOW}Cleaning up existing virtualenv${NOCOLOR}"
210      rm -rf $CERT_TEST_VENV/*
211      rm -rf $CERT_TEST_VENV
212      mkdir -p ${CERT_TEST_VENV}
213      INSTALL_ARGS=""
214    else
215      echo -e "${YELLOW}Try to reuse existing virtualenv at ${CERT_TEST_VENV}${NOCOLOR}"
216    fi
217
218}
219
220function select_devices {
221    if [ "$DEVICE_TEST" == true ] ; then
222        RR="$(cat ${TEST_CONFIG}|grep \"CERT\\\|DUT\")"
223        if [ "$RR" != "" ]; then
224            DUT_SERIAL="$(menu-adb DUT)"
225            DUT_ADB="adb -s ${DUT_SERIAL}"
226            DUT_NAME="$(adb devices -l | grep -v "List of device" | grep ${DUT_SERIAL} | awk '{ print $6 }' | cut -d ':' -f 2)"
227            CERT_SERIAL="$(menu-adb CERT)"
228            CERT_ADB="adb -s ${CERT_SERIAL}"
229            CERT_NAME="$(adb devices -l | grep -v "List of device" | grep ${CERT_SERIAL} | awk '{ print $6 }' | cut -d ':' -f 2)"
230
231            if [ "${CERT_SERIAL}" == "${DUT_SERIAL}" ]; then
232                echo
233                echo "ERROR: CERT and DUT cannot be the same device, or you only have one device connected!"
234                echo
235                exit 1
236            fi
237
238            ## Set android devices in config
239            pushd .
240            cd "${CONFIG_DIR}"
241            popd
242            sed -i "s/\"DUT\"/\"${DUT_SERIAL}\"/g" ${TEST_CONFIG}
243            sed -i "s/\"CERT\"/\"${CERT_SERIAL}\"/g" ${TEST_CONFIG}
244        fi
245    fi
246}
247
248function soong_build {
249    if [ "$CLEAN_VENV" == true ] ; then
250        $ANDROID_BUILD_TOP/build/soong/soong_ui.bash --build-mode --"modules-in-a-dir" --dir="${ANDROID_BUILD_TOP}/system/bt/gd" dist $BUILD_TARGET -j20
251        if [[ $? -ne 0 ]] ; then
252            echo "Failed to build ${BUILD_TARGET}"
253            exit 1
254        fi
255    else
256        $ANDROID_BUILD_TOP/build/soong/soong_ui.bash --build-mode --"all-modules" --dir="${ANDROID_BUILD_TOP}/system/bt/gd" $BUILD_TARGET -j20
257        if [[ $? -ne 0 ]] ; then
258            echo "Failed to build ${BUILD_TARGET}"
259            exit 1
260        fi
261    fi
262}
263
264function setup_venv {
265    # Make venv in memory, decreases --clean build times by 40%
266    # Caveat is you lose the venv if the computer reboots
267    if [ "${USE_ASHMEM_VENV}" == true ] ; then
268        echo -e "${BLUE}Using ashmem virtual environment.${NOCOLOR}"
269        if [[ ! -L ${CERT_TEST_VENV} ]] ; then
270            echo -e "${BLUE}"
271            echo -ne "Creating ashmem dist folder..."
272            mkdir -p "${ASHMEM_VENV}"
273            # Ensure the directory doesn't exist
274            rm -rf "${CERT_TEST_VENV}"
275            echo -e "Done"
276            echo -ne "Sym linking ${ASHMEM_VENV} to ${CERT_TEST_VENV}..."
277            ln -s "${ASHMEM_VENV}" "${CERT_TEST_VENV}"
278            echo -e "Done"
279            echo -e "${NOCOLOR}"
280        fi
281    else
282        echo -e "${RED}Not using ashmem virtual environment.${NOCOLOR}"
283        if [[ -L ${CERT_TEST_VENV} ]] ; then
284            echo -e "${RED}"
285            echo -en "Removing sym link from ${ASHMEM_VENV} to ${CERT_TEST_VENV}..."
286            rm -rf ""${ASHMEM_VENV} "${CERT_TEST_VENV}"
287            echo -e "Done"
288            echo -en "Cleaning up memory..."
289            rm -rf "${ASHMEM_VENV}"
290            echo -e "Done"
291            echo -e "${NOCOLOR}"
292        fi
293    fi
294    python3.9 -m virtualenv --python `which python3.9` "${CERT_TEST_VENV}"
295    if [[ $? -ne 0 ]] ; then
296        echo "Error setting up virtualenv"
297        exit 1
298    fi
299
300    unzip -o -q "${ANDROID_BUILD_TOP}/out/dist/bluetooth_cert_tests.zip" -d "${CERT_TEST_VENV}/acts"
301    if [[ $? -ne 0 ]] ; then
302        echo "Error unzipping bluetooth_cert_tests.zip"
303        exit 1
304    fi
305
306    venv_common
307}
308
309function incremental_venv {
310#LINT.IfChange
311    HOST_BIN="${ANDROID_BUILD_TOP}/out/host/linux-x86/bin"
312    HOST_LIB="${ANDROID_BUILD_TOP}/out/host/linux-x86/lib64"
313    DEST_DIR="${ANDROID_BUILD_TOP}/out/dist/bluetooth_venv/acts"
314    DEST_LIB_DIR="${DEST_DIR}/lib64"
315    cp {$HOST_BIN,$DEST_DIR}/bluetooth_stack_with_facade
316    cp {$HOST_BIN,$DEST_DIR}/bluetooth_with_facades
317    cp {$HOST_BIN,$DEST_DIR}/root-canal
318
319    cp {$HOST_LIB,$DEST_DIR}/bluetooth_packets_python3.so
320
321    cp {$HOST_LIB,$DEST_LIB_DIR}/libbase.so
322    cp {$HOST_LIB,$DEST_LIB_DIR}/libbluetooth_gd.so
323    cp {$HOST_LIB,$DEST_LIB_DIR}/libc++.so
324    cp {$HOST_LIB,$DEST_LIB_DIR}/libchrome.so
325    cp {$HOST_LIB,$DEST_LIB_DIR}/libcrypto-host.so
326    cp {$HOST_LIB,$DEST_LIB_DIR}/libevent-host.so
327    cp {$HOST_LIB,$DEST_LIB_DIR}/libgrpc++_unsecure.so
328    cp {$HOST_LIB,$DEST_LIB_DIR}/libgrpc++.so
329    cp {$HOST_LIB,$DEST_LIB_DIR}/libgrpc_wrap.so
330    cp {$HOST_LIB,$DEST_LIB_DIR}/liblog.so
331    cp {$HOST_LIB,$DEST_LIB_DIR}/libssl-host.so
332    cp {$HOST_LIB,$DEST_LIB_DIR}/libz-host.so
333    cp {$HOST_LIB,$DEST_LIB_DIR}/libprotobuf-cpp-full.so
334    cp {$HOST_LIB,$DEST_LIB_DIR}/libunwindstack.so
335    cp {$HOST_LIB,$DEST_LIB_DIR}/liblzma.so
336    cp {$HOST_LIB,$DEST_LIB_DIR}/libbacktrace.so
337
338    for i in `find ${ANDROID_BUILD_TOP}/system/bt/gd -name "*.py" -type f`; do
339        cp {${ANDROID_BUILD_TOP}/system/bt/gd,$DEST_DIR}${i#${ANDROID_BUILD_TOP}/system/bt/gd}
340    done
341#LINT.ThenChange(../Android.mk)
342
343    venv_common
344}
345
346function venv_common {
347    $(echo "${CERT_TEST_VENV}/bin/python" "${CERT_TEST_VENV}/acts/setup.py" --quiet build --force)
348    if [[ $? -ne 0 ]] ; then
349        echo "Error building GD Python libraries"
350        echo -e "${YELLOW}NOTE:${NOCOLOR} To build external libraries the first time, please add --clean option."
351        exit 1
352    fi
353
354    $(echo "${CERT_TEST_VENV}/bin/python" "${CERT_TEST_VENV}/acts/setup.py" --quiet install --skip-build --force "${INSTALL_ARGS}")
355    if [[ $? -ne 0 ]] ; then
356        echo "Error installing GD Python libraries"
357        exit 1
358    fi
359
360"${CERT_TEST_VENV}/bin/python" -c "
361import bluetooth_packets_python3 as bp3
362bp3.BaseStruct
363"
364if [[ $? -ne 0 ]] ; then
365  echo "Setup failed as bluetooth_packets_python3 cannot be imported"
366  exit 1
367fi
368
369if [ "${VERBOSE_MODE}" == true ] ; then
370  TEMP_CONFIG=/tmp/temp_acts_config.json
371  cat "${TEST_CONFIG}" | "${CERT_TEST_VENV}/bin/python" -c "
372import sys
373import json
374from acts import keys
375config = json.load(sys.stdin)
376config['verbose_mode'] = True
377print(json.dumps(config))
378  " > "${TEMP_CONFIG}"
379  TEST_CONFIG="${TEMP_CONFIG}"
380  if [[ $? -ne 0 ]] ; then
381    echo "Setup failed as verbose mode is chosen but cannot be enabled"
382    exit 1
383  fi
384fi
385}
386
387function gotta_go_fast {
388    if [ "${GOTTA_GO_FAST}" == true ] ; then
389        # Call here to explicitly note the flag is in use
390        happy_hedgehog
391        if [[ ! -L "${CERT_HOST_LOGS}" ]] ; then
392            rm -rf "${CERT_HOST_LOGS}"
393            mkdir -p "${ASHMEM_HOST_LOGS}"
394            ln -s "${ASHMEM_HOST_LOGS}" "${CERT_HOST_LOGS}"
395        fi
396
397        if [[ ! -L "${OUT_TARGET}" ]] ; then
398            rm -rf "${OUT_TARGET}"
399            mkdir -p "${ASHMEM_OUT_TARGET}"
400            ln -s  "${ASHMEM_OUT_TARGET}" "${OUT_TARGET}"
401        fi
402    else
403        if [[ -L "${CERT_HOST_LOGS}" ]] ; then
404            # Call here so we don't spam anyone not using the flag
405            sad_hedgehog
406            rm -rf "${CERT_HOST_LOGS}"
407            rm -rf "${ASHMEM_HOST_LOGS}"
408        fi
409
410        if [[ -L "${OUT_TARGET}" ]] ; then
411            rm -rf "${OUT_TARGET}"
412            rm -rf "${ASHMEM_OUT_TARGET}"
413        fi
414    fi
415}
416
417function run_tests {
418    for n in $(seq "${NUM_REPETITIONS}"); do
419      $(echo "${CERT_TEST_VENV}/bin/python" "${CERT_TEST_VENV}/bin/act.py" \
420          -c "${TEST_CONFIG}" \
421          "${TEST_FILTER}" \
422          -tp "${CERT_TEST_VENV}"/acts)
423    done
424
425    if [ "${CLEAN_VENV}" != true ] ; then
426      echo -e "${YELLOW}NOTE:${NOCOLOR} Completed tests using existing external libraries in virtualenv."
427      echo -e "${YELLOW}NOTE:${NOCOLOR} To update external libraries, please add --clean option."
428    fi
429}
430
431function menu-adb() {
432    TMP=$(adb devices -l | grep -v "List of device" | awk '{ print $1 }')
433    # TODO(optedoblivion): If the device doesn't have a name (offline), it misnames them
434    NTMP=$(adb devices -l | grep -v "List of device" | awk '{ print $6 }' | cut -d ':' -f 2)
435    SERIALS=($TMP)
436    DEVICES=($NTMP)
437    LEN=${#SERIALS[@]}
438    result=0
439    if [ $LEN -lt 1 ]; then
440        echo "No devices connected!"
441        return 1
442    fi
443
444    if [ "$LEN" == "" ]; then
445        LEN=0
446    fi
447
448    answer=0
449
450    DEVICE_NAME="$1 device"
451
452    if [ $LEN -gt 1 ]; then
453        echo "+-------------------------------------------------+" 1>&2
454        echo "| Choose a ${DEVICE_NAME}:                         " 1>&2
455        echo "+-------------------------------------------------+" 1>&2
456        echo "|                                                 |" 1>&2
457        let fixed_len=$LEN-1
458        for i in `seq 0 $fixed_len`;
459        do
460            serial=${SERIALS[i]}
461            device=${DEVICES[i]}
462            echo "| $i) $serial $device" 1>&2
463            ## TODO[MSB]: Find character count, fill with space and ending box wall
464        done
465        echo "|                                                 |" 1>&2
466        echo "+-------------------------------------------------+" 1>&2
467        echo 1>&2
468        echo -n "Index number: " 1>&2
469        read answer
470    fi
471
472    if [ $answer -ge $LEN ]; then
473        echo
474        echo "Please choose a correct index!" 1>&2
475        echo
476        return 1
477    fi
478
479    SERIAL=${SERIALS[$answer]}
480    echo $SERIAL
481}
482
483function main {
484    check_environment
485    parse_options $@
486    select_devices
487    if [[ "${SKIP_SOONG_BUILD}" != true ]] ; then
488        soong_build
489    fi
490    if [ "$CLEAN_VENV" == true ] ; then
491        setup_venv
492    else
493        incremental_venv
494    fi
495    gotta_go_fast
496    run_tests
497}
498
499main $@
500