Add an API lossless test

diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 4511bd0..a2a5767 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -118,6 +118,11 @@
     target_include_directories(avifiostatstest PRIVATE ${GTEST_INCLUDE_DIRS})
     add_test(NAME avifiostatstest COMMAND avifiostatstest ${CMAKE_CURRENT_SOURCE_DIR}/data/)
 
+    add_executable(aviflosslesstest gtest/aviflosslesstest.cc)
+    target_link_libraries(aviflosslesstest aviftest_helpers ${GTEST_BOTH_LIBRARIES})
+    target_include_directories(aviflosslesstest PRIVATE ${GTEST_INCLUDE_DIRS})
+    add_test(NAME aviflosslesstest COMMAND aviflosslesstest ${CMAKE_CURRENT_SOURCE_DIR}/data/)
+
     add_executable(avifmetadatatest gtest/avifmetadatatest.cc)
     target_link_libraries(avifmetadatatest aviftest_helpers ${GTEST_LIBRARIES})
     target_include_directories(avifmetadatatest PRIVATE ${GTEST_INCLUDE_DIRS})
@@ -167,7 +172,9 @@
         # These tests are supported with aom being the encoder and decoder. If aom is unavailable,
         # these tests are disabled because other codecs may not implement all the necessary features.
         # For example, SVT-AV1 requires 4:2:0 images with even dimensions of at least 64x64 px.
-        set_tests_properties(avifallocationtest avifgridapitest avifmetadatatest avifincrtest PROPERTIES DISABLED True)
+        set_tests_properties(
+            avifallocationtest avifgridapitest avifincrtest aviflosslesstest avifmetadatatest PROPERTIES DISABLED True
+        )
 
         message(STATUS "Some tests are disabled because aom is unavailable for encoding or decoding.")
     endif()
diff --git a/tests/gtest/aviflosslesstest.cc b/tests/gtest/aviflosslesstest.cc
new file mode 100644
index 0000000..82c446c
--- /dev/null
+++ b/tests/gtest/aviflosslesstest.cc
@@ -0,0 +1,97 @@
+// Copyright 2023 Google LLC
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include "avif/avif.h"
+#include "aviftest_helpers.h"
+#include "avifutil.h"
+#include "gtest/gtest.h"
+
+namespace libavif {
+namespace {
+
+// Used to pass the data folder path to the GoogleTest suites.
+const char* data_path = nullptr;
+
+// Tests encode/decode round trips under different matrix coefficients.
+TEST(BasicTest, EncodeDecodeMatrixCoefficients) {
+  for (const auto& file_name :
+       {"paris_icc_exif_xmp.png", "paris_exif_xmp_icc.jpg"}) {
+    // Read a ground truth image with default matrix coefficients.
+    const std::string file_path = std::string(data_path) + file_name;
+    const testutil::AvifImagePtr ground_truth_image =
+        testutil::ReadImage(data_path, file_name);
+
+    for (auto matrix_coefficient :
+         {AVIF_MATRIX_COEFFICIENTS_IDENTITY, AVIF_MATRIX_COEFFICIENTS_YCGCO}) {
+      // Read a ground truth image but ask for certain matrix coefficients.
+      testutil::AvifImagePtr image(avifImageCreateEmpty(), avifImageDestroy);
+      ASSERT_NE(image, nullptr);
+      image->matrixCoefficients = (avifMatrixCoefficients)matrix_coefficient;
+      const avifAppFileFormat file_format = avifReadImage(
+          file_path.c_str(),
+          /*requestedFormat=*/AVIF_PIXEL_FORMAT_NONE,
+          /*requestedDepth=*/0,
+          /*chromaDownsampling=*/AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC,
+          /*ignoreICC=*/false, /*ignoreExif=*/false,
+          /*ignoreXMP=*/false, image.get(),
+          /*outDepth=*/nullptr, /*sourceTiming=*/nullptr,
+          /*frameIter=*/nullptr);
+      ASSERT_NE(file_format, AVIF_APP_FILE_FORMAT_UNKNOWN);
+
+      // Encode.
+      testutil::AvifEncoderPtr encoder(avifEncoderCreate(), avifEncoderDestroy);
+      ASSERT_NE(encoder, nullptr);
+      encoder->speed = AVIF_SPEED_FASTEST;
+      encoder->quality = AVIF_QUALITY_LOSSLESS;
+      encoder->qualityAlpha = AVIF_QUALITY_LOSSLESS;
+      testutil::AvifRwData encoded;
+      avifResult result =
+          avifEncoderWrite(encoder.get(), image.get(), &encoded);
+      ASSERT_EQ(result, AVIF_RESULT_OK) << avifResultToString(result);
+
+      // Decode to RAM.
+      testutil::AvifImagePtr decoded(avifImageCreateEmpty(), avifImageDestroy);
+      ASSERT_NE(decoded, nullptr);
+      testutil::AvifDecoderPtr decoder(avifDecoderCreate(), avifDecoderDestroy);
+      ASSERT_NE(decoder, nullptr);
+      result = avifDecoderReadMemory(decoder.get(), decoded.get(), encoded.data,
+                                     encoded.size);
+      ASSERT_EQ(result, AVIF_RESULT_OK) << avifResultToString(result);
+
+      // Convert to a temporary PNG and read back, to have default matrix
+      // coefficients.
+      const std::string temp_dir = testing::TempDir();
+      const std::string temp_file = "decoded_default.png";
+      const std::string tmp_path = temp_dir + temp_file;
+      ASSERT_TRUE(testutil::WriteImage(decoded.get(), tmp_path.c_str()));
+      const testutil::AvifImagePtr decoded_default =
+          testutil::ReadImage(temp_dir.c_str(), temp_file.c_str());
+
+      // Verify that the ground truth and decoded images are the same.
+      const bool are_images_equal =
+          testutil::AreImagesEqual(*ground_truth_image, *decoded_default);
+      if (matrix_coefficient == AVIF_MATRIX_COEFFICIENTS_IDENTITY) {
+        ASSERT_TRUE(are_images_equal);
+      } else {
+        // AVIF_MATRIX_COEFFICIENTS_YCGCO is not lossless because it does not
+        // expand the input bit range for YCgCo values.
+        ASSERT_FALSE(are_images_equal);
+      }
+    }
+  }
+}
+
+}  // namespace
+}  // namespace libavif
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  if (argc != 2) {
+    std::cerr << "There must be exactly one argument containing the path to "
+                 "the test data folder"
+              << std::endl;
+    return 1;
+  }
+  libavif::data_path = argv[1];
+  return RUN_ALL_TESTS();
+}