#!/bin/bash # Copyright (c) 2009 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # Script to run client or server tests on a live remote image. # Load common constants. This should be the first executable line. # The path to common.sh should be relative to your script's location. . "$(dirname $0)/common.sh" . "$(dirname $0)/remote_access.sh" get_default_board DEFINE_string args "" "Command line arguments for test, separated with comma" a DEFINE_string board "$DEFAULT_BOARD" \ "The board for which you are building autotest" DEFINE_string chroot "${DEFAULT_CHROOT_DIR}" "alternate chroot location" c DEFINE_boolean cleanup ${FLAGS_FALSE} "Clean up temp directory" DEFINE_integer iterations 1 "Iterations to run every top level test" i DEFINE_string prepackaged_autotest "" "Use this prepackaged autotest dir" DEFINE_string results_dir_root "" "alternate root results directory" DEFINE_boolean verbose ${FLAGS_FALSE} "Show verbose autoserv output" v RAN_ANY_TESTS=${FLAGS_FALSE} # Check if our stdout is a tty function is_a_tty() { local stdout=$(readlink /proc/$$/fd/1) [[ "${stdout#/dev/tty}" != "${stdout}" ]] && return 0 [[ "${stdout#/dev/pts}" != "${stdout}" ]] && return 0 return 1 } # Writes out text in specified color if stdout is a tty # Arguments: # $1 - color # $2 - text to color # $3 - text following colored text (default colored) # Returns: # None function echo_color() { local color=0 [[ "$1" == "red" ]] && color=31 [[ "$1" == "green" ]] && color=32 [[ "$1" == "yellow" ]] && color=33 if is_a_tty; then echo -e "\033[1;${color}m$2\033[0m$3" else echo "$2$3" fi } function cleanup() { if [[ $FLAGS_cleanup -eq ${FLAGS_TRUE} ]] || \ [[ ${RAN_ANY_TESTS} -eq ${FLAGS_FALSE} ]]; then rm -rf "${TMP}" else echo ">>> Details stored under ${TMP}" fi cleanup_remote_access } # Returns an error if the test_result_file has text which indicates # the test was not run successfully. # Arguments: # $1 - file name of autotest status for to check for success # Returns: # None function is_successful_test() { local file="$1" # To be successful, must not have BAD, ERROR or FAIL in the file. if egrep -q "(BAD|ERROR|FAIL)" "${file}"; then return 1 fi # To be successful, must have GOOD in the file. if ! grep -q GOOD "${file}"; then return 1 fi return 0 } # Adds attributes to all tests run # Arguments: # $1 - results directory # $2 - attribute name (key) # $3 - attribute value (value) function add_test_attribute() { local results_dir="$1" local attribute_name="$2" local attribute_value="$3" if [[ -z "$attribute_value" ]]; then return; fi for status_file in $(echo "${results_dir}"/*/status); do local keyval_file=$(dirname $status_file)/keyval echo "Updating ${keyval_file}" echo "${attribute_name}=${attribute_value}" >> "${keyval_file}" done } # Ask the target what board it is function learn_board() { if [[ -n "${FLAGS_board}" ]]; then return fi remote_sh grep CHROMEOS_RELEASE_BOARD /etc/lsb-release FLAGS_board=$(echo "${REMOTE_OUT}" | cut -d= -f2) if [[ -z "${FLAGS_board}" ]]; then check_board fi echo "Target reports board is ${FLAGS_board}" } # Determine if a control is for a client or server test. Echos # either "server" or "client". # Arguments: # $1 - control file path function read_test_type() { local control_file=$1 # Assume a line starts with TEST_TYPE = local type=$(egrep -m1 \ '^[[:space:]]*TEST_TYPE[[:space:]]*=' "${control_file}") if [[ -z "${type}" ]]; then echo_color "red" ">>> Unable to find TEST_TYPE line in ${control_file}" exit 1 fi type=$(python -c "${type}; print TEST_TYPE.lower()") if [[ "${type}" != "client" ]] && [[ "${type}" != "server" ]]; then echo_color "red" ">>> Unknown type of test (${type}) in ${control_file}" exit 1 fi echo ${type} } function main() { cd $(dirname "$0") FLAGS "$@" || exit 1 if [[ -z "${FLAGS_ARGV}" ]]; then echo "Please specify tests to run. For example:" echo " $0 --remote=MyMachine BootPerfServer" exit 1 fi # Check the validity of the user-specified result directory # It must be within the /tmp directory if [[ -n "${FLAGS_results_dir_root}" ]]; then SUBSTRING=${FLAGS_results_dir_root:0:5} if [[ ${SUBSTRING} != "/tmp/" ]]; then echo "User-specified result directory must be within the /tmp directory" echo "ex: --results_dir_root=/tmp/" exit 1 fi fi set -e # Set global TMP for remote_access.sh's sake # and if --results_dir_root is specified, # set TMP and create dir appropriately if [[ ${INSIDE_CHROOT} -eq 0 ]]; then if [[ -n "${FLAGS_results_dir_root}" ]]; then TMP=${FLAGS_chroot}${FLAGS_results_dir_root} mkdir -m 777 ${TMP} else TMP=$(mktemp -d ${FLAGS_chroot}/tmp/run_remote_tests.XXXX) fi TMP_INSIDE_CHROOT=$(echo ${TMP#${FLAGS_chroot}}) else if [[ -n "${FLAGS_results_dir_root}" ]]; then TMP=${FLAGS_results_dir_root} mkdir -m 777 ${TMP} else TMP=$(mktemp -d /tmp/run_remote_tests.XXXX) fi TMP_INSIDE_CHROOT=${TMP} fi trap cleanup EXIT remote_access_init local autotest_dir="" if [[ -z "${FLAGS_prepackaged_autotest}" ]]; then learn_board autotest_dir="${GCLIENT_ROOT}/src/third_party/autotest/files" else autotest_dir="${FLAGS_prepackaged_autotest}" fi local control_files_to_run="" # Now search for tests which unambiguously include the given identifier local search_path=$(echo {client,server}/{tests,site_tests}) pushd ${autotest_dir} > /dev/null for test_request in $FLAGS_ARGV; do test_request=$(remove_quotes "${test_request}") ! finds=$(find ${search_path} -maxdepth 2 -type f \( -name control.\* -or \ -name control \) | egrep -v "~$" | egrep "${test_request}") if [[ -z "${finds}" ]]; then echo_color "red" ">>> Cannot find match for \"${test_request}\"" exit 1 fi local matches=$(echo "${finds}" | wc -l) if [[ ${matches} -gt 1 ]]; then echo "" echo_color "red" \ ">>> \"${test_request}\" is ambiguous. These control file paths match:" for FIND in ${finds}; do echo_color "red" " * " "${FIND}" done echo "" echo ">>> Disambiguate by copy-and-pasting the whole path above" \ "instead of passing \"${test_request}\"." exit 1 fi for i in $(seq 1 $FLAGS_iterations); do control_files_to_run="${control_files_to_run} '${finds}'" done done popd > /dev/null echo "" if [[ -z "${control_files_to_run}" ]]; then echo_color "red" ">>> Found no control files" exit 1 fi echo_color "yellow" ">>> Running the following control files:" for CONTROL_FILE in ${control_files_to_run}; do echo_color "yellow" " * " "${CONTROL_FILE}" done for control_file in ${control_files_to_run}; do # Assume a line starts with TEST_TYPE = control_file=$(remove_quotes "${control_file}") local type=$(read_test_type "${autotest_dir}/${control_file}") local option if [[ "${type}" == "client" ]]; then option="-c" else option="-s" fi echo "" echo_color "yellow" ">>> Running ${type} test " ${control_file} local short_name=$(basename $(dirname "${control_file}")) local results_dir_name="${short_name}" local results_dir="${TMP_INSIDE_CHROOT}/${results_dir_name}" rm -rf "${results_dir}" local verbose="" if [[ ${FLAGS_verbose} -eq $FLAGS_TRUE ]]; then verbose="--verbose" fi local args=() if [[ -n "${FLAGS_args}" ]]; then local with_spaces="" with_spaces=${FLAGS_args//,/ } args=("-a" "${with_spaces}") fi RAN_ANY_TESTS=${FLAGS_TRUE} # HACK: Temporary hack for cros-workon conversion [[ -n "${CROS_WORKON_SRCROOT}" ]] && WORKON_SUFFIX=_workon local enter_chroot="" local autotest="${GCLIENT_ROOT}/src/scripts/autotest${WORKON_SUFFIX}" if [[ ${INSIDE_CHROOT} -eq 0 ]]; then enter_chroot="./enter_chroot.sh --chroot ${FLAGS_chroot} --" autotest="./autotest${WORKON_SUFFIX}" fi ${enter_chroot} ${autotest} --board "${FLAGS_board}" -m "${FLAGS_remote}" \ --ssh-port ${FLAGS_ssh_port} \ "${option}" "${control_file}" -r "${results_dir}" ${verbose} \ "${args[@]}" results_dir="${TMP}/${results_dir_name}" local test_status="${results_dir}/status.log" local test_result_dir="${results_dir}/${short_name}" local keyval_file="${test_result_dir}/results/keyval" echo "" if is_successful_test "${test_status}"; then echo_color "green" ">>> SUCCESS: ${control_file}" if [[ -f "${keyval_file}" ]]; then echo ">>> Keyval was:" cat "${keyval_file}" fi else echo_color "red" ">>> FAILED: ${control_file}" cat "${test_status}" fi local end_time=$(date '+%s') done } main $@