Refactor avifrgbtoyuvtest.cc

Add coverage-based tests ExhaustiveSettings and AllMatrixCoefficients.
Restrict most threshold-based tests to AVIF_RGB_FORMAT_RGBA.
diff --git a/tests/gtest/avifrgbtoyuvtest.cc b/tests/gtest/avifrgbtoyuvtest.cc
index ddcce45..3272f49 100644
--- a/tests/gtest/avifrgbtoyuvtest.cc
+++ b/tests/gtest/avifrgbtoyuvtest.cc
@@ -111,33 +111,15 @@
 
 //------------------------------------------------------------------------------
 
-class RGBToYUVTest
-    : public testing::TestWithParam<std::tuple<
-          /*rgb_depth=*/int, /*yuv_depth=*/int, avifRGBFormat, avifPixelFormat,
-          avifRange, avifMatrixCoefficients, avifChromaDownsampling,
-          /*add_noise=*/bool, /*rgb_step=*/uint32_t,
-          /*max_abs_average_diff=*/double, /*min_psnr=*/double>> {};
-
 // Converts from RGB to YUV and back to RGB for all RGB combinations, separated
 // by a color step for reasonable timing. If add_noise is true, also applies
 // some noise to the input samples to exercise chroma subsampling.
-TEST_P(RGBToYUVTest, ConvertWholeRange) {
-  const int rgb_depth = std::get<0>(GetParam());
-  const int yuv_depth = std::get<1>(GetParam());
-  const avifRGBFormat rgb_format = std::get<2>(GetParam());
-  const avifPixelFormat yuv_format = std::get<3>(GetParam());
-  const avifRange yuv_range = std::get<4>(GetParam());
-  const avifMatrixCoefficients matrix_coefficients = std::get<5>(GetParam());
-  const avifChromaDownsampling chroma_downsampling = std::get<6>(GetParam());
-  // Whether to add noise to the input RGB samples. Should only impact
-  // subsampled chroma (4:2:2 and 4:2:0).
-  const bool add_noise = std::get<7>(GetParam());
-  // Testing each RGB combination would be more accurate but results are similar
-  // with faster settings.
-  const uint32_t rgb_step = std::get<8>(GetParam());
-  // Thresholds to pass.
-  const double max_abs_average_diff = std::get<9>(GetParam());
-  const double min_psnr = std::get<10>(GetParam());
+void ConvertWholeRange(int rgb_depth, int yuv_depth, avifRGBFormat rgb_format,
+                       avifPixelFormat yuv_format, avifRange yuv_range,
+                       avifMatrixCoefficients matrix_coefficients,
+                       avifChromaDownsampling chroma_downsampling,
+                       bool add_noise, uint32_t rgb_step,
+                       double max_abs_average_diff, double min_psnr, bool log) {
   // Deduced constants.
   const bool is_monochrome =
       (yuv_format == AVIF_PIXEL_FORMAT_YUV400);  // If so, only test grey input.
@@ -233,32 +215,27 @@
   EXPECT_LE(std::abs(average_diff), max_abs_average_diff);
   EXPECT_GE(psnr, min_psnr);
 
-  // Print stats for convenience and easier threshold tuning.
-  static constexpr const char* kAvifRgbFormatToString[] = {
-      "RGB", "RGBA", "ARGB", "BGR", "BGRA", "ABGR"};
-  std::cout << " RGB " << rgb_depth << " bits, YUV " << yuv_depth << " bits, "
-            << kAvifRgbFormatToString[rgb_format] << ", "
-            << avifPixelFormatToString(yuv_format) << ", "
-            << (yuv_range ? "full" : "lmtd") << ", MC " << matrix_coefficients
-            << ", " << (add_noise ? "noisy" : "plain") << ", avg "
-            << average_diff << ", abs avg " << average_abs_diff << ", max "
-            << max_abs_diff << ", PSNR " << psnr << "dB" << std::endl;
+  if (log) {
+    // Print stats for convenience and easier threshold tuning.
+    static constexpr const char* kAvifRgbFormatToString[] = {
+        "RGB", "RGBA", "ARGB", "BGR", "BGRA", "ABGR"};
+    std::cout << " RGB " << rgb_depth << " bits, YUV " << yuv_depth << " bits, "
+              << kAvifRgbFormatToString[rgb_format] << ", "
+              << avifPixelFormatToString(yuv_format) << ", "
+              << (yuv_range ? "full" : "lmtd") << ", MC " << matrix_coefficients
+              << ", " << (add_noise ? "noisy" : "plain") << ", avg "
+              << average_diff << ", abs avg " << average_abs_diff << ", max "
+              << max_abs_diff << ", PSNR " << psnr << "dB" << std::endl;
+  }
 }
 
 // Converts from RGB to YUV and back to RGB for multiple buffer dimensions to
 // exercise stride computation and subsampling edge cases.
-TEST_P(RGBToYUVTest, ConvertWholeBuffer) {
-  const int rgb_depth = std::get<0>(GetParam());
-  const int yuv_depth = std::get<1>(GetParam());
-  const avifRGBFormat rgb_format = std::get<2>(GetParam());
-  const avifPixelFormat yuv_format = std::get<3>(GetParam());
-  const avifRange yuv_range = std::get<4>(GetParam());
-  const avifMatrixCoefficients matrix_coefficients = std::get<5>(GetParam());
-  const avifChromaDownsampling chroma_downsampling = std::get<6>(GetParam());
-  // Whether to add noise to the input RGB samples.
-  const bool add_noise = std::get<7>(GetParam());
-  // Threshold to pass.
-  const double min_psnr = std::get<9>(GetParam());
+void ConvertWholeBuffer(int rgb_depth, int yuv_depth, avifRGBFormat rgb_format,
+                        avifPixelFormat yuv_format, avifRange yuv_range,
+                        avifMatrixCoefficients matrix_coefficients,
+                        avifChromaDownsampling chroma_downsampling,
+                        bool add_noise, double min_psnr) {
   // Deduced constants.
   const bool is_monochrome =
       (yuv_format == AVIF_PIXEL_FORMAT_YUV400);  // If so, only test grey input.
@@ -316,11 +293,166 @@
             min_psnr);
 }
 
-constexpr avifRGBFormat kAllRgbFormats[] = {
-    AVIF_RGB_FORMAT_RGB, AVIF_RGB_FORMAT_RGBA, AVIF_RGB_FORMAT_ARGB,
-    AVIF_RGB_FORMAT_BGR, AVIF_RGB_FORMAT_BGRA, AVIF_RGB_FORMAT_ABGR};
+//------------------------------------------------------------------------------
+// Exhaustive settings
+// These tests would generate too many GoogleTest instances as parameterized
+// tests (TEST_P) so loops are used instead.
+
+TEST(RGBToYUVTest, ExhaustiveSettings) {
+  // Coverage of all configurations with all min/max input combinations.
+  for (int rgb_depth : {8, 10, 12}) {
+    for (int yuv_depth : {8, 10, 12}) {
+      for (avifRGBFormat rgb_format :
+           {AVIF_RGB_FORMAT_RGB, AVIF_RGB_FORMAT_RGBA, AVIF_RGB_FORMAT_ARGB,
+            AVIF_RGB_FORMAT_BGR, AVIF_RGB_FORMAT_BGRA, AVIF_RGB_FORMAT_ABGR}) {
+        for (avifPixelFormat yuv_format :
+             {AVIF_PIXEL_FORMAT_YUV444, AVIF_PIXEL_FORMAT_YUV422,
+              AVIF_PIXEL_FORMAT_YUV420, AVIF_PIXEL_FORMAT_YUV400}) {
+          for (avifRange yuv_range : {AVIF_RANGE_LIMITED, AVIF_RANGE_FULL}) {
+            for (decltype(AVIF_MATRIX_COEFFICIENTS_IDENTITY)
+                     matrix_coefficients : {AVIF_MATRIX_COEFFICIENTS_IDENTITY,
+                                            AVIF_MATRIX_COEFFICIENTS_BT601}) {
+              if (matrix_coefficients == AVIF_MATRIX_COEFFICIENTS_IDENTITY &&
+                  yuv_format != AVIF_PIXEL_FORMAT_YUV444) {
+                // See avifPrepareReformatState().
+                continue;
+              }
+              for (avifChromaDownsampling chroma_downsampling :
+                   {AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC,
+                    AVIF_CHROMA_DOWNSAMPLING_FASTEST,
+                    AVIF_CHROMA_DOWNSAMPLING_BEST_QUALITY,
+                    AVIF_CHROMA_DOWNSAMPLING_AVERAGE,
+                    AVIF_CHROMA_DOWNSAMPLING_SHARP_YUV}) {
+                if (chroma_downsampling == AVIF_CHROMA_DOWNSAMPLING_SHARP_YUV &&
+                    yuv_depth > 12) {
+                  // SharpYuvConvert() only supports YUV bit depths up to 12.
+                  continue;
+                }
+                ConvertWholeRange(
+                    rgb_depth, yuv_depth, rgb_format, yuv_format, yuv_range,
+                    static_cast<avifMatrixCoefficients>(matrix_coefficients),
+                    chroma_downsampling,
+                    /*add_noise=*/true,
+                    // Just try min and max values.
+                    /*rgb_step=*/(1u << rgb_depth) - 1u,
+                    // Barely check the results, this is mostly for coverage.
+                    /*max_abs_average_diff=*/(1u << rgb_depth) - 1u,
+                    /*min_psnr=*/5.0,
+                    // Avoid spam.
+                    /*log=*/false);
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+TEST(RGBToYUVTest, AllMatrixCoefficients) {
+  // Coverage of all configurations with all min/max input combinations.
+  for (int rgb_depth : {8, 10, 12}) {
+    for (int yuv_depth : {8, 10, 12}) {
+      for (avifPixelFormat yuv_format :
+           {AVIF_PIXEL_FORMAT_YUV444, AVIF_PIXEL_FORMAT_YUV422,
+            AVIF_PIXEL_FORMAT_YUV420, AVIF_PIXEL_FORMAT_YUV400}) {
+        for (avifRange yuv_range : {AVIF_RANGE_LIMITED, AVIF_RANGE_FULL}) {
+          for (decltype(AVIF_MATRIX_COEFFICIENTS_IDENTITY) matrix_coefficients :
+               {
+                   AVIF_MATRIX_COEFFICIENTS_BT709,
+                   AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED,
+                   AVIF_MATRIX_COEFFICIENTS_FCC,
+                   AVIF_MATRIX_COEFFICIENTS_BT470BG,
+                   AVIF_MATRIX_COEFFICIENTS_BT601,
+                   AVIF_MATRIX_COEFFICIENTS_SMPTE240,
+                   AVIF_MATRIX_COEFFICIENTS_YCGCO,
+                   AVIF_MATRIX_COEFFICIENTS_BT2020_NCL,
+                   AVIF_MATRIX_COEFFICIENTS_CHROMA_DERIVED_NCL
+                   // These are unsupported. See avifPrepareReformatState().
+                   // AVIF_MATRIX_COEFFICIENTS_BT2020_CL
+                   // AVIF_MATRIX_COEFFICIENTS_SMPTE2085
+                   // AVIF_MATRIX_COEFFICIENTS_CHROMA_DERIVED_CL
+                   // AVIF_MATRIX_COEFFICIENTS_ICTCP
+               }) {
+            if (matrix_coefficients == AVIF_MATRIX_COEFFICIENTS_YCGCO &&
+                yuv_range == AVIF_RANGE_LIMITED) {
+              // See avifPrepareReformatState().
+              continue;
+            }
+            for (avifChromaDownsampling chroma_downsampling :
+                 {AVIF_CHROMA_DOWNSAMPLING_FASTEST,
+                  AVIF_CHROMA_DOWNSAMPLING_BEST_QUALITY}) {
+              ConvertWholeRange(
+                  rgb_depth, yuv_depth, AVIF_RGB_FORMAT_RGBA, yuv_format,
+                  yuv_range,
+                  static_cast<avifMatrixCoefficients>(matrix_coefficients),
+                  chroma_downsampling,
+                  /*add_noise=*/true,
+                  // Just try min and max values.
+                  /*rgb_step=*/(1u << rgb_depth) - 1u,
+                  // Barely check the results, this is mostly for coverage.
+                  /*max_abs_average_diff=*/(1u << rgb_depth) - 1u,
+                  /*min_psnr=*/5.0,
+                  // Avoid spam.
+                  /*log=*/false);
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+//------------------------------------------------------------------------------
+// Selected configurations
+
+class RGBToYUVTest
+    : public testing::TestWithParam<std::tuple<
+          /*rgb_depth=*/int, /*yuv_depth=*/int, avifRGBFormat, avifPixelFormat,
+          avifRange, avifMatrixCoefficients, avifChromaDownsampling,
+          /*add_noise=*/bool, /*rgb_step=*/uint32_t,
+          /*max_abs_average_diff=*/double, /*min_psnr=*/double>> {};
+
+TEST_P(RGBToYUVTest, ConvertWholeRange) {
+  ConvertWholeRange(
+      /*rgb_depth=*/std::get<0>(GetParam()),
+      /*yuv_depth=*/std::get<1>(GetParam()),
+      /*rgb_format=*/std::get<2>(GetParam()),
+      /*yuv_format=*/std::get<3>(GetParam()),
+      /*yuv_range=*/std::get<4>(GetParam()),
+      /*matrix_coefficients=*/std::get<5>(GetParam()),
+      /*chroma_downsampling=*/std::get<6>(GetParam()),
+      // Whether to add noise to the input RGB samples.
+      // Should only impact subsampled chroma (4:2:2 and 4:2:0).
+      /*add_noise=*/std::get<7>(GetParam()),
+      // Testing each RGB combination would be more accurate but results are
+      // similar with faster settings.
+      /*rgb_step=*/std::get<8>(GetParam()),
+      // Thresholds to pass.
+      /*max_abs_average_diff=*/std::get<9>(GetParam()),
+      /*min_psnr=*/std::get<10>(GetParam()),
+      // Useful to see surrounding results when there is a failure.
+      /*log=*/true);
+}
+
+TEST_P(RGBToYUVTest, ConvertWholeBuffer) {
+  ConvertWholeBuffer(
+      /*rgb_depth=*/std::get<0>(GetParam()),
+      /*yuv_depth=*/std::get<1>(GetParam()),
+      /*rgb_format=*/std::get<2>(GetParam()),
+      /*yuv_format=*/std::get<3>(GetParam()),
+      /*yuv_range=*/std::get<4>(GetParam()),
+      /*matrix_coefficients=*/std::get<5>(GetParam()),
+      /*chroma_downsampling=*/std::get<6>(GetParam()),
+      // Whether to add noise to the input RGB samples.
+      /*add_noise=*/std::get<7>(GetParam()),
+      // Threshold to pass.
+      /*min_psnr=*/std::get<10>(GetParam()));
+}
 
 // avifMatrixCoefficients-typed constants for testing::Values() to work on MSVC.
+// typedef or using decltype(AVIF_MATRIX_COEFFICIENTS_IDENTITY) does not work
+// (GTest template "declared using unnamed type, is used but never defined").
 constexpr avifMatrixCoefficients kMatrixCoefficientsBT601 =
     AVIF_MATRIX_COEFFICIENTS_BT601;
 constexpr avifMatrixCoefficients kMatrixCoefficientsBT709 =
@@ -348,7 +480,7 @@
 INSTANTIATE_TEST_SUITE_P(Identity8b, RGBToYUVTest,
                          Combine(/*rgb_depth=*/Values(8),
                                  /*yuv_depth=*/Values(8, 10, 12),
-                                 ValuesIn(kAllRgbFormats),
+                                 Values(AVIF_RGB_FORMAT_RGBA),
                                  Values(AVIF_PIXEL_FORMAT_YUV444),
                                  Values(AVIF_RANGE_FULL),
                                  Values(kMatrixCoefficientsIdentity),
@@ -360,7 +492,7 @@
 INSTANTIATE_TEST_SUITE_P(Identity10b, RGBToYUVTest,
                          Combine(/*rgb_depth=*/Values(10),
                                  /*yuv_depth=*/Values(10, 12),
-                                 ValuesIn(kAllRgbFormats),
+                                 Values(AVIF_RGB_FORMAT_RGBA),
                                  Values(AVIF_PIXEL_FORMAT_YUV444),
                                  Values(AVIF_RANGE_FULL),
                                  Values(kMatrixCoefficientsIdentity),
@@ -372,7 +504,7 @@
 INSTANTIATE_TEST_SUITE_P(Identity12b, RGBToYUVTest,
                          Combine(/*rgb_depth=*/Values(12),
                                  /*yuv_depth=*/Values(12),
-                                 ValuesIn(kAllRgbFormats),
+                                 Values(AVIF_RGB_FORMAT_RGBA),
                                  Values(AVIF_PIXEL_FORMAT_YUV444),
                                  Values(AVIF_RANGE_FULL),
                                  Values(kMatrixCoefficientsIdentity),
@@ -387,7 +519,7 @@
     PlainAnySubsampling8b, RGBToYUVTest,
     Combine(
         /*rgb_depth=*/Values(8),
-        /*yuv_depth=*/Values(8), ValuesIn(kAllRgbFormats),
+        /*yuv_depth=*/Values(8), Values(AVIF_RGB_FORMAT_RGBA),
         Values(AVIF_PIXEL_FORMAT_YUV444, AVIF_PIXEL_FORMAT_YUV422,
                AVIF_PIXEL_FORMAT_YUV420),
         Values(AVIF_RANGE_FULL), Values(kMatrixCoefficientsBT601),
@@ -404,7 +536,7 @@
 INSTANTIATE_TEST_SUITE_P(MonochromeLossless8b, RGBToYUVTest,
                          Combine(/*rgb_depth=*/Values(8),
                                  /*yuv_depth=*/Values(8, 10, 12),
-                                 ValuesIn(kAllRgbFormats),
+                                 Values(AVIF_RGB_FORMAT_RGBA),
                                  Values(AVIF_PIXEL_FORMAT_YUV400),
                                  Values(AVIF_RANGE_FULL),
                                  Values(kMatrixCoefficientsBT601),
@@ -416,7 +548,7 @@
 INSTANTIATE_TEST_SUITE_P(MonochromeLossless10b, RGBToYUVTest,
                          Combine(/*rgb_depth=*/Values(10),
                                  /*yuv_depth=*/Values(10, 12),
-                                 ValuesIn(kAllRgbFormats),
+                                 Values(AVIF_RGB_FORMAT_RGBA),
                                  Values(AVIF_PIXEL_FORMAT_YUV400),
                                  Values(AVIF_RANGE_FULL),
                                  Values(kMatrixCoefficientsBT601),
@@ -428,7 +560,7 @@
 INSTANTIATE_TEST_SUITE_P(MonochromeLossless12b, RGBToYUVTest,
                          Combine(/*rgb_depth=*/Values(12),
                                  /*yuv_depth=*/Values(12),
-                                 ValuesIn(kAllRgbFormats),
+                                 Values(AVIF_RGB_FORMAT_RGBA),
                                  Values(AVIF_PIXEL_FORMAT_YUV400),
                                  Values(AVIF_RANGE_FULL),
                                  Values(kMatrixCoefficientsBT601),
@@ -438,13 +570,12 @@
                                  /*max_abs_average_diff=*/Values(0.),
                                  /*min_psnr=*/Values(99.)));
 
-// Can be used to print the drift of all RGB to YUV conversion possibilities.
-// Also used for coverage.
+// Coverage for reformat_libsharpyuv.c.
 INSTANTIATE_TEST_SUITE_P(
     SharpYuv8Bit, RGBToYUVTest,
     Combine(
         /*rgb_depth=*/Values(8),
-        /*yuv_depth=*/Values(8, 10, 12), ValuesIn(kAllRgbFormats),
+        /*yuv_depth=*/Values(8, 10, 12), Values(AVIF_RGB_FORMAT_RGBA),
         Values(AVIF_PIXEL_FORMAT_YUV420),
         Values(AVIF_RANGE_LIMITED, AVIF_RANGE_FULL),
         Values(kMatrixCoefficientsBT601, kMatrixCoefficientsBT709),
@@ -459,7 +590,7 @@
     SharpYuv10Bit, RGBToYUVTest,
     Combine(
         /*rgb_depth=*/Values(10),
-        /*yuv_depth=*/Values(8, 10, 12), ValuesIn(kAllRgbFormats),
+        /*yuv_depth=*/Values(8, 10, 12), Values(AVIF_RGB_FORMAT_RGBA),
         Values(AVIF_PIXEL_FORMAT_YUV420),
         Values(AVIF_RANGE_LIMITED, AVIF_RANGE_FULL),
         Values(kMatrixCoefficientsBT601),
@@ -474,7 +605,7 @@
     SharpYuv12Bit, RGBToYUVTest,
     Combine(
         /*rgb_depth=*/Values(12),
-        /*yuv_depth=*/Values(8, 10, 12), ValuesIn(kAllRgbFormats),
+        /*yuv_depth=*/Values(8, 10, 12), Values(AVIF_RGB_FORMAT_RGBA),
         Values(AVIF_PIXEL_FORMAT_YUV420),
         Values(AVIF_RANGE_LIMITED, AVIF_RANGE_FULL),
         Values(kMatrixCoefficientsBT601),
@@ -491,7 +622,10 @@
 INSTANTIATE_TEST_SUITE_P(
     All8b, RGBToYUVTest,
     Combine(/*rgb_depth=*/Values(8),
-            /*yuv_depth=*/Values(8, 10, 12), ValuesIn(kAllRgbFormats),
+            /*yuv_depth=*/Values(8, 10, 12),
+            Values(AVIF_RGB_FORMAT_RGB, AVIF_RGB_FORMAT_RGBA,
+                   AVIF_RGB_FORMAT_ARGB, AVIF_RGB_FORMAT_BGR,
+                   AVIF_RGB_FORMAT_BGRA, AVIF_RGB_FORMAT_ABGR),
             Values(AVIF_PIXEL_FORMAT_YUV444, AVIF_PIXEL_FORMAT_YUV422,
                    AVIF_PIXEL_FORMAT_YUV420),
             Values(AVIF_RANGE_LIMITED, AVIF_RANGE_FULL),
@@ -505,7 +639,7 @@
 INSTANTIATE_TEST_SUITE_P(
     All10b, RGBToYUVTest,
     Combine(/*rgb_depth=*/Values(10),
-            /*yuv_depth=*/Values(8, 10, 12), ValuesIn(kAllRgbFormats),
+            /*yuv_depth=*/Values(8, 10, 12), Values(AVIF_RGB_FORMAT_RGBA),
             Values(AVIF_PIXEL_FORMAT_YUV444, AVIF_PIXEL_FORMAT_YUV422,
                    AVIF_PIXEL_FORMAT_YUV420),
             Values(AVIF_RANGE_LIMITED, AVIF_RANGE_FULL),
@@ -519,7 +653,7 @@
 INSTANTIATE_TEST_SUITE_P(
     All12b, RGBToYUVTest,
     Combine(/*rgb_depth=*/Values(12),
-            /*yuv_depth=*/Values(8, 10, 12), ValuesIn(kAllRgbFormats),
+            /*yuv_depth=*/Values(8, 10, 12), Values(AVIF_RGB_FORMAT_RGBA),
             Values(AVIF_PIXEL_FORMAT_YUV444, AVIF_PIXEL_FORMAT_YUV422,
                    AVIF_PIXEL_FORMAT_YUV420),
             Values(AVIF_RANGE_LIMITED, AVIF_RANGE_FULL),
@@ -531,14 +665,12 @@
                                                    // of high rgb_step.
             /*min_psnr=*/Values(52.)));
 
-// TODO: Test other matrix coefficients than identity and bt.601.
-
 // This was used to estimate the quality loss of libyuv for RGB-to-YUV.
 // Disabled because it takes a few minutes.
 INSTANTIATE_TEST_SUITE_P(
     DISABLED_All8bTo8b, RGBToYUVTest,
     Combine(/*rgb_depth=*/Values(8),
-            /*yuv_depth=*/Values(8), ValuesIn(kAllRgbFormats),
+            /*yuv_depth=*/Values(8), Values(AVIF_RGB_FORMAT_RGBA),
             Values(AVIF_PIXEL_FORMAT_YUV444, AVIF_PIXEL_FORMAT_YUV422,
                    AVIF_PIXEL_FORMAT_YUV420, AVIF_PIXEL_FORMAT_YUV400),
             Values(AVIF_RANGE_FULL, AVIF_RANGE_LIMITED),