Allow providing a custom decoder to testutil::DecodeIncrementally. (#1592)

This allows testing decoders with different settings.
diff --git a/tests/gtest/avifincrtest.cc b/tests/gtest/avifincrtest.cc
index ed88a42..18d6a88 100644
--- a/tests/gtest/avifincrtest.cc
+++ b/tests/gtest/avifincrtest.cc
@@ -55,10 +55,11 @@
 
   // Cell height is hardcoded because there is no API to extract it from an
   // encoded payload.
-  testutil::DecodeIncrementally(
-      encoded_avif, /*is_persistent=*/true, /*give_size_hint=*/true,
-      /*use_nth_image_api=*/false, *reference, /*cell_height=*/154,
-      /*enable_fine_incremental_check=*/true);
+  testutil::DecodeIncrementally(encoded_avif, decoder.get(),
+                                /*is_persistent=*/true, /*give_size_hint=*/true,
+                                /*use_nth_image_api=*/false, *reference,
+                                /*cell_height=*/154,
+                                /*enable_fine_incremental_check=*/true);
 }
 
 //------------------------------------------------------------------------------
@@ -98,8 +99,9 @@
   testutil::EncodeRectAsIncremental(*image, width, height, create_alpha,
                                     flat_cells, &encoded_avif, &cell_width,
                                     &cell_height);
+
   testutil::DecodeNonIncrementallyAndIncrementally(
-      encoded_avif, encoded_avif_is_persistent, give_size_hint,
+      encoded_avif, decoder.get(), encoded_avif_is_persistent, give_size_hint,
       use_nth_image_api, cell_height, /*enable_fine_incremental_check=*/true);
 }
 
diff --git a/tests/gtest/avifincrtest_helpers.cc b/tests/gtest/avifincrtest_helpers.cc
index 2aeb2fa..76361fb 100644
--- a/tests/gtest/avifincrtest_helpers.cc
+++ b/tests/gtest/avifincrtest_helpers.cc
@@ -4,7 +4,9 @@
 #include "avifincrtest_helpers.h"
 
 #include <algorithm>
+#include <cstdint>
 #include <cstring>
+#include <memory>
 #include <vector>
 
 #include "avif/avif.h"
@@ -275,9 +277,10 @@
 
 //------------------------------------------------------------------------------
 
-void DecodeIncrementally(const avifRWData& encoded_avif, bool is_persistent,
-                         bool give_size_hint, bool use_nth_image_api,
-                         const avifImage& reference, uint32_t cell_height,
+void DecodeIncrementally(const avifRWData& encoded_avif, avifDecoder* decoder,
+                         bool is_persistent, bool give_size_hint,
+                         bool use_nth_image_api, const avifImage& reference,
+                         uint32_t cell_height,
                          bool enable_fine_incremental_check) {
   // AVIF cells are at least 64 pixels tall.
   if (cell_height != reference.height) {
@@ -292,35 +295,39 @@
       /*destroy=*/nullptr, PartialRead,
       /*write=*/nullptr,   give_size_hint ? encoded_avif.size : 0,
       is_persistent,       &data};
+  avifDecoderSetIO(decoder, &io);
+  // Reset the decoder's IO to nullptr before 'io' goes out of scope and becomes
+  // invalid.
+  auto cleanup_io_fn = [](avifDecoder* decoder) {
+    avifDecoderSetIO(decoder, nullptr);
+  };
+  std::unique_ptr<avifDecoder, decltype(cleanup_io_fn)> cleanup_io(
+      decoder, cleanup_io_fn);  // Call automatically at end of scope.
 
-  testutil::AvifDecoderPtr decoder(avifDecoderCreate(), avifDecoderDestroy);
-  ASSERT_NE(decoder, nullptr);
-  avifDecoderSetIO(decoder.get(), &io);
   decoder->allowIncremental = AVIF_TRUE;
   const size_t step = std::max<size_t>(1, data.full_size / 10000);
 
   // Parsing is not incremental.
-  avifResult parse_result = avifDecoderParse(decoder.get());
+  avifResult parse_result = avifDecoderParse(decoder);
   while (parse_result == AVIF_RESULT_WAITING_ON_IO) {
     ASSERT_LT(data.available.size, data.full_size)
         << "avifDecoderParse() returned WAITING_ON_IO instead of OK";
     data.available.size = std::min(data.available.size + step, data.full_size);
-    parse_result = avifDecoderParse(decoder.get());
+    parse_result = avifDecoderParse(decoder);
   }
   ASSERT_EQ(parse_result, AVIF_RESULT_OK);
 
   // Decoding is incremental.
   uint32_t previously_decoded_row_count = 0;
   avifResult next_image_result = use_nth_image_api
-                                     ? avifDecoderNthImage(decoder.get(), 0)
-                                     : avifDecoderNextImage(decoder.get());
+                                     ? avifDecoderNthImage(decoder, 0)
+                                     : avifDecoderNextImage(decoder);
   while (next_image_result == AVIF_RESULT_WAITING_ON_IO) {
     ASSERT_LT(data.available.size, data.full_size)
         << (use_nth_image_api ? "avifDecoderNthImage(0)"
                               : "avifDecoderNextImage()")
         << " returned WAITING_ON_IO instead of OK";
-    const uint32_t decoded_row_count =
-        avifDecoderDecodedRowCount(decoder.get());
+    const uint32_t decoded_row_count = avifDecoderDecodedRowCount(decoder);
     ASSERT_GE(decoded_row_count, previously_decoded_row_count);
     const uint32_t min_decoded_row_count = GetMinDecodedRowCount(
         reference.height, cell_height, reference.alphaPlane != nullptr,
@@ -330,30 +337,28 @@
 
     previously_decoded_row_count = decoded_row_count;
     data.available.size = std::min(data.available.size + step, data.full_size);
-    next_image_result = use_nth_image_api
-                            ? avifDecoderNthImage(decoder.get(), 0)
-                            : avifDecoderNextImage(decoder.get());
+    next_image_result = use_nth_image_api ? avifDecoderNthImage(decoder, 0)
+                                          : avifDecoderNextImage(decoder);
   }
   ASSERT_EQ(next_image_result, AVIF_RESULT_OK);
   ASSERT_EQ(data.available.size, data.full_size);
-  ASSERT_EQ(avifDecoderDecodedRowCount(decoder.get()), decoder->image->height);
+  ASSERT_EQ(avifDecoderDecodedRowCount(decoder), decoder->image->height);
 
   ComparePartialYuva(reference, *decoder->image, reference.height);
 }
 
 void DecodeNonIncrementallyAndIncrementally(
-    const avifRWData& encoded_avif, bool is_persistent, bool give_size_hint,
-    bool use_nth_image_api, uint32_t cell_height,
+    const avifRWData& encoded_avif, avifDecoder* decoder, bool is_persistent,
+    bool give_size_hint, bool use_nth_image_api, uint32_t cell_height,
     bool enable_fine_incremental_check) {
   AvifImagePtr reference(avifImageCreateEmpty(), avifImageDestroy);
   ASSERT_NE(reference, nullptr);
-  testutil::AvifDecoderPtr decoder(avifDecoderCreate(), avifDecoderDestroy);
-  ASSERT_NE(decoder, nullptr);
-  ASSERT_EQ(avifDecoderReadMemory(decoder.get(), reference.get(),
-                                  encoded_avif.data, encoded_avif.size),
+  decoder->allowIncremental = AVIF_FALSE;
+  ASSERT_EQ(avifDecoderReadMemory(decoder, reference.get(), encoded_avif.data,
+                                  encoded_avif.size),
             AVIF_RESULT_OK);
 
-  DecodeIncrementally(encoded_avif, is_persistent, give_size_hint,
+  DecodeIncrementally(encoded_avif, decoder, is_persistent, give_size_hint,
                       use_nth_image_api, *reference, cell_height,
                       enable_fine_incremental_check);
 }
diff --git a/tests/gtest/avifincrtest_helpers.h b/tests/gtest/avifincrtest_helpers.h
index 4706902..1541d8d 100644
--- a/tests/gtest/avifincrtest_helpers.h
+++ b/tests/gtest/avifincrtest_helpers.h
@@ -4,6 +4,8 @@
 #ifndef LIBAVIF_TESTS_AVIFINCRTEST_HELPERS_H_
 #define LIBAVIF_TESTS_AVIFINCRTEST_HELPERS_H_
 
+#include <cstdint>
+
 #include "avif/avif.h"
 
 namespace libavif {
@@ -24,16 +26,17 @@
 // incremental granularity. enable_fine_incremental_check checks that sample
 // rows are gradually output when feeding more and more input bytes to the
 // decoder.
-void DecodeIncrementally(const avifRWData& encoded_avif, bool is_persistent,
-                         bool give_size_hint, bool use_nth_image_api,
-                         const avifImage& reference, uint32_t cell_height,
+void DecodeIncrementally(const avifRWData& encoded_avif, avifDecoder* decoder,
+                         bool is_persistent, bool give_size_hint,
+                         bool use_nth_image_api, const avifImage& reference,
+                         uint32_t cell_height,
                          bool enable_fine_incremental_check = false);
 
 // Calls DecodeIncrementally() with the reference being a regular decoding of
 // encoded_avif.
 void DecodeNonIncrementallyAndIncrementally(
-    const avifRWData& encoded_avif, bool is_persistent, bool give_size_hint,
-    bool use_nth_image_api, uint32_t cell_height,
+    const avifRWData& encoded_avif, avifDecoder* decoder, bool is_persistent,
+    bool give_size_hint, bool use_nth_image_api, uint32_t cell_height,
     bool enable_fine_incremental_check = false);
 
 }  // namespace testutil