diff options
-rw-r--r-- | conftest.py | 110 | ||||
-rw-r--r-- | scss/tests/conftest.py | 81 | ||||
-rw-r--r-- | scss/tests/test_files.py | 50 |
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 |