tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

input_suspension_test.cc (22719B)


      1 // Copyright (c) the JPEG XL Project Authors. All rights reserved.
      2 //
      3 // Use of this source code is governed by a BSD-style
      4 // license that can be found in the LICENSE file.
      5 
      6 #include <jxl/types.h>
      7 
      8 #include <algorithm>
      9 #include <cstddef>
     10 #include <cstdint>
     11 #include <cstring>
     12 #include <ostream>
     13 #include <sstream>
     14 #include <string>
     15 #include <utility>
     16 #include <vector>
     17 
     18 #include "lib/jpegli/decode.h"
     19 #include "lib/jpegli/libjpeg_test_util.h"
     20 #include "lib/jpegli/test_params.h"
     21 #include "lib/jpegli/test_utils.h"
     22 #include "lib/jpegli/testing.h"
     23 #include "lib/jxl/base/status.h"
     24 
     25 namespace jpegli {
     26 namespace {
     27 
     28 constexpr uint8_t kFakeEoiMarker[2] = {0xff, 0xd9};
     29 
     30 struct SourceManager {
     31  SourceManager(const uint8_t* data, size_t len, size_t max_chunk_size,
     32                bool is_partial_file)
     33      : data_(data),
     34        len_(len),
     35        pos_(0),
     36        max_chunk_size_(max_chunk_size),
     37        is_partial_file_(is_partial_file) {
     38    pub_.init_source = init_source;
     39    pub_.fill_input_buffer = fill_input_buffer;
     40    pub_.next_input_byte = nullptr;
     41    pub_.bytes_in_buffer = 0;
     42    pub_.skip_input_data = skip_input_data;
     43    pub_.resync_to_restart = jpegli_resync_to_restart;
     44    pub_.term_source = term_source;
     45    if (max_chunk_size_ == 0) max_chunk_size_ = len;
     46  }
     47 
     48  ~SourceManager() {
     49    EXPECT_EQ(0, pub_.bytes_in_buffer);
     50    if (!is_partial_file_) {
     51      EXPECT_EQ(len_, pos_);
     52    }
     53  }
     54 
     55  bool LoadNextChunk() {
     56    if (pos_ >= len_ && !is_partial_file_) {
     57      return false;
     58    }
     59    if (pub_.bytes_in_buffer > 0) {
     60      EXPECT_LE(pub_.bytes_in_buffer, buffer_.size());
     61      memmove(buffer_.data(), pub_.next_input_byte, pub_.bytes_in_buffer);
     62    }
     63    size_t chunk_size =
     64        pos_ < len_ ? std::min(len_ - pos_, max_chunk_size_) : 2;
     65    buffer_.resize(pub_.bytes_in_buffer + chunk_size);
     66    memcpy(&buffer_[pub_.bytes_in_buffer],
     67           pos_ < len_ ? data_ + pos_ : kFakeEoiMarker, chunk_size);
     68    pub_.next_input_byte = buffer_.data();
     69    pub_.bytes_in_buffer += chunk_size;
     70    pos_ += chunk_size;
     71    return true;
     72  }
     73 
     74 private:
     75  jpeg_source_mgr pub_;
     76  std::vector<uint8_t> buffer_;
     77  const uint8_t* data_;
     78  size_t len_;
     79  size_t pos_;
     80  size_t max_chunk_size_;
     81  bool is_partial_file_;
     82 
     83  static void init_source(j_decompress_ptr cinfo) {
     84    auto* src = reinterpret_cast<SourceManager*>(cinfo->src);
     85    src->pub_.next_input_byte = nullptr;
     86    src->pub_.bytes_in_buffer = 0;
     87  }
     88 
     89  static boolean fill_input_buffer(j_decompress_ptr cinfo) { return FALSE; }
     90 
     91  static void skip_input_data(j_decompress_ptr cinfo,
     92                              long num_bytes /* NOLINT*/) {
     93    auto* src = reinterpret_cast<SourceManager*>(cinfo->src);
     94    if (num_bytes <= 0) {
     95      return;
     96    }
     97    if (src->pub_.bytes_in_buffer >= static_cast<size_t>(num_bytes)) {
     98      src->pub_.bytes_in_buffer -= num_bytes;
     99      src->pub_.next_input_byte += num_bytes;
    100    } else {
    101      src->pos_ += num_bytes - src->pub_.bytes_in_buffer;
    102      src->pub_.bytes_in_buffer = 0;
    103    }
    104  }
    105 
    106  static void term_source(j_decompress_ptr cinfo) {}
    107 };
    108 
    109 uint8_t markers_seen[kMarkerSequenceLen];
    110 size_t num_markers_seen = 0;
    111 
    112 uint8_t get_next_byte(j_decompress_ptr cinfo) {
    113  cinfo->src->bytes_in_buffer--;
    114  return *cinfo->src->next_input_byte++;
    115 }
    116 
    117 boolean test_marker_processor(j_decompress_ptr cinfo) {
    118  markers_seen[num_markers_seen] = cinfo->unread_marker;
    119  if (cinfo->src->bytes_in_buffer < 2) {
    120    return FALSE;
    121  }
    122  size_t marker_len = (get_next_byte(cinfo) << 8) + get_next_byte(cinfo);
    123  EXPECT_EQ(2 + ((num_markers_seen + 2) % sizeof(kMarkerData)), marker_len);
    124  if (marker_len > 2) {
    125    (*cinfo->src->skip_input_data)(cinfo, marker_len - 2);
    126  }
    127  ++num_markers_seen;
    128  return TRUE;
    129 }
    130 
    131 jxl::Status ReadOutputImage(const DecompressParams& dparams,
    132                            j_decompress_ptr cinfo, SourceManager* src,
    133                            TestImage* output) {
    134  output->ysize = cinfo->output_height;
    135  output->xsize = cinfo->output_width;
    136  output->components = cinfo->num_components;
    137  if (cinfo->raw_data_out) {
    138    output->color_space = cinfo->jpeg_color_space;
    139    for (int c = 0; c < cinfo->num_components; ++c) {
    140      size_t xsize = cinfo->comp_info[c].width_in_blocks * DCTSIZE;
    141      size_t ysize = cinfo->comp_info[c].height_in_blocks * DCTSIZE;
    142      std::vector<uint8_t> plane(ysize * xsize);
    143      output->raw_data.emplace_back(std::move(plane));
    144    }
    145  } else {
    146    output->color_space = cinfo->out_color_space;
    147    output->AllocatePixels();
    148  }
    149  size_t total_output_lines = 0;
    150  while (cinfo->output_scanline < cinfo->output_height) {
    151    size_t max_lines;
    152    size_t num_output_lines;
    153    if (cinfo->raw_data_out) {
    154      size_t iMCU_height = cinfo->max_v_samp_factor * DCTSIZE;
    155      EXPECT_EQ(cinfo->output_scanline, cinfo->output_iMCU_row * iMCU_height);
    156      max_lines = iMCU_height;
    157      std::vector<std::vector<JSAMPROW>> rowdata(cinfo->num_components);
    158      std::vector<JSAMPARRAY> data(cinfo->num_components);
    159      for (int c = 0; c < cinfo->num_components; ++c) {
    160        size_t xsize = cinfo->comp_info[c].width_in_blocks * DCTSIZE;
    161        size_t ysize = cinfo->comp_info[c].height_in_blocks * DCTSIZE;
    162        size_t num_lines = cinfo->comp_info[c].v_samp_factor * DCTSIZE;
    163        rowdata[c].resize(num_lines);
    164        size_t y0 = cinfo->output_iMCU_row * num_lines;
    165        for (size_t i = 0; i < num_lines; ++i) {
    166          rowdata[c][i] =
    167              y0 + i < ysize ? &output->raw_data[c][(y0 + i) * xsize] : nullptr;
    168        }
    169        data[c] = rowdata[c].data();
    170      }
    171      while ((num_output_lines =
    172                  jpegli_read_raw_data(cinfo, data.data(), max_lines)) == 0) {
    173        JXL_ENSURE(src && src->LoadNextChunk());
    174      }
    175    } else {
    176      size_t max_output_lines = dparams.max_output_lines;
    177      if (max_output_lines == 0) max_output_lines = cinfo->output_height;
    178      size_t lines_left = cinfo->output_height - cinfo->output_scanline;
    179      max_lines = std::min<size_t>(max_output_lines, lines_left);
    180      size_t stride = cinfo->output_width * cinfo->num_components;
    181      std::vector<JSAMPROW> scanlines(max_lines);
    182      for (size_t i = 0; i < max_lines; ++i) {
    183        size_t yidx = cinfo->output_scanline + i;
    184        scanlines[i] = &output->pixels[yidx * stride];
    185      }
    186      while ((num_output_lines = jpegli_read_scanlines(cinfo, scanlines.data(),
    187                                                       max_lines)) == 0) {
    188        JXL_ENSURE(src && src->LoadNextChunk());
    189      }
    190    }
    191    total_output_lines += num_output_lines;
    192    EXPECT_EQ(total_output_lines, cinfo->output_scanline);
    193    if (num_output_lines < max_lines) {
    194      JXL_ENSURE(src && src->LoadNextChunk());
    195    }
    196  }
    197  return true;
    198 }
    199 
    200 struct TestConfig {
    201  std::string fn;
    202  std::string fn_desc;
    203  TestImage input;
    204  CompressParams jparams;
    205  DecompressParams dparams;
    206  float max_rms_dist = 1.0f;
    207 };
    208 
    209 jxl::StatusOr<std::vector<uint8_t>> GetTestJpegData(TestConfig& config) {
    210  std::vector<uint8_t> compressed;
    211  if (!config.fn.empty()) {
    212    JXL_ASSIGN_OR_RETURN(compressed, ReadTestData(config.fn));
    213  } else {
    214    GeneratePixels(&config.input);
    215    JXL_RETURN_IF_ERROR(
    216        EncodeWithJpegli(config.input, config.jparams, &compressed));
    217  }
    218  return compressed;
    219 }
    220 
    221 bool IsSequential(const TestConfig& config) {
    222  if (!config.fn.empty()) {
    223    return config.fn_desc.find("PROGR") == std::string::npos;
    224  }
    225  return config.jparams.progressive_mode <= 0;
    226 }
    227 
    228 class InputSuspensionTestParam : public ::testing::TestWithParam<TestConfig> {};
    229 
    230 TEST_P(InputSuspensionTestParam, InputOutputLockStepNonBuffered) {
    231  TestConfig config = GetParam();
    232  const DecompressParams& dparams = config.dparams;
    233  JXL_ASSIGN_OR_QUIT(std::vector<uint8_t> compressed, GetTestJpegData(config),
    234                     "Failed to create test data.");
    235  bool is_partial = config.dparams.size_factor < 1.0f;
    236  if (is_partial) {
    237    compressed.resize(compressed.size() * config.dparams.size_factor);
    238  }
    239  SourceManager src(compressed.data(), compressed.size(), dparams.chunk_size,
    240                    is_partial);
    241  TestImage output0;
    242  jpeg_decompress_struct cinfo;
    243  const auto try_catch_block = [&]() -> bool {
    244    ERROR_HANDLER_SETUP(jpegli);
    245    jpegli_create_decompress(&cinfo);
    246    cinfo.src = reinterpret_cast<jpeg_source_mgr*>(&src);
    247 
    248    if (config.jparams.add_marker) {
    249      jpegli_save_markers(&cinfo, kSpecialMarker0, 0xffff);
    250      jpegli_save_markers(&cinfo, kSpecialMarker1, 0xffff);
    251      num_markers_seen = 0;
    252      jpegli_set_marker_processor(&cinfo, 0xe6, test_marker_processor);
    253      jpegli_set_marker_processor(&cinfo, 0xe7, test_marker_processor);
    254      jpegli_set_marker_processor(&cinfo, 0xe8, test_marker_processor);
    255    }
    256    while (jpegli_read_header(&cinfo, TRUE) == JPEG_SUSPENDED) {
    257      JPEGLI_TEST_ENSURE_TRUE(src.LoadNextChunk());
    258    }
    259    SetDecompressParams(dparams, &cinfo);
    260    jpegli_set_output_format(&cinfo, dparams.data_type, dparams.endianness);
    261    if (config.jparams.add_marker) {
    262      EXPECT_EQ(num_markers_seen, kMarkerSequenceLen);
    263      EXPECT_EQ(0, memcmp(markers_seen, kMarkerSequence, num_markers_seen));
    264    }
    265    VerifyHeader(config.jparams, &cinfo);
    266    cinfo.raw_data_out = TO_JXL_BOOL(dparams.output_mode == RAW_DATA);
    267 
    268    if (dparams.output_mode == COEFFICIENTS) {
    269      jvirt_barray_ptr* coef_arrays;
    270      while ((coef_arrays = jpegli_read_coefficients(&cinfo)) == nullptr) {
    271        JPEGLI_TEST_ENSURE_TRUE(src.LoadNextChunk());
    272      }
    273      CopyCoefficients(&cinfo, coef_arrays, &output0);
    274    } else {
    275      while (!jpegli_start_decompress(&cinfo)) {
    276        JPEGLI_TEST_ENSURE_TRUE(src.LoadNextChunk());
    277      }
    278      JPEGLI_TEST_ENSURE_TRUE(ReadOutputImage(dparams, &cinfo, &src, &output0));
    279    }
    280 
    281    while (!jpegli_finish_decompress(&cinfo)) {
    282      JPEGLI_TEST_ENSURE_TRUE(src.LoadNextChunk());
    283    }
    284    return true;
    285  };
    286  ASSERT_TRUE(try_catch_block());
    287  jpegli_destroy_decompress(&cinfo);
    288 
    289  TestImage output1;
    290  DecodeWithLibjpeg(config.jparams, dparams, compressed, &output1);
    291  VerifyOutputImage(output1, output0, config.max_rms_dist);
    292 }
    293 
    294 TEST_P(InputSuspensionTestParam, InputOutputLockStepBuffered) {
    295  TestConfig config = GetParam();
    296  if (config.jparams.add_marker) return;
    297  const DecompressParams& dparams = config.dparams;
    298  JXL_ASSIGN_OR_QUIT(std::vector<uint8_t> compressed, GetTestJpegData(config),
    299                     "Failed to create test data.");
    300  bool is_partial = config.dparams.size_factor < 1.0f;
    301  if (is_partial) {
    302    compressed.resize(compressed.size() * config.dparams.size_factor);
    303  }
    304  SourceManager src(compressed.data(), compressed.size(), dparams.chunk_size,
    305                    is_partial);
    306  std::vector<TestImage> output_progression0;
    307  jpeg_decompress_struct cinfo;
    308  const auto try_catch_block = [&]() -> bool {
    309    ERROR_HANDLER_SETUP(jpegli);
    310    jpegli_create_decompress(&cinfo);
    311 
    312    cinfo.src = reinterpret_cast<jpeg_source_mgr*>(&src);
    313 
    314    while (jpegli_read_header(&cinfo, TRUE) == JPEG_SUSPENDED) {
    315      JPEGLI_TEST_ENSURE_TRUE(src.LoadNextChunk());
    316    }
    317    SetDecompressParams(dparams, &cinfo);
    318    jpegli_set_output_format(&cinfo, dparams.data_type, dparams.endianness);
    319 
    320    cinfo.buffered_image = TRUE;
    321    cinfo.raw_data_out = TO_JXL_BOOL(dparams.output_mode == RAW_DATA);
    322 
    323    EXPECT_TRUE(jpegli_start_decompress(&cinfo));
    324    EXPECT_FALSE(jpegli_input_complete(&cinfo));
    325    EXPECT_EQ(0, cinfo.output_scan_number);
    326 
    327    int sos_marker_cnt = 1;  // read_header reads the first SOS marker
    328    while (!jpegli_input_complete(&cinfo)) {
    329      EXPECT_EQ(cinfo.input_scan_number, sos_marker_cnt);
    330      EXPECT_TRUE(jpegli_start_output(&cinfo, cinfo.input_scan_number));
    331      // start output sets output_scan_number, but does not change
    332      // input_scan_number
    333      EXPECT_EQ(cinfo.output_scan_number, cinfo.input_scan_number);
    334      EXPECT_EQ(cinfo.input_scan_number, sos_marker_cnt);
    335      TestImage output;
    336      JPEGLI_TEST_ENSURE_TRUE(ReadOutputImage(dparams, &cinfo, &src, &output));
    337      output_progression0.emplace_back(std::move(output));
    338      // read scanlines/read raw data does not change input/output scan number
    339      EXPECT_EQ(cinfo.input_scan_number, sos_marker_cnt);
    340      EXPECT_EQ(cinfo.output_scan_number, cinfo.input_scan_number);
    341      while (!jpegli_finish_output(&cinfo)) {
    342        JPEGLI_TEST_ENSURE_TRUE(src.LoadNextChunk());
    343      }
    344      ++sos_marker_cnt;  // finish output reads the next SOS marker or EOI
    345      if (dparams.output_mode == COEFFICIENTS) {
    346        jvirt_barray_ptr* coef_arrays = jpegli_read_coefficients(&cinfo);
    347        JPEGLI_TEST_ENSURE_TRUE(coef_arrays != nullptr);
    348        CopyCoefficients(&cinfo, coef_arrays, &output_progression0.back());
    349      }
    350    }
    351 
    352    EXPECT_TRUE(jpegli_finish_decompress(&cinfo));
    353    return true;
    354  };
    355  ASSERT_TRUE(try_catch_block());
    356  jpegli_destroy_decompress(&cinfo);
    357 
    358  std::vector<TestImage> output_progression1;
    359  DecodeAllScansWithLibjpeg(config.jparams, dparams, compressed,
    360                            &output_progression1);
    361  ASSERT_EQ(output_progression0.size(), output_progression1.size());
    362  for (size_t i = 0; i < output_progression0.size(); ++i) {
    363    const TestImage& output = output_progression0[i];
    364    const TestImage& expected = output_progression1[i];
    365    VerifyOutputImage(expected, output, config.max_rms_dist);
    366  }
    367 }
    368 
    369 TEST_P(InputSuspensionTestParam, PreConsumeInputBuffered) {
    370  TestConfig config = GetParam();
    371  if (config.jparams.add_marker) return;
    372  const DecompressParams& dparams = config.dparams;
    373  JXL_ASSIGN_OR_QUIT(std::vector<uint8_t> compressed, GetTestJpegData(config),
    374                     "Failed to create test data.");
    375  bool is_partial = config.dparams.size_factor < 1.0f;
    376  if (is_partial) {
    377    compressed.resize(compressed.size() * config.dparams.size_factor);
    378  }
    379  std::vector<TestImage> output_progression1;
    380  DecodeAllScansWithLibjpeg(config.jparams, dparams, compressed,
    381                            &output_progression1);
    382  SourceManager src(compressed.data(), compressed.size(), dparams.chunk_size,
    383                    is_partial);
    384  TestImage output0;
    385  jpeg_decompress_struct cinfo;
    386  const auto try_catch_block = [&]() -> bool {
    387    ERROR_HANDLER_SETUP(jpegli);
    388    jpegli_create_decompress(&cinfo);
    389    cinfo.src = reinterpret_cast<jpeg_source_mgr*>(&src);
    390 
    391    int status;
    392    while ((status = jpegli_consume_input(&cinfo)) != JPEG_REACHED_SOS) {
    393      if (status == JPEG_SUSPENDED) {
    394        JPEGLI_TEST_ENSURE_TRUE(src.LoadNextChunk());
    395      }
    396    }
    397    EXPECT_EQ(JPEG_REACHED_SOS, jpegli_consume_input(&cinfo));
    398    cinfo.buffered_image = TRUE;
    399    cinfo.raw_data_out = TO_JXL_BOOL(dparams.output_mode == RAW_DATA);
    400    cinfo.do_block_smoothing = TO_JXL_BOOL(dparams.do_block_smoothing);
    401 
    402    EXPECT_TRUE(jpegli_start_decompress(&cinfo));
    403    EXPECT_FALSE(jpegli_input_complete(&cinfo));
    404    EXPECT_EQ(1, cinfo.input_scan_number);
    405    EXPECT_EQ(0, cinfo.output_scan_number);
    406 
    407    while ((status = jpegli_consume_input(&cinfo)) != JPEG_REACHED_EOI) {
    408      if (status == JPEG_SUSPENDED) {
    409        JPEGLI_TEST_ENSURE_TRUE(src.LoadNextChunk());
    410      }
    411    }
    412 
    413    EXPECT_TRUE(jpegli_input_complete(&cinfo));
    414    EXPECT_EQ(output_progression1.size(), cinfo.input_scan_number);
    415    EXPECT_EQ(0, cinfo.output_scan_number);
    416 
    417    EXPECT_TRUE(jpegli_start_output(&cinfo, cinfo.input_scan_number));
    418    EXPECT_EQ(output_progression1.size(), cinfo.input_scan_number);
    419    EXPECT_EQ(cinfo.output_scan_number, cinfo.input_scan_number);
    420 
    421    JPEGLI_TEST_ENSURE_TRUE(
    422        ReadOutputImage(dparams, &cinfo, nullptr, &output0));
    423    EXPECT_EQ(output_progression1.size(), cinfo.input_scan_number);
    424    EXPECT_EQ(cinfo.output_scan_number, cinfo.input_scan_number);
    425 
    426    EXPECT_TRUE(jpegli_finish_output(&cinfo));
    427    if (dparams.output_mode == COEFFICIENTS) {
    428      jvirt_barray_ptr* coef_arrays = jpegli_read_coefficients(&cinfo);
    429      JPEGLI_TEST_ENSURE_TRUE(coef_arrays != nullptr);
    430      CopyCoefficients(&cinfo, coef_arrays, &output0);
    431    }
    432    EXPECT_TRUE(jpegli_finish_decompress(&cinfo));
    433    return true;
    434  };
    435  ASSERT_TRUE(try_catch_block());
    436  jpegli_destroy_decompress(&cinfo);
    437 
    438  VerifyOutputImage(output_progression1.back(), output0, config.max_rms_dist);
    439 }
    440 
    441 TEST_P(InputSuspensionTestParam, PreConsumeInputNonBuffered) {
    442  TestConfig config = GetParam();
    443  if (config.jparams.add_marker || IsSequential(config)) return;
    444  const DecompressParams& dparams = config.dparams;
    445  JXL_ASSIGN_OR_QUIT(std::vector<uint8_t> compressed, GetTestJpegData(config),
    446                     "Failed to create test data.");
    447  bool is_partial = config.dparams.size_factor < 1.0f;
    448  if (is_partial) {
    449    compressed.resize(compressed.size() * config.dparams.size_factor);
    450  }
    451  SourceManager src(compressed.data(), compressed.size(), dparams.chunk_size,
    452                    is_partial);
    453  TestImage output0;
    454  jpeg_decompress_struct cinfo;
    455  const auto try_catch_block = [&]() -> bool {
    456    ERROR_HANDLER_SETUP(jpegli);
    457    jpegli_create_decompress(&cinfo);
    458    cinfo.src = reinterpret_cast<jpeg_source_mgr*>(&src);
    459 
    460    int status;
    461    while ((status = jpegli_consume_input(&cinfo)) != JPEG_REACHED_SOS) {
    462      if (status == JPEG_SUSPENDED) {
    463        JPEGLI_TEST_ENSURE_TRUE(src.LoadNextChunk());
    464      }
    465    }
    466    EXPECT_EQ(JPEG_REACHED_SOS, jpegli_consume_input(&cinfo));
    467    cinfo.raw_data_out = TO_JXL_BOOL(dparams.output_mode == RAW_DATA);
    468    cinfo.do_block_smoothing = TO_JXL_BOOL(dparams.do_block_smoothing);
    469 
    470    if (dparams.output_mode == COEFFICIENTS) {
    471      jpegli_read_coefficients(&cinfo);
    472    } else {
    473      while (!jpegli_start_decompress(&cinfo)) {
    474        JPEGLI_TEST_ENSURE_TRUE(src.LoadNextChunk());
    475      }
    476    }
    477 
    478    while ((status = jpegli_consume_input(&cinfo)) != JPEG_REACHED_EOI) {
    479      if (status == JPEG_SUSPENDED) {
    480        JPEGLI_TEST_ENSURE_TRUE(src.LoadNextChunk());
    481      }
    482    }
    483 
    484    if (dparams.output_mode == COEFFICIENTS) {
    485      jvirt_barray_ptr* coef_arrays = jpegli_read_coefficients(&cinfo);
    486      JPEGLI_TEST_ENSURE_TRUE(coef_arrays != nullptr);
    487      CopyCoefficients(&cinfo, coef_arrays, &output0);
    488    } else {
    489      JPEGLI_TEST_ENSURE_TRUE(
    490          ReadOutputImage(dparams, &cinfo, nullptr, &output0));
    491    }
    492 
    493    EXPECT_TRUE(jpegli_finish_decompress(&cinfo));
    494    return true;
    495  };
    496  ASSERT_TRUE(try_catch_block());
    497  jpegli_destroy_decompress(&cinfo);
    498 
    499  TestImage output1;
    500  DecodeWithLibjpeg(config.jparams, dparams, compressed, &output1);
    501  VerifyOutputImage(output1, output0, config.max_rms_dist);
    502 }
    503 
    504 std::vector<TestConfig> GenerateTests() {
    505  std::vector<TestConfig> all_tests;
    506  std::vector<std::pair<std::string, std::string>> testfiles({
    507      {"jxl/flower/flower.png.im_q85_444.jpg", "Q85YUV444"},
    508      {"jxl/flower/flower.png.im_q85_420_R13B.jpg", "Q85YUV420R13B"},
    509      {"jxl/flower/flower.png.im_q85_420_progr.jpg", "Q85YUV420PROGR"},
    510  });
    511  for (const auto& it : testfiles) {
    512    for (size_t chunk_size : {1, 64, 65536}) {
    513      for (size_t max_output_lines : {0, 1, 8, 16}) {
    514        TestConfig config;
    515        config.fn = it.first;
    516        config.fn_desc = it.second;
    517        config.dparams.chunk_size = chunk_size;
    518        config.dparams.max_output_lines = max_output_lines;
    519        all_tests.push_back(config);
    520        if (max_output_lines == 16) {
    521          config.dparams.output_mode = RAW_DATA;
    522          all_tests.push_back(config);
    523          config.dparams.output_mode = COEFFICIENTS;
    524          all_tests.push_back(config);
    525        }
    526      }
    527    }
    528  }
    529  for (size_t r : {1, 17, 1024}) {
    530    for (size_t chunk_size : {1, 65536}) {
    531      TestConfig config;
    532      config.dparams.chunk_size = chunk_size;
    533      config.jparams.progressive_mode = 2;
    534      config.jparams.restart_interval = r;
    535      all_tests.push_back(config);
    536    }
    537  }
    538  for (size_t chunk_size : {1, 4, 1024}) {
    539    TestConfig config;
    540    config.input.xsize = 256;
    541    config.input.ysize = 256;
    542    config.dparams.chunk_size = chunk_size;
    543    config.jparams.add_marker = true;
    544    all_tests.push_back(config);
    545  }
    546  // Tests for partial input.
    547  for (float size_factor : {0.1f, 0.33f, 0.5f, 0.75f}) {
    548    for (int progr : {0, 1, 3}) {
    549      for (int samp : {1, 2}) {
    550        for (JpegIOMode output_mode : {PIXELS, RAW_DATA}) {
    551          TestConfig config;
    552          config.input.xsize = 517;
    553          config.input.ysize = 523;
    554          config.jparams.h_sampling = {samp, 1, 1};
    555          config.jparams.v_sampling = {samp, 1, 1};
    556          config.jparams.progressive_mode = progr;
    557          config.dparams.size_factor = size_factor;
    558          config.dparams.output_mode = output_mode;
    559          // The last partially available block can behave differently.
    560          // TODO(szabadka) Figure out if we can make the behaviour more
    561          // similar.
    562          config.max_rms_dist = samp == 1 ? 1.75f : 3.0f;
    563          all_tests.push_back(config);
    564        }
    565      }
    566    }
    567  }
    568  // Tests for block smoothing.
    569  for (float size_factor : {0.1f, 0.33f, 0.5f, 0.75f, 1.0f}) {
    570    for (int samp : {1, 2}) {
    571      TestConfig config;
    572      config.input.xsize = 517;
    573      config.input.ysize = 523;
    574      config.jparams.h_sampling = {samp, 1, 1};
    575      config.jparams.v_sampling = {samp, 1, 1};
    576      config.jparams.progressive_mode = 2;
    577      config.dparams.size_factor = size_factor;
    578      config.dparams.do_block_smoothing = true;
    579      // libjpeg does smoothing for incomplete scans differently at
    580      // the border between current and previous scans.
    581      config.max_rms_dist = 8.0f;
    582      all_tests.push_back(config);
    583    }
    584  }
    585  return all_tests;
    586 }
    587 
    588 std::ostream& operator<<(std::ostream& os, const TestConfig& c) {
    589  if (!c.fn.empty()) {
    590    os << c.fn_desc;
    591  } else {
    592    os << c.input;
    593  }
    594  os << c.jparams;
    595  if (c.dparams.chunk_size == 0) {
    596    os << "CompleteInput";
    597  } else {
    598    os << "InputChunks" << c.dparams.chunk_size;
    599  }
    600  if (c.dparams.size_factor < 1.0f) {
    601    os << "Partial" << static_cast<int>(c.dparams.size_factor * 100) << "p";
    602  }
    603  if (c.dparams.max_output_lines == 0) {
    604    os << "CompleteOutput";
    605  } else {
    606    os << "OutputLines" << c.dparams.max_output_lines;
    607  }
    608  if (c.dparams.output_mode == RAW_DATA) {
    609    os << "RawDataOut";
    610  } else if (c.dparams.output_mode == COEFFICIENTS) {
    611    os << "CoeffsOut";
    612  }
    613  if (c.dparams.do_block_smoothing) {
    614    os << "BlockSmoothing";
    615  }
    616  return os;
    617 }
    618 
    619 std::string TestDescription(
    620    const testing::TestParamInfo<InputSuspensionTestParam::ParamType>& info) {
    621  std::stringstream name;
    622  name << info.param;
    623  return name.str();
    624 }
    625 
    626 JPEGLI_INSTANTIATE_TEST_SUITE_P(InputSuspensionTest, InputSuspensionTestParam,
    627                                testing::ValuesIn(GenerateTests()),
    628                                TestDescription);
    629 
    630 }  // namespace
    631 }  // namespace jpegli