Commit 4f4b908605a0c0e9cf3fc568302b074801eb7419

Authored by Jay Berkenbilt
1 parent 3d029fb1

Add a file with arrays with lots of nulls to the test suite

A bug was fixed between qpdf 8.4.2 and 9.0.0 regarding this type of
file (see #305 and #311), but it was necessary to retest after some
major refactoring work at the lexical and parsing layers. This lays
the groundwork for including this in performance benchmarks and in the
qpdf test suite rather than having to keep a large,
non-redistributable file around.

20 arrays of 20K nulls is plenty for performance memory testing and
doesn't take too long to run. Compared to qpdf 8.4.2, in qpdf 11.0.0,
the file generated here uses 3% of the RAM and runs over 4 times
faster.
... ... @@ -7,9 +7,9 @@ Before Release:
7 7 * Review in order #726
8 8 * Make ./performance_check usable by other people by having published
9 9 files to use for testing.
10   - * https://opensource.adobe.com/dc-acrobat-sdk-docs/standards/pdfstandards/pdf/PDF32000_2008.pdf
11   -* Incorporate --report-mem-usage into performance testing. Make sure
12   - there is some test somewhere that exercises the millions of nulls case.
  10 + * Site https://opensource.adobe.com/dc-acrobat-sdk-docs/standards/pdfstandards/pdf/PDF32000_2008.pdf
  11 + * Incorporate --report-mem-usage into performance testing.
  12 + * Include output of test_many_nulls
13 13 * Evaluate issues tagged with `next`
14 14 * Stay on top of https://github.com/pikepdf/pikepdf/pull/315
15 15  
... ...
qpdf/CMakeLists.txt
... ... @@ -5,6 +5,7 @@ set(MAIN_CXX_PROGRAMS
5 5 sizes
6 6 test_driver
7 7 test_large_file
  8 + test_many_nulls
8 9 test_parsedoffset
9 10 test_pdf_doc_encoding
10 11 test_pdf_unicode
... ...
qpdf/qtest/many-nulls.test 0 → 100644
  1 +#!/usr/bin/env perl
  2 +require 5.008;
  3 +use warnings;
  4 +use strict;
  5 +
  6 +unshift(@INC, '.');
  7 +require qpdf_test_helpers;
  8 +
  9 +chdir("qpdf") or die "chdir testdir failed: $!\n";
  10 +
  11 +require TestDriver;
  12 +
  13 +cleanup();
  14 +
  15 +my $td = new TestDriver('many-nulls');
  16 +
  17 +# The output of test_many_nulls is also used in performance testing.
  18 +# If it changes, consider whether it should be updated in
  19 +# performance-test-files as well. See performance_check at the top of
  20 +# the source tree.
  21 +$td->runtest("create file with many nulls",
  22 + {$td->COMMAND => "test_many_nulls a.pdf"},
  23 + {$td->STRING => "", $td->EXIT_STATUS => 0},
  24 + $td->NORMALIZE_NEWLINES);
  25 +$td->runtest("compare output",
  26 + {$td->FILE => "a.pdf"},
  27 + {$td->FILE => "many-nulls.pdf"},
  28 + $td->NORMALIZE_NEWLINES);
  29 +$td->runtest("run check file",
  30 + {$td->COMMAND => "qpdf --check a.pdf"},
  31 + {$td->FILE => "many-nulls.out", $td->EXIT_STATUS => 0},
  32 + $td->NORMALIZE_NEWLINES);
  33 +cleanup();
  34 +$td->report(3);
... ...
qpdf/qtest/qpdf/many-nulls.out 0 → 100644
  1 +checking a.pdf
  2 +PDF Version: 1.5
  3 +File is not encrypted
  4 +File is not linearized
  5 +No syntax or stream encoding errors found; the file may still contain
  6 +errors that qpdf cannot detect
... ...
qpdf/qtest/qpdf/many-nulls.pdf 0 → 100644
No preview for this file type
qpdf/test_many_nulls.cc 0 → 100644
  1 +#include <qpdf/QPDF.hh>
  2 +#include <qpdf/QPDFObjectHandle.hh>
  3 +#include <qpdf/QPDFWriter.hh>
  4 +#include <qpdf/QUtil.hh>
  5 +#include <cstdlib>
  6 +#include <iostream>
  7 +
  8 +int
  9 +main(int argc, char* argv[])
  10 +{
  11 + auto whoami = QUtil::getWhoami(argv[0]);
  12 + if (argc != 2) {
  13 + std::cerr << "Usage: " << whoami << " outfile.pdf" << std::endl;
  14 + exit(2);
  15 + }
  16 + char const* outfile = argv[1];
  17 +
  18 + // Create a file with lots of arrays containing very large numbers
  19 + // of nulls. Prior to qpdf 9.0.0, qpdf had a lot of trouble with
  20 + // this kind of file. This program is used to generate a file that
  21 + // can be used in the test suite and performance benchmarking.
  22 + QPDF q;
  23 + q.emptyPDF();
  24 + auto null = QPDFObjectHandle::newNull();
  25 + auto top = "[]"_qpdf;
  26 + for (int i = 0; i < 20; ++i) {
  27 + auto inner = "[]"_qpdf;
  28 + for (int j = 0; j < 20000; ++j) {
  29 + inner.appendItem(null);
  30 + }
  31 + top.appendItem(inner);
  32 + }
  33 + q.getTrailer().replaceKey("/Nulls", q.makeIndirectObject(top));
  34 + auto page = "<< /Type /Page /MediaBox [0 0 612 792] >>"_qpdf;
  35 + page = q.makeIndirectObject(page);
  36 + q.getRoot().getKey("/Pages").getKey("/Kids").appendItem(page);
  37 + QPDFWriter w(q, outfile);
  38 + w.setObjectStreamMode(qpdf_o_generate);
  39 + w.setDeterministicID(true);
  40 + w.write();
  41 + return 0;
  42 +}
... ...