summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEevee (Alex Munroe) <eevee.git@veekun.com>2014-08-26 13:03:48 -0700
committerEevee (Alex Munroe) <eevee.git@veekun.com>2014-08-26 14:32:01 -0700
commite045cd72c467eacd16aaee2613ef0850e559a3ce (patch)
treefd6dec2df10907e5f2c912a8ab83f070223e4278
parent77f24e0a2e8fb394004023191c8662febd8ceda0 (diff)
downloadpyscss-e045cd72c467eacd16aaee2613ef0850e559a3ce.tar.gz
Revamp py.test's handling of test files.
Each .scss file is now its own test -- you can run them directly, with `py.test scss/tests/files/foo/bar.scss`, or use wildcards, or specify a partial directory, or whatever. Accordingly, `test_files.py` is gone, as is the `--test-file-filter` option that was so cumbersome I never actually used it. Regressions: - xfail is no longer supported. - Ruby tests are now not collected at all, rather than marked as skipped.
-rw-r--r--conftest.py110
-rw-r--r--scss/tests/conftest.py81
-rw-r--r--scss/tests/test_files.py50
3 files changed, 102 insertions, 139 deletions
diff --git a/conftest.py b/conftest.py
index 445657d..8019aa8 100644
--- a/conftest.py
+++ b/conftest.py
@@ -1,4 +1,27 @@
"""py.test plugin configuration."""
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import logging
+
+import pytest
+
+import scss
+import scss.config
+
+try:
+ import fontforge
+except ImportError:
+ fontforge = None
+
+
+# Turn on pyscss's logging
+console = logging.StreamHandler()
+logger = logging.getLogger('scss')
+logger.setLevel(logging.ERROR)
+logger.addHandler(console)
+
def pytest_addoption(parser):
"""Add options for filtering which file tests run.
@@ -7,16 +30,87 @@ def pytest_addoption(parser):
recursively look for conftest.py files until after it's parsed the command
line.
"""
- parser.addoption('--include-ruby',
+ parser.addoption(
+ '--include-ruby',
help='run tests imported from Ruby and sassc, most of which fail',
action='store_true',
dest='include_ruby',
)
- parser.addoption('--test-file-filter',
- help='comma-separated regexes to select test files',
- action='store',
- type='string',
- dest='test_file_filter',
- default=None,
- )
+
+def pytest_ignore_collect(path, config):
+ # Ruby/sassc tests don't even exist without this option
+ if path.basename in ('from_ruby', 'from-sassc'):
+ if not config.getoption('include_ruby'):
+ return True
+
+
+def pytest_collect_file(path, parent):
+ if path.ext == '.scss':
+ parts = str(path).split(path.sep)
+ # -4 tests / -3 files / -2 directory / -1 file.scss
+ if parts[-4:-2] == ['tests', 'files']:
+ return SassFile(path, parent)
+
+
+class SassFile(pytest.File):
+ def collect(self):
+ parent_name = self.fspath.dirpath().basename
+ if not fontforge and parent_name == 'fonts':
+ pytest.skip("font tests require fontforge")
+
+ yield SassItem(str(self.fspath), self)
+
+
+class SassItem(pytest.Item):
+ """A Sass test input file, collected as its own test item.
+
+ A file of the same name but with a .css extension is assumed to contain the
+ expected output.
+ """
+ _nodeid = None
+
+ @property
+ def nodeid(self):
+ # Rig the nodeid to be "directory::filename", so all the files in the
+ # same directory are treated as grouped together
+ if not self._nodeid:
+ self._nodeid = "{0}::{1}".format(
+ self.fspath.dirpath().relto(self.session.fspath),
+ self.fspath.basename,
+ )
+ return self._nodeid
+
+ def reportinfo(self):
+ return (
+ self.fspath.dirpath(),
+ None,
+ self.fspath.relto(self.session.fspath),
+ )
+
+ def runtest(self):
+ scss_file = self.fspath
+ css_file = scss_file.new(ext='css')
+
+ with scss_file.open('rb') as fh:
+ source = fh.read()
+ with css_file.open('rb') as fh:
+ # Output is Unicode, so decode this here
+ expected = fh.read().decode('utf8')
+
+ scss.config.STATIC_ROOT = str(scss_file.dirpath('static'))
+
+ compiler = scss.Scss(
+ scss_opts=dict(style='expanded'),
+ search_paths=[
+ str(scss_file.dirpath('include')),
+ str(scss_file.dirname),
+ ],
+ )
+ actual = compiler.compile(source)
+
+ # Normalize leading and trailing newlines
+ actual = actual.strip('\n')
+ expected = expected.strip('\n')
+
+ assert expected == actual
diff --git a/scss/tests/conftest.py b/scss/tests/conftest.py
deleted file mode 100644
index b2036ff..0000000
--- a/scss/tests/conftest.py
+++ /dev/null
@@ -1,81 +0,0 @@
-"""py.test plugin configuration."""
-
-import glob
-import os.path
-import re
-
-import pytest
-
-try:
- import fontforge
-except ImportError:
- fontforge = None
-
-FILES_DIR = os.path.relpath(os.path.join(os.path.dirname(__file__), 'files'))
-
-# Globals, yuck! Populated below.
-test_file_tuples = None
-test_file_ids = None
-
-
-def pytest_configure(config):
- """Scan for test files. Done here because other hooks tend to run once
- *per test*, and there's no reason to do this work more than once.
- """
- global test_file_tuples
- global test_file_ids
-
- include_ruby = config.getoption('include_ruby')
- test_file_filter = config.getoption('test_file_filter')
- if test_file_filter:
- file_filters = [
- re.compile(filt)
- for filt in test_file_filter.split(',')
- ]
- else:
- file_filters = []
-
- # Tuples are 3-tuples of the form (scss filename, css filename, pytest
- # marker). That last one is used to carry xfail/skip, and is None for
- # regular tests.
- # "ids" are just names for the tests, in a parellel list. We just use
- # relative paths to the input file.
- test_file_tuples = []
- test_file_ids = []
- for fn in glob.glob(os.path.join(FILES_DIR, '*%s*.scss' % os.sep)):
- if os.path.basename(fn)[0] == '_':
- continue
-
- relfn = os.path.relpath(fn, FILES_DIR)
- pytest_trigger = None
-
- if relfn.startswith(('from-sassc' + os.sep, 'from-ruby' + os.sep)):
- pytest_trigger = pytest.mark.skipif(
- not include_ruby, reason="skipping ruby tests by default")
- elif relfn.startswith('xfail' + os.sep):
- pytest_trigger = pytest.mark.xfail
- elif relfn.startswith('fonts' + os.sep):
- pytest_trigger = pytest.mark.skipif(
- not fontforge, reason="font tests require fontforge")
-
- if file_filters and not any(rx.search(relfn) for rx in file_filters):
- pytest_trigger = pytest.mark.skipif(
- reason="skipping due to --test-file-filter")
-
- pair = (fn, fn[:-5] + '.css')
- if pytest_trigger:
- pair = pytest_trigger(pair)
-
- test_file_tuples.append(pair)
- test_file_ids.append(fn)
-
-
-def pytest_generate_tests(metafunc):
- """Inject the test files as parametrizations.
-
- Relies on the command-line option `--test-file-filter`, set by the root
- conftest.py.
- """
-
- if 'scss_file_pair' in metafunc.fixturenames:
- metafunc.parametrize('scss_file_pair', test_file_tuples, ids=test_file_ids)
diff --git a/scss/tests/test_files.py b/scss/tests/test_files.py
deleted file mode 100644
index eaaba4c..0000000
--- a/scss/tests/test_files.py
+++ /dev/null
@@ -1,50 +0,0 @@
-"""Evaluates all the tests that live in `scss/tests/files`.
-
-A test is any file with a `.scss` extension. It'll be compiled, and the output
-will be compared to the contents of a file named `foo.css`.
-
-Currently, test files must be nested exactly one directory below `files/`.
-This limitation is completely arbitrary. Files starting with '_' are skipped.
-
-"""
-
-from __future__ import absolute_import, unicode_literals
-
-import os.path
-import logging
-
-import six
-
-import scss
-
-
-if six.PY2:
- from io import open
-
-
-console = logging.StreamHandler()
-logger = logging.getLogger('scss')
-logger.setLevel(logging.ERROR)
-logger.addHandler(console)
-
-
-def test_pair_programmatic(scss_file_pair):
- scss_fn, css_fn = scss_file_pair
-
- with open(scss_fn, 'rb') as fh:
- source = fh.read()
- with open(css_fn, 'r', encoding='utf8') as fh:
- expected = fh.read()
-
- directory, _ = os.path.split(scss_fn)
- include_dir = os.path.join(directory, 'include')
- scss.config.STATIC_ROOT = os.path.join(directory, 'static')
-
- compiler = scss.Scss(scss_opts=dict(style='expanded'), search_paths=[include_dir, directory])
- actual = compiler.compile(source)
-
- # Normalize leading and trailing newlines
- actual = actual.strip('\n')
- expected = expected.strip('\n')
-
- assert expected == actual