Add Tests (#13)

Basic functional tests that cover 90% of the usecases. 
Doesn't cover zoomlevel, remote fetch yet.
This commit is contained in:
Nemo 2021-07-04 12:57:18 +05:30 committed by GitHub
parent af4752bee1
commit cc2a58bddc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 140 additions and 36 deletions

29
.github/workflows/tests.yml vendored Normal file
View File

@ -0,0 +1,29 @@
name: Run Tests
on: push
jobs:
all:
runs-on: ubuntu-latest
strategy:
matrix:
python: ["3.6", "3.7", "3.8", "3.9"]
env:
PYTHON_VERSION: ${{matrix.python}}
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{matrix.python}}
uses: actions/setup-python@v2
with:
python-version: ${{matrix.python}}
- name: Install deps
run: |
python -m pip install --upgrade pip
pip install -e .[testing]
- name: Run pytest
run: |
pytest --cache-clear --cov=./ --cov-report=xml --cov-report=html
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage.xml
env_vars: RUNNER_OS,PYTHON_VERSION,CI,GITHUB_SHA,RUNNER_OS,GITHUB_RUN_ID

View File

@ -82,9 +82,9 @@ console_scripts =
# in order to write a coverage file that can be read by Jenkins.
# CAUTION: --cov flags may prohibit setting breakpoints while debugging.
# Comment those flags to avoid this py.test issue.
addopts =
--cov pystitcher --cov-report term-missing
--verbose
addopts = --verbose
# --cov pystitcher --cov-report term-missing
norecursedirs =
dist
build

View File

@ -1,5 +1,6 @@
existing_bookmarks: remove
author: Wiki, the Cat
title: Super Jelly Book
subject: A book about adventures of Wiki, the cat.
keywords: wiki,potato,jelly
# Super Potato Book

View File

@ -1,5 +1,4 @@
existing_bookmarks: flatten
title: Super Jelly Book
# Super Potato Book

10
tests/book-headings.md Normal file
View File

@ -0,0 +1,10 @@
# Heading 1
[Part 1](1.pdf)
## Heading 2
[Part 2](1.pdf)
### Heading 3
[Part 3](1.pdf)

View File

@ -1,7 +1,4 @@
existing_bookmarks: remove
author: Wiki, the Cat
subject: A book about adventures of Wiki, the cat.
keywords: wiki,potato,jelly
existing_bookmarks: keep
# Super Potato Book
# Volume 1

View File

@ -1,7 +1,4 @@
existing_bookmarks: remove
author: Wiki, the Cat
subject: A book about adventures of Wiki, the cat.
keywords: wiki,potato,jelly
# Super Potato Book
# Volume 1

96
tests/test_integration.py Normal file
View File

@ -0,0 +1,96 @@
import os
import io
import PyPDF3
from pystitcher.stitcher import Stitcher
from pystitcher import __version__
import pytest
from contextlib import redirect_stdout
ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) + "/../"
"""
Fixtures for the integration tests. Each test is a tuple consisting of 4 things:
- input name (used as book-{name}.md)
- total expected page count
- A dictionary of expected metadata. Leave empty if nothing is set
- A flattened list of expected bookmarks, with each bookmark as a tuple containing:
- Title
- Destination Page Number
- Bookmark Level (default = 0)
Each of the above 4 is passed to test_book as an argument
"""
TEST_DATA = [
("clean",6, {'Author': 'Wiki, the Cat', 'Title': 'Super Jelly Book', 'Subject': 'A book about adventures of Wiki, the cat.', 'Keywords': 'wiki,potato,jelly'}, [('Super Potato Book', 0, 0), ('Volume 1', 0, 0), ('Part 1', 0, 1), ('Volume 2', 3, 0), ('Part 2', 3, 1)]),
("keep",6, {'Title': 'Super Potato Book'}, [('Super Potato Book', 0, 0), ('Volume 1', 0, 0), ('Part 1', 0, 1), ('Chapter 1', 0, 2), ('Chapter 2', 1, 2), ('Scene 1', 1, 3), ('Scene 2', 2, 3), ('Volume 2', 3, 0), ('Part 3', 3, 1), ('Chapter 3', 3, 2), ('Chapter 4', 4, 2), ('Scene 3', 4, 3), ('Scene 4', 5, 3)]),
("flatten", 6, {}, [('Super Potato Book', 0, 0), ('Volume 1', 0, 0), ('Part 1', 0, 1), ('Chapter 1', 0, 2), ('Chapter 2', 1, 2), ('Scene 1', 1, 2), ('Scene 2', 2, 2), ('Volume 2', 3, 0), ('Part 3', 3, 1), ('Chapter 3', 3, 2), ('Chapter 4', 4, 2), ('Scene 3', 4, 2), ('Scene 4', 5, 2)]),
("rotate", 9, {}, [('Super Potato Book', 0, 0), ('Volume 1', 0, 0), ('Part 1', 0, 1), ('Volume 2', 3, 0), ('Part 2', 3, 1), ('Volume 3', 6, 0), ('Part 3', 6, 1)]),
("min",3, {}, [('Part 1', 0, 0), ('Chapter 1', 0, 1), ('Chapter 2', 1, 1), ('Scene 1', 1, 2), ('Scene 2', 2, 2)]),
("page-select", 9, {}, [('Super Potato Book', 0, 0), ('Volume 1', 0, 0), ('Part 1', 0, 1), ('Chapter 1', 0, 2), ('Chapter 2', 1, 2), ('Scene 1', 1, 3), ('Volume 2', 2, 0), ('Part 2', 2, 1), ('Scene 2', 2, 2), ('Chapter 3', 2, 2), ('Chapter 4', 3, 2), ('Scene 3', 3, 3), ('Volume 3', 4, 0), ('Part 3', 4, 1), ('Scene 4', 4, 2), ('Chapter 1', 4, 2), ('Chapter 2', 5, 2), ('Scene 1', 5, 3), ('Volume 4', 6, 0), ('Part 4', 6, 1), ('Scene 2', 6, 2), ('Chapter 3', 6, 2), ('Chapter 4', 7, 2), ('Scene 3', 7, 3), ('Scene 4', 8, 3)]),
("headings", 9, {'Title': 'Heading 1'}, [('Heading 1', 0, 0), ('Part 1', 0, 1), ('Heading 2', 3, 1), ('Part 2', 3, 2), ('Heading 3', 6, 2), ('Part 3', 6, 3)])
]
def pdf_name(name):
return "tests/%s.pdf" % name
def render(name, cleanup=True):
input_file = open("tests/book-%s.md" % name, 'r')
output_file = "%s.pdf" % name
stitcher = Stitcher(input_file)
stitcher.generate(output_file, cleanup)
# Switch back to main directory
os.chdir(ROOT_DIR)
return pdf_name(name)
def flatten_bookmarks(bookmarks, level=0):
"""Given a list, possibly nested to any level, return it flattened."""
output = []
for destination in bookmarks:
if type(destination) == type([]):
output.extend(flatten_bookmarks(destination, level+1))
else:
output.append((destination, level))
return output
def get_all_bookmarks(pdf):
""" Returns a list of all bookmarks with title, page number, and level in a PDF file"""
bookmarks = flatten_bookmarks(pdf.getOutlines())
return [(d[0]['/Title'], pdf.getDestinationPageNumber(d[0]), d[1]) for d in bookmarks]
@pytest.mark.parametrize("name,pages,metadata,bookmarks", TEST_DATA)
def test_book(name, pages, metadata, bookmarks):
output_file = render(name)
pdf = PyPDF3.PdfFileReader(output_file)
assert pages == pdf.getNumPages()
assert bookmarks == get_all_bookmarks(pdf)
info = pdf.getDocumentInfo()
identity = "pystitcher/%s" % __version__
assert identity == info['/Producer']
assert identity == info['/Creator']
for key in metadata:
assert info["/%s" % key] == metadata[key]
def test_rotation():
""" Validates the book-rotate.pdf with pages rotated."""
output_file = render("rotate")
pdf = PyPDF3.PdfFileReader(output_file)
# Note that inputs to getPage are 0-indexed
assert 90 == pdf.getPage(3)['/Rotate']
assert 90 == pdf.getPage(4)['/Rotate']
assert 90 == pdf.getPage(5)['/Rotate']
assert 180 == pdf.getPage(6)['/Rotate']
assert 180 == pdf.getPage(7)['/Rotate']
assert 180 == pdf.getPage(8)['/Rotate']
def test_cleanup_disabled():
f = io.StringIO()
with redirect_stdout(f):
output_file = render("min", False)
temp_filename = f.getvalue()[29:-1]
assert os.path.exists(temp_filename)
pdf = PyPDF3.PdfFileReader(temp_filename)
assert 3 == pdf.getNumPages()
assert [] == pdf.getOutlines()
# Clean it up manually to avoid cluttering
os.remove(temp_filename)

View File

@ -1,25 +0,0 @@
import pytest
from pystitcher.skeleton import fib, main
__author__ = "Nemo"
__copyright__ = "Nemo"
__license__ = "MIT"
def test_fib():
"""API Tests"""
assert fib(1) == 1
assert fib(2) == 1
assert fib(7) == 13
with pytest.raises(AssertionError):
fib(-10)
def test_main(capsys):
"""CLI Tests"""
# capsys is a pytest fixture that allows asserts agains stdout/stderr
# https://docs.pytest.org/en/stable/capture.html
main(["7"])
captured = capsys.readouterr()
assert "The 7-th Fibonacci number is 13" in captured.out