travis debug

Update README.md

track travis branch on CI

install ttfautohint; run ctest with output-on-failure

check more c++11 features

use local browser to test

install selenium through pip in travis

refactor tests

use html instead of png as reference
This commit is contained in:
Lu Wang 2014-11-16 22:57:38 +08:00
parent bd891e7eb0
commit 757810f170
33 changed files with 1175 additions and 120 deletions

View File

@ -4,15 +4,20 @@ before_install:
- sudo add-apt-repository ppa:fontforge/fontforge --yes
- sudo add-apt-repository ppa:coolwanglu/pdf2htmlex --yes
- sudo apt-get update -qq
- sudo apt-get install -qq libpoppler-dev libpoppler-private-dev libspiro-dev libcairo-dev libpango1.0-dev libfreetype6-dev libltdl-dev libfontforge-dev python-imaging wkhtmltopdf
- sudo apt-get install -qq libpoppler-dev libpoppler-private-dev libspiro-dev libcairo-dev libpango1.0-dev libfreetype6-dev libltdl-dev libfontforge-dev python-imaging python-pip ttfautohint firefox xvfb
- sudo pip install selenium
- export DISPLAY=:99.0
- test/start_xvfb.sh
- sleep 5
before_script:
- cmake -DENABLE_SVG=ON .
script:
- make
- make test
- ctest --output-on-failure
- sudo make install
- /usr/local/bin/pdf2htmlEX -v
branches:
only:
- master
- incoming
- travis

View File

@ -84,10 +84,20 @@ else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
endif()
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag("${CMAKE_CXX_FLAGS}" CXX0X_SUPPORT)
# check the C++11 features we need
include(CheckCXXSourceCompiles)
check_cxx_source_compiles("
#include <vector>
int main()
{
char * ptr = nullptr;
std::vector<int> v;
auto f = [&](){ for(auto & i : v) ++i; };
f();
}
" CXX0X_SUPPORT)
if(NOT CXX0X_SUPPORT)
message(FATAL_ERROR "Error: your compiler does not support C++0x, please update it.")
message(FATAL_ERROR "Error: your compiler does not support C++0x/C++11, please update it.")
endif()

View File

@ -1 +0,0 @@
SET(CTEST_CUSTOM_POST_TEST "cat Testing/Temporary/LastTest.log")

View File

@ -3,7 +3,7 @@
<!--
[![Build Status](https://travis-ci.org/coolwanglu/pdf2htmlEX.png?branch=master)](https://travis-ci.org/coolwanglu/pdf2htmlEX)
-->
>一图胜千言<br>A beautiful demo is worth a thousand words:
>一图胜千言<br>A beautiful demo is worth a thousand words
- **Bible de Genève, 1564** (fonts and typography): [HTML](http://coolwanglu.github.com/pdf2htmlEX/demo/geneve.html) / [PDF](https://github.com/raphink/geneve_1564/raw/master/geneve_1564.pdf)
- **Cheat Sheet** (math formulas): [HTML](http://coolwanglu.github.com/pdf2htmlEX/demo/cheat.html) / [PDF](http://www.tug.org/texshowcase/cheat.pdf)

1
test/.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
*.pdf binary

View File

@ -1,14 +1,18 @@
### Dependencies
- wkhtmltoimage
- python2
- Python Imaging Library
- python2 and packages
- Python Imaging Library
- Selenium
- unittest
- Firefox
### Usage
- Run all tests:
- `./test.py`
- Run selected tests:
- `./test.py test_A test_B ...`
- Run selected test suites:
- `./test.py test_local_browser`
- Run selected test case:
- `./test.py test_basic_text
- Environment variables:
- set `P2H_TEST_SAVE_TMP=1` to keep the temporary files
- set `P2H_TEST_GEN=1` to generate new reference images instead of comparing with old ones
@ -21,6 +25,5 @@
- One page only, unless the test case is about multiple pages.
- Grayscale only, unless the test case is about colors.
- Remove unnecessary elements.
- Set proper parameters for cropping in `wkhtml2image_args`.
- [Optional] Include the source files that the PDF file is generated from.

89
test/browser_tests.py Normal file
View File

@ -0,0 +1,89 @@
#!/usr/bin/env python
import unittest
import os
import subprocess
import shutil
from PIL import Image, ImageChops
from test import Common
class BrowserTests(Common):
TTFAUTOHINT = 'ttfautohint'
TEST_DATA_DIR = os.path.join(Common.TEST_DIR, 'browser_tests')
DEFAULT_PDF2HTMLEX_ARGS = [
'--external-hint-tool', 'ttfautohint',
'--fit-width', 800,
'--last-page', 1,
'--correct-text-visibility', 1,
'--embed', 'fi', # avoid base64 to make it faster
]
BROWSER_WIDTH=800
BROWSER_HEIGHT=1200
@classmethod
def setUpClass(cls):
exit_code = subprocess.call([cls.TTFAUTOHINT, '--version'])
assert (exit_code == 0), 'Cannot execute ' + cls.TTFAUTOHINT
@classmethod
def tearDownClass(cls):
pass
def run_test_case(self, filename, pdf2htmlEX_args=[]):
basefilename, extension = os.path.splitext(filename)
htmlfilename = basefilename + '.html'
ref_htmlfolder = os.path.join(self.TEST_DATA_DIR, basefilename)
ref_htmlfilename = os.path.join(ref_htmlfolder, htmlfilename)
out_htmlfilename = os.path.join(self.cur_output_dir, htmlfilename)
self.assertEquals(extension.lower(), '.pdf', 'Input file is not PDF')
pdf2htmlEX_args = self.DEFAULT_PDF2HTMLEX_ARGS \
+ list(pdf2htmlEX_args) + [
os.path.join(self.TEST_DATA_DIR, filename),
htmlfilename
]
result = self.run_pdf2htmlEX(pdf2htmlEX_args)
self.assertIn(htmlfilename, result['output_files'], 'HTML file is not generated')
if self.GENERATING_MODE:
# copy generated html files
shutil.rmtree(ref_htmlfolder, True)
shutil.copytree(self.cur_output_dir, ref_htmlfolder)
return
png_out_dir = os.path.join(self.cur_temp_dir, 'png_out')
os.mkdir(png_out_dir)
pngfilename_out_fullpath = os.path.join(png_out_dir, basefilename + '.out.png')
self.generate_image(out_htmlfilename, pngfilename_out_fullpath)
out_img = Image.open(pngfilename_out_fullpath)
pngfilename_ref_fullpath = os.path.join(png_out_dir, basefilename + '.ref.png')
self.generate_image(ref_htmlfilename, pngfilename_ref_fullpath)
ref_img = Image.open(pngfilename_ref_fullpath)
diff_img = ImageChops.difference(ref_img, out_img);
if diff_img.getbbox() is not None:
if self.SAVE_TMP:
# save the diff image
# http://stackoverflow.com/questions/15721484/saving-in-png-using-pil-library-after-taking-imagechops-difference-of-two-png
diff_img.convert('RGB').save(os.path.join(png_out_dir, basefilename + '.diff.png'))
self.fail('PNG files differ')
def test_basic_text(self):
self.run_test_case('basic_text.pdf')
def test_geneve_1564(self):
self.run_test_case('geneve_1564.pdf')
def test_text_visibility(self):
self.run_test_case('text_visibility.pdf')

View File

@ -0,0 +1,107 @@
<!DOCTYPE html>
<!-- Created by pdf2htmlEX (https://github.com/coolwanglu/pdf2htmlex) -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8"/>
<meta name="generator" content="pdf2htmlEX"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<style type="text/css">
/*!
* Base CSS for pdf2htmlEX
* Copyright 2012,2013 Lu Wang <coolwanglu@gmail.com>
* https://github.com/coolwanglu/pdf2htmlEX/blob/master/share/LICENSE
*/#sidebar{position:absolute;top:0;left:0;bottom:0;width:250px;padding:0;margin:0;overflow:auto}#page-container{position:absolute;top:0;left:0;margin:0;padding:0;border:0}@media screen{#sidebar.opened+#page-container{left:250px}#page-container{bottom:0;right:0;overflow:auto}.loading-indicator{display:none}.loading-indicator.active{display:block;position:absolute;width:64px;height:64px;top:50%;left:50%;margin-top:-32px;margin-left:-32px}.loading-indicator img{position:absolute;top:0;left:0;bottom:0;right:0}}@media print{@page{margin:0}html{margin:0}body{margin:0;-webkit-print-color-adjust:exact}#sidebar{display:none}#page-container{width:auto;height:auto;overflow:visible;background-color:transparent}.d{display:none}}.pf{position:relative;background-color:white;overflow:hidden;margin:0;border:0}.pc{position:absolute;border:0;padding:0;margin:0;top:0;left:0;width:100%;height:100%;overflow:hidden;display:block;transform-origin:0 0;-ms-transform-origin:0 0;-webkit-transform-origin:0 0}.pc.opened{display:block}.bf{position:absolute;border:0;margin:0;top:0;bottom:0;width:100%;height:100%;-ms-user-select:none;-moz-user-select:none;-webkit-user-select:none;user-select:none}.bi{position:absolute;border:0;margin:0;-ms-user-select:none;-moz-user-select:none;-webkit-user-select:none;user-select:none}@media print{.pf{margin:0;box-shadow:none;page-break-after:always;page-break-inside:avoid}@-moz-document url-prefix(){.pf{overflow:visible;border:1px solid #fff}.pc{overflow:visible}}}.c{position:absolute;border:0;padding:0;margin:0;overflow:hidden;display:block}.t{position:absolute;white-space:pre;font-size:1px;transform-origin:0 100%;-ms-transform-origin:0 100%;-webkit-transform-origin:0 100%;unicode-bidi:bidi-override;-moz-font-feature-settings:"liga" 0}.t:after{content:''}.t span{position:relative;display:inline-block;unicode-bidi:bidi-override}._{color:transparent;z-index:-1}::selection{background:rgba(127,255,255,0.4)}::-moz-selection{background:rgba(127,255,255,0.4)}.pi{display:none}.d{position:absolute;transform-origin:0 100%;-ms-transform-origin:0 100%;-webkit-transform-origin:0 100%}</style>
<style type="text/css">
/* CSS for test cases */
#page-container {
overflow:hidden;
}
</style>
<style type="text/css">
.ff0{font-family:sans-serif;visibility:hidden;}
@font-face{font-family:ff1;src:url(f1.woff)format("woff");}.ff1{font-family:ff1;line-height:0.899000;font-style:normal;font-weight:normal;visibility:visible;}
@font-face{font-family:ff2;src:url(f2.woff)format("woff");}.ff2{font-family:ff2;line-height:0.882000;font-style:normal;font-weight:normal;visibility:visible;}
.m2{transform:matrix(0.231081,-0.231081,0.231081,0.231081,0,0);-ms-transform:matrix(0.231081,-0.231081,0.231081,0.231081,0,0);-webkit-transform:matrix(0.231081,-0.231081,0.231081,0.231081,0,0);}
.m0{transform:matrix(0.326797,0.000000,0.000000,0.326797,0,0);-ms-transform:matrix(0.326797,0.000000,0.000000,0.326797,0,0);-webkit-transform:matrix(0.326797,0.000000,0.000000,0.326797,0,0);}
.m3{transform:matrix(0.462161,-0.462161,0.231081,0.231081,0,0);-ms-transform:matrix(0.462161,-0.462161,0.231081,0.231081,0,0);-webkit-transform:matrix(0.462161,-0.462161,0.231081,0.231081,0,0);}
.m1{transform:matrix(0.653595,0.000000,0.000000,0.326797,0,0);-ms-transform:matrix(0.653595,0.000000,0.000000,0.326797,0,0);-webkit-transform:matrix(0.653595,0.000000,0.000000,0.326797,0,0);}
.v0{vertical-align:0.000000px;}
.ls0{letter-spacing:0.000000px;}
.ls1{letter-spacing:20.000000px;}
.ls2{letter-spacing:20.081833px;}
.sc_{text-shadow:none;}
.sc0{text-shadow:-0.015em 0 transparent,0 0.015em transparent,0.015em 0 transparent,0 -0.015em transparent;}
@media screen and (-webkit-min-device-pixel-ratio:0){
.sc_{-webkit-text-stroke:0px transparent;}
.sc0{-webkit-text-stroke:0.015em transparent;text-shadow:none;}
}
.ws0{word-spacing:0.000000px;}
._1{display:inline;margin-left:-3.997949px;}
._2{display:inline;margin-left:-1.115811px;}
._0{display:inline-block;width:39.850619px;}
.fc0{color:rgb(0,0,0);}
.fs1{font-size:19.925200px;}
.fs0{font-size:39.850400px;}
.fs2{font-size:40.013453px;}
.y8{bottom:210.583007px;}
.y7{bottom:628.221281px;}
.y6{bottom:650.413190px;}
.y4{bottom:672.604170px;}
.y5{bottom:715.900248px;}
.y3{bottom:796.620915px;}
.y2{bottom:827.875817px;}
.y0{bottom:859.130719px;}
.y1{bottom:865.666667px;}
.h1{height:28.094532px;}
.h2{height:28.209484px;}
.h0{height:1035.294118px;}
.w0{width:800.000000px;}
.x0{left:194.394771px;}
.x2{left:199.825216px;}
.x4{left:222.016196px;}
.x3{left:233.840248px;}
.x5{left:244.208105px;}
.x1{left:312.996078px;}
.x6{left:396.252288px;}
@media print{
.v0{vertical-align:0.000000pt;}
.ls0{letter-spacing:0.000000pt;}
.ls1{letter-spacing:20.400000pt;}
.ls2{letter-spacing:20.483469pt;}
.ws0{word-spacing:0.000000pt;}
._1{display:inline;margin-left:-4.077908pt;}
._2{display:inline;margin-left:-1.138127pt;}
._0{display:inline-block;width:40.647631pt;}
.fs1{font-size:20.323704pt;}
.fs0{font-size:40.647408pt;}
.fs2{font-size:40.813722pt;}
.y8{bottom:214.794667pt;}
.y7{bottom:640.785707pt;}
.y6{bottom:663.421453pt;}
.y4{bottom:686.056253pt;}
.y5{bottom:730.218253pt;}
.y3{bottom:812.553333pt;}
.y2{bottom:844.433333pt;}
.y0{bottom:876.313333pt;}
.y1{bottom:882.980000pt;}
.h1{height:28.656423pt;}
.h2{height:28.773674pt;}
.h0{height:1056.000000pt;}
.w0{width:816.000000pt;}
.x0{left:198.282667pt;}
.x2{left:203.821720pt;}
.x4{left:226.456520pt;}
.x3{left:238.517053pt;}
.x5{left:249.092267pt;}
.x1{left:319.256000pt;}
.x6{left:404.177333pt;}
}
</style>
<title></title>
</head>
<body>
<div id="page-container">
<div id="pf1" class="pf w0 h0" data-page-no="1"><div class="pc pc1 w0 h0"><div class="t m0 x0 h1 y0 ff1 fs0 fc0 sc0 ls0 ws0">Normal<span class="_ _0"> </span><span class="ff2 fs1">tiny<span class="_ _0"> </span></span>T<span class="_ _1"></span>ext</div><div class="t m0 x1 h1 y1 ff1 fs0 fc0 sc0 ls0 ws0">Rise</div><div class="t m0 x0 h1 y2 ff1 fs0 fc0 sc0 ls1 ws0">CharSpace</div><div class="t m1 x0 h1 y3 ff1 fs0 fc0 sc0 ls0 ws0">Horizon<span class="_ _2"></span>tal<span class="_ _0"> </span>Scale</div><div class="t m2 x2 h2 y4 ff1 fs2 fc0 sc0 ls0 ws0">Rotated</div><div class="t m2 x3 h2 y5 ff1 fs2 fc0 sc0 ls0 ws0">Rise</div><div class="t m2 x4 h2 y6 ff1 fs2 fc0 sc0 ls2 ws0">CharSpace</div><div class="t m3 x5 h2 y7 ff1 fs2 fc0 sc0 ls0 ws0">Horizon<span class="_ _2"></span>tal<span class="_ _0"> </span>Scale</div><div class="t m0 x6 h1 y8 ff1 fs0 fc0 sc0 ls0 ws0">1</div></div><div class="pi" data-data='{"ctm":[1.307190,0.000000,0.000000,1.307190,0.000000,0.000000]}'></div></div>
</div>
</body>
</html>

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

2
test/start_xvfb.sh Executable file
View File

@ -0,0 +1,2 @@
#!/bin/sh
/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1920x16

View File

@ -14,6 +14,8 @@ class Common(object):
PDF2HTMLEX_PATH = os.path.join(SRC_DIR, 'pdf2htmlEX')
SAVE_TMP = os.environ.get('P2H_TEST_SAVE_TMP')
GENERATING_MODE = os.environ.get('P2H_TEST_GEN')
CANONICAL_TEMPDIR = '/tmp/pdf2htmlEX_test'
def setUp(self):
@ -83,14 +85,23 @@ if __name__ == '__main__':
exit(1)
suites = []
loader = unittest.TestLoader()
test_names = list(map(lambda x: 'T.'+x, sys.argv[1:]))
for module_name in ['test_naming', 'test_conversion']:
all_modules = ['test_output', 'test_local_browser']
test_names = []
for name in sys.argv[1:]:
if name in all_modules or name.find('.') != -1:
test_names.append(name)
else:
for m in all_modules:
test_names.append(m + '.' + name)
for module_name in all_modules:
__import__(module_name)
if len(test_names) > 0:
try:
suites.append(loader.loadTestsFromNames(test_names, sys.modules[module_name]))
except:
pass
for n in test_names:
try:
suites.append(loader.loadTestsFromName(n, sys.modules[module_name]))
except:
pass
else:
suites.append(loader.loadTestsFromModule(sys.modules[module_name]))

View File

@ -1,98 +0,0 @@
#!/usr/bin/env python
import unittest
import os
import subprocess
from PIL import Image, ImageChops
from test import Common
class T(Common, unittest.TestCase):
GENERATING_MODE = os.environ.get('P2H_TEST_GEN')
WKHTML2IMAGE = 'wkhtmltoimage'
TTFAUTOHINT = 'ttfautohint'
TEST_DATA_DIR = os.path.join(Common.TEST_DIR, 'test_conversion')
DEFAULT_PDF2HTMLEX_ARGS = [
'--external-hint-tool', 'ttfautohint',
'--fit-width', 800,
'--last-page', 1,
'--correct-text-visibility', 1,
]
DEFAULT_WKHTML2IMAGE_ARGS = [
'-f', 'png',
'--height', 600,
'--width', 800,
'--quality', 0,
'--quiet'
]
@classmethod
def setUpClass(cls):
subprocess.check_call([cls.WKHTML2IMAGE, '--version'])
subprocess.check_call([cls.TTFAUTOHINT, '--version'])
def run_test_case(self, filename, pdf2htmlEX_args=[], wkhtml2image_args=[]):
basefilename, extension = os.path.splitext(filename)
htmlfilename = basefilename + '.html'
pngfilename = basefilename + '.png'
self.assertEquals(extension.lower(), '.pdf', 'Input file is not PDF')
pdf2htmlEX_args = self.DEFAULT_PDF2HTMLEX_ARGS \
+ list(pdf2htmlEX_args) + [
os.path.join(self.TEST_DATA_DIR, filename),
htmlfilename
]
result = self.run_pdf2htmlEX(pdf2htmlEX_args)
self.assertIn(htmlfilename, result['output_files'], 'HTML file is not generated')
png_out_dir = os.path.join(self.cur_temp_dir, 'png_out')
os.mkdir(png_out_dir)
pngfilename_out_fullpath = os.path.join(png_out_dir, pngfilename)
pngfilename_raw_fullpath = os.path.join(self.TEST_DATA_DIR, pngfilename)
wkhtml2image_args = [self.WKHTML2IMAGE] \
+ self.DEFAULT_WKHTML2IMAGE_ARGS \
+ list(wkhtml2image_args) + [
os.path.join(self.cur_output_dir, htmlfilename),
pngfilename_out_fullpath
]
return_code = subprocess.call(list(map(str, wkhtml2image_args)))
self.assertEquals(return_code, 0, 'cannot execute ' + self.WKHTML2IMAGE)
if self.GENERATING_MODE:
shutil.copy(pngfilename_out_fullpath, pngfilename_raw_fullpath)
else:
original_img = Image.open(pngfilename_raw_fullpath)
new_img = Image.open(pngfilename_out_fullpath)
diff_img = ImageChops.difference(original_img, new_img);
if diff_img.getbbox() is not None:
if self.SAVE_TMP:
# save the diff image
# http://stackoverflow.com/questions/15721484/saving-in-png-using-pil-library-after-taking-imagechops-difference-of-two-png
diff_img.convert('RGB').save(os.path.join(png_out_dir, basefilename + '.diff.png'))
self.fail('PNG files differ')
def test_basic_text(self):
self.run_test_case('basic_text.pdf',
wkhtml2image_args=[
'--crop-x', 180,
'--crop-y', 150,
'--crop-w', 220,
'--crop-h', 260
])
def test_geneve_1564(self):
self.run_test_case('geneve_1564.pdf', wkhtml2image_args=['--height', 1100])
def test_text_visibility(self):
self.run_test_case('text_visibility.pdf', wkhtml2image_args=['--height', 1200])

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

33
test/test_local_browser.py Executable file
View File

@ -0,0 +1,33 @@
#!/usr/bin/env python
import unittest
import os
import subprocess
import shutil
from PIL import Image, ImageChops
from selenium import webdriver
from browser_tests import BrowserTests
class test_local_browser(BrowserTests, unittest.TestCase):
@classmethod
def setUpClass(cls):
super(test_local_browser, cls).setUpClass()
if not cls.GENERATING_MODE:
cls.browser = webdriver.Firefox()
cls.browser.maximize_window()
size = cls.browser.get_window_size()
assert ((size['width'] >= cls.BROWSER_WIDTH) and (size['height'] >= cls.BROWSER_HEIGHT)), 'Screen is not large enough'
cls.browser.set_window_size(cls.BROWSER_WIDTH, cls.BROWSER_HEIGHT)
@classmethod
def tearDownClass(cls):
if not cls.GENERATING_MODE:
cls.browser.quit()
super(test_local_browser, cls).tearDownClass()
def generate_image(self, html_file, png_file):
assert not self.GENERATING_MODE
self.browser.get('file://' + html_file)
self.browser.save_screenshot(png_file)

View File

@ -1,16 +1,18 @@
#!/usr/bin/env python
# Test --split-page and --page-filename
# Test output files
import unittest
import os
from test import Common
class T(Common, unittest.TestCase):
class test_output(Common, unittest.TestCase):
def run_test_case(self, input_file, expected_output_files, args=[]):
if self.GENERATING_MODE:
self.skipTest("Skipping test_output test cases in generating mode")
args = list(args)
args.insert(0, os.path.join(self.TEST_DIR, 'test_naming', input_file))
args.insert(0, os.path.join(self.TEST_DIR, 'test_output', input_file))
self.assertItemsEqual(self.run_pdf2htmlEX(args)['output_files'], expected_output_files)
def test_generate_single_html_default_name_single_page_pdf(self):