Add golden tests for the written AVIF metadata structure. (#1554)
Depends on MP4Box (from the gpac project) to dump an AVIF file's
metadata as XML.
This test catches changes in write.c that may or may not be intentional.
On failure, it prints instructions on how to update the goldens if needed.
The test is disabled by default, but enabled in a new CI workflow.
diff --git a/.github/workflows/ci-linux-golden-tests.yml b/.github/workflows/ci-linux-golden-tests.yml
new file mode 100644
index 0000000..0b0075f
--- /dev/null
+++ b/.github/workflows/ci-linux-golden-tests.yml
@@ -0,0 +1,90 @@
+# Workflow that builds libabvif with aom and libyuv, also builds MP4box,
+# and runs tests with "golden" in their name. Test results are saved as artifacts
+# which can be downloaded from GitHub'S UI or with 'gh run download'.
+
+name: CI Linux Golden Tests
+on: [push, pull_request]
+
+permissions:
+ contents: read
+
+jobs:
+ build-shared-run-golden-tests:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest]
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: Set GCC & G++ 10 compiler (on Linux)
+ run: echo "CC=gcc-10" >> $GITHUB_ENV && echo "CXX=g++-10" >> $GITHUB_ENV
+ - uses: actions/setup-python@v4
+ with:
+ python-version: '3.x'
+
+ - name: Cache external dependencies
+ id: cache-ext
+ uses: actions/cache@v3
+ with:
+ path: ext
+ key: ${{ runner.os }}-golden-tests-${{ hashFiles('ext/aom.cmd', 'ext/libyuv.cmd', 'ext/mp4box.sh') }}
+ - name: Setup cmake
+ uses: jwlawson/actions-setup-cmake@v1.13
+ with:
+ cmake-version: '3.13.x'
+ - name: Print cmake version
+ run: cmake --version
+ - uses: ilammy/setup-nasm@v1
+ with:
+ version: 2.15.05
+ - uses: seanmiddleditch/gha-setup-ninja@v3
+ - name: Build aom
+ if: steps.cache-ext.outputs.cache-hit != 'true'
+ working-directory: ./ext
+ run: bash -e aom.cmd
+ - name: Build libyuv
+ if: steps.cache-ext.outputs.cache-hit != 'true'
+ working-directory: ./ext
+ run: bash -e libyuv.cmd
+ - name: Build mp4box
+ if: steps.cache-ext.outputs.cache-hit != 'true'
+ working-directory: ./ext
+ run: bash -e mp4box.sh
+
+ - name: Prepare libavif (cmake)
+ run: >
+ mkdir build && cd build
+
+ cmake .. -G Ninja
+ -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF
+ -DAVIF_CODEC_AOM=ON -DAVIF_LOCAL_AOM=ON
+ -DAVIF_LOCAL_LIBYUV=ON
+ -DAVIF_BUILD_EXAMPLES=ON -DAVIF_BUILD_APPS=ON
+ -DAVIF_BUILD_TESTS=ON -DAVIF_ENABLE_GOLDEN_TESTS=ON
+ -DAVIF_ENABLE_GTEST=OFF
+ -DGOLDEN_TESTS_OUTPUT_DIR=${{ runner.temp }}/golden_tests
+ - name: Build libavif (ninja)
+ working-directory: ./build
+ run: ninja
+ - name: Run AVIF golden tests
+ working-directory: ./build
+ # Runs test that have "golden" in their name.
+ run: ctest -j $(getconf _NPROCESSORS_ONLN) --output-on-failure -R golden
+
+ # See https://docs.github.com/en/actions/using-workflows/storing-workflow-data-as-artifacts
+ - name: Archive golden tests output for debugging
+ if: failure()
+ uses: actions/upload-artifact@v3
+ with:
+ name: golden-tests-output
+ path: ${{ runner.temp }}/golden_tests/**/*
+
+ # Print instructions to help fix the tests.
+ - name: How to fix failing tests
+ if: failure()
+ run: >
+ echo "If the tests fail, download the test results from the Artifacts list at https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} or with:
+ gh run download --dir /tmp/golden-tests-output --repo ${{ github.repository }} --name golden-tests-output ${{ github.run_id }}
+ Then look at the instructions in the unzipped file or in /tmp/golden-tests-output if you used the command line."
diff --git a/.gitignore b/.gitignore
index 57748fb..c98df16 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@
/ext/avm
/ext/dav1d
/ext/googletest
+/ext/gpac
/ext/libjpeg
/ext/libgav1
/ext/libpng
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 378cbf6..d2d4c60 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -588,6 +588,9 @@
option(AVIF_BUILD_APPS "Build avif apps." OFF)
option(AVIF_BUILD_TESTS "Build avif tests." OFF)
+option(AVIF_ENABLE_GOLDEN_TESTS
+ "Build tests that compare encoding outputs to golden files. Needs AVIF_BUILD_APPS=ON, and depends on MP4box which can be built with ext/mp4box.sh" OFF
+)
option(AVIF_ENABLE_GTEST
"Build avif C++ tests, which depend on GoogleTest. Requires GoogleTest. Has no effect unless AVIF_BUILD_TESTS is ON." ON
)
diff --git a/ext/mp4box.sh b/ext/mp4box.sh
new file mode 100755
index 0000000..a519981
--- /dev/null
+++ b/ext/mp4box.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+#
+# Local build of MP4Box (part of the gpac project) for tests.
+
+set -e
+
+git clone https://github.com/gpac/gpac.git
+
+cd gpac
+git checkout b34e385 # GPAC 2.2.1
+
+./configure --static-bin
+make -j
+# MP4Box is in ext/gpac/bin/gcc/MP4Box
+
+cd ..
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 4af8636..64b9f1f 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -270,6 +270,22 @@
${CMAKE_CURRENT_SOURCE_DIR}/data
)
+ if(AVIF_ENABLE_GOLDEN_TESTS AND AVIF_CODEC_AOM_ENCODE)
+ # test_cmd_enc_boxes_golden.sh depends on MP4Box
+ # Only allow a locally built version to avoid differences with versioning.
+ set(MP4BOX_DIR ${CMAKE_SOURCE_DIR}/ext/gpac/bin/gcc/)
+ if(NOT EXISTS ${MP4BOX_DIR}/MP4Box)
+ message(FATAL_ERROR "AVIF_ENABLE_GOLDEN_TESTS is ON but ${MP4BOX_DIR}/MP4Box is missing. Run ext/mp4box.sh")
+ endif()
+
+ set(GOLDEN_TESTS_OUTPUT_DIR "" CACHE STRING "Output path for golden tests (will be a temp dir if empty)")
+
+ add_test(NAME test_cmd_enc_boxes_golden
+ COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/test_cmd_enc_boxes_golden.sh ${CMAKE_BINARY_DIR} ${MP4BOX_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/data ${GOLDEN_TESTS_OUTPUT_DIR}
+ )
+ endif()
+
if(NOT AVIF_CODEC_AOM OR NOT AVIF_CODEC_AOM_ENCODE)
# Only aom encoder supports AV1 lossless encoding.
set_property(TEST test_cmd_animation PROPERTY DISABLED True)
diff --git a/tests/data/goldens/circle-trns-after-plte.png.avif.xml b/tests/data/goldens/circle-trns-after-plte.png.avif.xml
new file mode 100644
index 0000000..d621d4f
--- /dev/null
+++ b/tests/data/goldens/circle-trns-after-plte.png.avif.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ISOBaseMediaFileTrace>
+<!--MP4Box dump trace-->
+<IsoMediaFile xmlns="urn:mpeg:isobmff:schema:file:2016" Name="circle-trns-after-plte.png.avif">
+<FileTypeBox Size="32" Type="ftyp" Specification="p12" Container="file otyp" MajorBrand="avif" MinorVersion="0">
+<BrandEntry AlternateBrand="avif"/>
+<BrandEntry AlternateBrand="mif1"/>
+<BrandEntry AlternateBrand="miaf"/>
+<BrandEntry AlternateBrand="MA1A"/>
+</FileTypeBox>
+<MetaBox Size="397" Type="meta" Version="0" Flags="0" Specification="p12" Container="file moov trak moof traf udta" >
+<HandlerBox Size="40" Type="hdlr" Version="0" Flags="0" Specification="p12" Container="mdia meta minf" hdlrType="pict" Name="libavif" reserved1="0" reserved2="data:application/octet-string,000000000000000000000000">
+</HandlerBox>
+<PrimaryItemBox Size="14" Type="pitm" Version="0" Flags="0" Specification="p12" Container="meta" item_ID="1">
+</PrimaryItemBox>
+<ItemLocationBox Size="44" Type="iloc" Version="0" Flags="0" Specification="p12" Container="meta" offset_size="4" length_size="4" base_offset_size="0" index_size="0">
+<ItemLocationEntry item_ID="1" data_reference_index="0" base_offset="0" construction_method="0">
+<ItemExtentEntry extent_offset="REDACTED" extent_length="REDACTED" extent_index="0" />
+</ItemLocationEntry>
+<ItemLocationEntry item_ID="2" data_reference_index="0" base_offset="0" construction_method="0">
+<ItemExtentEntry extent_offset="REDACTED" extent_length="REDACTED" extent_index="0" />
+</ItemLocationEntry>
+</ItemLocationBox>
+<ItemInfoBox Size="66" Type="iinf" Version="0" Flags="0" Specification="p12" Container="meta" >
+<ItemInfoEntryBox Size="26" Type="infe" Version="2" Flags="0" Specification="p12" Container="iinf" item_ID="1" item_protection_index="0" item_name="Color" content_type="(null)" content_encoding="(null)" item_type="av01">
+</ItemInfoEntryBox>
+<ItemInfoEntryBox Size="26" Type="infe" Version="2" Flags="0" Specification="p12" Container="iinf" item_ID="2" item_protection_index="0" item_name="Alpha" content_type="(null)" content_encoding="(null)" item_type="av01">
+</ItemInfoEntryBox>
+</ItemInfoBox>
+<ItemReferenceBox Size="26" Type="iref" Version="0" Flags="0" Specification="p12" Container="meta" >
+<ItemReferenceBox Size="14" Type="auxl" Specification="p12" Container="iref" from_item_id="2">
+<ItemReferenceBoxEntry ItemID="1"/>
+</ItemReferenceBox>
+</ItemReferenceBox>
+<ItemPropertiesBox Size="195" Type="iprp" Specification="iff" Container="meta" >
+<ItemPropertyContainerBox Size="157" Type="ipco" Specification="iff" Container="iprp" >
+<ImageSpatialExtentsPropertyBox Size="20" Type="ispe" Version="0" Flags="0" Specification="iff" Container="ipco" image_width="100" image_height="60">
+</ImageSpatialExtentsPropertyBox>
+<PixelInformationPropertyBox Size="16" Type="pixi" Version="0" Flags="0" Specification="iff" Container="ipco" >
+<BitPerChannel bits_per_channel="8"/>
+<BitPerChannel bits_per_channel="8"/>
+<BitPerChannel bits_per_channel="8"/>
+</PixelInformationPropertyBox>
+<AV1ConfigurationBox>
+<AV1Config version="1" profile="1" level_idx0="0" tier="0" high_bitdepth="0" twelve_bit="0" monochrome="0" chroma_subsampling_x="0" chroma_subsampling_y="0" chroma_sample_position="0" initial_presentation_delay="1" OBUs_count="0">
+</AV1Config>
+</AV1ConfigurationBox>
+<ColourInformationBox Size="19" Type="colr" Specification="iff" Container="video_sample_entry ipco encv resv" colour_type="nclx" colour_primaries="1" transfer_characteristics="13" matrix_coefficients="6" full_range_flag="1">
+</ColourInformationBox>
+<PixelInformationPropertyBox Size="14" Type="pixi" Version="0" Flags="0" Specification="iff" Container="ipco" >
+<BitPerChannel bits_per_channel="8"/>
+</PixelInformationPropertyBox>
+<AV1ConfigurationBox>
+<AV1Config version="1" profile="0" level_idx0="0" tier="0" high_bitdepth="0" twelve_bit="0" monochrome="1" chroma_subsampling_x="1" chroma_subsampling_y="1" chroma_sample_position="0" initial_presentation_delay="1" OBUs_count="0">
+</AV1Config>
+</AV1ConfigurationBox>
+<AuxiliaryTypePropertyBox Size="56" Type="auxC" Version="0" Flags="0" Specification="iff" Container="ipco" aux_type="urn:mpeg:mpegB:cicp:systems:auxiliary:alpha" aux_subtype="">
+</AuxiliaryTypePropertyBox>
+</ItemPropertyContainerBox>
+<ItemPropertyAssociationBox Size="30" Type="ipma" Version="0" Flags="0" Specification="iff" Container="iprp" entry_count="2">
+<AssociationEntry item_ID="1" association_count="4">
+<Property index="1" essential="0"/>
+<Property index="2" essential="0"/>
+<Property index="3" essential="1"/>
+<Property index="4" essential="0"/>
+</AssociationEntry>
+<AssociationEntry item_ID="2" association_count="4">
+<Property index="1" essential="0"/>
+<Property index="5" essential="0"/>
+<Property index="6" essential="1"/>
+<Property index="7" essential="0"/>
+</AssociationEntry>
+</ItemPropertyAssociationBox>
+</ItemPropertiesBox>
+</MetaBox>
+<MediaDataBox Size="REDACTED" Type="mdat" Specification="p12" Container="file" dataSize="REDACTED">
+</MediaDataBox>
+</IsoMediaFile>
+<Tracks>
+</Tracks>
+</ISOBaseMediaFileTrace>
diff --git a/tests/data/goldens/dog_exif_extended_xmp_icc.jpg.avif.xml b/tests/data/goldens/dog_exif_extended_xmp_icc.jpg.avif.xml
new file mode 100644
index 0000000..71ea77f
--- /dev/null
+++ b/tests/data/goldens/dog_exif_extended_xmp_icc.jpg.avif.xml
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ISOBaseMediaFileTrace>
+<!--MP4Box dump trace-->
+<IsoMediaFile xmlns="urn:mpeg:isobmff:schema:file:2016" Name="dog_exif_extended_xmp_icc.jpg.avif">
+<FileTypeBox Size="32" Type="ftyp" Specification="p12" Container="file otyp" MajorBrand="avif" MinorVersion="0">
+<BrandEntry AlternateBrand="avif"/>
+<BrandEntry AlternateBrand="mif1"/>
+<BrandEntry AlternateBrand="miaf"/>
+<BrandEntry AlternateBrand="MA1B"/>
+</FileTypeBox>
+<MetaBox Size="495" Type="meta" Version="0" Flags="0" Specification="p12" Container="file moov trak moof traf udta" >
+<HandlerBox Size="40" Type="hdlr" Version="0" Flags="0" Specification="p12" Container="mdia meta minf" hdlrType="pict" Name="libavif" reserved1="0" reserved2="data:application/octet-string,000000000000000000000000">
+</HandlerBox>
+<PrimaryItemBox Size="14" Type="pitm" Version="0" Flags="0" Specification="p12" Container="meta" item_ID="1">
+</PrimaryItemBox>
+<ItemLocationBox Size="86" Type="iloc" Version="0" Flags="0" Specification="p12" Container="meta" offset_size="4" length_size="4" base_offset_size="0" index_size="0">
+<ItemLocationEntry item_ID="1" data_reference_index="0" base_offset="0" construction_method="0">
+<ItemExtentEntry extent_offset="REDACTED" extent_length="REDACTED" extent_index="0" />
+</ItemLocationEntry>
+<ItemLocationEntry item_ID="2" data_reference_index="0" base_offset="0" construction_method="0">
+<ItemExtentEntry extent_offset="REDACTED" extent_length="REDACTED" extent_index="0" />
+</ItemLocationEntry>
+<ItemLocationEntry item_ID="3" data_reference_index="0" base_offset="0" construction_method="0">
+<ItemExtentEntry extent_offset="REDACTED" extent_length="REDACTED" extent_index="0" />
+</ItemLocationEntry>
+<ItemLocationEntry item_ID="4" data_reference_index="0" base_offset="0" construction_method="0">
+<ItemExtentEntry extent_offset="REDACTED" extent_length="REDACTED" extent_index="0" />
+</ItemLocationEntry>
+<ItemLocationEntry item_ID="5" data_reference_index="0" base_offset="0" construction_method="0">
+<ItemExtentEntry extent_offset="REDACTED" extent_length="REDACTED" extent_index="0" />
+</ItemLocationEntry>
+</ItemLocationBox>
+<ItemInfoBox Size="144" Type="iinf" Version="0" Flags="0" Specification="p12" Container="meta" >
+<ItemInfoEntryBox Size="26" Type="infe" Version="2" Flags="0" Specification="p12" Container="iinf" item_ID="1" item_protection_index="0" item_name="Color" content_type="(null)" content_encoding="(null)" item_type="grid">
+</ItemInfoEntryBox>
+<ItemInfoEntryBox Size="26" Type="infe" Version="2" Flags="1" Specification="p12" Container="iinf" item_ID="2" item_protection_index="0" item_name="Color" content_type="(null)" content_encoding="(null)" item_type="av01">
+</ItemInfoEntryBox>
+<ItemInfoEntryBox Size="26" Type="infe" Version="2" Flags="1" Specification="p12" Container="iinf" item_ID="3" item_protection_index="0" item_name="Color" content_type="(null)" content_encoding="(null)" item_type="av01">
+</ItemInfoEntryBox>
+<ItemInfoEntryBox Size="26" Type="infe" Version="2" Flags="1" Specification="p12" Container="iinf" item_ID="4" item_protection_index="0" item_name="Color" content_type="(null)" content_encoding="(null)" item_type="av01">
+</ItemInfoEntryBox>
+<ItemInfoEntryBox Size="26" Type="infe" Version="2" Flags="1" Specification="p12" Container="iinf" item_ID="5" item_protection_index="0" item_name="Color" content_type="(null)" content_encoding="(null)" item_type="av01">
+</ItemInfoEntryBox>
+</ItemInfoBox>
+<ItemReferenceBox Size="32" Type="iref" Version="0" Flags="0" Specification="p12" Container="meta" >
+<ItemReferenceBox Size="20" Type="dimg" Specification="p12" Container="iref" from_item_id="1">
+<ItemReferenceBoxEntry ItemID="2"/>
+<ItemReferenceBoxEntry ItemID="3"/>
+<ItemReferenceBoxEntry ItemID="4"/>
+<ItemReferenceBoxEntry ItemID="5"/>
+</ItemReferenceBox>
+</ItemReferenceBox>
+<ItemPropertiesBox Size="167" Type="iprp" Specification="iff" Container="meta" >
+<ItemPropertyContainerBox Size="104" Type="ipco" Specification="iff" Container="iprp" >
+<ImageSpatialExtentsPropertyBox Size="20" Type="ispe" Version="0" Flags="0" Specification="iff" Container="ipco" image_width="4032" image_height="3024">
+</ImageSpatialExtentsPropertyBox>
+<PixelInformationPropertyBox Size="16" Type="pixi" Version="0" Flags="0" Specification="iff" Container="ipco" >
+<BitPerChannel bits_per_channel="8"/>
+<BitPerChannel bits_per_channel="8"/>
+<BitPerChannel bits_per_channel="8"/>
+</PixelInformationPropertyBox>
+<ColourInformationBox Size="19" Type="colr" Specification="iff" Container="video_sample_entry ipco encv resv" colour_type="nclx" colour_primaries="2" transfer_characteristics="2" matrix_coefficients="6" full_range_flag="1">
+</ColourInformationBox>
+<ImageRotationBox Size="9" Type="irot" Specification="iff" Container="ipco" angle="270">
+</ImageRotationBox>
+<ImageSpatialExtentsPropertyBox Size="20" Type="ispe" Version="0" Flags="0" Specification="iff" Container="ipco" image_width="2016" image_height="1512">
+</ImageSpatialExtentsPropertyBox>
+<AV1ConfigurationBox>
+<AV1Config version="1" profile="0" level_idx0="12" tier="0" high_bitdepth="0" twelve_bit="0" monochrome="0" chroma_subsampling_x="1" chroma_subsampling_y="1" chroma_sample_position="0" initial_presentation_delay="1" OBUs_count="0">
+</AV1Config>
+</AV1ConfigurationBox>
+</ItemPropertyContainerBox>
+<ItemPropertyAssociationBox Size="55" Type="ipma" Version="0" Flags="0" Specification="iff" Container="iprp" entry_count="5">
+<AssociationEntry item_ID="1" association_count="4">
+<Property index="1" essential="0"/>
+<Property index="2" essential="0"/>
+<Property index="3" essential="0"/>
+<Property index="4" essential="1"/>
+</AssociationEntry>
+<AssociationEntry item_ID="2" association_count="5">
+<Property index="5" essential="0"/>
+<Property index="2" essential="0"/>
+<Property index="6" essential="1"/>
+<Property index="3" essential="0"/>
+<Property index="4" essential="1"/>
+</AssociationEntry>
+<AssociationEntry item_ID="3" association_count="5">
+<Property index="5" essential="0"/>
+<Property index="2" essential="0"/>
+<Property index="6" essential="1"/>
+<Property index="3" essential="0"/>
+<Property index="4" essential="1"/>
+</AssociationEntry>
+<AssociationEntry item_ID="4" association_count="5">
+<Property index="5" essential="0"/>
+<Property index="2" essential="0"/>
+<Property index="6" essential="1"/>
+<Property index="3" essential="0"/>
+<Property index="4" essential="1"/>
+</AssociationEntry>
+<AssociationEntry item_ID="5" association_count="5">
+<Property index="5" essential="0"/>
+<Property index="2" essential="0"/>
+<Property index="6" essential="1"/>
+<Property index="3" essential="0"/>
+<Property index="4" essential="1"/>
+</AssociationEntry>
+</ItemPropertyAssociationBox>
+</ItemPropertiesBox>
+</MetaBox>
+<MediaDataBox Size="REDACTED" Type="mdat" Specification="p12" Container="file" dataSize="REDACTED">
+</MediaDataBox>
+</IsoMediaFile>
+<Tracks>
+</Tracks>
+</ISOBaseMediaFileTrace>
diff --git a/tests/data/goldens/kodim03_23_animation.avif.xml b/tests/data/goldens/kodim03_23_animation.avif.xml
new file mode 100644
index 0000000..be94105
--- /dev/null
+++ b/tests/data/goldens/kodim03_23_animation.avif.xml
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ISOBaseMediaFileTrace>
+<!--MP4Box dump trace-->
+<IsoMediaFile xmlns="urn:mpeg:isobmff:schema:file:2016" Name="kodim03_23_animation.avif">
+<FileTypeBox Size="44" Type="ftyp" Specification="p12" Container="file otyp" MajorBrand="avis" MinorVersion="0">
+<BrandEntry AlternateBrand="avif"/>
+<BrandEntry AlternateBrand="avis"/>
+<BrandEntry AlternateBrand="msf1"/>
+<BrandEntry AlternateBrand="iso8"/>
+<BrandEntry AlternateBrand="mif1"/>
+<BrandEntry AlternateBrand="miaf"/>
+<BrandEntry AlternateBrand="MA1B"/>
+</FileTypeBox>
+<MetaBox Size="242" Type="meta" Version="0" Flags="0" Specification="p12" Container="file moov trak moof traf udta" >
+<HandlerBox Size="40" Type="hdlr" Version="0" Flags="0" Specification="p12" Container="mdia meta minf" hdlrType="pict" Name="libavif" reserved1="0" reserved2="data:application/octet-string,000000000000000000000000">
+</HandlerBox>
+<PrimaryItemBox Size="14" Type="pitm" Version="0" Flags="0" Specification="p12" Container="meta" item_ID="1">
+</PrimaryItemBox>
+<ItemLocationBox Size="30" Type="iloc" Version="0" Flags="0" Specification="p12" Container="meta" offset_size="4" length_size="4" base_offset_size="0" index_size="0">
+<ItemLocationEntry item_ID="1" data_reference_index="0" base_offset="0" construction_method="0">
+<ItemExtentEntry extent_offset="REDACTED" extent_length="REDACTED" extent_index="0" />
+</ItemLocationEntry>
+</ItemLocationBox>
+<ItemInfoBox Size="40" Type="iinf" Version="0" Flags="0" Specification="p12" Container="meta" >
+<ItemInfoEntryBox Size="26" Type="infe" Version="2" Flags="0" Specification="p12" Container="iinf" item_ID="1" item_protection_index="0" item_name="Color" content_type="(null)" content_encoding="(null)" item_type="av01">
+</ItemInfoEntryBox>
+</ItemInfoBox>
+<ItemPropertiesBox Size="106" Type="iprp" Specification="iff" Container="meta" >
+<ItemPropertyContainerBox Size="75" Type="ipco" Specification="iff" Container="iprp" >
+<ImageSpatialExtentsPropertyBox Size="20" Type="ispe" Version="0" Flags="0" Specification="iff" Container="ipco" image_width="768" image_height="512">
+</ImageSpatialExtentsPropertyBox>
+<PixelInformationPropertyBox Size="16" Type="pixi" Version="0" Flags="0" Specification="iff" Container="ipco" >
+<BitPerChannel bits_per_channel="8"/>
+<BitPerChannel bits_per_channel="8"/>
+<BitPerChannel bits_per_channel="8"/>
+</PixelInformationPropertyBox>
+<AV1ConfigurationBox>
+<AV1Config version="1" profile="0" level_idx0="4" tier="0" high_bitdepth="0" twelve_bit="0" monochrome="0" chroma_subsampling_x="1" chroma_subsampling_y="1" chroma_sample_position="0" initial_presentation_delay="1" OBUs_count="0">
+</AV1Config>
+</AV1ConfigurationBox>
+<ColourInformationBox Size="19" Type="colr" Specification="iff" Container="video_sample_entry ipco encv resv" colour_type="nclx" colour_primaries="1" transfer_characteristics="13" matrix_coefficients="6" full_range_flag="1">
+</ColourInformationBox>
+</ItemPropertyContainerBox>
+<ItemPropertyAssociationBox Size="23" Type="ipma" Version="0" Flags="0" Specification="iff" Container="iprp" entry_count="1">
+<AssociationEntry item_ID="1" association_count="4">
+<Property index="1" essential="0"/>
+<Property index="2" essential="0"/>
+<Property index="3" essential="1"/>
+<Property index="4" essential="0"/>
+</AssociationEntry>
+</ItemPropertyAssociationBox>
+</ItemPropertiesBox>
+</MetaBox>
+<MovieBox Size="717" Type="moov" Specification="p12" Container="file" >
+<MovieHeaderBox Size="120" Type="mvhd" Version="1" Flags="0" Specification="p12" Container="moov" CreationTime="REDACTED" ModificationTime="REDACTED" TimeScale="25" Duration="-1" NextTrackID="1">
+</MovieHeaderBox>
+<TrackBox Size="589" Type="trak" Specification="p12" Container="moov" >
+<TrackHeaderBox Size="104" Type="tkhd" Version="1" Flags="1" Specification="p12" Container="trak" CreationTime="REDACTED" ModificationTime="REDACTED" TrackID="1" Duration="-1" Width="768.00" Height="512.00">
+<Matrix m11="0x00010000" m12="0x00000000" m13="0x00000000" m21="0x00000000" m22="0x00010000" m23="0x00000000" m31="0x00000000" m32="0x00000000" m33="0x40000000"/>
+</TrackHeaderBox>
+<EditBox Size="44" Type="edts" Specification="p12" Container="trak" >
+<EditListBox Size="36" Type="elst" Version="1" Flags="1" Specification="p12" Container="edts" EntryCount="1">
+<EditListEntry Duration="2" MediaTime="0" MediaRate="1"/>
+</EditListBox>
+</EditBox>
+<MediaBox Size="433" Type="mdia" Specification="p12" Container="trak" >
+<MediaHeaderBox Size="44" Type="mdhd" Version="1" Flags="0" Specification="p12" Container="mdia" CreationTime="REDACTED" ModificationTime="REDACTED" TimeScale="25" Duration="2" LanguageCode="und">
+</MediaHeaderBox>
+<HandlerBox Size="40" Type="hdlr" Version="0" Flags="0" Specification="p12" Container="mdia meta minf" hdlrType="pict" Name="libavif" reserved1="0" reserved2="data:application/octet-string,000000000000000000000000">
+</HandlerBox>
+<MediaInformationBox Size="341" Type="minf" Specification="p12" Container="mdia" >
+<VideoMediaHeaderBox Size="20" Type="vmhd" Version="0" Flags="1" Specification="p12" Container="minf" >
+</VideoMediaHeaderBox>
+<DataInformationBox Size="36" Type="dinf" Specification="p12" Container="minf meta" >
+<DataReferenceBox Size="28" Type="dref" Version="0" Flags="0" Specification="p12" Container="dinf" >
+<URLDataEntryBox Size="12" Type="url " Version="0" Flags="1" Specification="p12" Container="dref" >
+<!--Data is contained in the movie file-->
+</URLDataEntryBox>
+</DataReferenceBox>
+</DataInformationBox>
+<SampleTableBox Size="277" Type="stbl" Specification="p12" Container="minf" >
+<SampleDescriptionBox Size="149" Type="stsd" Version="0" Flags="0" Specification="p12" Container="stbl" >
+<AV1SampleEntryBox Size="133" Type="av01" Specification="av1" Container="stsd" DataReferenceIndex="1" Width="768" Height="512" XDPI="4718592" YDPI="4718592" BitDepth="24" CompressorName="AOM Coding"
+>
+<AV1ConfigurationBox>
+<AV1Config version="1" profile="0" level_idx0="4" tier="0" high_bitdepth="0" twelve_bit="0" monochrome="0" chroma_subsampling_x="1" chroma_subsampling_y="1" chroma_sample_position="0" initial_presentation_delay="1" OBUs_count="0">
+</AV1Config>
+</AV1ConfigurationBox>
+<ColourInformationBox Size="19" Type="colr" Specification="iff" Container="video_sample_entry ipco encv resv" colour_type="nclx" colour_primaries="1" transfer_characteristics="13" matrix_coefficients="6" full_range_flag="1">
+</ColourInformationBox>
+<CodingConstraintsBox Size="16" Type="ccst" Version="0" Flags="0" Specification="iff" Container="sample_entry" all_ref_pics_intra="0" intra_pred_used="1" max_ref_per_pic="15" reserved="0">
+</CodingConstraintsBox>
+</AV1SampleEntryBox>
+</SampleDescriptionBox>
+<TimeToSampleBox Size="24" Type="stts" Version="0" Flags="0" Specification="p12" Container="stbl" EntryCount="1">
+<TimeToSampleEntry SampleDelta="1" SampleCount="2"/>
+<!-- counted 2 samples in STTS entries -->
+</TimeToSampleBox>
+<SampleToChunkBox Size="28" Type="stsc" Version="0" Flags="0" Specification="p12" Container="stbl" EntryCount="1">
+<SampleToChunkEntry FirstChunk="1" SamplesPerChunk="2" SampleDescriptionIndex="1"/>
+<!-- counted 2 samples in STSC entries (could be less than sample count) -->
+</SampleToChunkBox>
+<SampleSizeBox Size="28" Type="stsz" Version="0" Flags="0" Specification="p12" Container="stbl" SampleCount="2">
+<SampleSizeEntry Size="REDACTED"/>
+<SampleSizeEntry Size="REDACTED"/>
+</SampleSizeBox>
+<ChunkOffsetBox Size="20" Type="stco" Version="0" Flags="0" Specification="p12" Container="stbl" EntryCount="1">
+<ChunkEntry offset="1011"/>
+</ChunkOffsetBox>
+<SyncSampleBox Size="20" Type="stss" Version="0" Flags="0" Specification="p12" Container="stbl" EntryCount="1">
+<SyncSampleEntry sampleNumber="1"/>
+</SyncSampleBox>
+</SampleTableBox>
+</MediaInformationBox>
+</MediaBox>
+</TrackBox>
+</MovieBox>
+<MediaDataBox Size="REDACTED" Type="mdat" Specification="p12" Container="file" dataSize="REDACTED">
+</MediaDataBox>
+</IsoMediaFile>
+<Tracks>
+<OBUTrack trackID="1" SampleCount="2" TimeScale="25">
+ <OBUConfig>
+ </OBUConfig>
+ <OBUSamples>
+ <Sample number="1" DTS="0" CTS="0" size="REDACTED" RAP="1" >
+ <OBU size="REDACTED" type="delimiter" header_size="2" has_size_field="1" has_ext="0" temporalID="0" spatialID="0" />
+ <OBU size="REDACTED" type="seq_header" header_size="2" has_size_field="1" has_ext="0" temporalID="0" spatialID="0" sequence_width="768" sequence_height="512" still_picture="0" OperatingPointIdc="0" profile="0" level="4" bit_depth="8" monochrome="0" color_description_present_flag="1" color_primaries="1" transfer_characteristics="13" matrix_coefficients="6" color_range="1" chroma_subsampling_x="1" chroma_subsampling_y="1" chroma_sample_position="0" film_grain_params_present="0" />
+ <OBU size="REDACTED" type="frame" header_size="4" has_size_field="1" has_ext="0" temporalID="0" spatialID="0" uncompressed_header_bytes="12" frame_type="key" refresh_frame_flags="255" show_frame="1" width="768" height="512" nb_tiles="1" >
+ <Tile number="0" start="16" size="REDACTED"/>
+ </OBU>
+ </Sample>
+
+ <Sample number="2" DTS="1" CTS="1" size="REDACTED" RAP="0" >
+ <OBU size="REDACTED" type="delimiter" header_size="2" has_size_field="1" has_ext="0" temporalID="0" spatialID="0" />
+ <OBU size="REDACTED" type="frame" header_size="4" has_size_field="1" has_ext="0" temporalID="0" spatialID="0" uncompressed_header_bytes="18" frame_type="inter" refresh_frame_flags="2" show_frame="1" width="768" height="512" nb_tiles="1" >
+ <Tile number="0" start="22" size="REDACTED"/>
+ </OBU>
+ </Sample>
+
+ </OBUSamples>
+</OBUTrack>
+</Tracks>
+</ISOBaseMediaFileTrace>
diff --git a/tests/data/goldens/kodim03_yuv420_8bpc.y4m.avif.xml b/tests/data/goldens/kodim03_yuv420_8bpc.y4m.avif.xml
new file mode 100644
index 0000000..302eff1
--- /dev/null
+++ b/tests/data/goldens/kodim03_yuv420_8bpc.y4m.avif.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ISOBaseMediaFileTrace>
+<!--MP4Box dump trace-->
+<IsoMediaFile xmlns="urn:mpeg:isobmff:schema:file:2016" Name="kodim03_yuv420_8bpc.y4m.avif">
+<FileTypeBox Size="32" Type="ftyp" Specification="p12" Container="file otyp" MajorBrand="avif" MinorVersion="0">
+<BrandEntry AlternateBrand="avif"/>
+<BrandEntry AlternateBrand="mif1"/>
+<BrandEntry AlternateBrand="miaf"/>
+<BrandEntry AlternateBrand="MA1B"/>
+</FileTypeBox>
+<MetaBox Size="242" Type="meta" Version="0" Flags="0" Specification="p12" Container="file moov trak moof traf udta" >
+<HandlerBox Size="40" Type="hdlr" Version="0" Flags="0" Specification="p12" Container="mdia meta minf" hdlrType="pict" Name="libavif" reserved1="0" reserved2="data:application/octet-string,000000000000000000000000">
+</HandlerBox>
+<PrimaryItemBox Size="14" Type="pitm" Version="0" Flags="0" Specification="p12" Container="meta" item_ID="1">
+</PrimaryItemBox>
+<ItemLocationBox Size="30" Type="iloc" Version="0" Flags="0" Specification="p12" Container="meta" offset_size="4" length_size="4" base_offset_size="0" index_size="0">
+<ItemLocationEntry item_ID="1" data_reference_index="0" base_offset="0" construction_method="0">
+<ItemExtentEntry extent_offset="REDACTED" extent_length="REDACTED" extent_index="0" />
+</ItemLocationEntry>
+</ItemLocationBox>
+<ItemInfoBox Size="40" Type="iinf" Version="0" Flags="0" Specification="p12" Container="meta" >
+<ItemInfoEntryBox Size="26" Type="infe" Version="2" Flags="0" Specification="p12" Container="iinf" item_ID="1" item_protection_index="0" item_name="Color" content_type="(null)" content_encoding="(null)" item_type="av01">
+</ItemInfoEntryBox>
+</ItemInfoBox>
+<ItemPropertiesBox Size="106" Type="iprp" Specification="iff" Container="meta" >
+<ItemPropertyContainerBox Size="75" Type="ipco" Specification="iff" Container="iprp" >
+<ImageSpatialExtentsPropertyBox Size="20" Type="ispe" Version="0" Flags="0" Specification="iff" Container="ipco" image_width="768" image_height="512">
+</ImageSpatialExtentsPropertyBox>
+<PixelInformationPropertyBox Size="16" Type="pixi" Version="0" Flags="0" Specification="iff" Container="ipco" >
+<BitPerChannel bits_per_channel="8"/>
+<BitPerChannel bits_per_channel="8"/>
+<BitPerChannel bits_per_channel="8"/>
+</PixelInformationPropertyBox>
+<AV1ConfigurationBox>
+<AV1Config version="1" profile="0" level_idx0="4" tier="0" high_bitdepth="0" twelve_bit="0" monochrome="0" chroma_subsampling_x="1" chroma_subsampling_y="1" chroma_sample_position="0" initial_presentation_delay="1" OBUs_count="0">
+</AV1Config>
+</AV1ConfigurationBox>
+<ColourInformationBox Size="19" Type="colr" Specification="iff" Container="video_sample_entry ipco encv resv" colour_type="nclx" colour_primaries="1" transfer_characteristics="13" matrix_coefficients="6" full_range_flag="1">
+</ColourInformationBox>
+</ItemPropertyContainerBox>
+<ItemPropertyAssociationBox Size="23" Type="ipma" Version="0" Flags="0" Specification="iff" Container="iprp" entry_count="1">
+<AssociationEntry item_ID="1" association_count="4">
+<Property index="1" essential="0"/>
+<Property index="2" essential="0"/>
+<Property index="3" essential="1"/>
+<Property index="4" essential="0"/>
+</AssociationEntry>
+</ItemPropertyAssociationBox>
+</ItemPropertiesBox>
+</MetaBox>
+<MediaDataBox Size="REDACTED" Type="mdat" Specification="p12" Container="file" dataSize="REDACTED">
+</MediaDataBox>
+</IsoMediaFile>
+<Tracks>
+</Tracks>
+</ISOBaseMediaFileTrace>
diff --git a/tests/data/goldens/paris_icc_exif_xmp.png.avif.xml b/tests/data/goldens/paris_icc_exif_xmp.png.avif.xml
new file mode 100644
index 0000000..18bd8fd
--- /dev/null
+++ b/tests/data/goldens/paris_icc_exif_xmp.png.avif.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ISOBaseMediaFileTrace>
+<!--MP4Box dump trace-->
+<IsoMediaFile xmlns="urn:mpeg:isobmff:schema:file:2016" Name="paris_icc_exif_xmp.png.avif">
+<FileTypeBox Size="32" Type="ftyp" Specification="p12" Container="file otyp" MajorBrand="avif" MinorVersion="0">
+<BrandEntry AlternateBrand="avif"/>
+<BrandEntry AlternateBrand="mif1"/>
+<BrandEntry AlternateBrand="miaf"/>
+<BrandEntry AlternateBrand="MA1A"/>
+</FileTypeBox>
+<MetaBox Size="988" Type="meta" Version="0" Flags="0" Specification="p12" Container="file moov trak moof traf udta" >
+<HandlerBox Size="40" Type="hdlr" Version="0" Flags="0" Specification="p12" Container="mdia meta minf" hdlrType="pict" Name="libavif" reserved1="0" reserved2="data:application/octet-string,000000000000000000000000">
+</HandlerBox>
+<PrimaryItemBox Size="14" Type="pitm" Version="0" Flags="0" Specification="p12" Container="meta" item_ID="1">
+</PrimaryItemBox>
+<ItemLocationBox Size="58" Type="iloc" Version="0" Flags="0" Specification="p12" Container="meta" offset_size="4" length_size="4" base_offset_size="0" index_size="0">
+<ItemLocationEntry item_ID="1" data_reference_index="0" base_offset="0" construction_method="0">
+<ItemExtentEntry extent_offset="REDACTED" extent_length="REDACTED" extent_index="0" />
+</ItemLocationEntry>
+<ItemLocationEntry item_ID="2" data_reference_index="0" base_offset="0" construction_method="0">
+<ItemExtentEntry extent_offset="REDACTED" extent_length="REDACTED" extent_index="0" />
+</ItemLocationEntry>
+<ItemLocationEntry item_ID="3" data_reference_index="0" base_offset="0" construction_method="0">
+<ItemExtentEntry extent_offset="REDACTED" extent_length="REDACTED" extent_index="0" />
+</ItemLocationEntry>
+</ItemLocationBox>
+<ItemInfoBox Size="109" Type="iinf" Version="0" Flags="0" Specification="p12" Container="meta" >
+<ItemInfoEntryBox Size="26" Type="infe" Version="2" Flags="0" Specification="p12" Container="iinf" item_ID="1" item_protection_index="0" item_name="Color" content_type="(null)" content_encoding="(null)" item_type="av01">
+</ItemInfoEntryBox>
+<ItemInfoEntryBox Size="25" Type="infe" Version="2" Flags="0" Specification="p12" Container="iinf" item_ID="2" item_protection_index="0" item_name="Exif" content_type="(null)" content_encoding="(null)" item_type="Exif">
+</ItemInfoEntryBox>
+<ItemInfoEntryBox Size="44" Type="infe" Version="2" Flags="0" Specification="p12" Container="iinf" item_ID="3" item_protection_index="0" item_name="XMP" content_type="application/rdf+xml" content_encoding="(null)" item_type="mime">
+</ItemInfoEntryBox>
+</ItemInfoBox>
+<ItemReferenceBox Size="40" Type="iref" Version="0" Flags="0" Specification="p12" Container="meta" >
+<ItemReferenceBox Size="14" Type="cdsc" Specification="p12" Container="iref" from_item_id="2">
+<ItemReferenceBoxEntry ItemID="1"/>
+</ItemReferenceBox>
+<ItemReferenceBox Size="14" Type="cdsc" Specification="p12" Container="iref" from_item_id="3">
+<ItemReferenceBoxEntry ItemID="1"/>
+</ItemReferenceBox>
+</ItemReferenceBox>
+<ItemPropertiesBox Size="715" Type="iprp" Specification="iff" Container="meta" >
+<ItemPropertyContainerBox Size="683" Type="ipco" Specification="iff" Container="iprp" >
+<ImageSpatialExtentsPropertyBox Size="20" Type="ispe" Version="0" Flags="0" Specification="iff" Container="ipco" image_width="403" image_height="302">
+</ImageSpatialExtentsPropertyBox>
+<PixelInformationPropertyBox Size="16" Type="pixi" Version="0" Flags="0" Specification="iff" Container="ipco" >
+<BitPerChannel bits_per_channel="8"/>
+<BitPerChannel bits_per_channel="8"/>
+<BitPerChannel bits_per_channel="8"/>
+</PixelInformationPropertyBox>
+<AV1ConfigurationBox>
+<AV1Config version="1" profile="1" level_idx0="0" tier="0" high_bitdepth="0" twelve_bit="0" monochrome="0" chroma_subsampling_x="0" chroma_subsampling_y="0" chroma_sample_position="0" initial_presentation_delay="1" OBUs_count="0">
+</AV1Config>
+</AV1ConfigurationBox>
+<ColourInformationBox Size="608" Type="colr" Specification="iff" Container="video_sample_entry ipco encv resv" colour_type="prof">
+<profile><![CDATA[AAACVAAAAAAEAAAAbW50clJHQiBYWVogB+AADAAIAAkAJgAcYWNzcAAAAAAAAAAAR09PRwAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA0y1HT09HdeGmsTw0N2MQyKtmBjKiigAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALZGVzYwAAAQgAAABAY3BydAAAAUgAAABad3RwdAAAAaQAAAAUYmtwdAAAAbgAAAAUclhZWgAAAcwAAAAUZ1hZWgAAAeAAAAAUYlhZWgAAAfQAAAAUclRSQwAAAggAAAAgY2hhZAAAAigAAAAsYlRSQwAAAggAAAAgZ1RSQwAAAggAAAAgbWx1YwAAAAAAAAABAAAADGVuVVMAAAAkAAAAHABzAFIARwBCACAASQBFAEMANgAxADkANgA2AC0AMgAuADEAAG1sdWMAAAAAAAAAAQAAAAxlblVTAAAAPgAAABwAQwBvAHAAeQByAGkAZwBoAHQAIAAoAGMAKQAgADIAMAAxADYAIABHAG8AbwBnAGwAZQAgAEkAbgBjAC4AAAAAWFlaIAAAAAAAAPNRAAEAAAABFsxYWVogAAAAAAAAAAAAAAAAAAAAAFhZWiAAAAAAAABvoAAAOPUAAAOQWFlaIAAAAAAAAGKXAAC3hwAAGNlYWVogAAAAAAAAJJ8AAA+EAAC2w3BhcmEAAAAAAAMAAAACZmYAAPKnAAANWQAAE9AAAApbc2YzMgAAAAAAAQxCAAAF3v//8ycAAAeTAAD9kP//+6P///2kAAAD3AAAwG4=]]></profile></ColourInformationBox>
+<ColourInformationBox Size="19" Type="colr" Specification="iff" Container="video_sample_entry ipco encv resv" colour_type="nclx" colour_primaries="2" transfer_characteristics="2" matrix_coefficients="6" full_range_flag="1">
+</ColourInformationBox>
+</ItemPropertyContainerBox>
+<ItemPropertyAssociationBox Size="24" Type="ipma" Version="0" Flags="0" Specification="iff" Container="iprp" entry_count="1">
+<AssociationEntry item_ID="1" association_count="5">
+<Property index="1" essential="0"/>
+<Property index="2" essential="0"/>
+<Property index="3" essential="1"/>
+<Property index="4" essential="0"/>
+<Property index="5" essential="0"/>
+</AssociationEntry>
+</ItemPropertyAssociationBox>
+</ItemPropertiesBox>
+</MetaBox>
+<MediaDataBox Size="REDACTED" Type="mdat" Specification="p12" Container="file" dataSize="REDACTED">
+</MediaDataBox>
+</IsoMediaFile>
+<Tracks>
+</Tracks>
+</ISOBaseMediaFileTrace>
diff --git a/tests/test_cmd_enc_boxes_golden.sh b/tests/test_cmd_enc_boxes_golden.sh
new file mode 100644
index 0000000..0466e1b
--- /dev/null
+++ b/tests/test_cmd_enc_boxes_golden.sh
@@ -0,0 +1,186 @@
+#!/bin/bash
+# Copyright 2023 Google LLC
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Test 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.
+#
+# Usage:
+# test_cmd_enc_boxes_golden.sh <avifenc_dir> <mp4box_dir> <testdata_dir>
+# <test_output_dir>
+# But most of the time you will be running this test with 'make test' or
+# 'ctest -V -R test_cmd_enc_boxes_golden'
+
+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"
+
+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 "==="
+
+ for f in $(find . -name '*.avif'); do
+ 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
+ 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.
+ else
+ rm "$xml.diff" # Delete empty diff files.
+ echo "Passed"
+ fi
+ else
+ echo "FAILED: Missing golden file $golden" >&2
+ echo "Actual file: $OUTPUT_DIR/$xml" >&2
+ has_errors=true
+ fi
+
+ echo "---"
+
+ done
+ echo "==="
+}
+
+# ===================================
+# ========== Encode files ===========
+# ===================================
+# To add new test case, just add new encode commands to this function.
+# All .avif files created here will become test cases.
+encode_test_files() {
+ set -x # Print the encoding command lines.
+
+ # Still images.
+ for f in "kodim03_yuv420_8bpc.y4m" "circle-trns-after-plte.png" "paris_icc_exif_xmp.png"; do
+ "${AVIFENC}" -s 9 "${TESTDATA_DIR}/$f" -o "$f.avif"
+ done
+
+ # Grid.
+ "${AVIFENC}" -s 9 --grid 2x2 "${TESTDATA_DIR}/dog_exif_extended_xmp_icc.jpg" \
+ -o "dog_exif_extended_xmp_icc.jpg.avif"
+
+ # Animation.
+ "${AVIFENC}" -s 9 "${TESTDATA_DIR}/kodim03_yuv420_8bpc.y4m" \
+ "${TESTDATA_DIR}/kodim23_yuv420_8bpc.y4m" -o "kodim03_23_animation.avif"
+
+ set +x
+}
+
+pushd "${OUTPUT_DIR}"
+ encode_test_files
+
+ diff_with_goldens
+popd > /dev/null
+
+if $has_errors; then
+ cat >&2 << EOF
+Test failed.
+
+# IF RUNNING ON GITHUB
+ Check the workflow step called "How to fix failing tests" for instructions on how to debug/fix this test.
+
+# IF RUNNING LOCALLY
+ Look at the .xml.diff files in $OUTPUT_DIR
+
+ If the diffs are expected, update the golden files with:
+ cp "$OUTPUT_DIR"/*.xml "$GOLDEN_DIR"
+EOF
+
+ cat > "$OUTPUT_DIR/README.txt" << EOF
+To debug test failures, look at the .xml.diff files.
+
+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
+fi
+
+exit 0