Merge pull request #283 from saimn/test-cli

Add tests for cli (build) and plugins
This commit is contained in:
Simon Conseil
2018-01-05 00:50:05 +01:00
committed by GitHub
10 changed files with 171 additions and 81 deletions

View File

@@ -52,7 +52,7 @@ def main():
resize images, create thumbnails with some options, generate html pages.
"""
pass
pass # pragma: no cover
@main.command()
@@ -199,15 +199,13 @@ def serve(destination, port, config):
settings = read_settings(config)
destination = settings.get('destination')
if not os.path.exists(destination):
sys.stderr.write("The '{}' directory doesn't exist, "
"maybe try building first?"
"\n".format(destination))
sys.stderr.write("The '{}' directory doesn't exist, maybe try "
"building first?\n".format(destination))
sys.exit(1)
else:
sys.stderr.write("The {destination} directory doesn't exist "
"and the config file ({config}) could not be "
"read."
"\n".format(destination=destination, config=config))
"and the config file ({config}) could not be read.\n"
.format(destination=destination, config=config))
sys.exit(2)
print('DESTINATION : {}'.format(destination))
@@ -224,6 +222,7 @@ def serve(destination, port, config):
except KeyboardInterrupt:
print('\nAll done!')
@main.command()
@argument('target')
@argument('keys', nargs=-1)
@@ -257,6 +256,6 @@ def set_meta(target, keys, overwrite=False):
with open(descfile, "w") as fp:
for i in range(len(keys)//2):
k,v = keys[i*2:(i+1)*2]
k, v = keys[i*2:(i+1)*2]
fp.write("{}: {}\n".format(k.capitalize(), v))
print("{} metadata key(s) written to {}".format(len(keys)//2, descfile))

View File

@@ -65,11 +65,16 @@ def init_logging(name, level=logging.INFO):
logger = logging.getLogger(name)
logger.setLevel(level)
if os.isatty(sys.stdout.fileno()) and not sys.platform.startswith('win'):
formatter = ColoredFormatter()
elif level == logging.DEBUG:
formatter = Formatter('%(levelname)s - %(message)s')
else:
try:
if os.isatty(sys.stdout.fileno()) and \
not sys.platform.startswith('win'):
formatter = ColoredFormatter()
elif level == logging.DEBUG:
formatter = Formatter('%(levelname)s - %(message)s')
else:
formatter = Formatter('%(message)s')
except Exception:
# This fails when running tests with click (test_build)
formatter = Formatter('%(message)s')
handler = logging.StreamHandler()

View File

@@ -32,7 +32,6 @@ previous/next :class:`~sigal.gallery.Media` objects.
"""
import codecs
import logging
import os
from sigal import signals
@@ -40,8 +39,6 @@ from sigal.writer import Writer
from sigal.utils import url_from_path
from sigal.pkgmeta import __url__ as sigal_link
logger = logging.getLogger(__name__)
class PageWriter(Writer):
'''A writer for writing media pages, based on writer'''

View File

@@ -60,18 +60,22 @@ def _remove_albums_with_subdirs(albums, keysToRemove, prefix=""):
for keyToRemove in keysToRemove:
for key in list(albums.keys()):
if key.startswith(prefix + keyToRemove):
# subdirs' target directories have already been created, remove them first
# subdirs' target directories have already been created,
# remove them first
try:
album = albums[key]
if album.medias:
os.rmdir(os.path.join(album.dst_path, album.settings['thumb_dir']))
os.rmdir(os.path.join(album.dst_path,
album.settings['thumb_dir']))
if album.medias and album.settings['keep_orig']:
os.rmdir(os.path.join(album.dst_path, album.settings['orig_dir']))
os.rmdir(os.path.join(album.dst_path,
album.settings['orig_dir']))
os.rmdir(album.dst_path)
except OSError:
# directory was created and populated with images in a previous run => keep it
# directory was created and populated with images in a
# previous run => keep it
pass
# now remove the album from the surrounding album/gallery

View File

@@ -2,11 +2,11 @@
"""Plugin to upload generated files to Amazon S3.
This plugin requires boto_. All generated files are uploaded to a specified S3 bucket.
When using this plugin you have to make sure that the bucket already exists and the
you have access to the S3 bucket. The access credentials are managed by boto_ and
can be given as environment variables, configuration files etc. More information
can be found on the boto_ documentation.
This plugin requires boto_. All generated files are uploaded to a specified S3
bucket. When using this plugin you have to make sure that the bucket already
exists and the you have access to the S3 bucket. The access credentials are
managed by boto_ and can be given as environment variables, configuration files
etc. More information can be found on the boto_ documentation.
.. _boto: https://pypi.python.org/pypi/boto
@@ -14,23 +14,25 @@ Settings (all settings are wrapped in ``upload_s3_options`` dict):
- ``bucket``: The to-be-used bucket for uploading.
- ``policy``: Specifying access control to the uploaded files. Possible values:
private, public-read, public-read-write, authenticated-read
- ``overwrite``: Boolean indicating if all files should be uploaded and overwritten
or if already uploaded files should be skipped.
- ``max_age``: Optional, Integer indicating the number of seconds that the cache
control should be set by default
private, public-read, public-read-write, authenticated-read
- ``overwrite``: Boolean indicating if all files should be uploaded and
overwritten or if already uploaded files should be skipped.
- ``max_age``: Optional, Integer indicating the number of seconds that the
cache control should be set by default
- ``media_max_age``: Optional, Integer indicates the number of seconds that
cache control hould be set for media files
cache control hould be set for media files
"""
import boto
import logging
import os
from sigal import signals
import boto
from boto.s3.key import Key
from click import progressbar
from sigal import signals
logger = logging.getLogger(__name__)
@@ -40,9 +42,9 @@ def upload_s3(gallery, settings=None):
# Get local files
for root, dirs, files in os.walk(gallery.settings['destination']):
for f in files:
path = os.path.join(root[len(gallery.settings['destination'])+1:], f)
path = os.path.join(root[len(gallery.settings['destination']) + 1:], f)
size = os.path.getsize(os.path.join(root, f))
upload_files += [ (path, size) ]
upload_files += [(path, size)]
# Connect to specified bucket
conn = boto.connect_s3()
@@ -54,12 +56,12 @@ def upload_s3(gallery, settings=None):
if gallery.settings['upload_s3_options']['overwrite'] == False:
# Check if file was uploaded before
key = bucket.get_key(f)
if key != None and key.size == size:
cache_metadata = generate_cache_metadata(gallery,f)
if key is not None and key.size == size:
cache_metadata = generate_cache_metadata(gallery, f)
if key.get_metadata('Cache-Control') != cache_metadata:
key.set_remote_metadata({
'Cache-Control':cache_metadata},{},True);
'Cache-Control': cache_metadata}, {}, True)
logger.debug("Skipping file %s" % (f))
else:
upload_file(gallery, bucket, f)
@@ -67,12 +69,13 @@ def upload_s3(gallery, settings=None):
# File is not available on S3 yet
upload_file(gallery, bucket, f)
def generate_cache_metadata(gallery, f):
filename, file_extension = os.path.splitext(f)
proposed_cache_control = None
if 'media_max_age' in gallery.settings['upload_s3_options'] and \
file_extension in ['.jpg','.png','.webm','.mp4']:
file_extension in ['.jpg', '.png', '.webm', '.mp4']:
proposed_cache_control = "max-age=%s" % \
gallery.settings['upload_s3_options']['media_max_age']
elif 'max_age' in gallery.settings['upload_s3_options']:
@@ -80,6 +83,7 @@ def generate_cache_metadata(gallery, f):
gallery.settings['upload_s3_options']['max_age']
return proposed_cache_control
def upload_file(gallery, bucket, f):
logger.debug("Uploading file %s" % (f))
@@ -92,7 +96,8 @@ def upload_file(gallery, bucket, f):
key.set_contents_from_filename(
os.path.join(gallery.settings['destination'], f),
policy = gallery.settings['upload_s3_options']['policy'])
policy=gallery.settings['upload_s3_options']['policy'])
def register(settings):
if settings.get('upload_s3_options'):

View File

@@ -27,21 +27,19 @@ Settings:
- ``watermark``: path to the watermark image.
- ``watermark_position``: the watermark position either 'scale' or 'tile' or
a 2-tuple giving the upper left corner, or
a 4-tuple defining the left, upper, right, and lower pixel coordinate, or
`None` (same as (0, 0)).
If a 4-tuple is given, the size of the pasted image must match the size of the region.
a 2-tuple giving the upper left corner, or
a 4-tuple defining the left, upper, right, and lower pixel coordinate, or
`None` (same as (0, 0)).
If a 4-tuple is given, the size of the pasted image must match the size of
the region.
- ``watermark_opacity``: the watermark opacity (0.0 to 1.0).
"""
import logging
from PIL import ImageDraw, Image, ImageEnhance
from PIL import Image, ImageEnhance
from sigal import signals
logger = logging.getLogger(__name__)
def reduce_opacity(im, opacity):
"""Returns an image with reduced opacity."""
@@ -76,7 +74,8 @@ def watermark(im, mark, position, opacity=1):
w = int(mark.size[0] * ratio)
h = int(mark.size[1] * ratio)
mark = mark.resize((w, h))
layer.paste(mark, ((im.size[0] - w) / 2, (im.size[1] - h) / 2))
layer.paste(mark, (int((im.size[0] - w) / 2),
int((im.size[1] - h) / 2)))
else:
layer.paste(mark, position)
# composite the watermark with the layer
@@ -84,6 +83,7 @@ def watermark(im, mark, position, opacity=1):
def add_watermark(img, settings=None):
logger = logging.getLogger(__name__)
logger.debug('Adding watermark to %r', img)
mark = Image.open(settings['watermark'])
position = settings.get('watermark_position', 'scale')
@@ -92,6 +92,7 @@ def add_watermark(img, settings=None):
def register(settings):
logger = logging.getLogger(__name__)
if settings.get('watermark'):
signals.img_resized.connect(add_watermark)
else:

View File

@@ -1,11 +1,15 @@
# -*- coding: utf-8 -*-
import blinker
import io
import logging
import os
from click.testing import CliRunner
from os.path import join
from sigal import init
from sigal import serve
from sigal import set_meta
from sigal import init, build, serve, set_meta, signals
TESTGAL = join(os.path.abspath(os.path.dirname(__file__)), 'sample')
def test_init(tmpdir):
@@ -22,6 +26,74 @@ def test_init(tmpdir):
"keep it safe.\n")
def test_build(tmpdir):
runner = CliRunner()
config_file = str(tmpdir.join('sigal.conf.py'))
tmpdir.mkdir('pictures')
tmpdir = str(tmpdir)
cwd = os.getcwd()
try:
result = runner.invoke(init, [config_file])
assert result.exit_code == 0
os.symlink(join(TESTGAL, 'watermark.png'),
join(tmpdir, 'watermark.png'))
os.symlink(join(TESTGAL, 'pictures', 'dir2', 'exo20101028-b-full.jpg'),
join(tmpdir, 'pictures', 'exo20101028-b-full.jpg'))
result = runner.invoke(build, ['-n', 1, '--debug'])
assert result.exit_code == 1
os.chdir(tmpdir)
result = runner.invoke(build, ['foo', '-n', 1, '--debug'])
assert result.exit_code == 1
result = runner.invoke(build, ['pictures', 'pictures/out',
'-n', 1, '--debug'])
assert result.exit_code == 1
with io.open(config_file) as f:
text = f.read()
text += """
theme = 'colorbox'
plugins = ['sigal.plugins.adjust', 'sigal.plugins.copyright',
'sigal.plugins.watermark', 'sigal.plugins.feeds',
'sigal.plugins.media_page' 'sigal.plugins.nomedia',
'sigal.plugins.extended_caching']
copyright = "An example copyright message"
copyright_text_font = "foobar"
watermark = "watermark.png"
watermark_position = "scale"
watermark_opacity = 0.3
"""
with io.open(config_file, 'w') as f:
f.write(text)
result = runner.invoke(build, ['pictures', 'build',
'-n', 1, '--debug'])
assert result.exit_code == 0
assert os.path.isfile(join(tmpdir, 'build', 'thumbnails',
'exo20101028-b-full.jpg'))
finally:
os.chdir(cwd)
# Reset logger
logger = logging.getLogger('sigal')
logger.handlers[:] = []
logger.setLevel(logging.INFO)
# Reset plugins
for name in dir(signals):
if not name.startswith('_'):
try:
sig = getattr(signals, name)
if isinstance(sig, blinker.Signal):
sig.receivers.clear()
except Exception:
pass
def test_serve(tmpdir):
config_file = str(tmpdir.join('sigal.conf.py'))
runner = CliRunner()
@@ -34,6 +106,7 @@ def test_serve(tmpdir):
result = runner.invoke(serve, ['-c', config_file])
assert result.exit_code == 1
def test_set_meta(tmpdir):
testdir = tmpdir.mkdir("test")
@@ -53,7 +126,8 @@ def test_set_meta(tmpdir):
result = runner.invoke(set_meta, [str(testdir), "title", "testing"])
assert result.exit_code == 2
result = runner.invoke(set_meta, [str(testdir.join("non-existant.jpg")), "title", "testing"])
result = runner.invoke(set_meta, [str(testdir.join("non-existant.jpg")),
"title", "testing"])
assert result.exit_code == 1
result = runner.invoke(set_meta, [str(testfile), "title", "testing"])

View File

@@ -225,7 +225,8 @@ def test_medias_sort(settings):
settings['medias_sort_reverse'] = False
a = Album('dir1/test2', settings, album['subdirs'], album['medias'], gal)
a.sort_medias(settings['medias_sort_attr'])
assert [im.filename for im in a.images] == ['archlinux-kiss-1024x640.png', '21.jpg', '22.jpg']
assert [im.filename for im in a.images] == [
'archlinux-kiss-1024x640.png', '21.jpg', '22.jpg']
def test_gallery(settings, tmpdir):

View File

@@ -1,27 +0,0 @@
# -*- coding:utf-8 -*-
import os
from sigal.gallery import Gallery
from sigal import init_plugins
CURRENT_DIR = os.path.dirname(__file__)
def test_nomedia_plugin(settings, tmpdir):
settings['destination'] = str(tmpdir)
if "plugins"in settings:
if not "sigal.plugins.nomedia" in settings["plugins"]:
settings['plugins'] += ["sigal.plugins.nomedia"]
else:
settings["plugins"] = ["sigal.plugins.nomedia"]
init_plugins(settings)
gal = Gallery(settings)
gal.build()
for path, dirs, files in os.walk(os.path.join(str(tmpdir), "nomedia")):
assert "ignore" not in path
for file in files:
assert "ignore" not in file

31
tests/test_plugins.py Normal file
View File

@@ -0,0 +1,31 @@
# -*- coding:utf-8 -*-
import os
from sigal.gallery import Gallery
from sigal import init_plugins
CURRENT_DIR = os.path.dirname(__file__)
def test_plugins(settings, tmpdir):
settings['destination'] = str(tmpdir)
if "sigal.plugins.nomedia" not in settings["plugins"]:
settings['plugins'] += ["sigal.plugins.nomedia"]
if "sigal.plugins.media_page" not in settings["plugins"]:
settings['plugins'] += ["sigal.plugins.media_page"]
init_plugins(settings)
gal = Gallery(settings)
gal.build()
out_html = os.path.join(settings['destination'],
'dir2', 'exo20101028-b-full.jpg.html')
assert os.path.isfile(out_html)
for path, dirs, files in os.walk(os.path.join(str(tmpdir), "nomedia")):
assert "ignore" not in path
for file in files:
assert "ignore" not in file