Merge pull request #283 from saimn/test-cli
Add tests for cli (build) and plugins
This commit is contained in:
@@ -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))
|
||||
|
||||
15
sigal/log.py
15
sigal/log.py
@@ -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()
|
||||
|
||||
@@ -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'''
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'):
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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"])
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
31
tests/test_plugins.py
Normal 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
|
||||
Reference in New Issue
Block a user