diff options
Diffstat (limited to 'libtests')
-rw-r--r-- | libtests/build.mk | 3 | ||||
-rw-r--r-- | libtests/closed_file_input_source.cc | 5 | ||||
-rw-r--r-- | libtests/json.cc | 155 | ||||
-rw-r--r-- | libtests/libtests.testcov | 5 | ||||
-rw-r--r-- | libtests/matrix.cc | 90 | ||||
-rw-r--r-- | libtests/numrange.cc | 36 | ||||
-rw-r--r-- | libtests/qtest/closedfile/output | 1 | ||||
-rw-r--r-- | libtests/qtest/json.test | 17 | ||||
-rw-r--r-- | libtests/qtest/json/json.out | 23 | ||||
-rw-r--r-- | libtests/qtest/matrix.test | 15 | ||||
-rw-r--r-- | libtests/qtest/numrange.test | 63 | ||||
-rw-r--r-- | libtests/qtest/qutil/qutil.out | 24 | ||||
-rw-r--r-- | libtests/qutil.cc | 125 |
13 files changed, 559 insertions, 3 deletions
diff --git a/libtests/build.mk b/libtests/build.mk index e8f20270..5c1ee56a 100644 --- a/libtests/build.mk +++ b/libtests/build.mk @@ -10,8 +10,11 @@ BINS_libtests = \ flate \ hex \ input_source \ + json \ lzw \ + matrix \ md5 \ + numrange \ pointer_holder \ predictors \ qutil \ diff --git a/libtests/closed_file_input_source.cc b/libtests/closed_file_input_source.cc index 9cf5ff04..7309ca31 100644 --- a/libtests/closed_file_input_source.cc +++ b/libtests/closed_file_input_source.cc @@ -65,6 +65,11 @@ int main() std::cout << "testing with ClosedFileInputSource\n"; ClosedFileInputSource cf("input"); do_tests(&cf); + std::cout << "testing with ClosedFileInputSource in stay open mode\n"; + ClosedFileInputSource cf2("input"); + cf2.stayOpen(true); + do_tests(&cf2); + cf2.stayOpen(false); std::cout << "testing with FileInputSource\n"; FileInputSource f; f.setFilename("input"); diff --git a/libtests/json.cc b/libtests/json.cc new file mode 100644 index 00000000..1a9123ad --- /dev/null +++ b/libtests/json.cc @@ -0,0 +1,155 @@ +#include <qpdf/JSON.hh> +#include <qpdf/QPDFObjectHandle.hh> +#include <iostream> +#include <assert.h> + +static void check(JSON& j, std::string const& exp) +{ + if (exp != j.unparse()) + { + std::cout << "Got " << j.unparse() << "; wanted " << exp << "\n"; + } +} + +static void test_main() +{ + JSON jstr = JSON::makeString( + "<1>\xcf\x80<2>\xf0\x9f\xa5\x94\\\"<3>\x03\t\b\r\n<4>"); + check(jstr, + "\"<1>\xcf\x80<2>\xf0\x9f\xa5\x94\\\\\\\"<3>" + "\\u0003\\t\\b\\r\\n<4>\""); + JSON jnull = JSON::makeNull(); + check(jnull, "null"); + JSON jarr = JSON::makeArray(); + check(jarr, "[]"); + JSON jstr2 = JSON::makeString("a\tb"); + JSON jint = JSON::makeInt(16059); + JSON jdouble = JSON::makeReal(3.14159); + JSON jexp = JSON::makeNumber("2.1e5"); + jarr.addArrayElement(jstr2); + jarr.addArrayElement(jnull); + jarr.addArrayElement(jint); + jarr.addArrayElement(jdouble); + jarr.addArrayElement(jexp); + check(jarr, + "[\n" + " \"a\\tb\",\n" + " null,\n" + " 16059,\n" + " 3.141590,\n" + " 2.1e5\n" + "]"); + JSON jmap = JSON::makeDictionary(); + check(jmap, "{}"); + jmap.addDictionaryMember("b", jstr2); + jmap.addDictionaryMember("a", jarr); + jmap.addDictionaryMember("c\r\nd", jnull); + jmap.addDictionaryMember("yes", JSON::makeBool(false)); + jmap.addDictionaryMember("no", JSON::makeBool(true)); + jmap.addDictionaryMember("empty_dict", JSON::makeDictionary()); + jmap.addDictionaryMember("empty_list", JSON::makeArray()); + jmap.addDictionaryMember("single", JSON::makeArray()). + addArrayElement(JSON::makeInt(12)); + check(jmap, + "{\n" + " \"a\": [\n" + " \"a\\tb\",\n" + " null,\n" + " 16059,\n" + " 3.141590,\n" + " 2.1e5\n" + " ],\n" + " \"b\": \"a\\tb\",\n" + " \"c\\r\\nd\": null,\n" + " \"empty_dict\": {},\n" + " \"empty_list\": [],\n" + " \"no\": true,\n" + " \"single\": [\n" + " 12\n" + " ],\n" + " \"yes\": false\n" + "}"); +} + +static void check_schema(JSON& obj, JSON& schema, bool exp, + std::string const& description) +{ + std::list<std::string> errors; + std::cout << "--- " << description << std::endl; + assert(exp == obj.checkSchema(schema, errors)); + for (std::list<std::string>::iterator iter = errors.begin(); + iter != errors.end(); ++iter) + { + std::cout << *iter << std::endl; + } + std::cout << "---" << std::endl; +} + +static void test_schema() +{ + // Since we don't have a JSON parser, use the PDF parser as a + // shortcut for creating a complex JSON structure. + JSON schema = QPDFObjectHandle::parse( + "<<" + " /one <<" + " /a <<" + " /q (queue)" + " /r <<" + " /x (ecks)" + " /y (why)" + " >>" + " /s [ (esses) ]" + " >>" + " >>" + " /two [" + " <<" + " /goose (gander)" + " /glarp (enspliel)" + " >>" + " ]" + ">>").getJSON(); + JSON a = QPDFObjectHandle::parse("[(not a) (dictionary)]").getJSON(); + check_schema(a, schema, false, "top-level type mismatch"); + JSON b = QPDFObjectHandle::parse( + "<<" + " /one <<" + " /a <<" + " /t (oops)" + " /r [" + " /x (ecks)" + " /y (why)" + " ]" + " /s << /z (esses) >>" + " >>" + " >>" + " /two [" + " <<" + " /goose (0 gander)" + " /glarp (0 enspliel)" + " >>" + " <<" + " /goose (1 gander)" + " /flarp (1 enspliel)" + " >>" + " 2" + " [ (three) ]" + " <<" + " /goose (4 gander)" + " /glarp (4 enspliel)" + " >>" + " ]" + ">>").getJSON(); + check_schema(b, schema, false, "top-level type mismatch"); + check_schema(a, a, false, "top-level schema array error"); + check_schema(b, b, false, "lower-level schema array error"); + check_schema(schema, schema, true, "pass"); +} + +int main() +{ + test_main(); + test_schema(); + + std::cout << "end of json tests\n"; + return 0; +} diff --git a/libtests/libtests.testcov b/libtests/libtests.testcov index 8b209281..775141d7 100644 --- a/libtests/libtests.testcov +++ b/libtests/libtests.testcov @@ -34,3 +34,8 @@ Pl_PNGFilter decodeUp 0 Pl_PNGFilter decodeAverage 0 Pl_PNGFilter decodePaeth 0 Pl_TIFFPredictor processRow 1 +JSON wanted dictionary 0 +JSON key missing in object 0 +JSON wanted array 0 +JSON schema array error 0 +JSON key extra in object 0 diff --git a/libtests/matrix.cc b/libtests/matrix.cc new file mode 100644 index 00000000..77a058ee --- /dev/null +++ b/libtests/matrix.cc @@ -0,0 +1,90 @@ +#include <qpdf/QPDFMatrix.hh> +#include <qpdf/QUtil.hh> +#include <assert.h> +#include <iostream> + +static void check(QPDFMatrix const& m, std::string const& exp) +{ + std::string u = m.unparse(); + if (u != exp) + { + std::cout << "got " << u << ", wanted " << exp << std::endl; + } +} + +static void check_xy(double x, double y, std::string const& exp) +{ + std::string u = (QUtil::double_to_string(x, 2) + " " + + QUtil::double_to_string(y, 2)); + if (u != exp) + { + std::cout << "got " << u << ", wanted " << exp << std::endl; + } +} + +static void check_rect(QPDFObjectHandle::Rectangle const& r, + double llx, double lly, double urx, double ury) +{ + std::string actual = ( + QUtil::double_to_string(r.llx, 2) + " " + + QUtil::double_to_string(r.lly, 2) + " " + + QUtil::double_to_string(r.urx, 2) + " " + + QUtil::double_to_string(r.ury, 2)); + std::string wanted = ( + QUtil::double_to_string(llx, 2) + " " + + QUtil::double_to_string(lly, 2) + " " + + QUtil::double_to_string(urx, 2) + " " + + QUtil::double_to_string(ury, 2)); + if (actual != wanted) + { + std::cout << "got " << actual << ", wanted " << wanted << std::endl; + } +} + +int main() +{ + QPDFMatrix m; + check(m, "1.00000 0.00000 0.00000 1.00000 0.00000 0.00000"); + m.translate(10, 20); + check(m, "1.00000 0.00000 0.00000 1.00000 10.00000 20.00000"); + m.scale(1.5, 2); + check(m, "1.50000 0.00000 0.00000 2.00000 10.00000 20.00000"); + double xp = 0; + double yp = 0; + m.transform(10, 100, xp, yp); + check_xy(xp, yp, "25.00 220.00"); + m.translate(30, 40); + check(m, "1.50000 0.00000 0.00000 2.00000 55.00000 100.00000"); + m.transform(10, 100, xp, yp); + check_xy(xp, yp, "70.00 300.00"); + m.concat(QPDFMatrix(1, 2, 3, 4, 5, 6)); + check(m, "1.50000 4.00000 4.50000 8.00000 62.50000 112.00000"); + m.rotatex90(90); + check(m, "4.50000 8.00000 -1.50000 -4.00000 62.50000 112.00000"); + m.rotatex90(180); + check(m, "-4.50000 -8.00000 1.50000 4.00000 62.50000 112.00000"); + m.rotatex90(270); + check(m, "-1.50000 -4.00000 -4.50000 -8.00000 62.50000 112.00000"); + m.rotatex90(180); + check(m, "1.50000 4.00000 4.50000 8.00000 62.50000 112.00000"); + m.rotatex90(12345); + check(m, "1.50000 4.00000 4.50000 8.00000 62.50000 112.00000"); + + m.transform(240, 480, xp, yp); + check_xy(xp, yp, "2582.50 4912.00"); + + check(QPDFMatrix( + QPDFObjectHandle::parse( + "[3 1 4 1 5 9.26535]").getArrayAsMatrix()), + "3.00000 1.00000 4.00000 1.00000 5.00000 9.26535"); + + m = QPDFMatrix(); + m.rotatex90(90); + m.translate(200, -100); + check_rect( + m.transformRectangle(QPDFObjectHandle::Rectangle(10, 20, 30, 50)), + 50, 210, 80, 230); + + std::cout << "matrix tests done" << std::endl; + return 0; +} diff --git a/libtests/numrange.cc b/libtests/numrange.cc new file mode 100644 index 00000000..6cbb90ac --- /dev/null +++ b/libtests/numrange.cc @@ -0,0 +1,36 @@ +#include <qpdf/QUtil.hh> +#include <iostream> + +static void test_numrange(char const* range) +{ + if (range == 0) + { + std::cout << "null" << std::endl; + } + else + { + std::vector<int> result = QUtil::parse_numrange(range, 15); + std::cout << "numeric range " << range << " ->"; + for (std::vector<int>::iterator iter = result.begin(); + iter != result.end(); ++iter) + { + std::cout << " " << *iter; + } + std::cout << std::endl; + } +} + +int main(int argc, char* argv[]) +{ + try + { + test_numrange(argv[1]); + } + catch (std::exception& e) + { + std::cout << e.what() << std::endl; + return 2; + } + + return 0; +} diff --git a/libtests/qtest/closedfile/output b/libtests/qtest/closedfile/output index 7a5554af..596d1462 100644 --- a/libtests/qtest/closedfile/output +++ b/libtests/qtest/closedfile/output @@ -1,3 +1,4 @@ testing with ClosedFileInputSource +testing with ClosedFileInputSource in stay open mode testing with FileInputSource all assertions passed diff --git a/libtests/qtest/json.test b/libtests/qtest/json.test new file mode 100644 index 00000000..b62994d3 --- /dev/null +++ b/libtests/qtest/json.test @@ -0,0 +1,17 @@ +#!/usr/bin/env perl +require 5.008; +use warnings; +use strict; + +chdir("json") or die "chdir testdir failed: $!\n"; + +require TestDriver; + +my $td = new TestDriver('json'); + +$td->runtest("json", + {$td->COMMAND => "json"}, + {$td->FILE => "json.out", $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); + +$td->report(1); diff --git a/libtests/qtest/json/json.out b/libtests/qtest/json/json.out new file mode 100644 index 00000000..f06cc1fb --- /dev/null +++ b/libtests/qtest/json/json.out @@ -0,0 +1,23 @@ +--- top-level type mismatch +top-level object is supposed to be a dictionary +--- +--- top-level type mismatch +json key "./one./a": key "/q" is present in schema but missing in object +json key "./one./a./r" is supposed to be a dictionary +json key "./one./a./s" is supposed to be an array +json key "./one./a": key "/t" is not present in schema but appears in object +json key "./two.1": key "/glarp" is present in schema but missing in object +json key "./two.1": key "/flarp" is not present in schema but appears in object +json key "./two.2" is supposed to be a dictionary +json key "./two.3" is supposed to be a dictionary +--- +--- top-level schema array error +top-level object schema array contains other than one item +--- +--- lower-level schema array error +json key "./one./a./r" schema array contains other than one item +json key "./two" schema array contains other than one item +--- +--- pass +--- +end of json tests diff --git a/libtests/qtest/matrix.test b/libtests/qtest/matrix.test new file mode 100644 index 00000000..90522f9c --- /dev/null +++ b/libtests/qtest/matrix.test @@ -0,0 +1,15 @@ +#!/usr/bin/env perl +require 5.008; +use warnings; +use strict; + +require TestDriver; + +my $td = new TestDriver('matrix'); + +$td->runtest("matrix", + {$td->COMMAND => "matrix"}, + {$td->STRING => "matrix tests done\n", $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); + +$td->report(1); diff --git a/libtests/qtest/numrange.test b/libtests/qtest/numrange.test new file mode 100644 index 00000000..9acf6ea4 --- /dev/null +++ b/libtests/qtest/numrange.test @@ -0,0 +1,63 @@ +#!/usr/bin/env perl +require 5.008; +use warnings; +use strict; + +require TestDriver; + +my $td = new TestDriver('numrange'); + +my @nrange_tests = ( + [",5", + "error at * in numeric range *,5: unexpected separator", + 2], + ["4,,5", + "error at * in numeric range 4,*,5: unexpected separator", + 2], + ["4,5,", + "error at * in numeric range 4,5,*: number expected", + 2], + ["z1,", + "error at * in numeric range z*1,: digit not expected", + 2], + ["1z,", + "error at * in numeric range 1*z,: z not expected", + 2], + ["1-5?", + "error at * in numeric range 1-5*?: unexpected character", + 2], + ["1-30", + "error in numeric range 1-30: number 30 out of range", + 2], + ["1-10,0,5", + "error in numeric range 1-10,0,5: number 0 out of range", + 2], + ["1-10,1234,5", + "error in numeric range 1-10,1234,5: number 1234 out of range", + 2], + ["1,r,3", + "error in numeric range 1,r,3: number 16 out of range", + 2], + ["1,r16,3", + "error in numeric range 1,r16,3: number 0 out of range", + 2], + ["1,3,5-10,z-13,13,9,z,2,r2-r4", + "numeric range 1,3,5-10,z-13,13,9,z,2,r2-r4" . + " -> 1 3 5 6 7 8 9 10 15 14 13 13 9 15 2 14 13 12", + 0], + ["r1-r15", # r\d+ at end + "numeric range r1-r15" . + " -> 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1", + 0], + ); +foreach my $d (@nrange_tests) +{ + my ($range, $output, $status) = @$d; + $td->runtest("numeric range $range", + {$td->COMMAND => ['numrange', $range], + $td->FILTER => "grep 'numeric range'"}, + {$td->STRING => $output . "\n", $td->EXIT_STATUS => $status}, + $td->NORMALIZE_NEWLINES); +} + +$td->report(scalar(@nrange_tests)); diff --git a/libtests/qtest/qutil/qutil.out b/libtests/qtest/qutil/qutil.out index 8223bf5b..c35f22e3 100644 --- a/libtests/qtest/qutil/qutil.out +++ b/libtests/qtest/qutil/qutil.out @@ -47,6 +47,30 @@ HAGOOGAMAGOOGLE: 0 0xdead -> ff fd 0x7fffffff -> ff fd 0x80000000 -> ff fd +---- utf8_to_ascii +¿Does π have fingers? +?Does ? have fingers? +*Does * have fingers? +<bf>Does * have fingers? +<c0>Does * have fingers? +---- transcoding +bidirectional pdf doc done +bidirectional win ansi done +bidirectional mac roman done +analysis done +alternatives +0: 86a9e99e +1: c692c2a9c3a9c5be +2: e280a0c2a9c3a9c5be +3: c39cc2a9c388c3bb +4: 83a9e99e +5: 81a9e99e +6: dca9c8fb +0: c692c2a9c3a9c5be +1: 86a9e99e +2: 83a9e99e +0: 717561636b +done alternatives ---- whoami quack1 quack2 diff --git a/libtests/qutil.cc b/libtests/qutil.cc index a7479fb5..27881c6e 100644 --- a/libtests/qutil.cc +++ b/libtests/qutil.cc @@ -5,8 +5,10 @@ #include <fcntl.h> #include <qpdf/QUtil.hh> #include <qpdf/PointerHolder.hh> +#include <qpdf/QPDFSystemError.hh> #include <string.h> #include <limits.h> +#include <assert.h> #ifdef _WIN32 # include <io.h> @@ -26,7 +28,7 @@ void test_to_number(char const* str, int_T wanted, bool error, result = fn(str); worked = (wanted == result); } - catch (std::runtime_error) + catch (std::runtime_error const&) { threw = true; } @@ -137,9 +139,10 @@ void fopen_wrapper_test() std::cout << "after fopen" << std::endl; (void) fclose(f); } - catch (std::runtime_error& s) + catch (QPDFSystemError& s) { std::cout << "exception: " << s.what() << std::endl; + assert(s.getErrno() != 0); } } @@ -217,6 +220,118 @@ void to_utf16_test() print_utf16(0x80000000UL); } +void utf8_to_ascii_test() +{ + char const* input = "\302\277Does \317\200 have fingers?"; + std::cout << input + << std::endl + << QUtil::utf8_to_ascii(input) + << std::endl + << QUtil::utf8_to_ascii(input, '*') + << std::endl; + std::string a = QUtil::utf8_to_win_ansi(input, '*'); + std::string b = QUtil::utf8_to_mac_roman(input, '*'); + std::cout + << "<" << QUtil::int_to_string_base( + static_cast<unsigned char>(a.at(0)), 16, 2) + << ">" << a.substr(1) << std::endl + << "<" << QUtil::int_to_string_base( + static_cast<unsigned char>(b.at(0)), 16, 2) + << ">" << b.substr(1) << std::endl; +} + +void transcoding_test(std::string (*to_utf8)(std::string const&), + std::string (*from_utf8)(std::string const&, char), + int last, std::string unknown) +{ + std::string in(" "); + std::string out; + std::string back; + for (int i = 128; i <= last; ++i) + { + in.at(0) = static_cast<unsigned char>(i); + out = (*to_utf8)(in); + std::string wanted = (out == "\xef\xbf\xbd") ? unknown : in; + back = (*from_utf8)(out, '?'); + if (back != wanted) + { + std::cout << i << ": " << in << " -> " << out + << " -> " << back << " (wanted " << wanted << ")" + << std::endl; + } + } +} + +void check_analyze(std::string const& str, bool has8bit, bool utf8, bool utf16) +{ + bool has_8bit_chars = false; + bool is_valid_utf8 = false; + bool is_utf16 = false; + QUtil::analyze_encoding(str, has_8bit_chars, is_valid_utf8, is_utf16); + if (! ((has_8bit_chars == has8bit) && + (is_valid_utf8 == utf8) && + (is_utf16 == utf16))) + { + std::cout << "analysis failed: " << str << std::endl; + } +} + +void print_alternatives(std::string const& str) +{ + std::vector<std::string> result = QUtil::possible_repaired_encodings(str); + size_t n = result.size(); + for (size_t i = 0; i < n; ++i) + { + std::cout << i << ": " << QUtil::hex_encode(result.at(i)) << std::endl; + } +} + +void transcoding_test() +{ + transcoding_test(&QUtil::pdf_doc_to_utf8, + &QUtil::utf8_to_pdf_doc, 160, "\x9f"); + std::cout << "bidirectional pdf doc done" << std::endl; + transcoding_test(&QUtil::win_ansi_to_utf8, + &QUtil::utf8_to_win_ansi, 160, "?"); + std::cout << "bidirectional win ansi done" << std::endl; + transcoding_test(&QUtil::mac_roman_to_utf8, + &QUtil::utf8_to_mac_roman, 255, "?"); + std::cout << "bidirectional mac roman done" << std::endl; + check_analyze("pi = \317\200", true, true, false); + check_analyze("pi != \317", true, false, false); + check_analyze("pi != 22/7", false, false, false); + check_analyze(std::string("\xfe\xff\00\x51", 4), true, false, true); + std::cout << "analysis done" << std::endl; + std::string input1("a\302\277b"); + std::string input2("a\317\200b"); + std::string input3("ab"); + std::string output; + assert(! QUtil::utf8_to_ascii(input1, output)); + assert(! QUtil::utf8_to_ascii(input2, output)); + assert(QUtil::utf8_to_ascii(input3, output)); + assert(QUtil::utf8_to_win_ansi(input1, output)); + assert(! QUtil::utf8_to_win_ansi(input2, output)); + assert(QUtil::utf8_to_win_ansi(input3, output)); + assert(QUtil::utf8_to_mac_roman(input1, output)); + assert(! QUtil::utf8_to_mac_roman(input2, output)); + assert(QUtil::utf8_to_mac_roman(input3, output)); + assert(QUtil::utf8_to_pdf_doc(input1, output)); + assert(! QUtil::utf8_to_pdf_doc(input2, output)); + assert(QUtil::utf8_to_pdf_doc(input3, output)); + std::cout << "alternatives" << std::endl; + // char name mac win pdf-doc + // U+0192 florin 304 203 206 + // U+00A9 copyright 251 251 251 + // U+00E9 eacute 216 351 351 + // U+017E zcaron - 236 236 + std::string pdfdoc = "\206\251\351\236"; + std::string utf8 = QUtil::pdf_doc_to_utf8(pdfdoc); + print_alternatives(pdfdoc); + print_alternatives(utf8); + print_alternatives("quack"); + std::cout << "done alternatives" << std::endl; +} + void print_whoami(char const* str) { PointerHolder<char> dup(true, QUtil::copy_string(str)); @@ -247,7 +362,7 @@ void same_file_test() fclose(QUtil::safe_fopen("qutil.out", "r")); fclose(QUtil::safe_fopen("other-file", "r")); } - catch (std::exception) + catch (std::exception const&) { std::cout << "same_file_test expects to have qutil.out and other-file" " exist in the current directory\n"; @@ -325,6 +440,10 @@ int main(int argc, char* argv[]) to_utf8_test(); std::cout << "---- utf16" << std::endl; to_utf16_test(); + std::cout << "---- utf8_to_ascii" << std::endl; + utf8_to_ascii_test(); + std::cout << "---- transcoding" << std::endl; + transcoding_test(); std::cout << "---- whoami" << std::endl; get_whoami_test(); std::cout << "---- file" << std::endl; |