blob: f6146ca444037c1f7484816cb7ecd5d42b959840 [file] [log] [blame]
#!/bin/bash
# Copyright 2023 Google LLC
# SPDX-License-Identifier: BSD-2-Clause
#
# Common code for tests that compares the structure (boxes) of AVIF files
# metadata, dumped as xml, with golden files stored in tests/data/goldens/.
# Depends on the command line tool MP4Box.
#
# To use this, export a function called 'encode_test_files', then
# 'source' this file.
# The paths to avifenc and to the testdata dir will be passed as arguments.
# See 'test_cmd_enc_boxes_golden.sh' for an example.
set -e
if [[ "$#" -ge 1 ]]; then
# eval so that the passed in directory can contain variables.
ENCODER_DIR="$(eval echo "$1")"
else
# Assume "tests" is the current directory.
ENCODER_DIR="$(pwd)/.."
fi
if [[ "$#" -ge 2 ]]; then
# eval so that the passed in directory can contain variables.
MP4BOX_DIR="$(eval echo "$2")"
else
# Assume "tests" is the current directory.
MP4BOX_DIR="$(pwd)/../ext/gpac/bin/gcc"
fi
if [[ "$#" -ge 3 ]]; then
TESTDATA_DIR="$(eval echo "$3")"
else
TESTDATA_DIR="$(pwd)/data"
fi
if [[ "$#" -ge 4 && ! -z "$4" ]]; then
OUTPUT_DIR="$(eval echo "$4")/test_cmd_enc_boxes_golden"
else
OUTPUT_DIR="$(mktemp -d)"
fi
GOLDEN_DIR="${TESTDATA_DIR}/goldens"
AVIFENC="${ENCODER_DIR}/avifenc"
MP4BOX="${MP4BOX_DIR}/MP4Box"
# Colors for pretty formatting.
RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
NO_COLOR='\033[0m'
if [[ ! -x "$AVIFENC" ]]; then
echo "'$AVIFENC' does not exist or is not executable"
exit 1
fi
if [[ ! -x "$MP4BOX" ]]; then
echo "'$MP4BOX' does not exist or is not executable, build it by running ext/mp4box.sh"
exit 1
fi
mkdir -p "$OUTPUT_DIR"
echo "Outputting to $OUTPUT_DIR"
has_errors=false
# Cleanup
cleanup() {
# Only delete temp files if the test succeeded, to allow debugging in case or error.
if ! $has_errors; then
find ${OUTPUT_DIR} -type f \( -name '*.avif' -o -name '*.xml' -o -name '*.xml.diff' \) -delete
fi
}
trap cleanup EXIT
# Replaces parts of the xml that are brittle with 'REDACTED', typically because they depend
# on the size of the encoded pixels.
redact_xml() {
local f="$1"
# Remove item data offsets and size so that the xml is stable even if the encoded size changes.
# The first 2 regexes are for the iloc box, the next 2 are for the mdat box.
# Note that "-i.bak" works on both Linux and Mac https://stackoverflow.com/a/22084103
sed -i.bak -e 's/extent_offset="[0-9]*"/extent_offset="REDACTED"/g' \
-e 's/extent_length="[0-9]*"/extent_length="REDACTED"/g' \
-e 's/dataSize="[0-9]*"/dataSize="REDACTED"/g' \
-e 's/<MediaDataBox\(.*\) Size="[0-9]*"/<MediaDataBox\1 Size="REDACTED"/g' \
"$f"
# For animations.
sed -i.bak -e 's/CreationTime="[0-9]*"/CreationTime="REDACTED"/g' \
-e 's/ModificationTime="[0-9]*"/ModificationTime="REDACTED"/g' \
-e 's/<Sample\(.*\) size="[0-9]*"/<Sample\1 size="REDACTED"/g' \
-e 's/<SampleSizeEntry\(.*\) Size="[0-9]*"/<SampleSizeEntry\1 Size="REDACTED"/g' \
-e 's/<OBU\(.*\) size="[0-9]*"/<OBU\1 size="REDACTED"/g' \
-e 's/<Tile\(.*\) size="[0-9]*"/<Tile\1 size="REDACTED"/g' \
"$f"
rm "$f.bak"
}
diff_with_goldens() {
echo "==="
diff_command=""
diff_command_with_full_paths=""
num_files=0
num_passed=0
files_with_diffs=""
files_missing_golden=""
for f in $(find . -name '*.avif'); do
num_files=$((num_files + 1))
echo "Testing $f"
xml="$(basename "$f").xml" # Remove leading ./ for prettier paths.
# Dump the file structure as XML
"${MP4BOX}" -dxml -out "$xml" "$f"
redact_xml "$xml"
# Compare with golden.
golden="$GOLDEN_DIR/$xml"
if [[ -f "$golden" ]]; then
golden_differs=false
diff -u "$golden" "$xml" > "$xml.diff" || golden_differs=true
if $golden_differs; then
has_errors=true
files_with_diffs="${files_with_diffs}$(basename "$f") "
echo "FAILED: Differences found for file $xml, see details in $OUTPUT_DIR/$xml.diff" >&2
echo "Expected file: $golden" >&2
echo "Actual file: $OUTPUT_DIR/$xml" >&2
cp "$golden" "$xml.golden" # Copy golden to output directory for easier debugging.
# "diff --color" is the default value if DIFFTOOL is not set.
printf -v diff_command "${diff_command}\${DIFFTOOL:-diff --color} $xml.golden $xml\n"
# Same as diff_command but with absolute paths and a two-space indent.
printf -v diff_command_with_full_paths "${diff_command_with_full_paths} \${DIFFTOOL:-diff --color} $OUTPUT_DIR/$xml.golden $OUTPUT_DIR/$xml\n"
else
num_passed=$((num_passed + 1))
# Remove files related to tests that passed to reduce visual noise in the output dir.
rm "$f"
rm "$xml"
rm "$xml.diff" # Delete empty diff files.
echo "Passed"
fi
else
files_missing_golden="${files_missing_golden}$(basename "$f") "
echo "FAILED: Missing golden file $golden" >&2
echo "Actual file: $OUTPUT_DIR/$xml" >&2
has_errors=true
fi
echo "---"
done
echo "==="
}
pushd "${OUTPUT_DIR}"
set -x # Print the encoding command lines.
# Expect this function to be exported by the calling script.
encode_test_files "${AVIFENC}" "${TESTDATA_DIR}"
set +x
diff_with_goldens
popd > /dev/null
if $has_errors; then
cat >&2 << EOF
$(echo -e "${RED}")Golden test failed. Num passed: ${num_passed}/${num_files}$(echo -e "${NO_COLOR}")
Files that caused diffs: ${files_with_diffs}
Files with missing golden: ${files_missing_golden}
# IF RUNNING ON GITHUB
Check the workflow step below called "How to fix failing tests" for instructions on how to debug/fix this test.
# IF RUNNING LOCALLY
View diffs with: (set DIFFTOOL to your favorite tool if needed)
$(echo -e "${BLUE}")$diff_command_with_full_paths$(echo -e "${NO_COLOR}")
Update golden files with:
$(echo -e "${BLUE}")cp $OUTPUT_DIR/*.xml "$GOLDEN_DIR"$(echo -e "${NO_COLOR}")
EOF
cat > "$OUTPUT_DIR/README.txt" << EOF
To debug test failures, look at the .xml.diff files or run (from this directory):
$diff_command
If the diffs are expected, update the golden files by copying the .xml files, e.g.:
cp *.xml $HOME/git/libavif/tests/data/goldens
You can also debug the test locally by setting AVIF_ENABLE_GOLDEN_TESTS=ON in cmake.
EOF
exit 1
else
echo -e "${GREEN}Golden tests passed (${num_passed} tests)${NO_COLOR}"
fi
exit 0