blob: ac81defa992e65d836c5533e90647f3506199514 [file] [log] [blame]
#!/bin/bash
# RuniOSUnitTestsUnderSimulator.sh.sh
# Copyright 2008-2012 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
#
# Runs all unittests through the iOS simulator. We don't handle running them
# on the device. To run on the device just choose "run".
set -o errexit
set -o nounset
# Uncomment the next line to trace execution.
#set -o verbose
# GTM_DEVICE_TYPE -
# Set to 'iPhone' or 'iPad' to control which simulator is used.
GTM_DEVICE_TYPE=${GTM_DEVICE_TYPE:=iPhone}
# GTM_SIMULATOR_SDK_VERSION
# Set to something like 5.1 to use a specific SDK version (the needed
# simulator support must be installed). Use 'default' to get the dev tools
# default value.
GTM_SIMULATOR_SDK_VERSION=${GTM_SIMULATOR_SDK_VERSION:=default}
# GTM_SIMULATOR_START_TIMEOUT
# Controls the simulator startup timeout. Things like machine load, running
# on a VM, etc.; can cause the startup to take longer.
GTM_SIMULATOR_START_TIMEOUT=${GTM_SIMULATOR_START_TIMEOUT:=120}
# GTM_KILL_SLEEP_TIME
# Controls the time the script will sleep when it kills a process. Things
# like machine load, running on a VM, etc.; can cause the time for things to
# die to take longer.
GTM_KILL_SLEEP_TIME=${GTM_KILL_SLEEP_TIME:=5}
# GTM_SIMULATOR_USER_HOME -
# Root directory for simulator file system. Allows persistence across runs.
GTM_SIMULATOR_USER_HOME=${GTM_SIMULATOR_USER_HOME:=default}
# GTM_SIMULATOR_EXTRA_ENV -
# Space separated set env variables in format of "KEY1=value1 KEY2=value2"
GTM_SIMULATOR_EXTRA_ENV=${GTM_SIMULATOR_EXTRA_ENV:=default}
# GTM_DISABLE_TERMINATION
# Set to a non-zero value so that the app doesn't terminate when it's finished
# running tests. This is useful when using it with external tools such
# as Instruments.
# GTM_REMOVE_GCOV_DATA
# Before starting the test, remove any *.gcda files for the current run so
# you won't get errors when the source file has changed and the data can't
# be merged.
#
GTM_REMOVE_GCOV_DATA=${GTM_REMOVE_GCOV_DATA:=0}
# GTM_USE_TEST_AFTER_BUILD
# When set to 1, tests are run only when TEST_AFTER_BUILD is set to "YES".
# This can be used to have tests run as an after build step when running
# from the command line, but not when running from within XCode.
GTM_USE_TEST_AFTER_BUILD=${GTM_USE_TEST_AFTER_BUILD:=0}
function realpath() {
OLDPWD=${PWD}
cd "$(dirname "$1")"
TARGET="$(readlink "$(basename "$1")")"
while [[ -n "${TARGET}" ]]; do
cd "$(dirname "${TARGET}")"
TARGET=$(readlink "$(basename "$1")")
done
REALPATH="${PWD}/$(basename "$1")"
cd "${OLDPWD}"
echo "${REALPATH}"
}
readonly ScriptDir=$(dirname "$(echo $0 | sed -e "s,^\([^/]\),$(pwd)/\1,")")
readonly ScriptName=$(basename "$0")
readonly ThisScript="${ScriptDir}/${ScriptName}"
# iossim fails if it's behind the right combination of symlinks
readonly SimExecutable="$(realpath "${ScriptDir}/iossim")"
# Simulator process name changes from Xcode 6.
if [[ ${XCODE_VERSION_MINOR} -ge "0600" ]]; then
readonly SimulatorProcessName='iOS Simulator'
else
readonly SimulatorProcessName='iPhone Simulator'
fi
# Variables that follow Xcode unittesting conventions
readonly TEST_BUNDLE_PATH="${TEST_BUNDLE_PATH:=${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.${WRAPPER_EXTENSION}}"
TEST_HOST="${TEST_HOST:=}"
XCODE_VERSION_MINOR=${XCODE_VERSION_MINOR:=0000}
# Old Xcode versions don't set TEST_AFTER_BUILD, default it to avoid errors
# for unset variables.
TEST_AFTER_BUILD=${TEST_AFTER_BUILD:=NO}
# Some helpers.
GTMXcodeNote() {
echo "${ThisScript}:${1}: note: GTM ${2}"
}
GTMXcodeWarning() {
echo "${ThisScript}:${1}: warning: GTM ${2}"
}
GTMXcodeError() {
echo "${ThisScript}:${1}: error: GTM ${2}"
}
GTMFakeUnitTestingMsg() {
echo "${DEVELOPER_DIR}/Tools/RunPlatformUnitTests.include:${1}: ${2}: ${3}"
}
GTMKillNamedAndWait() {
# If there is something killed, sleep for few seconds to let the process
# spin down so it isn't still seen as running when the next thing tries to
# launch it.
/usr/bin/killall "${1}" 2> /dev/null && sleep "${GTM_KILL_SLEEP_TIME}" || true
}
GTMKillSimulator() {
GTMKillNamedAndWait "${SimulatorProcessName}"
}
GTMResetSimulator() {
GTMKillSimulator
device_id=`xcrun simctl list | grep "${GTM_DEVICE_TYPE} (" | sed -n 2p | \
cut -d "(" -f2 | cut -d ")" -f1`
xcrun simctl erase $device_id || true
}
# Honor TEST_AFTER_BUILD if requested.
if [[ "$GTM_USE_TEST_AFTER_BUILD" == 1 && "$TEST_AFTER_BUILD" == "NO" ]]; then
GTMXcodeNote ${LINENO} "Skipping running of unittests since TEST_AFTER_BUILD=NO."
exit 0
fi
# Only support simulator builds.
if [[ "${PLATFORM_NAME}" != "iphonesimulator" ]]; then
GTMXcodeNote ${LINENO} "Skipping running of unittests for device build."
exit 0
fi
# Make sure the iossim executable exists and is executable.
if [[ ! -x "${SimExecutable}" ]]; then
GTMXcodeError ${LINENO} "Unable to run tests: ${SimExecutable} was not found/executable."
exit 1
fi
# Make sure the test bundle (and test host) exists.
if [[ ! -e "${TEST_BUNDLE_PATH}" ]]; then
GTMXcodeError ${LINENO} "Unable to find test bundle: ${TEST_BUNDLE_PATH}"
exit 1
fi
if [[ -n "${TEST_HOST}" ]]; then
if [[ ! -e "${TEST_HOST}" ]]; then
GTMXcodeError ${LINENO} "Unable to find test host: ${TEST_HOST}"
exit 1
fi
fi
# Sanity check Xcode version.
if [[ "${XCODE_VERSION_MINOR}" -lt "0430" ]]; then
GTMXcodeWarning ${LINENO} "Unit testing process not supported on Xcode < 4.3 (${XCODE_VERSION_MINOR})"
fi
# We kill the iPhone Simulator because otherwise we run into issues where
# the unittests fail becuase the simulator is currently running, and
# at this time the iOS SDK won't allow two simulators running at the same
# time.
GTMKillSimulator
if [[ $GTM_REMOVE_GCOV_DATA -ne 0 ]]; then
if [[ "${OBJECT_FILE_DIR}-${CURRENT_VARIANT}" != "-" ]]; then
if [[ -d "${OBJECT_FILE_DIR}-${CURRENT_VARIANT}" ]]; then
GTMXcodeNote ${LINENO} "Removing any .gcda files"
(cd "${OBJECT_FILE_DIR}-${CURRENT_VARIANT}" && \
find . -type f -name "*.gcda" -print0 | xargs -0 rm -f )
fi
fi
fi
#
# Build up the command line to run.
#
GTM_TEST_APP_NAME=
GTM_TEST_COMMAND=(
"${SimExecutable}"
"-d" "${GTM_DEVICE_TYPE}"
"-t" "${GTM_SIMULATOR_START_TIMEOUT}"
)
if [[ "${GTM_SIMULATOR_SDK_VERSION}" != "default" ]] ; then
GTM_TEST_COMMAND+=( "-s" "${GTM_SIMULATOR_SDK_VERSION}" )
fi
if [[ "${GTM_SIMULATOR_USER_HOME}" != "default" ]]; then
GTM_TEST_COMMAND+=( "-u" "${GTM_SIMULATOR_USER_HOME}" )
fi
if [[ "${GTM_SIMULATOR_EXTRA_ENV}" != "default" ]]; then
EXTRA_ENV_ARRAY=(${GTM_SIMULATOR_EXTRA_ENV})
for i in "${!EXTRA_ENV_ARRAY[@]}"
do
GTM_TEST_COMMAND+=( "-e" ${EXTRA_ENV_ARRAY[i]} )
done
fi
if [[ -n "${TEST_HOST}" ]]; then
# When using a test host, it is usually set to the executable within the app
# bundle, back up one to point at the bundle.
GTM_TEST_APP_NAME=$(basename "${TEST_HOST}")
TEST_HOST_EXTENSION="${GTM_TEST_APP_NAME##*.}"
if [[ "${TEST_HOST_EXTENSION}" != "app" ]] ; then
TEST_HOST=$(dirname "${TEST_HOST}")
else
# Drop the extension.
GTM_TEST_APP_NAME="${GTM_TEST_APP_NAME%.*}"
fi
# Yes the DYLD_INSERT_LIBRARIES value below looks odd, that is found from
# looking at what Xcode sets when it invokes unittests directly.
GTM_TEST_COMMAND+=(
"-e" "DYLD_INSERT_LIBRARIES=/../../Library/PrivateFrameworks/IDEBundleInjection.framework/IDEBundleInjection"
"-e" "XCInjectBundle=${TEST_BUNDLE_PATH}"
"-e" "XCInjectBundleInto=${TEST_HOST}"
"${TEST_HOST}"
)
else
GTM_TEST_APP_NAME=$(basename "${TEST_BUNDLE_PATH}")
TEST_BUNDLE_EXTENSION="${GTM_TEST_APP_NAME##*.}"
if [[ "${TEST_BUNDLE_EXTENSION}" == "app" ]] ; then
# Drop the extension.
GTM_TEST_APP_NAME="${GTM_TEST_APP_NAME%.*}"
fi
GTM_TEST_COMMAND+=( "${TEST_BUNDLE_PATH}" )
fi
GTM_TEST_COMMAND+=(
"-NSTreatUnknownArgumentsAsOpen" "NO"
"-ApplePersistenceIgnoreState" "YES"
"-SenTest" "All"
)
# There was a test host, add the test bundle at the end as an arg to the app.
if [[ -n "${TEST_HOST}" ]]; then
GTM_TEST_COMMAND+=( "${TEST_BUNDLE_PATH}" )
fi
# Kill the test host just to make sure it wasn't left running and will cause
# problems.
GTMKillNamedAndWait "${GTM_TEST_APP_NAME}"
# These two lines seem to fake out Xcode just enough that its log parser acts
# as though Xcode were running the unit test via the UI. This prevents false
# failures based on lines including "error" and such (which tends to happen in
# raw NSLogs in code).
GTMFakeUnitTestingMsg ${LINENO} "note" "Started tests for architectures 'i386'"
GTMFakeUnitTestingMsg ${LINENO} "note" "Running tests for architecture 'i386' (GC OFF)"
set +e
"${GTM_TEST_COMMAND[@]}"
TEST_HOST_RESULT=$?
set -e
GTMKillSimulator
GTMKillNamedAndWait "${GTM_TEST_APP_NAME}"
# If the simulator fails to open with error FBSOpenApplicationErrorDomain:4,
# reset the sim and try again (Known simulator issue for Xcode 6).
if [ ${TEST_HOST_RESULT} -eq 4 ] && [ ${XCODE_VERSION_MINOR} -ge "0600" ]; then
GTMFakeUnitTestingMsg ${LINENO} "note" "Simulator failed to open with result $TEST_HOST_RESULT, trying again."
GTMResetSimulator
set +e
"${GTM_TEST_COMMAND[@]}"
TEST_HOST_RESULT=$?
set -e
fi
if [[ ${TEST_HOST_RESULT} -ne 0 ]]; then
GTMXcodeError ${LINENO} "Tests failed."
exit 1
fi
exit 0