aosp12/system/core/bootstat/boot_reason_test.sh

1415 lines
43 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#! /bin/bash
#
# Bootstat boot reason tests
#
# throughout testing:
# - manual tests can only run on eng/userdebug builds
# - watch adb logcat -b all -d -s bootstat
# - watch adb logcat -b all -d | audit2allow
# - wait until screen is up, boot has completed, can mean wait for
# sys.boot_completed=1 and sys.bootstat.first_boot_completed=1 to be true
#
# All test frames, and nothing else, must be function names prefixed and
# specifiged with the pattern 'test_<test>() {' as this is also how the
# script discovers the full list of tests by inspecting its own code.
#
# Helper variables
SPACE=" "
ESCAPE=""
TAB=" "
GREEN="${ESCAPE}[38;5;40m"
RED="${ESCAPE}[38;5;196m"
NORMAL="${ESCAPE}[0m"
# Best guess to an average device's reboot time, refined as tests return
DURATION_DEFAULT=45
STOP_ON_FAILURE=false
progname="${0##*/}"
progpath="${0%${progname}}"
# Helper functions
[ "USAGE: inFastboot
Returns: true if device is in fastboot mode" ]
inFastboot() {
fastboot devices | grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
}
[ "USAGE: inAdb
Returns: true if device is in adb mode" ]
inAdb() {
adb devices | grep -v 'List of devices attached' | grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
}
[ "USAGE: adb_sh <commands> </dev/stdin >/dev/stdout 2>/dev/stderr
Returns: true if the command succeeded" ]
adb_sh() {
local args=
for i in "${@}"; do
[ -z "${args}" ] || args="${args} "
if [ X"${i}" != X"${i#\'}" ]; then
args="${args}${i}"
elif [ X"${i}" != X"${i#*\\}" ]; then
args="${args}`echo ${i} | sed 's/\\\\/\\\\\\\\/g'`"
elif [ X"${i}" != X"${i#* }" ]; then
args="${args}'${i}'"
elif [ X"${i}" != X"${i#*${TAB}}" ]; then
args="${args}'${i}'"
else
args="${args}${i}"
fi
done
adb shell "${args}"
}
[ "USAGE: adb_su <commands> </dev/stdin >/dev/stdout 2>/dev/stderr
Returns: true if the command running as root succeeded" ]
adb_su() {
adb_sh su root "${@}"
}
[ "USAGE: hasPstore
Returns: true if device (likely) has pstore data" ]
hasPstore() {
if inAdb && [ 0 -eq `adb_su ls /sys/fs/pstore </dev/null | wc -l` ]; then
false
fi
}
[ "USAGE: get_property <prop>
Returns the property value" ]
get_property() {
adb_sh getprop ${1} 2>&1 </dev/null
}
[ "USAGE: isDebuggable
Returns: true if device is (likely) a debug build" ]
isDebuggable() {
if inAdb && [ 1 -ne `get_property ro.debuggable` ]; then
false
fi
}
[ "USAGE: checkDebugBuild [--noerror]
Returns: true if device is a userdebug or eng release" ]
checkDebugBuild() {
if isDebuggable; then
echo "INFO: '${TEST}' test requires userdebug build"
elif [ -n "${1}" ]; then
echo "WARNING: '${TEST}' test requires userdebug build"
false
else
echo "ERROR: '${TEST}' test requires userdebug build, skipping FAILURE"
duration_prefix="~"
duration_estimate=1
false
fi >&2
}
[ "USAGE: setBootloaderBootReason [value]
Returns: true if device supports and set boot reason injection" ]
setBootloaderBootReason() {
inAdb || ( echo "ERROR: device not in adb mode." >&2 ; false ) || return 1
if [ -z "`adb_sh ls /etc/init/bootstat-debug.rc 2>/dev/null </dev/null`" ]; then
echo "ERROR: '${TEST}' test requires /etc/init/bootstat-debug.rc" >&2
return 1
fi
checkDebugBuild || return 1
if adb_su "cat /proc/cmdline | tr '\\0 ' '\\n\\n'" </dev/null |
grep '^androidboot[.]bootreason=[^ ]' >/dev/null; then
echo "ERROR: '${TEST}' test requires a device with a bootloader that" >&2
echo " does not set androidboot.bootreason kernel parameter." >&2
return 1
fi
adb_su setprop persist.test.boot.reason "'${1}'" 2>/dev/null </dev/null
test_reason="`get_property persist.test.boot.reason`"
if [ X"${test_reason}" != X"${1}" ]; then
echo "ERROR: can not set persist.test.boot.reason to '${1}'." >&2
return 1
fi
}
[ "USAGE: enterPstore
Prints a warning string requiring functional pstore
Returns: pstore_ok variable set to true or false" ]
enterPstore() {
if hasPstore; then
echo "INFO: '${TEST}' test requires functional and reliable pstore"
pstore_ok=true
else
echo "WARNING: '${TEST}' test requires functional pstore"
pstore_ok=false
fi >&2
${pstore_ok}
}
[ "USAGE: exitPstore
Prints an error string requiring functional pstore
Returns: clears error if pstore dysfunctional" ]
exitPstore() {
save_ret=${?}
if [ ${save_ret} != 0 ]; then
if hasPstore; then
return ${save_ret}
fi
if [ true = ${pstore_ok} ]; then
echo "WARNING: '${TEST}' test requires functional pstore"
return ${save_ret}
fi
echo "ERROR: '${TEST}' test requires functional pstore, skipping FAILURE"
duration_prefix="~"
duration_estimate=1
fi >&2
}
[ "USAGE: format_duration <seconds>
human readable output whole seconds, whole minutes or mm:ss" ]
format_duration() {
if [ -z "${1}" ]; then
echo unknown
return
fi
seconds=`expr ${1} % 60`
minutes=`expr ${1} / 60`
if [ 0 -eq ${minutes} ]; then
if [ 1 -eq ${1} ]; then
echo 1 second
return
fi
echo ${1} seconds
return
elif [ 60 -eq ${1} ]; then
echo 1 minute
return
elif [ 0 -eq ${seconds} ]; then
echo ${minutes} minutes
return
fi
echo ${minutes}:`expr ${seconds} / 10``expr ${seconds} % 10`
}
wait_for_screen_timeout=900
[ "USAGE: wait_for_screen [-n] [TIMEOUT]
-n - echo newline at exit
TIMEOUT - default `format_duration ${wait_for_screen_timeout}`" ]
wait_for_screen() {
exit_function=true
if [ X"-n" = X"${1}" ]; then
exit_function=echo
shift
fi
timeout=${wait_for_screen_timeout}
if [ ${#} -gt 0 ]; then
timeout=${1}
shift
fi
counter=0
while true; do
if inFastboot; then
fastboot reboot
elif inAdb; then
if [ 0 != ${counter} ]; then
adb wait-for-device </dev/null >/dev/null 2>/dev/null
fi
if [ -n "`get_property sys.boot.reason`" ]
then
vals=`get_property |
sed -n 's/[[]sys[.]\(boot_completed\|logbootcomplete\|bootstat[.]first_boot_completed\)[]]: [[]\([01]\)[]]$/\1=\2/p'`
if [ X"${vals}" != X"${vals##*boot_completed=1}" ]; then
if [ X"${vals}" != X"${vals##*logbootcomple=1}" ]; then
sleep 1
break
fi
if [ X"${vals}" != X"${vals##*bootstat.first_boot_completed=1}" ]; then
sleep 1
break
fi
fi
fi
fi
counter=`expr ${counter} + 1`
if [ ${counter} -gt ${timeout} ]; then
${exit_function}
echo "ERROR: wait_for_screen() timed out (`format_duration ${timeout}`)" >&2
return 1
fi
sleep 1
done
${exit_function}
}
[ "USAGE: EXPECT_EQ <lval> <rval> [message]
Returns true if (regex) lval matches rval" ]
EXPECT_EQ() {
lval="${1}"
rval="${2}"
shift 2
if ! ( echo X"${rval}" | grep '^X'"${lval}"'$' >/dev/null 2>/dev/null ); then
if [ `echo ${lval}${rval}${*} | wc -c` -gt 50 -o "${rval}" != "${rval%
*}" ]; then
echo "ERROR: expected \"${lval}\"" >&2
echo " got \"${rval}\"" |
sed ': again
N
s/\(\n\)\([^ ]\)/\1 \2/
t again' >&2
if [ -n "${*}" ] ; then
echo " ${*}" >&2
fi
else
echo "ERROR: expected \"${lval}\" got \"${rval}\" ${*}" >&2
fi
return 1
fi
if [ -n "${*}" ] ; then
if [ X"${lval}" != X"${rval}" ]; then
if [ `echo ${lval}${rval}${*} | wc -c` -gt 60 -o "${rval}" != "${rval%
*}" ]; then
echo "INFO: ok \"${lval}\"" >&2
echo " = \"${rval}\"" |
sed ': again
N
s/\(\n\)\([^ ]\)/\1 \2/
t again' >&2
if [ -n "${*}" ] ; then
echo " ${*}" >&2
fi
else
echo "INFO: ok \"${lval}\" = \"${rval}\" ${*}" >&2
fi
else
echo "INFO: ok \"${lval}\" ${*}" >&2
fi
fi
return 0
}
BAD_BOOTLOADER_REASON=
[ "USAGE: EXPECT_PROPERTY <prop> <value> [--allow_failure]
Returns true (0) if current return (regex) value is true and the result matches
and the incoming return value is true as well (wired-or)" ]
EXPECT_PROPERTY() {
save_ret=${?}
property="${1}"
value="${2}"
shift 2
val=`get_property ${property}`
EXPECT_EQ "${value}" "${val}" for Android property ${property}
local_ret=${?}
if [ 0 != ${local_ret} -a "ro.boot.bootreason" = "${property}" ]; then
if [ -z "${BAD_BOOTLOADER_REASON}" ]; then
BAD_BOOTLOADER_REASON=${val}
elif [ X"${BAD_BOOTLOADER_REASON}" = X"${val}" ]; then
local_ret=0
fi
fi
if [ 0 != ${local_ret} ]; then
if [ -z "${1}" ] ; then
save_ret=${local_ret}
fi
fi
return ${save_ret}
}
[ "USAGE: adb_date >/dev/stdout
Returns: report device epoch time (suitable for logcat -t)" ]
adb_date() {
adb_sh date +%s.%N </dev/null
}
[ "USAGE: report_bootstat_logs [-t<timestamp>] <expected> ...
if not prefixed with a minus (-), <expected> will become a series of expected
matches:
bootstat: Canonical boot reason: <expected_property_value>
If prefixed with a minus, <expected> will look for an exact match after
removing the minux prefix. All expected content is _dropped_ from the output
and in essence forms a known blacklist, unexpected content will show.
Report any logs, minus a known blacklist, preserve the current exit status" ]
report_bootstat_logs() {
save_ret=${?}
match=
timestamp=-d
for i in "${@}"; do
if [ X"${i}" != X"${i#-t}" ]; then
timestamp="${i}"
elif [ X"${i}" != X"${i#-}" ]; then
match="${match}
${i#-}"
else
match="${match}
bootstat: Canonical boot reason: ${i}"
fi
done
adb logcat -b all ${timestamp} |
grep bootstat[^e] |
grep -v -F "bootstat: Service started: /system/bin/bootstat --record_boot_complete${match}
bootstat: Failed to read /data/misc/bootstat/post_decrypt_time_elapsed: No such file or directory
bootstat: Failed to parse boot time record: /data/misc/bootstat/post_decrypt_time_elapsed
bootstat: Service started: /system/bin/bootstat --record_boot_reason
bootstat: Service started: /system/bin/bootstat --set_system_boot_reason
bootstat: Service started: /system/bin/bootstat --record_time_since_factory_reset
bootstat: Service started: /system/bin/bootstat -l
bootstat: Service started: /system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l
bootstat: Battery level at shutdown 100%
bootstat: Battery level at startup 100%
init : Parsing file /system/etc/init/bootstat.rc...
init : Parsing file /system/etc/init/bootstat-debug.rc...
init : processing action (persist.test.boot.reason=*) from (/system/etc/init/bootstat-debug.rc:
init : Command 'setprop ro.boot.bootreason \${persist.test.boot.reason}' action=persist.test.boot.reason=* (/system/etc/init/bootstat-debug.rc:
init : processing action (post-fs-data) from (/system/etc/init/bootstat.rc
init : processing action (boot) from (/system/etc/init/bootstat.rc
init : processing action (ro.boot.bootreason=*) from (/system/etc/init/bootstat.rc
init : processing action (ro.boot.bootreason=* && post-fs) from (/system/etc/init/bootstat.rc
init : processing action (sys.bootstat.first_zygote_start=0 && zygote-start) from (/system/etc/init/bootstat.rc
init : processing action (sys.boot_completed=1 && sys.bootstat.first_boot_completed=0) from (/system/etc/init/bootstat.rc
(/system/bin/bootstat --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l)'
(/system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l)'
(/system/bin/bootstat -r post_decrypt_time_elapsed)'
init : Command 'exec - system log -- /system/bin/bootstat --record_boot_complete' action=sys.boot_completed=1 && sys.bootstat.first_boot_completed=0 (/system/etc/init/bootstat.rc:
init : Command 'exec - system log -- /system/bin/bootstat --record_boot_reason' action=sys.boot_completed=1 && sys.bootstat.first_boot_completed=0 (/system/etc/init/bootstat.rc:
init : Command 'exec - system log -- /system/bin/bootstat --record_time_since_factory_reset' action=sys.boot_completed=1 && sys.bootstat.first_boot_completed=0 (/system/etc/init/bootstat.rc:
init : Command 'exec_background - system log -- /system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l' action=sys.boot_completed=1 && sys.bootstat.first_boot_completed=0 (/system/etc/init/bootstat.rc
(/system/bin/bootstat --record_boot_complete)'...
(/system/bin/bootstat --record_boot_complete)' (pid${SPACE}
(/system/bin/bootstat --record_boot_reason)'...
(/system/bin/bootstat --record_boot_reason)' (pid${SPACE}
(/system/bin/bootstat --record_time_since_factory_reset)'...
(/system/bin/bootstat --record_time_since_factory_reset)' (pid${SPACE}
(/system/bin/bootstat --set_system_boot_reason)'...
(/system/bin/bootstat --set_system_boot_reason)' (pid${SPACE}
(/system/bin/bootstat -l)'...
(/system/bin/bootstat -l)' (pid " |
grep -v 'bootstat: Unknown boot reason: $' # Hikey Special
return ${save_ret}
}
[ "USAGE: start_test [message]
Record start of test, preserve exit status" ]
start_test() {
save_ret=${?}
duration_prefix="~"
duration_estimate=1
START=`date +%s`
echo "${GREEN}[ RUN ]${NORMAL} ${TEST} ${*}"
return ${save_ret}
}
duration_sum_diff=0
duration_num=0
[ "USAGE: duration_test [[prefix]seconds]
Report the adjusted and expected test duration" ]
duration_test() {
duration_prefix=${1%%[0123456789]*}
if [ -z "${duration_prefix}" ]; then
duration_prefix="~"
fi
duration_estimate="${1#${duration_prefix}}"
if [ -z "${duration_estimate}" ]; then
duration_estimate="${DURATION_DEFAULT}"
fi
duration_new_estimate="${duration_estimate}"
if [ 0 -ne ${duration_num} ]; then
duration_new_estimate=`expr ${duration_new_estimate} + \
\( ${duration_num} / 2 + ${duration_sum_diff} \) / ${duration_num}`
# guard against catastrophe
if [ -z "${duration_new_estimate}" ]; then
duration_new_estimate=${duration_estimate}
fi
fi
# negative values are so undignified
if [ 0 -ge ${duration_new_estimate} ]; then
duration_new_estimate=1
fi
echo "INFO: expected duration of '${TEST}' test" \
"${duration_prefix}`format_duration ${duration_new_estimate}`" >&2
}
[ "USAGE: end_test [message]
Document duration and success of test, preserve exit status" ]
end_test() {
save_ret=${?}
END=`date +%s`
duration=`expr ${END} - ${START} 2>/dev/null`
[ 0 -ge ${duration} ] ||
echo "INFO: '${TEST}' test duration `format_duration ${duration}`" >&2
if [ ${save_ret} = 0 ]; then
if [ 0 -lt ${duration} -a 0 -lt ${duration_estimate} -a \( \
X"~" = X"${duration_prefix}" -o \
${duration_estimate} -gt ${duration} \) ]; then
duration_sum_diff=`expr ${duration_sum_diff} + \
${duration} - ${duration_estimate}`
duration_num=`expr ${duration_num} + 1`
fi
echo "${GREEN}[ OK ]${NORMAL} ${TEST} ${*}"
else
echo "${RED}[ FAILED ]${NORMAL} ${TEST} ${*}"
if ${STOP_ON_FAILURE}; then
exit ${save_ret}
fi
fi
return ${save_ret}
}
[ "USAGE: wrap_test <test> [message]
All tests below are wrapped with this helper" ]
wrap_test() {
if [ -z "${1}" -o X"nothing" = X"${1}" ]; then
return
fi
TEST=${1}
shift
start_test ${1}
eval test_${TEST}
end_test ${2}
}
[ "USAGE: validate_reason <value>
Check property for CTS compliance with our expectations. Return a cleansed
string representing what is acceptable.
NB: must also roughly match heuristics in system/core/bootstat/bootstat.cpp" ]
validate_reason() {
var=`echo -n ${*} |
tr '[A-Z]' '[a-z]' |
tr ' \f\t\r\n' '_____'`
case ${var} in
watchdog | watchdog,?* ) ;;
kernel_panic | kernel_panic,?* ) ;;
recovery | recovery,?* ) ;;
bootloader | bootloader,?* ) ;;
cold | cold,?* ) ;;
hard | hard,?* ) ;;
warm | warm,?* ) ;;
shutdown | shutdown,?* ) ;;
reboot,reboot | reboot,reboot,* ) var=${var#reboot,} ; var=${var%,} ;;
reboot,cold | reboot,cold,* ) var=${var#reboot,} ; var=${var%,} ;;
reboot,hard | reboot,hard,* ) var=${var#reboot,} ; var=${var%,} ;;
reboot,warm | reboot,warm,* ) var=${var#reboot,} ; var=${var%,} ;;
reboot,recovery | reboot,recovery,* ) var=${var#reboot,} ; var=${var%,} ;;
reboot,bootloader | reboot,bootloader,* ) var=${var#reboot,} ; var=${var%,} ;;
reboot | reboot,?* ) ;;
# Aliases and Heuristics
*wdog* | *watchdog* ) var="watchdog" ;;
*powerkey* | *power_key* | *PowerKey* ) var="cold,powerkey" ;;
*panic* | *kernel_panic* ) var="kernel_panic" ;;
*thermal* ) var="shutdown,thermal" ;;
*s3_wakeup* ) var="warm,s3_wakeup" ;;
*hw_reset* ) var="hard,hw_reset" ;;
*usb* ) var="cold,charger" ;;
*rtc* ) var="cold,rtc" ;;
*2sec_reboot* ) var="cold,rtc,2sec" ;;
*wdt_by_pass_pwk* ) var="warm" ;;
wdt ) var="reboot" ;;
*tool_by_pass_pwk* ) var="reboot,tool" ;;
*bootloader* ) var="bootloader" ;;
* ) var="reboot" ;;
esac
echo ${var}
}
[ "USAGE: validate_property <property>
Check property for CTS compliance with our expectations. Return a cleansed
string representing what is acceptable.
NB: must also roughly match heuristics in system/core/bootstat/bootstat.cpp" ]
validate_property() {
val=`get_property ${1}`
ret=`validate_reason "${val}"`
if [ "reboot" = "${ret}" ]; then
ret=`validate_reason "reboot,${val}"`
fi
echo ${ret}
}
[ "USAGE: check_boilerblate_properties
Check for common property values" ]
check_boilerplate_properties() {
EXPECT_PROPERTY persist.sys.boot.reason ""
save_ret=${?}
reason=`validate_property sys.boot.reason`
( exit ${save_ret} ) # because one can not just do ?=${save_ret}
EXPECT_PROPERTY persist.sys.boot.reason.history "${reason},[1-9][0-9]*\(\|[^0-9].*\)"
}
#
# Actual test frames
#
[ "USAGE: test_properties
properties test
- (wait until screen is up, boot has completed)
- adb shell getprop ro.boot.bootreason (bootloader reason)
- adb shell getprop persist.sys.boot.reason (last reason)
- adb shell getprop sys.boot.reason.last (last last reason)
- adb shell getprop sys.boot.reason (system reason)
- NB: all should have a value that is compliant with our known set." ]
test_properties() {
duration_test 1
wait_for_screen
retval=0
# sys.boot.reason is last for a reason
check_set="ro.boot.bootreason sys.boot.reason.last sys.boot.reason"
bootloader=""
# NB: this test could fail if performed _after_ optional_factory_reset test
# and will report
# ERROR: expected "reboot" got ""
# for Android property sys.boot.reason.last
# following is mitigation for the persist.sys.boot.reason, skip it
if [ "reboot,factory_reset" = "`validate_property ro.boot_bootreason`" ]; then
check_set="ro.boot.bootreason sys.boot.reason"
bootloader="bootloader"
fi
for prop in ${check_set}; do
reason=`validate_property ${prop}`
EXPECT_PROPERTY ${prop} ${reason} || retval=${?}
done
check_boilerplate_properties || retval=${?}
report_bootstat_logs ${reason} ${bootloader}
return ${retval}
}
[ "USAGE: test_ota
ota test
- rm out/.kati_stamp-* out/build_date.txt out/build_number.txt
- rm out/target/product/*/*/*.prop
- rm -r out/target/product/*/obj/ETC/system_build_prop_intermediates
- m
- NB: ro.build.date.utc should update
- fastboot flashall
- (wait until screen is up, boot has completed)
- adb shell getprop sys.boot.reason
- NB: should report ota
Decision to change the build itself rather than trick bootstat by
rummaging through its data files was made." ]
test_ota() {
duration_test ">300"
echo " extended by build and flashing times" >&2
if [ -z "${TARGET_PRODUCT}" -o \
-z "${ANDROID_PRODUCT_OUT}" -o \
-z "${ANDROID_BUILD_TOP}" -o \
-z "${TARGET_BUILD_VARIANT}" ]; then
echo "ERROR: Missing envsetup.sh and lunch" >&2
return 1
fi
rm ${ANDROID_PRODUCT_OUT%/out/*}/out/.kati_stamp-* ||
true
rm ${ANDROID_PRODUCT_OUT%/out/*}/out/build_date.txt ||
true
rm ${ANDROID_PRODUCT_OUT%/out/*}/out/build_number.txt ||
true
rm ${ANDROID_PRODUCT_OUT}/*/*.prop ||
true
rm -r ${ANDROID_PRODUCT_OUT}/obj/ETC/system_build_prop_intermediates ||
true
pushd ${ANDROID_BUILD_TOP} >&2
build/soong/soong_ui.bash --make-mode >&2
if [ ${?} != 0 ]; then
popd >&2
return 1
fi
if ! inFastboot; then
adb reboot-bootloader >&2
fi
fastboot flashall >&2
popd >&2
wait_for_screen
EXPECT_PROPERTY sys.boot.reason "\(reboot,ota\|bootloader\)"
EXPECT_PROPERTY sys.boot.reason.last bootloader
check_boilerplate_properties
report_bootstat_logs reboot,ota bootloader
}
[ "USAGE: test_optional_ota
fast and fake (touch build_date on device to make it different)" ]
test_optional_ota() {
checkDebugBuild || return
duration_test
adb_su touch /data/misc/bootstat/build_date >&2 </dev/null
adb reboot ota
wait_for_screen
EXPECT_PROPERTY sys.boot.reason reboot,ota
EXPECT_PROPERTY sys.boot.reason.last reboot,ota
check_boilerplate_properties
report_bootstat_logs reboot,ota
}
[ "USAGE: [TEST=<test>] blind_reboot_test
Simple tests helper
- adb reboot <test>
- (wait until screen is up, boot has completed)
- adb shell getprop sys.boot.reason
- NB: should report <test>, or reboot,<test> depending on canonical rules
We interleave the simple reboot tests between the hard/complex ones
as a means of checking sanity and any persistent side effect of the
other tests." ]
blind_reboot_test() {
duration_test
case ${TEST} in
bootloader | recovery | cold | hard | warm ) reason=${TEST} ;;
*) reason=reboot,${TEST#optional_} ;;
esac
adb reboot ${TEST#optional_}
wait_for_screen
bootloader_reason=`validate_property ro.boot.bootreason`
EXPECT_PROPERTY ro.boot.bootreason ${bootloader_reason}
# to make sys.boot.reason report user friendly
reasons=${reason}
if [ "${bootloader_reason}" != "${reason}" -a -n "${bootloader_reason}" ]; then
reasons="\(${reason}\|${bootloader_reason}\)"
fi
EXPECT_PROPERTY sys.boot.reason ${reasons}
EXPECT_PROPERTY sys.boot.reason.last ${reason}
check_boilerplate_properties
report_bootstat_logs ${reason} ${bootloader_reason}
}
[ "USAGE: test_cold
cold test
- adb reboot cold
- (wait until screen is up, boot has completed)
- adb shell getprop sys.boot.reason
- NB: should report cold" ]
test_cold() {
blind_reboot_test
}
[ "USAGE: test_factory_reset
factory_reset test
- adb shell su root rm /data/misc/bootstat/build_date
- adb reboot
- (wait until screen is up, boot has completed)
- adb shell getprop sys.boot.reason
- NB: should report factory_reset
Decision to rummage through bootstat data files was made as
a _real_ factory_reset is too destructive to the device." ]
test_factory_reset() {
checkDebugBuild || return
duration_test
adb_su rm /data/misc/bootstat/build_date >&2 </dev/null
adb reboot >&2
wait_for_screen
EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
EXPECT_PROPERTY sys.boot.reason.last "reboot,.*"
check_boilerplate_properties
report_bootstat_logs reboot,factory_reset reboot, reboot,adb \
"-bootstat: Failed to read /data/misc/bootstat/build_date: No such file or directory" \
"-bootstat: Failed to parse boot time record: /data/misc/bootstat/build_date"
}
[ "USAGE: test_optional_factory_reset
factory_reset test
- adb reboot-bootloader
- fastboot format userdata
- fastboot reboot
- (wait until screen is up, boot has completed)
- adb shell getprop sys.boot.reason
- NB: should report factory_reset
For realz, and disruptive" ]
test_optional_factory_reset() {
duration_test 60
if ! inFastboot; then
adb reboot-bootloader
fi
fastboot format userdata >&2
save_ret=${?}
if [ 0 != ${save_ret} ]; then
echo "ERROR: fastboot can not format userdata" >&2
fi
fastboot reboot >&2
wait_for_screen
( exit ${save_ret} ) # because one can not just do ?=${save_ret}
EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
EXPECT_PROPERTY sys.boot.reason.last "\(\|bootloader\)"
check_boilerplate_properties
report_bootstat_logs reboot,factory_reset bootloader \
"-bootstat: Failed to read /data/misc/bootstat/last_boot_time_utc: No such file or directory" \
"-bootstat: Failed to parse boot time record: /data/misc/bootstat/last_boot_time_utc" \
"-bootstat: Failed to read /data/misc/bootstat/build_date: No such file or directory" \
"-bootstat: Failed to parse boot time record: /data/misc/bootstat/build_date" \
"-bootstat: Failed to read /data/misc/bootstat/factory_reset: No such file or directory" \
"-bootstat: Failed to parse boot time record: /data/misc/bootstat/factory_reset"
}
[ "USAGE: test_hard
hard test:
- adb reboot hard
- (wait until screen is up, boot has completed)
- adb shell getprop sys.boot.reason
- NB: should report hard" ]
test_hard() {
blind_reboot_test
}
[ "USAGE: test_battery
battery test (trick):
- echo healthd: battery l=2<space> | adb shell su root tee /dev/kmsg
- adb reboot cold
- (wait until screen is up, boot has completed)
- adb shell getprop sys.boot.reason
- NB: should report reboot,battery, unless healthd managed to log
before reboot in above trick.
- Bonus points (manual extras)
- Make sure the following is added to the /init.rc file in post-fs
section before logd is started:
+ setprop logd.kernel false
+ rm /sys/fs/pstore/console-ramoops
+ rm /sys/fs/pstore/console-ramoops-0
+ write /dev/kmsg \"healthd: battery l=2${SPACE}
+\"
- adb reboot fs
- (wait until screen is up, boot has completed)
- adb shell getprop sys.boot.reason
- NB: should report reboot,battery
- (replace set logd.kernel true to the above, and retry test)" ]
test_battery() {
checkDebugBuild || return
duration_test 120
enterPstore
# Send it _many_ times to combat devices with flakey pstore
for i in a b c d e f g h i j k l m n o p q r s t u v w x y z; do
echo 'healthd: battery l=2 ' | adb_su tee /dev/kmsg >/dev/null
done
adb reboot cold >&2
adb wait-for-device
wait_for_screen
adb_su </dev/null \
cat /proc/fs/pstore/console-ramoops \
/proc/fs/pstore/console-ramoops-0 2>/dev/null |
grep 'healthd: battery l=' |
tail -1 |
grep 'healthd: battery l=2 ' >/dev/null || (
if ! EXPECT_PROPERTY sys.boot.reason reboot,battery >/dev/null 2>/dev/null; then
# retry
for i in a b c d e f g h i j k l m n o p q r s t u v w x y z; do
echo 'healthd: battery l=2 ' | adb_su tee /dev/kmsg >/dev/null
done
adb reboot cold >&2
adb wait-for-device
wait_for_screen
fi
)
EXPECT_PROPERTY sys.boot.reason shutdown,battery
EXPECT_PROPERTY sys.boot.reason.last cold
check_boilerplate_properties
report_bootstat_logs shutdown,battery "-bootstat: Battery level at shutdown 2%"
exitPstore
}
[ "USAGE: test_optional_battery
battery shutdown test:
- adb shell setprop sys.powerctl shutdown,battery
- (power up the device)
- (wait until screen is up, boot has completed)
- adb shell getprop sys.boot.reason
- NB: should report shutdown,battery" ]
test_optional_battery() {
duration_test ">60"
echo " power on request" >&2
adb_sh setprop sys.powerctl shutdown,battery </dev/null
sleep 5
echo -n "WARNING: Please power device back up, waiting ... " >&2
wait_for_screen -n >&2
EXPECT_PROPERTY sys.boot.reason shutdown,battery
EXPECT_PROPERTY sys.boot.reason.last shutdown,battery
check_boilerplate_properties
report_bootstat_logs shutdown,battery
}
[ "USAGE: test_optional_battery_thermal
battery thermal shutdown test:
- adb shell setprop sys.powerctl shutdown,thermal,battery
- (power up the device)
- (wait until screen is up, boot has completed)
- adb shell getprop sys.boot.reason
- NB: should report shutdown,thermal,battery" ]
test_optional_battery_thermal() {
duration_test ">60"
echo " power on request" >&2
adb_sh setprop sys.powerctl shutdown,thermal,battery </dev/null
sleep 5
echo -n "WARNING: Please power device back up, waiting ... " >&2
wait_for_screen -n >&2
EXPECT_PROPERTY sys.boot.reason shutdown,thermal,battery
EXPECT_PROPERTY sys.boot.reason.last shutdown,thermal,battery
check_boilerplate_properties
report_bootstat_logs shutdown,thermal,battery
}
[ "USAGE: test_unknown
unknown test
- adb reboot unknown
- (wait until screen is up, boot has completed)
- adb shell getprop sys.boot.reason
- NB: should report reboot,unknown
- NB: expect log \"... I bootstat: Unknown boot reason: reboot,unknown\"" ]
test_unknown() {
blind_reboot_test
}
[ "USAGE: test_kernel_panic
kernel_panic test:
- echo c | adb shell su root tee /proc/sysrq-trigger
- (wait until screen is up, boot has completed)
- adb shell getprop sys.boot.reason
- NB: should report kernel_panic,sysrq" ]
test_kernel_panic() {
checkDebugBuild || return
duration_test ">90"
panic_msg="kernel_panic,sysrq"
enterPstore
if [ ${?} != 0 ]; then
echo " or functional bootloader" >&2
panic_msg="\(kernel_panic,sysrq\|kernel_panic\)"
pstore_ok=true
fi
echo c | adb_su tee /proc/sysrq-trigger >/dev/null
wait_for_screen
EXPECT_PROPERTY sys.boot.reason ${panic_msg}
EXPECT_PROPERTY sys.boot.reason.last ${panic_msg}
check_boilerplate_properties
report_bootstat_logs kernel_panic,sysrq
exitPstore
}
[ "USAGE: test_kernel_panic_subreason
kernel_panic_subreason test:
- echo SysRq : Trigger a crash : 'test' | adb shell su root tee /dev/kmsg
- echo c | adb shell su root tee /proc/sysrq-trigger
- (wait until screen is up, boot has completed)
- adb shell getprop sys.boot.reason
- NB: should report kernel_panic,sysrq,test" ]
test_kernel_panic_subreason() {
checkDebugBuild || return
duration_test ">90"
panic_msg="kernel_panic,sysrq,test"
enterPstore
if [ ${?} != 0 ]; then
echo " or functional bootloader" >&2
panic_msg="\(kernel_panic,sysrq,test\|kernel_panic\)"
pstore_ok=true
fi
echo "SysRq : Trigger a crash : 'test'" | adb_su tee /dev/kmsg
echo c | adb_su tee /proc/sysrq-trigger >/dev/null
wait_for_screen
EXPECT_PROPERTY sys.boot.reason ${panic_msg}
EXPECT_PROPERTY sys.boot.reason.last ${panic_msg}
check_boilerplate_properties
report_bootstat_logs kernel_panic,sysrq,test \
"-bootstat: Unknown boot reason: kernel_panic,sysrq,test"
exitPstore
}
[ "USAGE: test_kernel_panic_hung
kernel_panic_hung test:
- echo Kernel panic - not synching: hung_task: blocked tasks |
adb shell su root tee /dev/kmsg
- adb reboot warm
- (wait until screen is up, boot has completed)
- adb shell getprop sys.boot.reason
- NB: should report kernel_panic,hung" ]
test_kernel_panic_hung() {
checkDebugBuild || return
duration_test
panic_msg="kernel_panic,hung"
enterPstore
if [ ${?} != 0 ]; then
echo " or functional bootloader" >&2
panic_msg="\(kernel_panic,hung\|reboot,hung\)"
pstore_ok=true
fi
echo "Kernel panic - not syncing: hung_task: blocked tasks" |
adb_su tee /dev/kmsg
adb reboot warm
wait_for_screen
EXPECT_PROPERTY sys.boot.reason ${panic_msg}
EXPECT_PROPERTY sys.boot.reason.last ${panic_msg}
check_boilerplate_properties
report_bootstat_logs kernel_panic,hung
exitPstore
}
[ "USAGE: test_warm
warm test
- adb reboot warm
- (wait until screen is up, boot has completed)
- adb shell getprop sys.boot.reason
- NB: should report warm" ]
test_warm() {
blind_reboot_test
}
[ "USAGE: test_thermal_shutdown
thermal shutdown test:
- adb shell setprop sys.powerctl shutdown,thermal
- (power up the device)
- (wait until screen is up, boot has completed)
- adb shell getprop sys.boot.reason
- NB: should report shutdown,thermal" ]
test_thermal_shutdown() {
duration_test ">60"
echo " power on request" >&2
adb_sh setprop sys.powerctl shutdown,thermal </dev/null
sleep 5
echo -n "WARNING: Please power device back up, waiting ... " >&2
wait_for_screen -n >&2
EXPECT_PROPERTY sys.boot.reason shutdown,thermal
EXPECT_PROPERTY sys.boot.reason.last shutdown,thermal
check_boilerplate_properties
report_bootstat_logs shutdown,thermal
}
[ "USAGE: test_userrequested_shutdown
userrequested shutdown test:
- adb shell setprop sys.powerctl shutdown,userrequested
- (power up the device)
- (wait until screen is up, boot has completed)
- adb shell getprop sys.boot.reason
- NB: should report shutdown,userrequested" ]
test_userrequested_shutdown() {
duration_test ">60"
echo " power on request" >&2
adb_sh setprop sys.powerctl shutdown,userrequested </dev/null
sleep 5
echo -n "WARNING: Please power device back up, waiting ... " >&2
wait_for_screen -n >&2
EXPECT_PROPERTY sys.boot.reason shutdown,userrequested
EXPECT_PROPERTY sys.boot.reason.last shutdown,userrequested
check_boilerplate_properties
report_bootstat_logs shutdown,userrequested
}
[ "USAGE: test_shell_reboot
shell reboot test:
- adb shell reboot
- (wait until screen is up, boot has completed)
- adb shell getprop sys.boot.reason
- NB: should report reboot,shell" ]
test_shell_reboot() {
duration_test
adb_sh reboot </dev/null
wait_for_screen
EXPECT_PROPERTY sys.boot.reason reboot,shell
EXPECT_PROPERTY sys.boot.reason.last reboot,shell
check_boilerplate_properties
report_bootstat_logs reboot,shell
}
[ "USAGE: test_adb_reboot
adb reboot test:
- adb reboot
- (wait until screen is up, boot has completed)
- adb shell getprop sys.boot.reason
- NB: should report reboot,adb" ]
test_adb_reboot() {
duration_test
adb reboot
wait_for_screen
EXPECT_PROPERTY sys.boot.reason reboot,adb
EXPECT_PROPERTY sys.boot.reason.last reboot,adb
check_boilerplate_properties
report_bootstat_logs reboot,adb
}
[ "USAGE: test_rescueparty
rescueparty test
- adb reboot rescueparty
- (wait until screen is up, boot has completed)
- adb shell getprop sys.boot.reason
- adb shell getprop ro.boot.bootreason
- NB: should report reboot,rescueparty" ]
test_optional_rescueparty() {
blind_reboot_test
echo "WARNING: legacy devices are allowed to fail following ro.boot.bootreason result" >&2
EXPECT_PROPERTY ro.boot.bootreason '\(reboot\|reboot,rescueparty\)'
}
[ "USAGE: test_Its_Just_So_Hard_reboot
Its Just So Hard reboot test:
- adb shell reboot 'Its Just So Hard'
- (wait until screen is up, boot has completed)
- adb shell getprop sys.boot.reason
- NB: should report reboot,its_just_so_hard
- NB: expect log \"... I bootstat: Unknown boot reason: reboot,its_just_so_hard\"" ]
test_Its_Just_So_Hard_reboot() {
if isDebuggable; then # see below
duration_test
else
duration_test `expr ${DURATION_DEFAULT} + ${DURATION_DEFAULT}`
fi
adb_sh 'reboot "Its Just So Hard"' </dev/null
wait_for_screen
EXPECT_PROPERTY sys.boot.reason reboot,its_just_so_hard
EXPECT_PROPERTY sys.boot.reason.last reboot,its_just_so_hard
check_boilerplate_properties
report_bootstat_logs reboot,its_just_so_hard
}
[ "USAGE: run_bootloader [value [expected]]
bootloader boot reason injection tests:
- setBootloaderBootReason value
- adb shell reboot
- (wait until screen is up, boot has completed)
- adb shell getprop sys.boot.reason
- NB: should report reboot,value" ]
run_bootloader() {
bootloader_expected="${1}"
if [ -z "${bootloader_expected}" ]; then
bootloader_expected="${TEST#bootloader_}"
fi
if ! setBootloaderBootReason ${bootloader_expected}; then
echo " Skipping FAILURE." 2>&1
return
fi
duration_test
if [ X"warm" = X"${bootloader_expected}" ]; then
last_expected=cold
else
last_expected=warm
fi
adb reboot ${last_expected}
wait_for_screen
# Reset so that other tests do not get unexpected injection
setBootloaderBootReason
# Determine the expected values
sys_expected="${2}"
if [ -z "${sys_expected}" ]; then
sys_expected="`validate_reason ${bootloader_expected}`"
if [ "reboot" = "${sys_expected}" ]; then
sys_expected="${last_expected}"
fi
else
sys_expected=`validate_reason ${sys_expected}`
fi
case ${sys_expected} in
kernel_panic | kernel_panic,* | watchdog | watchdog,* )
last_expected=${sys_expected}
;;
esac
# Check values
EXPECT_PROPERTY ro.boot.bootreason "${bootloader_expected}"
EXPECT_PROPERTY sys.boot.reason "${sys_expected}"
EXPECT_PROPERTY sys.boot.reason.last "${last_expected}"
check_boilerplate_properties
report_bootstat_logs "${sys_expected}"
}
[ "USAGE: test_bootloader_<type>
bootloader boot reasons test injection" ]
test_bootloader_normal() {
run_bootloader
}
test_bootloader_watchdog() {
run_bootloader
}
test_bootloader_kernel_panic() {
run_bootloader
}
test_bootloader_oem_powerkey() {
run_bootloader
}
test_bootloader_wdog_reset() {
run_bootloader
}
test_bootloader_cold() {
run_bootloader
}
test_bootloader_warm() {
run_bootloader
}
test_bootloader_hard() {
run_bootloader
}
test_bootloader_recovery() {
run_bootloader
}
[ "USAGE: run_kBootReasonMap [--boot_reason_enum] value expected
bootloader boot reason injection tests:
- if --boot_reason_enum run bootstat executable for result instead.
- inject boot reason into sys.boot.reason
- run bootstat --set_system_boot_reason
- check for expected enum
- " ]
run_kBootReasonMap() {
if [ X"--boot_reason_enum" = X"${1}" ]; then
shift
local sys_expected="${1}"
shift
local enum_expected="${1}"
adb_su bootstat --boot_reason_enum="${sys_expected}" |
(
local retval=-1
while read -r id match; do
if [ ${retval} = -1 -a ${enum_expected} = ${id} ]; then
retval=0
fi
if [ ${enum_expected} != ${id} ]; then
echo "ERROR: ${enum_expected} ${sys_expected} got ${id} ${match}" >&2
retval=1
fi
done
exit ${retval}
)
return
fi
local sys_expected="${1}"
shift
local enum_expected="${1}"
adb_su setprop sys.boot.reason "${sys_expected}" </dev/null
adb_su bootstat --record_boot_reason </dev/null
# Check values
EXPECT_PROPERTY sys.boot.reason "${sys_expected}"
local retval=${?}
local result=`adb_su stat -c %Y /data/misc/bootstat/system_boot_reason </dev/null 2>/dev/null`
[ "${enum_expected}" = "${result}" ] ||
(
[ -n "${result}" ] || result="<nothing>"
echo "ERROR: ${enum_expected} ${sys_expected} got ${result}" >&2
false
) ||
retval=${?}
return ${retval}
}
[ "USAGE: filter_kBootReasonMap </dev/stdin >/dev/stdout
convert any regex expressions into a series of non-regex test strings" ]
filter_kBootReasonMap() {
while read -r id match; do
case ${match} in
'reboot,[empty]')
echo ${id} # matches b/c of special case
echo ${id} reboot,y # matches b/c of regex
echo 1 reboot,empty # negative test (ID for unknown is 1)
;;
reboot)
echo 1 reboog # negative test (ID for unknown is 1)
;;
'reboot,pmic_off_fault,.*')
echo ${id} reboot,pmic_off_fault,hello,world
echo ${id} reboot,pmic_off_fault,
echo 1 reboot,pmic_off_fault
;;
esac
echo ${id} "${match}" # matches b/c of exact
done
}
[ "USAGE: test_kBootReasonMap
kBootReasonMap test
- (wait until screen is up, boot has completed)
- read bootstat for kBootReasonMap entries and test them all" ]
test_kBootReasonMap() {
checkDebugBuild || return
duration_test 15
local tempfile="`mktemp`"
local arg=--boot_reason_enum
adb_su bootstat ${arg} </dev/null 2>/dev/null |
filter_kBootReasonMap >${tempfile}
if [ ! -s "${tempfile}" ]; then
wait_for_screen
arg=
sed -n <${progpath}bootstat.cpp \
'/kBootReasonMap = {/,/^};/s/.*{"\([^"]*\)", *\([0-9][0-9]*\)},.*/\2 \1/p' |
sed 's/\\\\/\\/g' |
filter_kBootReasonMap >${tempfile}
fi
T=`adb_date`
retval=0
while read -r enum string; do
if [ X"${string}" != X"${string#*[[].[]]}" -o X"${string}" != X"${string#*\\.}" ]; then
if [ 'reboot\.empty' != "${string}" ]; then
echo "WARNING: regex snuck through filter_kBootReasonMap ${enum} ${string}" >&2
enum=1
fi
fi
run_kBootReasonMap ${arg} "${string}" "${enum}" </dev/null || retval=${?}
done <${tempfile}
rm ${tempfile}
( exit ${retval} )
# See filter_kBootReasonMap() for negative tests and add them here too
report_bootstat_logs -t${T} \
'-bootstat: Service started: bootstat --boot_reason_enum=' \
'-bootstat: Unknown boot reason: reboot,empty' \
'-bootstat: Unknown boot reason: reboog' \
'-bootstat: Unknown boot reason: reboot,pmic_off_fault'
}
[ "USAGE: ${progname} [-s SERIAL] [tests]...
Mainline executive to run the above tests" ]
# Rudimentary argument parsing
if [ ${#} -ge 2 -a X"-s" = X"${1}" ]; then
export ANDROID_SERIAL="${2}"
shift 2
fi
# Helpful for debugging, allows us to import the functions.
if [ X"--macros" != X"${1}" ]; then
if [ X"--help" = X"${1}" -o X"-h" = X"${1}" -o X"-?" = X"${1}" ]; then
echo "USAGE: ${progname} [-s SERIAL] [tests]..."
echo tests - `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null`
exit 0
fi
if [ X"--stop" = X"${1}" ]; then
STOP_ON_FAILURE=true
shift
fi
# Check if all conditions for the script are valid
if [ -z "${ANDROID_SERIAL}" ]; then
ndev=`(
adb devices | grep -v 'List of devices attached'
fastboot devices
) |
grep -v "^[${SPACE}${TAB}]*\$" |
wc -l`
if [ ${ndev} -gt 1 ]; then
echo "ERROR: no target device specified, ${ndev} connected" >&2
echo "${RED}[ FAILED ]${NORMAL}"
exit 1
fi
echo "WARNING: no target device specified" >&2
fi
ret=0
# Test Series
if [ X"all" = X"${*}" ]; then
# automagically pick up all test_<function>s.
eval set nothing `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null`
if [ X"nothing" = X"${1}" ]; then
shift 1
fi
fi
if [ -z "$*" ]; then
# automagically pick up all test_<function>, except test_optional_<function>.
eval set nothing `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null |
grep -v '^optional_'`
if [ -z "${2}" ]; then
# Hard coded should shell fail to find them (search/permission issues)
eval set properties ota cold factory_reset hard battery unknown \
kernel_panic kernel_panic_subreason kernel_panic_hung warm \
thermal_shutdown userrequested_shutdown shell_reboot adb_reboot \
Its_Just_So_Hard_reboot bootloader_normal bootloader_watchdog \
bootloader_kernel_panic bootloader_oem_powerkey \
bootloader_wdog_reset bootloader_cold bootloader_warm \
bootloader_hard bootloader_recovery kBootReasonMap
fi
if [ X"nothing" = X"${1}" ]; then
shift 1
fi
fi
echo "INFO: selected test(s): ${@}" >&2
echo
# Prepare device
setBootloaderBootReason 2>/dev/null
# Start pouring through the tests.
failures=
successes=
for t in "${@}"; do
wrap_test ${t}
retval=${?}
if [ 0 = ${retval} ]; then
if [ -z "${successes}" ]; then
successes=${t}
else
successes="${successes} ${t}"
fi
else
ret=${retval}
if [ -z "${failures}" ]; then
failures=${t}
else
failures="${failures} ${t}"
fi
fi
echo
done
if [ -n "${successes}" ]; then
echo "${GREEN}[ PASSED ]${NORMAL} ${successes}"
fi
if [ -n "${failures}" ]; then
echo "${RED}[ FAILED ]${NORMAL} ${failures}"
fi
exit ${ret}
fi