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 * High-level API/doc overhaul: #593
* Refactor test_driver.cc so that runtest is not one huge function.
Documentation Documentation
============= =============

View File

@ -1 +1,2 @@
no forms 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) static void test_0_1(QPDF& pdf, 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))
{ {
QPDFObjectHandle trailer = pdf.getTrailer(); QPDFObjectHandle trailer = pdf.getTrailer();
QPDFObjectHandle qtest = trailer.getKey("/QTest"); QPDFObjectHandle qtest = trailer.getKey("/QTest");
@ -411,7 +319,8 @@ void runtest(int n, char const* filename1, char const* arg2)
<< "unparseResolved: " << qtest.unparseResolved() << "unparseResolved: " << qtest.unparseResolved()
<< std::endl; << 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 // Encrypted file. This test case is designed for a specific
// PDF file. // 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); PointerHolder<Pl_StdioFile> out = new Pl_StdioFile("filtered", stdout);
contents.pipeStreamData(out.getPointer(), 0, qpdf_dl_generalized); 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"); QPDFObjectHandle streams = pdf.getTrailer().getKey("/QStreams");
for (int i = 0; i < streams.getArrayNItems(); ++i) 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); 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 // Mutability testing: Make /QTest direct recursively, then
// copy to /Info. Also make some other mutations so we can // 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 // Prevent "done" message from getting appended
exit(0); exit(0);
} }
else if (n == 5)
static void test_5(QPDF& pdf, char const* arg2)
{ {
QPDFPageDocumentHelper dh(pdf); QPDFPageDocumentHelper dh(pdf);
std::vector<QPDFPageObjectHelper> pages = dh.getAllPages(); 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 root = pdf.getRoot();
QPDFObjectHandle metadata = root.getKey("/Metadata"); QPDFObjectHandle metadata = root.getKey("/Metadata");
@ -592,7 +505,8 @@ void runtest(int n, char const* filename1, char const* arg2)
<< (cleartext ? 1 : 0) << (cleartext ? 1 : 0)
<< std::endl; << std::endl;
} }
else if (n == 7)
static void test_7(QPDF& pdf, char const* arg2)
{ {
QPDFObjectHandle root = pdf.getRoot(); QPDFObjectHandle root = pdf.getRoot();
QPDFObjectHandle qstream = root.getKey("/QStream"); 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.setStreamDataMode(qpdf_s_preserve);
w.write(); w.write();
} }
else if (n == 8)
static void test_8(QPDF& pdf, char const* arg2)
{ {
QPDFObjectHandle root = pdf.getRoot(); QPDFObjectHandle root = pdf.getRoot();
QPDFObjectHandle qstream = root.getKey("/QStream"); 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; std::cout << "exception: " << e.what() << std::endl;
} }
} }
else if (n == 9)
static void test_9(QPDF& pdf, char const* arg2)
{ {
QPDFObjectHandle root = pdf.getRoot(); QPDFObjectHandle root = pdf.getRoot();
// Explicitly exercise the Buffer version of newStream // 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.setStreamDataMode(qpdf_s_preserve);
w.write(); w.write();
} }
else if (n == 10)
static void test_10(QPDF& pdf, char const* arg2)
{ {
std::vector<QPDFPageObjectHelper> pages = std::vector<QPDFPageObjectHelper> pages =
QPDFPageDocumentHelper(pdf).getAllPages(); QPDFPageDocumentHelper(pdf).getAllPages();
@ -696,7 +613,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setStreamDataMode(qpdf_s_preserve); w.setStreamDataMode(qpdf_s_preserve);
w.write(); w.write();
} }
else if (n == 11)
static void test_11(QPDF& pdf, char const* arg2)
{ {
QPDFObjectHandle root = pdf.getRoot(); QPDFObjectHandle root = pdf.getRoot();
QPDFObjectHandle qstream = root.getKey("/QStream"); 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; 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.setOutputStreams(0, 0);
pdf.showLinearizationData(); pdf.showLinearizationData();
} }
else if (n == 13)
static void test_13(QPDF& pdf, char const* arg2)
{ {
std::ostringstream out; std::ostringstream out;
std::ostringstream err; std::ostringstream err;
@ -729,15 +649,15 @@ void runtest(int n, char const* filename1, char const* arg2)
<< "---error---" << std::endl << "---error---" << std::endl
<< err.str(); << 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 // Exercise swap and replace. This test case is designed for
// a specific file. // a specific file.
std::vector<QPDFObjectHandle> pages = pdf.getAllPages(); std::vector<QPDFObjectHandle> pages = pdf.getAllPages();
if (pages.size() != 4) if (pages.size() != 4)
{ {
throw std::logic_error("test " + QUtil::int_to_string(n) + throw std::logic_error("test 14 not called 4-page file");
" not called 4-page file");
} }
// Swap pages 2 and 3 // Swap pages 2 and 3
auto orig_page2 = pages.at(1); auto orig_page2 = pages.at(1);
@ -812,7 +732,8 @@ void runtest(int n, char const* filename1, char const* arg2)
delete b; delete b;
} }
} }
else if (n == 15)
static void test_15(QPDF& pdf, char const* arg2)
{ {
std::vector<QPDFObjectHandle> const& pages = pdf.getAllPages(); std::vector<QPDFObjectHandle> const& pages = pdf.getAllPages();
// Reference to original page numbers for this test case are // 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.setStreamDataMode(qpdf_s_preserve);
w.write(); w.write();
} }
else if (n == 16)
static void test_16(QPDF& pdf, char const* arg2)
{ {
// Insert a page manually and then update the cache. // Insert a page manually and then update the cache.
std::vector<QPDFObjectHandle> const& all_pages = pdf.getAllPages(); 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.setStreamDataMode(qpdf_s_preserve);
w.write(); 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. // The input file to this test case has a duplicated page.
QPDFObjectHandle page_kids = QPDFObjectHandle page_kids =
@ -947,7 +870,8 @@ void runtest(int n, char const* filename1, char const* arg2)
b->getSize()); b->getSize());
assert(contents.find("page 0") != std::string::npos); 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. // Remove a page and re-insert it in the same file.
std::vector<QPDFObjectHandle> const& pages = pdf.getAllPages(); 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.setStreamDataMode(qpdf_s_preserve);
w.write(); 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. // Remove a page and re-insert it in the same file.
std::vector<QPDFObjectHandle> const& pages = pdf.getAllPages(); 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() == assert(last.getKey("/Contents").getObjGen() ==
newpage.getKey("/Contents").getObjGen()); newpage.getKey("/Contents").getObjGen());
} }
else if (n == 20)
static void test_20(QPDF& pdf, char const* arg2)
{ {
// Shallow copy an array // Shallow copy an array
QPDFObjectHandle trailer = pdf.getTrailer(); QPDFObjectHandle trailer = pdf.getTrailer();
@ -998,7 +924,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setStreamDataMode(qpdf_s_preserve); w.setStreamDataMode(qpdf_s_preserve);
w.write(); w.write();
} }
else if (n == 21)
static void test_21(QPDF& pdf, char const* arg2)
{ {
// Try to shallow copy a stream // Try to shallow copy a stream
std::vector<QPDFObjectHandle> const& pages = pdf.getAllPages(); std::vector<QPDFObjectHandle> const& pages = pdf.getAllPages();
@ -1007,7 +934,8 @@ void runtest(int n, char const* filename1, char const* arg2)
contents.shallowCopy(); contents.shallowCopy();
std::cout << "you can't see this" << std::endl; 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 // Try to remove a page we don't have
QPDFPageDocumentHelper dh(pdf); QPDFPageDocumentHelper dh(pdf);
@ -1017,13 +945,15 @@ void runtest(int n, char const* filename1, char const* arg2)
dh.removePage(page); dh.removePage(page);
std::cout << "you can't see this" << std::endl; 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); QPDFPageDocumentHelper dh(pdf);
std::vector<QPDFPageObjectHelper> pages = dh.getAllPages(); std::vector<QPDFPageObjectHelper> pages = dh.getAllPages();
dh.removePage(pages.back()); dh.removePage(pages.back());
} }
else if (n == 24)
static void test_24(QPDF& pdf, char const* arg2)
{ {
// Test behavior of reserved objects // Test behavior of reserved objects
QPDFObjectHandle res1 = QPDFObjectHandle::newReserved(&pdf); 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.setStreamDataMode(qpdf_s_preserve);
w.write(); 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 // The copy object tests are designed to work with a specific
// file. Look at the test suite for the file, and look at the // 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.setStreamDataMode(qpdf_s_preserve);
w.write(); w.write();
} }
else if (n == 26)
static void test_26(QPDF& pdf, char const* arg2)
{ {
// Copy the O3 page using addPage. Copy qtest without // Copy the O3 page using addPage. Copy qtest without
// crossing page boundaries. In addition to previous results, // 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.setStreamDataMode(qpdf_s_preserve);
w.write(); 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. // Copy O3 and the page O3 refers to before copying qtest.
// Should get qtest plus only the O3 page and the page that O3 // 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.setDecodeLevel(qpdf_dl_generalized);
w.write(); w.write();
} }
else if (n == 28)
static void test_28(QPDF& pdf, char const* arg2)
{ {
// Copy foreign object errors // Copy foreign object errors
try try
@ -1254,7 +1188,8 @@ void runtest(int n, char const* filename1, char const* arg2)
std::cout << "logic error: " << e.what() << std::endl; 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 // Detect mixed objects in QPDFWriter
assert(arg2 != 0); 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; std::cout << "logic error: " << e.what() << std::endl;
} }
} }
else if (n == 30)
static void test_30(QPDF& pdf, char const* arg2)
{ {
assert(arg2 != 0); assert(arg2 != 0);
QPDF encrypted; QPDF encrypted;
@ -1312,7 +1248,8 @@ void runtest(int n, char const* filename1, char const* arg2)
<< std::endl; << 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. // 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() == &pdf, "[1 0 R]", "indirect test").unparse() ==
"[ 1 0 R ]"); "[ 1 0 R ]");
} }
else if (n == 32)
static void test_32(QPDF& pdf, char const* arg2)
{ {
// Extra header text // Extra header text
char const* filenames[] = {"a.pdf", "b.pdf", "c.pdf", "d.pdf"}; 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(); w.write();
} }
} }
else if (n == 33)
static void test_33(QPDF& pdf, char const* arg2)
{ {
// Test writing to a custom pipeline // Test writing to a custom pipeline
Pl_Buffer p("buffer"); 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); fwrite(b->getBuffer(), b->getSize(), 1, f);
fclose(f); fclose(f);
} }
else if (n == 34)
static void test_34(QPDF& pdf, char const* arg2)
{ {
// Look at Extensions dictionary // Look at Extensions dictionary
std::cout << "version: " << pdf.getPDFVersion() << std::endl std::cout << "version: " << pdf.getPDFVersion() << std::endl
<< "extension level: " << pdf.getExtensionLevel() << std::endl << "extension level: " << pdf.getExtensionLevel() << std::endl
<< pdf.getRoot().getKey("/Extensions").unparse() << std::endl; << pdf.getRoot().getKey("/Extensions").unparse() << std::endl;
} }
else if (n == 35)
static void test_35(QPDF& pdf, char const* arg2)
{ {
// Extract attachments // Extract attachments
@ -1449,7 +1390,8 @@ void runtest(int n, char const* filename1, char const* arg2)
std::cout << filename << ":\n" << data << "--END--\n"; std::cout << filename << ":\n" << data << "--END--\n";
} }
} }
else if (n == 36)
static void test_36(QPDF& pdf, char const* arg2)
{ {
// Extract raw unfilterable attachment // 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 // Parse content streams of all pages
std::vector<QPDFPageObjectHelper> pages = std::vector<QPDFPageObjectHelper> pages =
@ -1494,7 +1437,8 @@ void runtest(int n, char const* filename1, char const* arg2)
page.parseContents(&cb); page.parseContents(&cb);
} }
} }
else if (n == 38)
static void test_38(QPDF& pdf, char const* arg2)
{ {
// Designed for override-compressed-object.pdf // Designed for override-compressed-object.pdf
QPDFObjectHandle qtest = pdf.getRoot().getKey("/QTest"); 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; 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 // Display image filter and color set for each image on each page
std::vector<QPDFPageObjectHelper> pages = 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 // Write PCLm. This requires specially crafted PDF files. This
// feature was implemented by Sahil Arora // feature was implemented by Sahil Arora
@ -1540,7 +1486,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setStaticID(true); w.setStaticID(true);
w.write(); 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 // Apply a token filter. This test case is crafted to work
// with coalesce.pdf. // with coalesce.pdf.
@ -1557,7 +1504,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setStaticID(true); w.setStaticID(true);
w.write(); 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 // Access objects as wrong type. This test case is crafted to
// work with object-types.pdf. // work with object-types.pdf.
@ -1663,7 +1611,8 @@ void runtest(int n, char const* filename1, char const* arg2)
assert(! uninitialized.isInteger()); assert(! uninitialized.isInteger());
assert(! uninitialized.isDictionary()); assert(! uninitialized.isDictionary());
} }
else if (n == 43)
static void test_43(QPDF& pdf, char const* arg2)
{ {
// Forms // Forms
QPDFAcroFormDocumentHelper afdh(pdf); 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. // Set form fields.
QPDFAcroFormDocumentHelper afdh(pdf); QPDFAcroFormDocumentHelper afdh(pdf);
@ -1793,7 +1743,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setSuppressOriginalObjectIDs(true); w.setSuppressOriginalObjectIDs(true);
w.write(); 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 // Decode obfuscated files. This is here to help test with
// files that trigger anti-virus warnings. See comments in // 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); 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 // Test number tree. This test is crafted to work with
// number-tree.pdf // number-tree.pdf
@ -1980,7 +1932,8 @@ void runtest(int n, char const* filename1, char const* arg2)
pdf.getTrailer().getKey("/Bad5"), pdf); pdf.getTrailer().getKey("/Bad5"), pdf);
assert(bad5.find(10) == bad5.end()); assert(bad5.find(10) == bad5.end());
} }
else if (n == 47)
static void test_47(QPDF& pdf, char const* arg2)
{ {
// Test page labels. // Test page labels.
QPDFPageLabelDocumentHelper pldh(pdf); QPDFPageLabelDocumentHelper pldh(pdf);
@ -1995,7 +1948,8 @@ void runtest(int n, char const* filename1, char const* arg2)
<< labels.at(i+1).unparse() << std::endl; << 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 // Test name tree. This test is crafted to work with
// name-tree.pdf // name-tree.pdf
@ -2160,7 +2114,8 @@ void runtest(int n, char const* filename1, char const* arg2)
pdf.getTrailer().getKey("/Bad6"), pdf); pdf.getTrailer().getKey("/Bad6"), pdf);
assert(bad6.insert("H", QPDFObjectHandle::newNull())->first == "H"); assert(bad6.insert("H", QPDFObjectHandle::newNull())->first == "H");
} }
else if (n == 49)
static void test_49(QPDF& pdf, char const* arg2)
{ {
// Outlines // Outlines
std::vector<QPDFPageObjectHelper> pages = 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 // Test dictionary merge. This test is crafted to work with
// merge-dict.pdf // merge-dict.pdf
@ -2200,7 +2156,8 @@ void runtest(int n, char const* filename1, char const* arg2)
std::cout << *iter << std::endl; 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 // Test radio button and checkbox field setting. The input
// files must have radios button called r1 and r2 and // 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.setStaticID(true);
w.write(); 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 // This test just sets a field value for appearance stream
// generating testing. // generating testing.
@ -2275,7 +2233,8 @@ void runtest(int n, char const* filename1, char const* arg2)
QPDFWriter w(pdf, "a.pdf"); QPDFWriter w(pdf, "a.pdf");
w.write(); w.write();
} }
else if (n == 53)
static void test_53(QPDF& pdf, char const* arg2)
{ {
// Test get all objects and dangling ref handling // Test get all objects and dangling ref handling
QPDFObjectHandle root = pdf.getRoot(); QPDFObjectHandle root = pdf.getRoot();
@ -2294,7 +2253,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setStaticID(true); w.setStaticID(true);
w.write(); w.write();
} }
else if (n == 54)
static void test_54(QPDF& pdf, char const* arg2)
{ {
// Test getFinalVersion. This must be invoked with a file // Test getFinalVersion. This must be invoked with a file
// whose final version is not 1.5. // 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; std::cout << "oops: " << w.getFinalVersion() << std::endl;
} }
} }
else if (n == 55)
static void test_55(QPDF& pdf, char const* arg2)
{ {
// Form XObjects // Form XObjects
std::vector<QPDFPageObjectHelper> pages = std::vector<QPDFPageObjectHelper> pages =
@ -2325,21 +2286,22 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setStaticID(true); w.setStaticID(true);
w.write(); 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 are from pdf, blue pages are from pdf2
// red pages always have stated rotation absolutely // red pages always have stated rotation absolutely
// 56: blue pages are overlaid exactly on top of red pages // 56: blue pages are overlaid exactly on top of red pages
// 57: blue pages have stated rotation relative to red pages // 57: blue pages have stated rotation relative to red pages
// 58: blue pages have no rotation (absolutely upright) // 58: blue pages have no rotation (absolutely upright)
// 59: blue pages have stated rotation absolutely // 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 = std::vector<QPDFPageObjectHelper> pages1 =
QPDFPageDocumentHelper(pdf).getAllPages(); QPDFPageDocumentHelper(pdf).getAllPages();
@ -2378,7 +2340,28 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setStaticID(true); w.setStaticID(true);
w.write(); 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; // Boundary condition testing for getUniqueResourceName;
// additional testing of mergeResources with conflict // additional testing of mergeResources with conflict
@ -2458,7 +2441,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setStaticID(true); w.setStaticID(true);
w.write(); 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 // Test to make sure exceptions can be caught properly across
// shared library boundaries. // 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; 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 // Test int size checks. This test will fail if int and long
// long are the same size. // 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(INT_MAX, t.getKey("/Q3").getIntValueAsInt());
assert_compare_numbers(UINT_MAX, t.getKey("/Q3").getUIntValueAsUInt()); 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); QPDFWriter w(pdf);
// Exercise setting encryption parameters before setting the // 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.setOutputFilename("a.pdf");
w.write(); 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. // Overlay file2 on file1.
// 64: allow neither shrink nor shrink // 64: allow neither shrink nor shrink
// 65: allow shrink but not expand // 65: allow shrink but not expand
// 66: allow expand but not shrink // 66: allow expand but not shrink
// 67: allow both shrink and expand // 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 = std::vector<QPDFPageObjectHelper> pages1 =
QPDFPageDocumentHelper(pdf).getAllPages(); QPDFPageDocumentHelper(pdf).getAllPages();
std::vector<QPDFPageObjectHelper> pages2 = std::vector<QPDFPageObjectHelper> pages2 =
@ -2586,7 +2573,28 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setStaticID(true); w.setStaticID(true);
w.write(); 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 root = pdf.getRoot();
QPDFObjectHandle qstream = root.getKey("/QStream"); 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; std::cout << "raw stream data okay" << std::endl;
} }
} }
else if (n == 69)
static void test_69(QPDF& pdf, char const* arg2)
{ {
pdf.setImmediateCopyFrom(true); pdf.setImmediateCopyFrom(true);
auto pages = pdf.getAllPages(); auto pages = pdf.getAllPages();
@ -2631,7 +2640,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.write(); w.write();
} }
} }
else if (n == 70)
static void test_70(QPDF& pdf, char const* arg2)
{ {
auto trailer = pdf.getTrailer(); auto trailer = pdf.getTrailer();
trailer.getKey("/S1").setFilterOnWrite(false); 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.setDecodeLevel(qpdf_dl_specialized);
w.write(); w.write();
} }
else if (n == 71)
static void test_71(QPDF& pdf, char const* arg2)
{ {
auto show = [](QPDFObjectHandle& obj, auto show = [](QPDFObjectHandle& obj,
QPDFObjectHandle& xobj_dict, 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; 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. // Call some QPDFPageObjectHelper methods on form XObjects.
auto page = QPDFPageDocumentHelper(pdf).getAllPages().at(0); 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); assert(s.find("/bye") != std::string::npos);
} }
} }
else if (n == 73)
static void test_73(QPDF& pdf, char const* arg2)
{ {
try try
{ {
@ -2741,7 +2754,8 @@ void runtest(int n, char const* filename1, char const* arg2)
pdf.closeInputSource(); pdf.closeInputSource();
pdf.getRoot().getKey("/Pages").unparseResolved(); 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 // This test is crafted to work with split-nntree.pdf
std::cout << "/Split1" << std::endl; std::cout << "/Split1" << std::endl;
@ -2792,7 +2806,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setQDFMode(true); w.setQDFMode(true);
w.write(); 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 // This test is crafted to work with erase-nntree.pdf
auto erase1 = QPDFNameTreeObjectHelper( auto erase1 = QPDFNameTreeObjectHelper(
@ -2851,7 +2866,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setQDFMode(true); w.setQDFMode(true);
w.write(); 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 // Embedded files. arg2 is a file to attach. Hard-code the
// mime type and file name for test purposes. // 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.setQDFMode(true);
w.write(); w.write();
} }
else if (n == 77)
static void test_77(QPDF& pdf, char const* arg2)
{ {
QPDFEmbeddedFileDocumentHelper efdh(pdf); QPDFEmbeddedFileDocumentHelper efdh(pdf);
assert(efdh.removeEmbeddedFile("att2")); assert(efdh.removeEmbeddedFile("att2"));
@ -2912,7 +2929,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setQDFMode(true); w.setQDFMode(true);
w.write(); w.write();
} }
else if (n == 78)
static void test_78(QPDF& pdf, char const* arg2)
{ {
// Test functional versions of replaceStreamData() // Test functional versions of replaceStreamData()
@ -2954,7 +2972,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setQDFMode(true); w.setQDFMode(true);
w.write(); w.write();
} }
else if (n == 79)
static void test_79(QPDF& pdf, char const* arg2)
{ {
// Exercise stream copier // Exercise stream copier
@ -3015,7 +3034,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setQDFMode(true); w.setQDFMode(true);
w.write(); w.write();
} }
else if (n == 80)
static void test_80(QPDF& pdf, char const* arg2)
{ {
// Exercise transform/copy annotations without passing in // Exercise transform/copy annotations without passing in
// QPDFAcroFormDocumentHelper pointers. The case of passing // QPDFAcroFormDocumentHelper pointers. The case of passing
@ -3062,7 +3082,8 @@ void runtest(int n, char const* filename1, char const* arg2)
w2.setQDFMode(true); w2.setQDFMode(true);
w2.write(); w2.write();
} }
else if (n == 81)
static void test_81(QPDF& pdf, char const* arg2)
{ {
// Exercise that type errors get their own special type // Exercise that type errors get their own special type
try try
@ -3075,11 +3096,130 @@ void runtest(int n, char const* filename1, char const* arg2)
assert(e.getErrorCode() == qpdf_e_object); 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 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 ") + throw std::runtime_error(std::string("invalid test ") +
QUtil::int_to_string(n)); QUtil::int_to_string(n));
} }
(fn->second)(pdf, arg2);
if (filep) if (filep)
{ {