summaryrefslogtreecommitdiff
path: root/runner.sh
diff options
context:
space:
mode:
authorMartin Peres <martin.peres@linux.intel.com>2017-01-18 18:26:07 +0200
committerMartin Peres <martin.peres@linux.intel.com>2017-02-07 17:09:33 +0200
commit41736505d5da6acb076dc41ea7484381befe59a4 (patch)
treec9a8b65205966d0d099ca5546d7f1b31403671bd /runner.sh
parent408fd1b17a86bd78f32d886c0f880118d47d7ce6 (diff)
runner.sh: introduce a new leaner and more modular runner
v2 (reported by Olivier Berthier): - make sure to write the deployed version to the journal when we start testing. This will prevent the DEPLOYMENT_ERROR from being reported. - write the compilation error code to the compilation log - update the readme to mention runner.sh and deprecate core.sh v3: - only set the timings on a full run, not on resumes - runner.py: make the command return a RunnerCmdResult - document functions
Diffstat (limited to 'runner.sh')
-rwxr-xr-xrunner.sh794
1 files changed, 794 insertions, 0 deletions
diff --git a/runner.sh b/runner.sh
new file mode 100755
index 0000000..65872b8
--- /dev/null
+++ b/runner.sh
@@ -0,0 +1,794 @@
+#!/bin/bash
+
+# Copyright (c) 2015, Intel Corporation
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of Intel Corporation nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# The script is known to work with recent versions of bash.
+# Authors:
+# - Martin Peres <martin.peres@intel.com>
+# - Chris Wilson <chris@chris-wilson.co.uk>
+
+# Uncomment the following to track all the executed commands
+#set -o xtrace
+
+# Start by defining all the errors
+typeset -A errors
+errors[0]="No Error"
+errors[1]="Unknown error"
+errors[5]="An instance of runner.sh is already running"
+errors[6]="The report is already locked"
+
+# Commands parsing
+errors[10]="Invalid command"
+errors[11]="Parameter already set"
+errors[12]="The profile does not exist"
+errors[13]="The profile is not specified"
+errors[14]="The report cannot be opened/created"
+errors[15]="The report is missing"
+errors[16]="Testing has not been started"
+errors[17]="Test execution type unsupported"
+errors[18]="Test execution type requires a valid result file"
+errors[19]="Result already complete"
+
+# OS
+errors[30]="The shell does not support globstat"
+errors[31]="Cannot create the log folder"
+errors[32]="Cannot move to the repo directory"
+
+# SCM
+errors[50]="Invalid version ID"
+
+# Environment
+errors[60]="Failed to deploy the environment"
+
+# Compilation and deployment
+errors[70]="Compilation or deployment failed"
+errors[71]="Compilation failed"
+errors[72]="Deployment failed"
+errors[73]="The deployed version does not match the wanted version"
+errors[74]="A reboot is necessary"
+
+# Running
+errors[80]="Early exit requested"
+
+# Test
+errors[100]="The test does not exist"
+
+function lock {
+ # Inspired from http://www.kfirlavi.com/blog/2012/11/06/elegant-locking-of-bash-program/
+ local lock_file="$1"
+ local fd="$2"
+
+ eval "exec $fd>$lock_file"
+
+ flock -w 0.1 -x $fd
+ return $?
+}
+
+function unlock {
+ local fd="$1"
+ flock -u $fd
+ return $?
+}
+
+function use_report {
+ local name=$1
+
+ [ -n "$reportName" ] && return 11
+
+ # Keep the stupid name for the rapl_check profile
+ local folder="$ezBenchDir/logs/${name:-$(date +"%Y-%m-%d-%T")}"
+
+ # Create the folder
+ [ -d $folder ] || mkdir -p $folder || return 30
+ echo "report,$name" >> $folder/runner.log
+ exec > >(tee -a $folder/runner.log)
+ exec 2>&1
+
+ reportName=$name
+ logsFolder=$folder
+
+ # generate the early-exit filename
+ abortFile="$logsFolder/requestExit"
+
+ return 0
+}
+
+function use_profile {
+ local prof=$1
+
+ [ -n "$profile" ] && return 11
+
+ # Check if the profile exists
+ profileDir="$ezBenchDir/profiles.d/$prof"
+ if [ ! -d "$profileDir" ]; then
+ return 12
+ fi
+
+ # The profile name is valid, set some environment variables
+ PROFILE_TMP_BUILD_DIR="$DEPLOY_BASE_DIR/$prof/tmp"
+ PROFILE_DEPLOY_BASE_DIR="$DEPLOY_BASE_DIR/$prof/builds"
+ PROFILE_DEPLOY_DIR="$DEPLOY_BASE_DIR/$prof/cur"
+
+ # Default user options
+ for conf in $profileDir/conf.d/**/*.conf; do
+ [ "$conf" = "$profileDir/conf.d/**/*.conf" ] && continue
+ source "$conf"
+ done
+ source "$profileDir/profile"
+
+ profile=$prof
+
+ return 0
+}
+
+function parse_tests_profiles {
+ [ $available_tests_parsed -eq 1 ] && return
+
+ # Parse the list of tests
+ for test_dir in ${testsDir:-$ezBenchDir/tests.d}; do
+ for test_file in $test_dir/**/*.test; do
+ unset test_name
+ unset test_unit
+ unset test_type
+ unset test_invert
+ unset test_exec_time
+
+ source "$test_file" || continue
+
+ # Sanity checks on the file
+ [ -z "$test_name" ] && continue
+ [ -z "$test_exec_time" ] && continue
+
+ # Set the default unit to FPS
+ [ -z "$test_unit" ] && test_unit="FPS"
+
+ # Set the default type to bench
+ [ -z "$test_type" ] && test_type="bench"
+
+ # Set the default type to bench
+ [ -z "$test_invert" ] && test_invert="0"
+
+ # Set the default error handling capabiliy
+ [ -z "$test_has_exit_code" ] && test_has_exit_code="0"
+
+ for test in $test_name; do
+ # TODO: Check that the run function exists
+
+ idx=${#availTestNames[@]}
+ availTestNames[$idx]=$test
+ availTestUnits[$idx]=$test_unit
+ availTestTypes[$idx]=$test_type
+ availTestIsInvert[$idx]=$test_invert
+ availTestExecTime[$idx]=$test_exec_time
+ availTestHasExitCode[$idx]=$test_has_exit_code
+ done
+ done
+ done
+ unset test_name
+ unset test_unit
+ unset test_type
+ unset test_invert
+ unset test_exec_time
+
+ available_tests_parsed=1
+}
+
+function done_testing {
+ [ "$testing_ready" -eq 1 ] || return
+
+ # Execute the user-defined post hook
+ callIfDefined ezbench_post_hook
+
+ # Delete the abort file
+ if [ -n "$abortFile" ]; then
+ rm $abortFile 2> /dev/null
+ fi
+
+ unlock 201
+ unlock 200
+
+ # Clear the traps before calling the finish hook
+ trap - EXIT
+ trap - INT # Needed for zsh
+
+ testing_ready=0
+}
+
+function start_testing {
+ [ "$testing_ready" -eq 0 ] || return
+ [ -n "$profile" ] || return 13
+ [ -n "$reportName" ] || return 15
+
+ # Lock against concurent testing with the runner
+ lock "$ezBenchDir/lock" 200 || return 5
+
+ # Lock the report, as it allows knowing which report is being worked on
+ if ! lock "$logsFolder/lock" 201; then
+ unlock 200
+ return 6
+ fi
+
+ # Execute the user-defined environment deployment hook
+ callIfDefined ezbench_env_deploy_hook
+
+ trap done_testing EXIT
+ trap done_testing INT # Needed for zsh
+
+ testing_ready=1
+
+ # Write in the journal what current version is deployed
+ write_to_journal deployed $(profile_repo_deployed_version)
+
+ return 0
+}
+
+function compile_and_deploy {
+ # Accessible variables
+ # $version [RO]: SHA1 id of the current version
+ # $versionName [RO]: Name of the version
+
+ profile_repo_get_patch $version > "$logsFolder/$1.patch"
+
+ # Select the version of interest
+ local versionListLog="$logsFolder/commit_list"
+ if [ -z "$(grep ^"$version" "$versionListLog" 2> /dev/null)" ]
+ then
+ local title=$(profile_repo_version_title "$version")
+ echo "$version $title" >> "$versionListLog"
+ fi
+
+ # early exit if the deployed version is the wanted version
+ deployed_version=$(profile_repo_deployed_version)
+ are_same_versions "$version" "$deployed_version" && return 0
+
+ local compile_logs=$logsFolder/${version}_compile_log
+
+ # Compile the version and check for failure. If it failed, go to the next version.
+ export REPO_COMPILE_AND_DEPLOY_VERSION=$version
+ eval "$makeAndDeployCmd" >> "$compile_logs" 2>&1
+ local exit_code=$?
+ unset REPO_COMPILE_AND_DEPLOY_VERSION
+
+ # Write the compilation status to the compile log.
+ # The exit code 74 actually means everything is fine but we need to reboot
+ if [ $exit_code -eq 74 ]
+ then
+ printf "Exiting with error code 0\n" >> "$compile_logs"
+ else
+ printf "Exiting with error code $exit_code\n" >> "$compile_logs"
+ fi
+
+ # Add to the log that we are now in the deployment phase
+ write_to_journal deploy $version
+
+ # Check for compilation errors
+ if [ "$exit_code" -ne '0' ]; then
+ if [[ $exit_code -lt 71 && $exit_code -gt 74 ]]; then
+ return 70
+ else
+ return $exit_code
+ fi
+ fi
+
+ # Check that the deployed image is the right one
+ are_same_versions "$version" "$(profile_repo_deployed_version)" || return 73
+
+ # Write in the journal that the version is deployed
+ write_to_journal deployed $version
+
+ return 0
+}
+
+function find_test {
+ # Accessible variables
+ # $availTestNames [RO]: Array containing the names of available tests
+ # $availTestUnits [RO]: Array containing the unit of available tests
+ # $availTestTypes [RO]: Array containing the result type of available tests
+ # $availTestIsInvert [RO]: Array containing the execution time of available tests
+ # $availTestExecTime [RO]: Array containing the execution time of available tests
+
+ local test=$1
+ local basetest=$(echo "$test" | cut -d [ -f 1)
+ local subtests=$(echo $test | cut -s -d '[' -f 2- | rev | cut -d ']' -f 2- | rev)
+
+ # Try to find the test in the list of available tests
+ for (( a=0; a<${#availTestNames[@]}; a++ )); do
+ if [[ ${availTestNames[$a]} == $basetest ]]; then
+ # We do not accept running subtests on non-complete matches of names
+ [[ $basetest != ${availTestNames[$a]} && -n $subtests ]] && continue
+
+ testName="${availTestNames[$a]}"
+ testSubTests="$subtests"
+ testUnit="${availTestUnits[$a]}"
+ testType="${availTestTypes[$a]}"
+ testInvert="${availTestIsInvert[$a]}"
+ testExecTime="${availTestExecTime[$a]}"
+ testHasExitCode="${availTestHasExitCode[$a]}"
+
+ return 0
+ fi
+ done
+
+ return 1
+}
+
+function execute_test {
+ local testExecutionType=$1
+ local versionName=$2
+ local testName=$3
+ local verboseOutput=$4
+ local run_log_file_name=$5
+
+ [ -n "$versionName" ] || return 10
+ [ -n "$testName" ] || return 10
+ [ -n "$verboseOutput" ] || return 10
+ [ -n "$profile" ] || return 13
+ [ -n "$reportName" ] || return 15
+ [ "$testing_ready" -eq 1 ] || return 16
+
+ # Verify that, if specified, the run_log_file_name exits
+ [[ -n "$run_log_file_name" && ! -f "$logsFolder/$run_log_file_name" ]] && return 18
+
+ # Find the test
+ parse_tests_profiles
+ find_test "$testName" || return 100
+
+ # Get the full version name
+ local version=$(profile_repo_version_from_human $versionName) || return 50
+
+ # verify that the type of execution is available
+ local execFuncName=${testName}_${testExecutionType}
+ if ! function_available "$execFuncName"; then
+ # If the type was resume, mark the test as tested because there is
+ # nothing else we can do for this run
+ if [ "$testExecutionType" == "resume" ]; then
+ write_to_journal tested "$version" "${testName}" "$run_log_file_name"
+ fi
+ return 17
+ fi
+
+ # compile and deploy the version
+ compile_and_deploy $version || return $?
+
+ # Exit if asked to
+ [ -e "$abortFile" ] && return 80
+
+ # Generate the logs file names
+ local reportFileName="${version}_${testType}_${testName}"
+ local reportFile="$logsFolder/$reportFileName"
+
+ # Only generate the run_log_file_name if it is unspecified
+ if [ -z "$run_log_file_name" ]; then
+ # Find the first run id available
+ if [ -f "$reportFile" ] || [ ${testType} == "unified" ]; then
+ # The logs file exist, look for the number of runs
+ local run=0
+ while [ -f "${reportFile}#${run}" ]
+ do
+ local run=$((run+1))
+ done
+ else
+ if [ -z "${testInvert}" ]; then
+ direction="more is better"
+ else
+ direction="less is better"
+ fi
+ echo "# ${testUnit} ($direction) of '${testName}' using version ${version}" > "$reportFile"
+ local run=0
+ fi
+ local run_log_file_name="${reportFileName}#$run"
+ fi
+
+ # compute the different hook names
+ local preHookFuncName=${testName}_run_pre_hook
+ local postHookFuncName=${testName}_run_post_hook
+
+ local run_log_file="$logsFolder/$run_log_file_name"
+ IFS='|' read -a run_sub_tests <<< "$testSubTests"
+
+ echo "$run_log_file_name"
+
+ callIfDefined "$preHookFuncName"
+ callIfDefined benchmark_run_pre_hook
+
+ # This function will return multiple fps readings
+ write_to_journal test "$version" "${testName}" "$run_log_file_name"
+ if [ "$verboseOutput" -eq 1 ]; then
+ "$execFuncName" > >(tee "$run_log_file")
+ else
+ "$execFuncName" > "$run_log_file" 2> /dev/null
+ fi
+ local exit_code=$?
+ write_to_journal tested "$version" "${testName}" "$run_log_file_name"
+
+ callIfDefined benchmark_run_post_hook
+ callIfDefined "$postHookFuncName"
+
+ if [ ${testType} != "unified" ]; then
+ if [ -s "$run_log_file" ]; then
+ if [ ${testType} == "bench" ]; then
+ # Add the reported values before adding the result to the average values for
+ # the run.
+ run_avg=$(awk '{sum=sum+$1} END {print sum/NR}' $run_log_file)
+ elif [ ${testType} == "unit" ]; then
+ run_avg=$(head -n 1 $run_log_file)
+ elif [ ${testType} == "imgval" ]; then
+ run_avg=0.0
+ fi
+ echo "$run_avg" >> "$reportFile"
+
+ else
+ echo "0" >> "$run_log_file"
+ echo "0" >> "$reportFile"
+ fi
+ fi
+
+ # If the test returns exit codes, then use it
+ if [ "$testHasExitCode" -eq 1 ]; then
+ return $exit_code
+ else
+ return 0
+ fi
+}
+
+# set the default run_bench function which can be overriden by the profiles:
+# Bash variables: $run_log_file : filename of the log report of the current run
+# $testName : Name of the test
+# $testSubTests : List of subtests
+# Arguments: $1 : timeout (set to 0 for infinite wait)
+# $2+: command line executing the test AND NOTHING ELSE!
+function run_bench {
+ timeout=$1
+ shift
+ cmd="LIBGL_DEBUG=verbose vblank_mode=0 stdbuf -oL timeout $timeout"
+ bench_binary=$(echo "$1" | rev | cut -d '/' -f 1 | rev)
+
+ env_dump_path="$ezBenchDir/utils/env_dump"
+ env_dump_lib="$env_dump_path/env_dump.so"
+ env_dump_launch="$env_dump_path/env_dump.sh"
+ env_dump_extend="$env_dump_path/env_dump_extend.sh"
+
+ if [ -f "$env_dump_lib" ]; then
+ run_log_file_env_dump="$run_log_file"
+ cmd="$cmd $env_dump_launch "$run_log_file" $@"
+ else
+ cmd="$cmd $@"
+ fi
+
+ run_log_file_stdout="$run_log_file.stdout"
+ run_log_file_stderr="$run_log_file.stderr"
+
+ callIfDefined run_bench_pre_hook
+ local time_before=$(date +%s.%N)
+
+ eval $cmd > "$run_log_file_stdout" 2> "$run_log_file_stderr"
+ local exit_code=$?
+
+ if [ -f "$env_dump_lib" ]; then
+ $env_dump_extend "$SHA1_DB" "$run_log_file.env_dump"
+ fi
+
+ local time_after=$(date +%s.%N)
+ test_exec_time=$(echo "$time_after - $time_before" | bc -l)
+ callIfDefined run_bench_post_hook
+
+ # If the test does not have subtests, then store the execution time
+ if [ -z "$testSubTests" && "$testExecutionType" != "resume" ]; then
+ "$ezBenchDir/timing_DB/timing.py" -n test -k "$testName" -a $test_exec_time
+ fi
+
+ # delete the log files if they are empty
+ if [ ! -s "$run_log_file_stdout" ] ; then
+ rm "$run_log_file_stdout"
+ else
+ cat "$run_log_file_stdout"
+ fi
+ if [ ! -s "$run_log_file_stderr" ] ; then
+ rm "$run_log_file_stderr"
+ else
+ cat "$run_log_file_stderr" >&2
+ fi
+
+ return $exit_code
+}
+
+function function_available() {
+ if [ "$(type -t "$1")" == 'function' ]; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+function callIfDefined() {
+ if [ "$(type -t "$1")" == 'function' ]; then
+ local funcName=$1
+ shift
+ $funcName "$@"
+ return 0
+ else
+ return 1
+ fi
+}
+
+function write_to_journal {
+ local journal="$logsFolder/journal"
+ local operation=$1
+ local key=$2
+ shift 2
+
+ [ -d "$logsFolder" ] || return
+
+ local time=$(date +"%s.%6N")
+
+ # if the operation is "deployed", check how long it took to deploy
+ if [[ "$operation" == "deployed" ]]; then
+ local deploy_time=$(tail -n 1 "$logsFolder/journal" 2> /dev/null | awk -F ',' "{ \
+ if (\$2 == \"deploy\" && \$3 == \"$key\") { \
+ print $time - \$1 \
+ } \
+ }")
+
+ if [ -n "$deploy_time" ]; then
+ "$ezBenchDir/timing_DB/timing.py" -n deploy -k "$profile" -a "$deploy_time"
+ fi
+ fi
+
+ echo -n "$time,$operation,$key" >> "$journal" 2> /dev/null
+
+ while [ "${1+defined}" ]; do
+ echo -n ",$1" >> "$journal" 2> /dev/null
+ shift
+ done
+ echo "" >> "$journal" 2> /dev/null
+
+ sync
+}
+
+function show_help {
+ echo "This tool is meant to be run by machines, but if you really want to deal with this"
+ echo "manually, here is the list of commands that can be used:"
+ echo ""
+ echo " - side-effect-free commands:"
+ echo " - conf_script:"
+ echo " return the list of configuration scripts set"
+ echo ""
+ echo " - echo:"
+ echo " does nothing, useful as a fence mechanism"
+ echo ""
+ echo " - help:"
+ echo " displays this help message"
+ echo ""
+ echo " - list_cached_versions:"
+ echo " list all the compiled versions for the profile"
+ echo ""
+ echo " - list_tests:"
+ echo " list the set of available tests"
+ echo ""
+ echo " - profile:"
+ echo " return the current profile"
+ echo ""
+ echo " - repo:"
+ echo " return information about the profile's repository"
+ echo ""
+ echo " - report:"
+ echo " return the current profile"
+ echo ""
+ echo " - version:"
+ echo " return the implemented version of the protocol"
+ echo ""
+ echo ""
+ echo " - state-changing commands:"
+ echo " - conf_script,<path>:"
+ echo " set the profile for the current session"
+ echo ""
+ echo " - done:"
+ echo " tear down the test environment and exit"
+ echo ""
+ echo " - profile,<profile>:"
+ echo " set the profile for the current session"
+ echo ""
+ echo " - reboot:"
+ echo " reboot the machine"
+ echo ""
+ echo " - report,<report_name>:"
+ echo " set the report for the current session"
+ echo ""
+ echo " - resume,<version>,<test>,<result_file>,<verbose>:"
+ echo " Run a test, verbose=1 for reading the output"
+ echo ""
+ echo " - run,<version>,<test>,<verbose>:"
+ echo " Run a test, verbose=1 for reading the output"
+ echo ""
+ echo " - start_testing:"
+ echo " start the test environment"
+ echo ""
+ echo "That's all, folks!"
+}
+
+shopt -s globstar || {
+ echo "ERROR: ezbench requires bash 4.0+ or zsh with globstat support."
+ exit 30
+}
+
+# Printf complains about floating point numbers having , as a delimiter otherwise
+LC_NUMERIC="C"
+
+# Get the current folder
+ezBenchDir=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
+
+# initial cleanup
+mkdir "$ezBenchDir/logs" 2> /dev/null
+
+# Read the user parameters
+source "$ezBenchDir/user_parameters.sh"
+
+# Initialise the default state
+typeset -A availTestNames
+typeset -A availTestUnits
+typeset -A availTestTypes
+typeset -A availTestIsInvert
+typeset -A availTestExecTime
+typeset -A availTestHasExitCode
+typeset -A confs
+protocol_version=1
+reportName=""
+profile=""
+available_tests_parsed=0
+testing_ready=0
+dry_run=0
+
+# Now start the main loop
+while read line
+do
+ start_time=$(date +%s.%N)
+ break=0
+ error=1
+
+ echo "-->,$line"
+ cmd=$(echo $line | cut -d ',' -f 1)
+
+ if [ "$cmd" == "help" ]; then
+ show_help
+ error=0
+
+ elif [ "$cmd" == "report" ]; then
+ arg=$(echo "$line" | cut -d ',' -s -f 2)
+ if [ -n "$arg" ]; then
+ use_report "$arg"
+ error=$?
+ else
+ echo "$reportName"
+ error=0
+ fi
+
+ reportName=$arg
+
+ elif [ "$cmd" == "profile" ]; then
+ arg=$(echo "$line" | cut -d ',' -s -f 2)
+ if [ -n "$arg" ]; then
+ use_profile "$arg"
+ error=$?
+ else
+ echo "$profile"
+ error=0
+ fi
+
+ elif [ "$cmd" == "repo" ]; then
+ if [ -n "$profile" ]; then
+ echo "type=$(profile_repo_type)"
+ echo "path=$repoDir"
+ echo "head=$(profile_repo_version)"
+ echo "deployed_version=$(profile_repo_deployed_version)"
+ error=$?
+ else
+ error=13
+ fi
+
+ elif [ "$cmd" == "conf_script" ]; then
+ arg=$(echo "$line" | cut -d ',' -s -f 2)
+ if [ -n "$arg" ]; then
+ source "$arg"
+ idx=${#confs[@]}
+ confs[$idx]="$arg"
+ error=0
+ else
+ for (( c=0; c<${#confs[@]}; c++ )); do
+ echo "${confs[$c]}"
+ done
+ error=0
+ fi
+
+ elif [ "$cmd" == "list_tests" ]; then
+ parse_tests_profiles
+
+ for (( a=0; a<${#availTestNames[@]}; a++ )); do
+ echo "${availTestNames[$a]},${availTestTypes[$a]},${availTestUnits[$a]},${availTestIsInvert[$a]},${availTestExecTime[$a]}"
+ done
+ error=0
+
+ elif [ "$cmd" == "list_cached_versions" ]; then
+ if [ -n "$profile" ]; then
+ for v in $(profile_get_built_versions); do
+ echo $v
+ done
+ error=0
+ else
+ error=13
+ fi
+
+ elif [ "$cmd" == "start_testing" ]; then
+ start_testing
+ error=$?
+
+ elif [[ "$cmd" == "done" || "$cmd" == "reboot" ]]; then
+ break=1
+ error=0
+
+ elif [ "$cmd" == "resume" ]; then
+ version=$(echo "$line" | cut -d ',' -s -f 2)
+ test=$(echo "$line" | cut -d ',' -s -f 3)
+ result_file=$(echo "$line" | cut -d ',' -s -f 4)
+ verbose=$(echo "$line" | cut -d ',' -s -f 5)
+
+ execute_test resume "$version" "$test" "$verbose" "$result_file"
+ error=$?
+
+ elif [ "$cmd" == "run" ]; then
+ version=$(echo "$line" | cut -d ',' -s -f 2)
+ test=$(echo "$line" | cut -d ',' -s -f 3)
+ verbose=$(echo "$line" | cut -d ',' -s -f 4)
+
+ execute_test run "$version" "$test" "$verbose"
+ error=$?
+
+ elif [ "$cmd" == "echo" ]; then
+ error=0
+
+ elif [ "$cmd" == "version" ]; then
+ echo "$protocol_version"
+ error=0
+
+ else
+ error=10
+ fi
+
+ t=$(echo "($(date +%s.%N) - $start_time) * 1000 / 1" | bc)
+ echo "<--,$error,${errors[$error]},$t ms"
+
+ # Exit, if requested
+ [ "$break" == 1 ] && break
+done < "${1:-/dev/stdin}"
+
+# Make sure we tear-down everything we started
+done_testing
+
+# If the last command received was reboot, do it!
+if [ "$cmd" == "reboot" ]; then
+ sudo reboot
+fi
+
+exit 0