test_driver: split runtest into separate functions

Too bad about git annotate but it was pretty crazy to have all those
test cases together like that.
This commit is contained in:
Jay Berkenbilt 2021-12-20 12:40:03 -05:00
parent c1e2b64a3f
commit cf7b2b5700
3 changed files with 2932 additions and 2793 deletions

2
TODO
View File

@ -3,8 +3,6 @@ Next
* High-level API/doc overhaul: #593
* Refactor test_driver.cc so that runtest is not one huge function.
Documentation
=============

View File

@ -1 +1,2 @@
no forms
test 43 done

View File

@ -193,99 +193,7 @@ static void compare_numbers(
}
}
void runtest(int n, char const* filename1, char const* arg2)
{
// Most tests here are crafted to work on specific files. Look at
// the test suite to see how the test is invoked to find the file
// that the test is supposed to operate on.
if (n == 0)
{
// Throw in some random test cases that don't fit anywhere
// else. This is in addition to whatever else is going on in
// test 0.
// The code to trim user passwords looks for 0x28 (which is
// "(") since it marks the beginning of the padding. Exercise
// the code to make sure it skips over 0x28 characters that
// aren't part of padding.
std::string password(
"1234567890123456789012(45678\x28\xbf\x4e\x5e");
assert(password.length() == 32);
QPDF::trim_user_password(password);
assert(password == "1234567890123456789012(45678");
QPDFObjectHandle uninitialized;
assert(uninitialized.getTypeCode() == QPDFObject::ot_uninitialized);
assert(strcmp(uninitialized.getTypeName(), "uninitialized") == 0);
}
QPDF pdf;
PointerHolder<char> file_buf;
FILE* filep = 0;
if (n == 0)
{
pdf.setAttemptRecovery(false);
}
if (((n == 35) || (n == 36)) && (arg2 != 0))
{
// arg2 is password
pdf.processFile(filename1, arg2);
}
else if (n == 45)
{
// Decode obfuscated files. To obfuscated, run the input file
// through this perl script, and save the result to
// filename.obfuscated. This pretends that the input was
// called filename.pdf and that that file contained the
// deobfuscated version.
// undef $/;
// my @str = split('', <STDIN>);
// for (my $i = 0; $i < scalar(@str); ++$i)
// {
// $str[$i] = chr(ord($str[$i]) ^ 0xcc);
// }
// print(join('', @str));
std::string filename(std::string(filename1) + ".obfuscated");
size_t size = 0;
QUtil::read_file_into_memory(filename.c_str(), file_buf, size);
char* p = file_buf.getPointer();
for (size_t i = 0; i < size; ++i)
{
p[i] = static_cast<char>(p[i] ^ 0xcc);
}
pdf.processMemoryFile((std::string(filename1) + ".pdf").c_str(),
p, size);
}
else if ((n == 61) || (n == 81))
{
// Ignore filename argument entirely
}
else if (n % 2 == 0)
{
if (n % 4 == 0)
{
QTC::TC("qpdf", "exercise processFile(name)");
pdf.processFile(filename1);
}
else
{
QTC::TC("qpdf", "exercise processFile(FILE*)");
filep = QUtil::safe_fopen(filename1, "rb");
pdf.processFile(filename1, filep, false);
}
}
else
{
QTC::TC("qpdf", "exercise processMemoryFile");
size_t size = 0;
QUtil::read_file_into_memory(filename1, file_buf, size);
pdf.processMemoryFile(filename1, file_buf.getPointer(), size);
}
if ((n == 0) || (n == 1))
static void test_0_1(QPDF& pdf, char const* arg2)
{
QPDFObjectHandle trailer = pdf.getTrailer();
QPDFObjectHandle qtest = trailer.getKey("/QTest");
@ -411,7 +319,8 @@ void runtest(int n, char const* filename1, char const* arg2)
<< "unparseResolved: " << qtest.unparseResolved()
<< std::endl;
}
else if (n == 2)
static void test_2(QPDF& pdf, char const* arg2)
{
// Encrypted file. This test case is designed for a specific
// PDF file.
@ -435,7 +344,8 @@ void runtest(int n, char const* filename1, char const* arg2)
PointerHolder<Pl_StdioFile> out = new Pl_StdioFile("filtered", stdout);
contents.pipeStreamData(out.getPointer(), 0, qpdf_dl_generalized);
}
else if (n == 3)
static void test_3(QPDF& pdf, char const* arg2)
{
QPDFObjectHandle streams = pdf.getTrailer().getKey("/QStreams");
for (int i = 0; i < streams.getArrayNItems(); ++i)
@ -450,7 +360,8 @@ void runtest(int n, char const* filename1, char const* arg2)
qpdf_ef_normalize, qpdf_dl_generalized);
}
}
else if (n == 4)
static void test_4(QPDF& pdf, char const* arg2)
{
// Mutability testing: Make /QTest direct recursively, then
// copy to /Info. Also make some other mutations so we can
@ -503,7 +414,8 @@ void runtest(int n, char const* filename1, char const* arg2)
// Prevent "done" message from getting appended
exit(0);
}
else if (n == 5)
static void test_5(QPDF& pdf, char const* arg2)
{
QPDFPageDocumentHelper dh(pdf);
std::vector<QPDFPageObjectHelper> pages = dh.getAllPages();
@ -566,7 +478,8 @@ void runtest(int n, char const* filename1, char const* arg2)
}
}
}
else if (n == 6)
static void test_6(QPDF& pdf, char const* arg2)
{
QPDFObjectHandle root = pdf.getRoot();
QPDFObjectHandle metadata = root.getKey("/Metadata");
@ -592,7 +505,8 @@ void runtest(int n, char const* filename1, char const* arg2)
<< (cleartext ? 1 : 0)
<< std::endl;
}
else if (n == 7)
static void test_7(QPDF& pdf, char const* arg2)
{
QPDFObjectHandle root = pdf.getRoot();
QPDFObjectHandle qstream = root.getKey("/QStream");
@ -608,7 +522,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setStreamDataMode(qpdf_s_preserve);
w.write();
}
else if (n == 8)
static void test_8(QPDF& pdf, char const* arg2)
{
QPDFObjectHandle root = pdf.getRoot();
QPDFObjectHandle qstream = root.getKey("/QStream");
@ -650,7 +565,8 @@ void runtest(int n, char const* filename1, char const* arg2)
std::cout << "exception: " << e.what() << std::endl;
}
}
else if (n == 9)
static void test_9(QPDF& pdf, char const* arg2)
{
QPDFObjectHandle root = pdf.getRoot();
// Explicitly exercise the Buffer version of newStream
@ -679,7 +595,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setStreamDataMode(qpdf_s_preserve);
w.write();
}
else if (n == 10)
static void test_10(QPDF& pdf, char const* arg2)
{
std::vector<QPDFPageObjectHelper> pages =
QPDFPageDocumentHelper(pdf).getAllPages();
@ -696,7 +613,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setStreamDataMode(qpdf_s_preserve);
w.write();
}
else if (n == 11)
static void test_11(QPDF& pdf, char const* arg2)
{
QPDFObjectHandle root = pdf.getRoot();
QPDFObjectHandle qstream = root.getKey("/QStream");
@ -713,12 +631,14 @@ void runtest(int n, char const* filename1, char const* arg2)
std::cout << "raw stream data okay" << std::endl;
}
}
else if (n == 12)
static void test_12(QPDF& pdf, char const* arg2)
{
pdf.setOutputStreams(0, 0);
pdf.showLinearizationData();
}
else if (n == 13)
static void test_13(QPDF& pdf, char const* arg2)
{
std::ostringstream out;
std::ostringstream err;
@ -729,15 +649,15 @@ void runtest(int n, char const* filename1, char const* arg2)
<< "---error---" << std::endl
<< err.str();
}
else if (n == 14)
static void test_14(QPDF& pdf, char const* arg2)
{
// Exercise swap and replace. This test case is designed for
// a specific file.
std::vector<QPDFObjectHandle> pages = pdf.getAllPages();
if (pages.size() != 4)
{
throw std::logic_error("test " + QUtil::int_to_string(n) +
" not called 4-page file");
throw std::logic_error("test 14 not called 4-page file");
}
// Swap pages 2 and 3
auto orig_page2 = pages.at(1);
@ -812,7 +732,8 @@ void runtest(int n, char const* filename1, char const* arg2)
delete b;
}
}
else if (n == 15)
static void test_15(QPDF& pdf, char const* arg2)
{
std::vector<QPDFObjectHandle> const& pages = pdf.getAllPages();
// Reference to original page numbers for this test case are
@ -895,7 +816,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setStreamDataMode(qpdf_s_preserve);
w.write();
}
else if (n == 16)
static void test_16(QPDF& pdf, char const* arg2)
{
// Insert a page manually and then update the cache.
std::vector<QPDFObjectHandle> const& all_pages = pdf.getAllPages();
@ -926,7 +848,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setStreamDataMode(qpdf_s_preserve);
w.write();
}
else if (n == 17)
static void test_17(QPDF& pdf, char const* arg2)
{
// The input file to this test case has a duplicated page.
QPDFObjectHandle page_kids =
@ -947,7 +870,8 @@ void runtest(int n, char const* filename1, char const* arg2)
b->getSize());
assert(contents.find("page 0") != std::string::npos);
}
else if (n == 18)
static void test_18(QPDF& pdf, char const* arg2)
{
// Remove a page and re-insert it in the same file.
std::vector<QPDFObjectHandle> const& pages = pdf.getAllPages();
@ -967,7 +891,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setStreamDataMode(qpdf_s_preserve);
w.write();
}
else if (n == 19)
static void test_19(QPDF& pdf, char const* arg2)
{
// Remove a page and re-insert it in the same file.
std::vector<QPDFObjectHandle> const& pages = pdf.getAllPages();
@ -983,7 +908,8 @@ void runtest(int n, char const* filename1, char const* arg2)
assert(last.getKey("/Contents").getObjGen() ==
newpage.getKey("/Contents").getObjGen());
}
else if (n == 20)
static void test_20(QPDF& pdf, char const* arg2)
{
// Shallow copy an array
QPDFObjectHandle trailer = pdf.getTrailer();
@ -998,7 +924,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setStreamDataMode(qpdf_s_preserve);
w.write();
}
else if (n == 21)
static void test_21(QPDF& pdf, char const* arg2)
{
// Try to shallow copy a stream
std::vector<QPDFObjectHandle> const& pages = pdf.getAllPages();
@ -1007,7 +934,8 @@ void runtest(int n, char const* filename1, char const* arg2)
contents.shallowCopy();
std::cout << "you can't see this" << std::endl;
}
else if (n == 22)
static void test_22(QPDF& pdf, char const* arg2)
{
// Try to remove a page we don't have
QPDFPageDocumentHelper dh(pdf);
@ -1017,13 +945,15 @@ void runtest(int n, char const* filename1, char const* arg2)
dh.removePage(page);
std::cout << "you can't see this" << std::endl;
}
else if (n == 23)
static void test_23(QPDF& pdf, char const* arg2)
{
QPDFPageDocumentHelper dh(pdf);
std::vector<QPDFPageObjectHelper> pages = dh.getAllPages();
dh.removePage(pages.back());
}
else if (n == 24)
static void test_24(QPDF& pdf, char const* arg2)
{
// Test behavior of reserved objects
QPDFObjectHandle res1 = QPDFObjectHandle::newReserved(&pdf);
@ -1099,7 +1029,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setStreamDataMode(qpdf_s_preserve);
w.write();
}
else if (n == 25)
static void test_25(QPDF& pdf, char const* arg2)
{
// The copy object tests are designed to work with a specific
// file. Look at the test suite for the file, and look at the
@ -1123,7 +1054,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setStreamDataMode(qpdf_s_preserve);
w.write();
}
else if (n == 26)
static void test_26(QPDF& pdf, char const* arg2)
{
// Copy the O3 page using addPage. Copy qtest without
// crossing page boundaries. In addition to previous results,
@ -1148,7 +1080,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setStreamDataMode(qpdf_s_preserve);
w.write();
}
else if (n == 27)
static void test_27(QPDF& pdf, char const* arg2)
{
// Copy O3 and the page O3 refers to before copying qtest.
// Should get qtest plus only the O3 page and the page that O3
@ -1232,7 +1165,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setDecodeLevel(qpdf_dl_generalized);
w.write();
}
else if (n == 28)
static void test_28(QPDF& pdf, char const* arg2)
{
// Copy foreign object errors
try
@ -1254,7 +1188,8 @@ void runtest(int n, char const* filename1, char const* arg2)
std::cout << "logic error: " << e.what() << std::endl;
}
}
else if (n == 29)
static void test_29(QPDF& pdf, char const* arg2)
{
// Detect mixed objects in QPDFWriter
assert(arg2 != 0);
@ -1287,7 +1222,8 @@ void runtest(int n, char const* filename1, char const* arg2)
std::cout << "logic error: " << e.what() << std::endl;
}
}
else if (n == 30)
static void test_30(QPDF& pdf, char const* arg2)
{
assert(arg2 != 0);
QPDF encrypted;
@ -1312,7 +1248,8 @@ void runtest(int n, char const* filename1, char const* arg2)
<< std::endl;
}
}
else if (n == 31)
static void test_31(QPDF& pdf, char const* arg2)
{
// Test object parsing from a string. The input file is not used.
@ -1347,7 +1284,8 @@ void runtest(int n, char const* filename1, char const* arg2)
&pdf, "[1 0 R]", "indirect test").unparse() ==
"[ 1 0 R ]");
}
else if (n == 32)
static void test_32(QPDF& pdf, char const* arg2)
{
// Extra header text
char const* filenames[] = {"a.pdf", "b.pdf", "c.pdf", "d.pdf"};
@ -1368,7 +1306,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.write();
}
}
else if (n == 33)
static void test_33(QPDF& pdf, char const* arg2)
{
// Test writing to a custom pipeline
Pl_Buffer p("buffer");
@ -1381,14 +1320,16 @@ void runtest(int n, char const* filename1, char const* arg2)
fwrite(b->getBuffer(), b->getSize(), 1, f);
fclose(f);
}
else if (n == 34)
static void test_34(QPDF& pdf, char const* arg2)
{
// Look at Extensions dictionary
std::cout << "version: " << pdf.getPDFVersion() << std::endl
<< "extension level: " << pdf.getExtensionLevel() << std::endl
<< pdf.getRoot().getKey("/Extensions").unparse() << std::endl;
}
else if (n == 35)
static void test_35(QPDF& pdf, char const* arg2)
{
// Extract attachments
@ -1449,7 +1390,8 @@ void runtest(int n, char const* filename1, char const* arg2)
std::cout << filename << ":\n" << data << "--END--\n";
}
}
else if (n == 36)
static void test_36(QPDF& pdf, char const* arg2)
{
// Extract raw unfilterable attachment
@ -1481,7 +1423,8 @@ void runtest(int n, char const* filename1, char const* arg2)
}
}
}
else if (n == 37)
static void test_37(QPDF& pdf, char const* arg2)
{
// Parse content streams of all pages
std::vector<QPDFPageObjectHelper> pages =
@ -1494,7 +1437,8 @@ void runtest(int n, char const* filename1, char const* arg2)
page.parseContents(&cb);
}
}
else if (n == 38)
static void test_38(QPDF& pdf, char const* arg2)
{
// Designed for override-compressed-object.pdf
QPDFObjectHandle qtest = pdf.getRoot().getKey("/QTest");
@ -1503,7 +1447,8 @@ void runtest(int n, char const* filename1, char const* arg2)
std::cout << qtest.getArrayItem(i).unparseResolved() << std::endl;
}
}
else if (n == 39)
static void test_39(QPDF& pdf, char const* arg2)
{
// Display image filter and color set for each image on each page
std::vector<QPDFPageObjectHelper> pages =
@ -1528,7 +1473,8 @@ void runtest(int n, char const* filename1, char const* arg2)
}
}
}
else if (n == 40)
static void test_40(QPDF& pdf, char const* arg2)
{
// Write PCLm. This requires specially crafted PDF files. This
// feature was implemented by Sahil Arora
@ -1540,7 +1486,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setStaticID(true);
w.write();
}
else if (n == 41)
static void test_41(QPDF& pdf, char const* arg2)
{
// Apply a token filter. This test case is crafted to work
// with coalesce.pdf.
@ -1557,7 +1504,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setStaticID(true);
w.write();
}
else if (n == 42)
static void test_42(QPDF& pdf, char const* arg2)
{
// Access objects as wrong type. This test case is crafted to
// work with object-types.pdf.
@ -1663,7 +1611,8 @@ void runtest(int n, char const* filename1, char const* arg2)
assert(! uninitialized.isInteger());
assert(! uninitialized.isDictionary());
}
else if (n == 43)
static void test_43(QPDF& pdf, char const* arg2)
{
// Forms
QPDFAcroFormDocumentHelper afdh(pdf);
@ -1765,7 +1714,8 @@ void runtest(int n, char const* filename1, char const* arg2)
}
}
}
else if (n == 44)
static void test_44(QPDF& pdf, char const* arg2)
{
// Set form fields.
QPDFAcroFormDocumentHelper afdh(pdf);
@ -1793,7 +1743,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setSuppressOriginalObjectIDs(true);
w.write();
}
else if (n == 45)
static void test_45(QPDF& pdf, char const* arg2)
{
// Decode obfuscated files. This is here to help test with
// files that trigger anti-virus warnings. See comments in
@ -1806,7 +1757,8 @@ void runtest(int n, char const* filename1, char const* arg2)
exit(3);
}
}
else if (n == 46)
static void test_46(QPDF& pdf, char const* arg2)
{
// Test number tree. This test is crafted to work with
// number-tree.pdf
@ -1980,7 +1932,8 @@ void runtest(int n, char const* filename1, char const* arg2)
pdf.getTrailer().getKey("/Bad5"), pdf);
assert(bad5.find(10) == bad5.end());
}
else if (n == 47)
static void test_47(QPDF& pdf, char const* arg2)
{
// Test page labels.
QPDFPageLabelDocumentHelper pldh(pdf);
@ -1995,7 +1948,8 @@ void runtest(int n, char const* filename1, char const* arg2)
<< labels.at(i+1).unparse() << std::endl;
}
}
else if (n == 48)
static void test_48(QPDF& pdf, char const* arg2)
{
// Test name tree. This test is crafted to work with
// name-tree.pdf
@ -2160,7 +2114,8 @@ void runtest(int n, char const* filename1, char const* arg2)
pdf.getTrailer().getKey("/Bad6"), pdf);
assert(bad6.insert("H", QPDFObjectHandle::newNull())->first == "H");
}
else if (n == 49)
static void test_49(QPDF& pdf, char const* arg2)
{
// Outlines
std::vector<QPDFPageObjectHelper> pages =
@ -2183,7 +2138,8 @@ void runtest(int n, char const* filename1, char const* arg2)
}
}
}
else if (n == 50)
static void test_50(QPDF& pdf, char const* arg2)
{
// Test dictionary merge. This test is crafted to work with
// merge-dict.pdf
@ -2200,7 +2156,8 @@ void runtest(int n, char const* filename1, char const* arg2)
std::cout << *iter << std::endl;
}
}
else if (n == 51)
static void test_51(QPDF& pdf, char const* arg2)
{
// Test radio button and checkbox field setting. The input
// files must have radios button called r1 and r2 and
@ -2249,7 +2206,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setStaticID(true);
w.write();
}
else if (n == 52)
static void test_52(QPDF& pdf, char const* arg2)
{
// This test just sets a field value for appearance stream
// generating testing.
@ -2275,7 +2233,8 @@ void runtest(int n, char const* filename1, char const* arg2)
QPDFWriter w(pdf, "a.pdf");
w.write();
}
else if (n == 53)
static void test_53(QPDF& pdf, char const* arg2)
{
// Test get all objects and dangling ref handling
QPDFObjectHandle root = pdf.getRoot();
@ -2294,7 +2253,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setStaticID(true);
w.write();
}
else if (n == 54)
static void test_54(QPDF& pdf, char const* arg2)
{
// Test getFinalVersion. This must be invoked with a file
// whose final version is not 1.5.
@ -2306,7 +2266,8 @@ void runtest(int n, char const* filename1, char const* arg2)
std::cout << "oops: " << w.getFinalVersion() << std::endl;
}
}
else if (n == 55)
static void test_55(QPDF& pdf, char const* arg2)
{
// Form XObjects
std::vector<QPDFPageObjectHelper> pages =
@ -2325,21 +2286,22 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setStaticID(true);
w.write();
}
else if ((n >= 56) && (n <= 59))
{
// Placing form XObjects
assert(arg2);
QPDF pdf2;
pdf2.processFile(arg2);
static void test_56_59(QPDF& pdf, char const* arg2,
bool handle_from_transformation,
bool invert_to_transformation)
{
// red pages are from pdf, blue pages are from pdf2
// red pages always have stated rotation absolutely
// 56: blue pages are overlaid exactly on top of red pages
// 57: blue pages have stated rotation relative to red pages
// 58: blue pages have no rotation (absolutely upright)
// 59: blue pages have stated rotation absolutely
bool handle_from_transformation = ((n == 57) || (n == 59));
bool invert_to_transformation = ((n == 58) || (n == 59));
// Placing form XObjects
assert(arg2);
QPDF pdf2;
pdf2.processFile(arg2);
std::vector<QPDFPageObjectHelper> pages1 =
QPDFPageDocumentHelper(pdf).getAllPages();
@ -2378,7 +2340,28 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setStaticID(true);
w.write();
}
else if (n == 60)
static void test_56(QPDF& pdf, char const* arg2)
{
test_56_59(pdf, arg2, false, false);
}
static void test_57(QPDF& pdf, char const* arg2)
{
test_56_59(pdf, arg2, true, false);
}
static void test_58(QPDF& pdf, char const* arg2)
{
test_56_59(pdf, arg2, false, true);
}
static void test_59(QPDF& pdf, char const* arg2)
{
test_56_59(pdf, arg2, true, true);
}
static void test_60(QPDF& pdf, char const* arg2)
{
// Boundary condition testing for getUniqueResourceName;
// additional testing of mergeResources with conflict
@ -2458,7 +2441,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setStaticID(true);
w.write();
}
else if (n == 61)
static void test_61(QPDF& pdf, char const* arg2)
{
// Test to make sure exceptions can be caught properly across
// shared library boundaries.
@ -2497,7 +2481,8 @@ void runtest(int n, char const* filename1, char const* arg2)
std::cout << "Caught runtime_error as expected" << std::endl;
}
}
else if (n == 62)
static void test_62(QPDF& pdf, char const* arg2)
{
// Test int size checks. This test will fail if int and long
// long are the same size.
@ -2522,7 +2507,8 @@ void runtest(int n, char const* filename1, char const* arg2)
assert_compare_numbers(INT_MAX, t.getKey("/Q3").getIntValueAsInt());
assert_compare_numbers(UINT_MAX, t.getKey("/Q3").getUIntValueAsUInt());
}
else if (n == 63)
static void test_63(QPDF& pdf, char const* arg2)
{
QPDFWriter w(pdf);
// Exercise setting encryption parameters before setting the
@ -2535,20 +2521,21 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setOutputFilename("a.pdf");
w.write();
}
else if ((n >= 64) && (n <= 67))
{
// Placing form XObjects: expand, shrink
assert(arg2);
QPDF pdf2;
pdf2.processFile(arg2);
static void test_64_67(QPDF& pdf, char const* arg2,
bool allow_shrink, bool allow_expand)
{
// Overlay file2 on file1.
// 64: allow neither shrink nor shrink
// 65: allow shrink but not expand
// 66: allow expand but not shrink
// 67: allow both shrink and expand
bool allow_shrink = ((n == 65) || (n == 67));
bool allow_expand = ((n == 66) || (n == 67));
// Placing form XObjects: expand, shrink
assert(arg2);
QPDF pdf2;
pdf2.processFile(arg2);
std::vector<QPDFPageObjectHelper> pages1 =
QPDFPageDocumentHelper(pdf).getAllPages();
std::vector<QPDFPageObjectHelper> pages2 =
@ -2586,7 +2573,28 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setStaticID(true);
w.write();
}
else if (n == 68)
static void test_64(QPDF& pdf, char const* arg2)
{
test_64_67(pdf, arg2, false, false);
}
static void test_65(QPDF& pdf, char const* arg2)
{
test_64_67(pdf, arg2, true, false);
}
static void test_66(QPDF& pdf, char const* arg2)
{
test_64_67(pdf, arg2, false, true);
}
static void test_67(QPDF& pdf, char const* arg2)
{
test_64_67(pdf, arg2, true, true);
}
static void test_68(QPDF& pdf, char const* arg2)
{
QPDFObjectHandle root = pdf.getRoot();
QPDFObjectHandle qstream = root.getKey("/QStream");
@ -2615,7 +2623,8 @@ void runtest(int n, char const* filename1, char const* arg2)
std::cout << "raw stream data okay" << std::endl;
}
}
else if (n == 69)
static void test_69(QPDF& pdf, char const* arg2)
{
pdf.setImmediateCopyFrom(true);
auto pages = pdf.getAllPages();
@ -2631,7 +2640,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.write();
}
}
else if (n == 70)
static void test_70(QPDF& pdf, char const* arg2)
{
auto trailer = pdf.getTrailer();
trailer.getKey("/S1").setFilterOnWrite(false);
@ -2641,7 +2651,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setDecodeLevel(qpdf_dl_specialized);
w.write();
}
else if (n == 71)
static void test_71(QPDF& pdf, char const* arg2)
{
auto show = [](QPDFObjectHandle& obj,
QPDFObjectHandle& xobj_dict,
@ -2692,7 +2703,8 @@ void runtest(int n, char const* filename1, char const* arg2)
std::cout << i.first << " -> " << i.second.unparse() << std::endl;
}
}
else if (n == 72)
static void test_72(QPDF& pdf, char const* arg2)
{
// Call some QPDFPageObjectHelper methods on form XObjects.
auto page = QPDFPageDocumentHelper(pdf).getAllPages().at(0);
@ -2726,7 +2738,8 @@ void runtest(int n, char const* filename1, char const* arg2)
assert(s.find("/bye") != std::string::npos);
}
}
else if (n == 73)
static void test_73(QPDF& pdf, char const* arg2)
{
try
{
@ -2741,7 +2754,8 @@ void runtest(int n, char const* filename1, char const* arg2)
pdf.closeInputSource();
pdf.getRoot().getKey("/Pages").unparseResolved();
}
else if (n == 74)
static void test_74(QPDF& pdf, char const* arg2)
{
// This test is crafted to work with split-nntree.pdf
std::cout << "/Split1" << std::endl;
@ -2792,7 +2806,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setQDFMode(true);
w.write();
}
else if (n == 75)
static void test_75(QPDF& pdf, char const* arg2)
{
// This test is crafted to work with erase-nntree.pdf
auto erase1 = QPDFNameTreeObjectHelper(
@ -2851,7 +2866,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setQDFMode(true);
w.write();
}
else if (n == 76)
static void test_76(QPDF& pdf, char const* arg2)
{
// Embedded files. arg2 is a file to attach. Hard-code the
// mime type and file name for test purposes.
@ -2901,7 +2917,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setQDFMode(true);
w.write();
}
else if (n == 77)
static void test_77(QPDF& pdf, char const* arg2)
{
QPDFEmbeddedFileDocumentHelper efdh(pdf);
assert(efdh.removeEmbeddedFile("att2"));
@ -2912,7 +2929,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setQDFMode(true);
w.write();
}
else if (n == 78)
static void test_78(QPDF& pdf, char const* arg2)
{
// Test functional versions of replaceStreamData()
@ -2954,7 +2972,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setQDFMode(true);
w.write();
}
else if (n == 79)
static void test_79(QPDF& pdf, char const* arg2)
{
// Exercise stream copier
@ -3015,7 +3034,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setQDFMode(true);
w.write();
}
else if (n == 80)
static void test_80(QPDF& pdf, char const* arg2)
{
// Exercise transform/copy annotations without passing in
// QPDFAcroFormDocumentHelper pointers. The case of passing
@ -3062,7 +3082,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w2.setQDFMode(true);
w2.write();
}
else if (n == 81)
static void test_81(QPDF& pdf, char const* arg2)
{
// Exercise that type errors get their own special type
try
@ -3075,11 +3096,130 @@ void runtest(int n, char const* filename1, char const* arg2)
assert(e.getErrorCode() == qpdf_e_object);
}
}
void runtest(int n, char const* filename1, char const* arg2)
{
// Most tests here are crafted to work on specific files. Look at
// the test suite to see how the test is invoked to find the file
// that the test is supposed to operate on.
if (n == 0)
{
// Throw in some random test cases that don't fit anywhere
// else. This is in addition to whatever else is going on in
// test 0.
// The code to trim user passwords looks for 0x28 (which is
// "(") since it marks the beginning of the padding. Exercise
// the code to make sure it skips over 0x28 characters that
// aren't part of padding.
std::string password(
"1234567890123456789012(45678\x28\xbf\x4e\x5e");
assert(password.length() == 32);
QPDF::trim_user_password(password);
assert(password == "1234567890123456789012(45678");
QPDFObjectHandle uninitialized;
assert(uninitialized.getTypeCode() == QPDFObject::ot_uninitialized);
assert(strcmp(uninitialized.getTypeName(), "uninitialized") == 0);
}
QPDF pdf;
PointerHolder<char> file_buf;
FILE* filep = 0;
if (n == 0)
{
pdf.setAttemptRecovery(false);
}
if (((n == 35) || (n == 36)) && (arg2 != 0))
{
// arg2 is password
pdf.processFile(filename1, arg2);
}
else if (n == 45)
{
// Decode obfuscated files. To obfuscated, run the input file
// through this perl script, and save the result to
// filename.obfuscated. This pretends that the input was
// called filename.pdf and that that file contained the
// deobfuscated version.
// undef $/;
// my @str = split('', <STDIN>);
// for (my $i = 0; $i < scalar(@str); ++$i)
// {
// $str[$i] = chr(ord($str[$i]) ^ 0xcc);
// }
// print(join('', @str));
std::string filename(std::string(filename1) + ".obfuscated");
size_t size = 0;
QUtil::read_file_into_memory(filename.c_str(), file_buf, size);
char* p = file_buf.getPointer();
for (size_t i = 0; i < size; ++i)
{
p[i] = static_cast<char>(p[i] ^ 0xcc);
}
pdf.processMemoryFile((std::string(filename1) + ".pdf").c_str(),
p, size);
}
else if ((n == 61) || (n == 81))
{
// Ignore filename argument entirely
}
else if (n % 2 == 0)
{
if (n % 4 == 0)
{
QTC::TC("qpdf", "exercise processFile(name)");
pdf.processFile(filename1);
}
else
{
QTC::TC("qpdf", "exercise processFile(FILE*)");
filep = QUtil::safe_fopen(filename1, "rb");
pdf.processFile(filename1, filep, false);
}
}
else
{
QTC::TC("qpdf", "exercise processMemoryFile");
size_t size = 0;
QUtil::read_file_into_memory(filename1, file_buf, size);
pdf.processMemoryFile(filename1, file_buf.getPointer(), size);
}
std::map<int, void (*)(QPDF&, char const*)> test_functions = {
{0, test_0_1}, {1, test_0_1}, {2, test_2}, {3, test_3},
{4, test_4}, {5, test_5}, {6, test_6}, {7, test_7},
{8, test_8}, {9, test_9}, {10, test_10}, {11, test_11},
{12, test_12}, {13, test_13}, {14, test_14}, {15, test_15},
{16, test_16}, {17, test_17}, {18, test_18}, {19, test_19},
{20, test_20}, {21, test_21}, {22, test_22}, {23, test_23},
{24, test_24}, {25, test_25}, {26, test_26}, {27, test_27},
{28, test_28}, {29, test_29}, {30, test_30}, {31, test_31},
{32, test_32}, {33, test_33}, {34, test_34}, {35, test_35},
{36, test_36}, {37, test_37}, {38, test_38}, {39, test_39},
{40, test_40}, {41, test_41}, {42, test_42}, {43, test_43},
{44, test_44}, {45, test_45}, {46, test_46}, {47, test_47},
{48, test_48}, {49, test_49}, {50, test_50}, {51, test_51},
{52, test_52}, {53, test_53}, {54, test_54}, {55, test_55},
{56, test_56}, {57, test_57}, {58, test_58}, {59, test_59},
{60, test_60}, {61, test_61}, {62, test_62}, {63, test_63},
{64, test_64}, {65, test_65}, {66, test_66}, {67, test_67},
{68, test_68}, {69, test_69}, {70, test_70}, {71, test_71},
{72, test_72}, {73, test_73}, {74, test_74}, {75, test_75},
{76, test_76}, {77, test_77}, {78, test_78}, {79, test_79},
{80, test_80}, {81, test_81},
};
auto fn = test_functions.find(n);
if (fn == test_functions.end())
{
throw std::runtime_error(std::string("invalid test ") +
QUtil::int_to_string(n));
}
(fn->second)(pdf, arg2);
if (filep)
{