blob: e7b97a06d90d2a20269fc60553aee42b59537f28 [file]
#!/bin/bash -eu
set -o pipefail
HARDWIRED_MOCK_TOP=
# Uncomment this to be able to view the source tree after a test is run
# HARDWIRED_MOCK_TOP=/tmp/td
REAL_TOP="$(readlink -f "$(dirname "$0")"/../../..)"
function make_mock_top {
mock=$(mktemp -t -d st.XXXXXX)
echo "$mock"
}
MOCK_TOP_TO_CLEAN_UP=()
WARMED_UP_MOCK_TOP_TO_CLEAN_UP=()
if [[ -n "$HARDWIRED_MOCK_TOP" ]]; then
MOCK_TOP="$HARDWIRED_MOCK_TOP"
elif [[ -z "${MOCK_TOP:-}" ]]; then
MOCK_TOP=$(make_mock_top)
MOCK_TOP_TO_CLEAN_UP+=("$MOCK_TOP")
fi
function warmup_mock_top {
info "Warming up mock top ..."
info "Mock top warmup archive: $WARMED_UP_MOCK_TOP"
cleanup_mock_top
mkdir -p "$MOCK_TOP"
cd "$MOCK_TOP"
create_mock_soong
run_soong
tar cf "$WARMED_UP_MOCK_TOP" --exclude out/.path_interposer_log *
}
function extend_mock_top {
local old_mock_top="$WARMED_UP_MOCK_TOP"
WARMED_UP_MOCK_TOP=$(mktemp -t soong_integration_tests_warmup.tar.XXXXXX)
WARMED_UP_MOCK_TOP_TO_CLEAN_UP+=("$WARMED_UP_MOCK_TOP")
info "Extending mock top with $1 ..."
info "New mock top archive: $WARMED_UP_MOCK_TOP"
cleanup_mock_top
mkdir -p "$MOCK_TOP"
cd "$MOCK_TOP"
tar xf "$old_mock_top"
$1
tar cf "$WARMED_UP_MOCK_TOP" --exclude out/.path_interposer_log *
}
function cleanup_mock_top {
cd /
if [[ -n "$MOCK_TOP" && -d "$MOCK_TOP" ]]; then
rm -fr "$MOCK_TOP"
fi
}
function cleanup_all {
cd /
if [[ -n "${MOCK_TOP_TO_CLEAN_UP:-}" ]]; then
for m in ${MOCK_TOP_TO_CLEAN_UP[@]}; do
if [[ -d "$m" ]]; then
rm -fr "$m"
fi
done
fi
if [[ -n "${WARMED_UP_MOCK_TOP_TO_CLEAN_UP:-}" ]]; then
for m in ${WARMED_UP_MOCK_TOP_TO_CLEAN_UP[@]}; do
if [[ -f "$m" ]]; then
rm -fr "$m"
fi
done
fi
}
function info {
echo -e "\e[92;1m[TEST HARNESS INFO]\e[0m" "$*"
}
function fail {
echo -e "\e[91;1mFAILED:\e[0m" "$*"
exit 1
}
function copy_directory {
local dir="$1"
local -r parent="$(dirname "$dir")"
mkdir -p "$MOCK_TOP/$parent"
cp -R "$REAL_TOP/$dir" "$MOCK_TOP/$parent"
}
function delete_directory {
rm -rf "$MOCK_TOP/$1"
}
function symlink_file {
local file="$1"
mkdir -p "$MOCK_TOP/$(dirname "$file")"
ln -s "$REAL_TOP/$file" "$MOCK_TOP/$file"
}
function symlink_directory {
local dir="$1"
mkdir -p "$MOCK_TOP/$dir"
# We need to symlink the contents of the directory individually instead of
# using one symlink for the whole directory because finder.go doesn't follow
# symlinks when looking for Android.bp files
for i in "$REAL_TOP/$dir"/*; do
i=$(basename "$i")
local target="$MOCK_TOP/$dir/$i"
local source="$REAL_TOP/$dir/$i"
if [[ -e "$target" ]]; then
if [[ ! -d "$source" || ! -d "$target" ]]; then
fail "Trying to symlink $dir twice"
fi
else
ln -s "$REAL_TOP/$dir/$i" "$MOCK_TOP/$dir/$i";
fi
done
}
function create_mock_soong {
copy_directory build/blueprint
copy_directory build/soong
copy_directory build/make
symlink_directory external/compiler-rt
symlink_directory external/go-cmp
symlink_directory external/golang-protobuf
symlink_directory external/licenseclassifier
symlink_directory external/pogreb
symlink_directory external/protobuf
symlink_directory external/python
symlink_directory external/spdx-tools
symlink_directory external/sqlite
symlink_directory external/starlark-go
symlink_directory prebuilts/build-tools
symlink_directory prebuilts/siso
symlink_directory prebuilts/clang/host
symlink_directory prebuilts/go
symlink_directory prebuilts/sdk
touch "$MOCK_TOP/Android.bp"
}
function setup {
cleanup_mock_top
mkdir -p "$MOCK_TOP"
echo
echo ----------------------------------------------------------------------------
info "Running test case \e[96;1m${FUNCNAME[1]}\e[0m"
cd "$MOCK_TOP"
tar xf "$WARMED_UP_MOCK_TOP"
}
# shellcheck disable=SC2120
function run_soong {
OUT_DIR=out USE_RBE=false TARGET_PRODUCT=test_arm64 TARGET_RELEASE=trunk_staging TARGET_BUILD_VARIANT=eng build/soong/soong_ui.bash --make-mode --skip-ninja --skip-config --soong-only --skip-soong-tests "$@"
}
function run_ninja {
OUT_DIR=out USE_RBE=false TARGET_PRODUCT=test_arm64 TARGET_RELEASE=trunk_staging TARGET_BUILD_VARIANT=eng build/soong/soong_ui.bash --make-mode --skip-config --soong-only --skip-soong-tests "$@"
}
info "Starting Soong integration test suite $(basename "$0")"
info "Mock top: $MOCK_TOP"
trap cleanup_all EXIT
export ALLOW_MISSING_DEPENDENCIES=true
export ALLOW_BP_UNDER_SYMLINKS=true
if [[ -z "${WARMED_UP_MOCK_TOP:-}" ]]; then
WARMED_UP_MOCK_TOP=$(mktemp -t soong_integration_tests_warmup.tar.gz.XXXXXX)
warmup_mock_top
WARMED_UP_MOCK_TOP_TO_CLEAN_UP+=("$WARMED_UP_MOCK_TOP")
fi
function scan_and_run_tests {
test_fns=("$@")
# find all test_ functions
# NB "declare -F" output is sorted, hence test order is deterministic
if [[ ${#test_fns[*]} -eq 0 ]]; then
while IFS= read test_fn; do
test_fns+=($test_fn)
done < <(declare -F | sed -n -e 's/^declare -f \(test_.*\)$/\1/p')
fi
info "Found ${#test_fns[*]} tests"
if [[ ${#test_fns[*]} -eq 0 ]]; then
fail "No tests found"
fi
for f in ${test_fns[*]}; do
$f
info "Completed test case \e[96;1m$f\e[0m"
done
}
function assert_files_equal {
if [ $# -ne 2 ]; then
echo "Usage: assert_files_equal file1 file2"
exit 1
fi
if ! cmp -s "$1" "$2"; then
echo "Files are different: $1 $2"
diff -u "$1" "$2"
exit 1
fi
}
function compare_incremental_files() {
local dir_full=$1; shift
local dir_incremental=$1; shift
count=0
for file_before in ${dir_full}/*.ninja*; do
basename=$(basename "$file_before")
file_after="${dir_incremental}/${basename}"
# The incremental file should be a superset of the full one.
if [[ "$basename" == "build.test_arm64.ninja" ]]; then
echo "Performing superset check for $basename..."
extra_lines=$(comm -23 <(sort "$file_before") <(sort "$file_after"))
if [[ -n "$extra_lines" ]]; then
# If there are extra lines, print an error and the differing lines, then exit.
echo "ERROR: $file_after is NOT a superset of $file_before."
echo "The following lines were in the 'before' file but not the 'after' file:"
echo "$extra_lines"
exit 1
fi
elif [[ "${basename}" == "build.test_arm64.ninja.globs_time" ]]; then
: # skip timestamp file
elif [[ "${basename}" == "build.test_arm64.ninja.glob_results" ]]; then
: # skip timestamp file
else
assert_files_equal $file_before $file_after
fi
((count++)) || true
done
echo "Compared $count ninja files"
}
function compare_files_parity() {
local dir_before=$1; shift
local dir_after=$1; shift
count=0
for file_before in ${dir_before}/*.*; do
basename="$(basename "${file_before}")"
file_after="${dir_after}/${basename}"
if [[ "${basename}" == "build.test_arm64.ninja.globs_time" ]]; then
: # skip timestamp file
elif [[ "${basename}" == "build.test_arm64.ninja.glob_results" ]]; then
: # skip timestamp file
else
assert_files_equal $file_before $file_after
((count++)) || true
fi
done
echo "Compared $count ninja files"
}
# Assumes an initial run of "run_soong SOONG_INCREMENTAL_ANALYSIS=true" and then
# the tree is modified.
function compare_incremental_and_full_analysis() {
run_soong SOONG_INCREMENTAL_ANALYSIS=true "$@"
mkdir incremental
cp -pr out/soong/build.test_arm64*.ninja* incremental
touch Android.bp
run_soong SOONG_INCREMENTAL_ANALYSIS=false "$@"
mkdir full
cp -pr out/soong/build.test_arm64*.ninja* full
compare_incremental_files full incremental
}