Merge branch 'offline-tests'
This commit is contained in:
commit
5b5cf6ad48
18 changed files with 104 additions and 56 deletions
|
@ -322,10 +322,10 @@ def get_tagged_scraper_name(scraperobj, limit=None, reasons=None):
|
||||||
return name + suffix
|
return name + suffix
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main(args=None):
|
||||||
"""Parse options and execute commands."""
|
"""Parse options and execute commands."""
|
||||||
try:
|
try:
|
||||||
options = setup_options().parse_args()
|
options = setup_options().parse_args(args=args)
|
||||||
options.basepath = os.path.expanduser(options.basepath)
|
options.basepath = os.path.expanduser(options.basepath)
|
||||||
res = run(options)
|
res = run(options)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
|
|
|
@ -177,6 +177,7 @@ def getComics(options):
|
||||||
finish()
|
finish()
|
||||||
finally:
|
finally:
|
||||||
events.getHandler().end()
|
events.getHandler().end()
|
||||||
|
events.clear_handlers()
|
||||||
return errors
|
return errors
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -328,6 +328,7 @@ def getHandlerNames():
|
||||||
return sorted(_handler_classes.keys())
|
return sorted(_handler_classes.keys())
|
||||||
|
|
||||||
|
|
||||||
|
# FIXME: Hidden singleton :(
|
||||||
_handlers = []
|
_handlers = []
|
||||||
|
|
||||||
|
|
||||||
|
@ -338,6 +339,10 @@ def addHandler(name, basepath=None, baseurl=None, allowDownscale=False):
|
||||||
_handlers.append(_handler_classes[name](basepath, baseurl, allowDownscale))
|
_handlers.append(_handler_classes[name](basepath, baseurl, allowDownscale))
|
||||||
|
|
||||||
|
|
||||||
|
def clear_handlers():
|
||||||
|
del _handlers[:]
|
||||||
|
|
||||||
|
|
||||||
class MultiHandler(object):
|
class MultiHandler(object):
|
||||||
"""Encapsulate a list of handlers."""
|
"""Encapsulate a list of handlers."""
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ from ..helpers import bounceStarter
|
||||||
|
|
||||||
class Xkcd(_ParserScraper):
|
class Xkcd(_ParserScraper):
|
||||||
name = 'xkcd'
|
name = 'xkcd'
|
||||||
url = 'http://xkcd.com/'
|
url = 'https://xkcd.com/'
|
||||||
starter = bounceStarter
|
starter = bounceStarter
|
||||||
stripUrl = url + '%s/'
|
stripUrl = url + '%s/'
|
||||||
firstStripUrl = stripUrl % '1'
|
firstStripUrl = stripUrl % '1'
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright (C) 2013-2014 Bastian Kleineidam
|
|
||||||
# Copyright (C) 2015-2017 Tobias Gruetzmacher
|
|
||||||
|
|
||||||
from __future__ import absolute_import, division, print_function
|
|
||||||
|
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
basedir = os.path.dirname(__file__)
|
|
||||||
dosage_cmd = os.path.join(os.path.dirname(basedir), "dosage")
|
|
||||||
|
|
||||||
|
|
||||||
def run(cmd, verbosity=0, **kwargs):
|
|
||||||
"""Run command without error checking.
|
|
||||||
@return: command return code"""
|
|
||||||
if kwargs.get("shell"):
|
|
||||||
# for shell calls the command must be a string
|
|
||||||
cmd = " ".join(cmd)
|
|
||||||
return subprocess.call(cmd, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
def run_checked(cmd, ret_ok=(0,), **kwargs):
|
|
||||||
"""Run command and raise OSError on error."""
|
|
||||||
retcode = run(cmd, **kwargs)
|
|
||||||
if retcode not in ret_ok:
|
|
||||||
msg = "Command `%s' returned non-zero exit status %d" % (cmd, retcode)
|
|
||||||
raise OSError(msg)
|
|
||||||
return retcode
|
|
52
tests/httpmocks.py
Normal file
52
tests/httpmocks.py
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2017 Tobias Gruetzmacher
|
||||||
|
|
||||||
|
from __future__ import absolute_import, division, print_function
|
||||||
|
|
||||||
|
import gzip
|
||||||
|
import os.path
|
||||||
|
import re
|
||||||
|
|
||||||
|
try:
|
||||||
|
from functools import lru_cache
|
||||||
|
except ImportError:
|
||||||
|
from backports.functools_lru_cache import lru_cache
|
||||||
|
|
||||||
|
from responses import add, GET, POST
|
||||||
|
|
||||||
|
|
||||||
|
_basedir = os.path.dirname(__file__)
|
||||||
|
|
||||||
|
|
||||||
|
def _file(name):
|
||||||
|
return os.path.join(_basedir, 'responses', name)
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache()
|
||||||
|
def _content(name):
|
||||||
|
with gzip.open(_file(name + '.html.gz'), 'r') as f:
|
||||||
|
return f.read()
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache()
|
||||||
|
def _img():
|
||||||
|
with open(_file('empty.png'), 'rb') as f:
|
||||||
|
return f.read()
|
||||||
|
|
||||||
|
|
||||||
|
def xkcd():
|
||||||
|
add(GET, 'https://xkcd.com/', _content('xkcd-1899'))
|
||||||
|
for page in (302, 303, 1898, 1899):
|
||||||
|
add(GET, 'https://xkcd.com/%i/' % page, _content('xkcd-%i' % page))
|
||||||
|
add(GET, re.compile(r'https://imgs\.xkcd\.com/.*\.png'), _img(), content_type='image/png')
|
||||||
|
|
||||||
|
|
||||||
|
def bloomingfaeries():
|
||||||
|
add(GET, 'http://www.bloomingfaeries.com/', _content('bf-home'))
|
||||||
|
add(GET, 'http://www.bloomingfaeries.com/comic/public/bloomin-faeries-405/', _content('bf-405'))
|
||||||
|
|
||||||
|
add(GET, re.compile(r'http://www\.bloomingfaeries\.com/.*\.jpg'), _img(), content_type='image/jpeg')
|
||||||
|
|
||||||
|
|
||||||
|
def vote():
|
||||||
|
add(POST, 'http://gaecounter.appspot.com/count/', 'no')
|
BIN
tests/responses/bf-405.html.gz
Normal file
BIN
tests/responses/bf-405.html.gz
Normal file
Binary file not shown.
BIN
tests/responses/bf-home.html.gz
Normal file
BIN
tests/responses/bf-home.html.gz
Normal file
Binary file not shown.
BIN
tests/responses/empty.png
Normal file
BIN
tests/responses/empty.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 67 B |
BIN
tests/responses/xkcd-1898.html.gz
Normal file
BIN
tests/responses/xkcd-1898.html.gz
Normal file
Binary file not shown.
BIN
tests/responses/xkcd-1899.html.gz
Normal file
BIN
tests/responses/xkcd-1899.html.gz
Normal file
Binary file not shown.
BIN
tests/responses/xkcd-302.html.gz
Normal file
BIN
tests/responses/xkcd-302.html.gz
Normal file
Binary file not shown.
BIN
tests/responses/xkcd-303.html.gz
Normal file
BIN
tests/responses/xkcd-303.html.gz
Normal file
Binary file not shown.
|
@ -1,19 +1,28 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright (C) 2004-2008 Tristan Seligmann and Jonathan Jacobs
|
# Copyright (C) 2004-2008 Tristan Seligmann and Jonathan Jacobs
|
||||||
# Copyright (C) 2012-2014 Bastian Kleineidam
|
# Copyright (C) 2012-2014 Bastian Kleineidam
|
||||||
# Copyright (C) 2015-2016 Tobias Gruetzmacher
|
# Copyright (C) 2015-2017 Tobias Gruetzmacher
|
||||||
|
|
||||||
from __future__ import absolute_import, division, print_function
|
from __future__ import absolute_import, division, print_function
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import sys
|
import responses
|
||||||
|
|
||||||
from . import dosage_cmd, run_checked
|
import dosagelib.cmd
|
||||||
|
import httpmocks
|
||||||
|
|
||||||
|
|
||||||
def run_with_options(options, cmd=dosage_cmd):
|
def cmd(*options):
|
||||||
"""Run dosage with given options."""
|
"""'Fake' run dosage with given options."""
|
||||||
run_checked([sys.executable, cmd, '--allow-multiple'] + options)
|
return dosagelib.cmd.main(('--allow-multiple',) + options)
|
||||||
|
|
||||||
|
|
||||||
|
def cmd_ok(*options):
|
||||||
|
assert cmd(*options) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def cmd_err(*options):
|
||||||
|
assert cmd(*options) == 1
|
||||||
|
|
||||||
|
|
||||||
class TestDosage(object):
|
class TestDosage(object):
|
||||||
|
@ -21,38 +30,43 @@ class TestDosage(object):
|
||||||
|
|
||||||
def test_list_comics(self):
|
def test_list_comics(self):
|
||||||
for option in ("-l", "--list", "--singlelist"):
|
for option in ("-l", "--list", "--singlelist"):
|
||||||
run_with_options([option])
|
cmd_ok(option)
|
||||||
|
|
||||||
def test_display_version(self):
|
def test_display_version(self):
|
||||||
run_with_options(["--version"])
|
cmd_ok("--version")
|
||||||
|
|
||||||
def test_display_help(self):
|
def test_display_help(self):
|
||||||
for option in ("-h", "--help"):
|
for option in ("-h", "--help"):
|
||||||
run_with_options([option])
|
with pytest.raises(SystemExit):
|
||||||
|
cmd(option)
|
||||||
|
|
||||||
def test_module_help(self):
|
def test_module_help(self):
|
||||||
run_with_options(["-m", "xkcd"])
|
cmd_ok("-m", "xkcd")
|
||||||
|
|
||||||
def test_no_comics_specified(self):
|
def test_no_comics_specified(self):
|
||||||
with pytest.raises(OSError):
|
cmd_err()
|
||||||
run_with_options([])
|
|
||||||
|
|
||||||
def test_unknown_option(self):
|
def test_unknown_option(self):
|
||||||
with pytest.raises(OSError):
|
with pytest.raises(SystemExit):
|
||||||
run_with_options(['--imadoofus'])
|
cmd('--imadoofus')
|
||||||
|
|
||||||
def test_multiple_comics_match(self):
|
def test_multiple_comics_match(self):
|
||||||
with pytest.raises(OSError):
|
cmd_err('Garfield')
|
||||||
run_with_options(['Garfield'])
|
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
def test_fetch_html_and_rss_json(self, tmpdir):
|
def test_fetch_html_and_rss_json(self, tmpdir):
|
||||||
run_with_options(["-n", "2", "-v", "-b", str(tmpdir), "-o", "html",
|
httpmocks.xkcd()
|
||||||
"-o", "rss", "-o", "json", "xkcd"])
|
cmd_ok("-n", "2", "-v", "-b", str(tmpdir), "-o", "html", "-o", "rss",
|
||||||
|
"-o", "json", "xkcd")
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
def test_fetch_html_and_rss_2(self, tmpdir):
|
def test_fetch_html_and_rss_2(self, tmpdir):
|
||||||
run_with_options(["--numstrips", "2", "--baseurl", "bla",
|
httpmocks.bloomingfaeries()
|
||||||
"--basepath", str(tmpdir), "--output", "rss",
|
cmd_ok("--numstrips", "2", "--baseurl", "bla", "--basepath",
|
||||||
"--output", "html", "--adult", "BloomingFaeries"])
|
str(tmpdir), "--output", "rss", "--output", "html", "--adult",
|
||||||
|
"BloomingFaeries")
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
def test_fetch_indexed(self, tmpdir):
|
def test_fetch_indexed(self, tmpdir):
|
||||||
run_with_options(["-n", "2", "-v", "-b", str(tmpdir), "xkcd:303"])
|
httpmocks.xkcd()
|
||||||
|
cmd_ok("-n", "2", "-v", "-b", str(tmpdir), "xkcd:303")
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright (C) 2004-2008 Tristan Seligmann and Jonathan Jacobs
|
# Copyright (C) 2004-2008 Tristan Seligmann and Jonathan Jacobs
|
||||||
# Copyright (C) 2012-2014 Bastian Kleineidam
|
# Copyright (C) 2012-2014 Bastian Kleineidam
|
||||||
# Copyright (C) 2015-2016 Tobias Gruetzmacher
|
# Copyright (C) 2015-2017 Tobias Gruetzmacher
|
||||||
|
|
||||||
from __future__ import absolute_import, division, print_function
|
from __future__ import absolute_import, division, print_function
|
||||||
|
|
||||||
|
import responses
|
||||||
|
|
||||||
from dosagelib import scraper
|
from dosagelib import scraper
|
||||||
|
import httpmocks
|
||||||
|
|
||||||
|
|
||||||
class ATestScraper(scraper._BasicScraper):
|
class ATestScraper(scraper._BasicScraper):
|
||||||
|
@ -14,6 +17,8 @@ class ATestScraper(scraper._BasicScraper):
|
||||||
|
|
||||||
class TestVote(object):
|
class TestVote(object):
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
def test_vote(self):
|
def test_vote(self):
|
||||||
|
httpmocks.vote()
|
||||||
answer = ATestScraper('Test_Test').vote()
|
answer = ATestScraper('Test_Test').vote()
|
||||||
assert answer in ('counted', 'no'), 'invalid answer %r' % answer
|
assert answer in ('counted', 'no'), 'invalid answer %r' % answer
|
||||||
|
|
2
tox.ini
2
tox.ini
|
@ -3,7 +3,7 @@ envlist = py27, py35, py36, flake8
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
commands =
|
commands =
|
||||||
{envbindir}/py.test --cov=dosage --cov=dosagelib --tb=short -n4 \
|
{envbindir}/py.test --cov=dosagelib --tb=short -n4 \
|
||||||
--cov-report=xml:{toxworkdir}/cov-{envname}.xml --cov-report=term \
|
--cov-report=xml:{toxworkdir}/cov-{envname}.xml --cov-report=term \
|
||||||
--junitxml={toxworkdir}/junit-{envname}.xml {posargs}
|
--junitxml={toxworkdir}/junit-{envname}.xml {posargs}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue