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