Skip to content

Commit

Permalink
Switch tests to pytest. (#86)
Browse files Browse the repository at this point in the history
* Move tests into modules prefixed with test_ so that pytest can locate them.

* Fix missing imports for doctests in genshi.filters.transforms.

* Add pytest configuration.

* Rename test_utils to utils so that it is not considered a test module by pytest.

* Rename test_util_x to test_util.

* Replace 'is' and 'is not' checks on ints with ones on bools to silence SyntaxWarning.

* Run tests with pytest.

* Set pytest options for CI.

* Add setuptools to tox (it supplies pkg_resources for tests.

* Install genshi before trying to run pytest.

* Add missing parenthesises to matrix pytest-extra-options value.

* Customize pytest options for Python 2.7.

* Ignore Expression deprecation warning on Python 3.13.

* Remove pypy2 and Python 3.13 from matrix so that they can be added via include.

* Add ALLOW_UNICODE flag to hopefully support Python 2.7 docstests.

* Expand astutil warning exclusion for Python 3.13.

* Expand warning exclusion for Python 3.13 further.
  • Loading branch information
hodgestar authored Aug 24, 2024
1 parent af13855 commit ca08e57
Show file tree
Hide file tree
Showing 26 changed files with 109 additions and 59 deletions.
34 changes: 30 additions & 4 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,19 @@ on: [push, pull_request]

jobs:
tests:
name: python ${{ matrix.python-version }}
runs-on: ubuntu-20.04

strategy:
matrix:
python-version: [3.6, 3.7, 3.8, 3.9, "3.10", "3.11", "3.12", "3.13.0-beta.2", pypy2, pypy3]
python-version: [3.6, 3.7, 3.8, 3.9, "3.10", "3.11", "3.12", pypy3]
pytest-extra-options: ["--strict-config -W \"ignore:pkg_resources is deprecated\""]
include:
- python-version: pypy2
pytest-extra-options: ""
- python-version: "3.13.0-beta.2"
pytest-extra-options: "--strict-config -W \"ignore:pkg_resources is deprecated\" -W ignore::DeprecationWarning"
fail-fast: false

steps:
- uses: actions/checkout@v2
Expand All @@ -16,10 +25,27 @@ jobs:
with:
python-version: ${{ matrix.python-version }}

- name: Install setuptools
- name: Install genshi
run: |
pip install -e .
- name: Install testing requirements
run: |
pip install setuptools
pip install setuptools pytest
- name: Run test suite
run: |
python setup.py test
pytest -Werror --strict-markers --verbosity=1 --color=yes ${{ matrix.pytest-extra-options }} genshi
# Above flags are:
# -Werror
# treat warnings as errors
# --strict-config
# error out if the configuration file is not parseable
# --strict-markers
# error out if a marker is used but not defined in the
# configuration file
# --verbosity=1
# turn the verbosity up so pytest prints the names of the tests
# it's currently working on
# --color=yes
# force coloured output in the terminal
2 changes: 1 addition & 1 deletion genshi/filters/i18n.py
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,7 @@ class DomainDirective(I18NDirective):
"""Implementation of the ``i18n:domain`` directive which allows choosing
another i18n domain(catalog) to translate from.
>>> from genshi.filters.tests.i18n import DummyTranslations
>>> from genshi.filters.tests.test_i18n import DummyTranslations
>>> tmpl = MarkupTemplate('''<html xmlns:i18n="http://genshi.edgewall.org/i18n">
... <p i18n:msg="">Bar</p>
... <div i18n:domain="foo">
Expand Down
6 changes: 3 additions & 3 deletions genshi/filters/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@
import unittest

def suite():
from genshi.filters.tests import test_html, i18n, transform
from genshi.filters.tests import test_html, test_i18n, test_transform
suite = unittest.TestSuite()
suite.addTest(test_html.suite())
suite.addTest(i18n.suite())
suite.addTest(test_i18n.suite())
if hasattr(doctest, 'NORMALIZE_WHITESPACE'):
suite.addTest(transform.suite())
suite.addTest(test_transform.suite())
return suite

if __name__ == '__main__':
Expand Down
2 changes: 1 addition & 1 deletion genshi/filters/tests/test_html.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from genshi.input import HTML, ParseError
from genshi.filters.html import HTMLFormFiller, HTMLSanitizer
from genshi.template import MarkupTemplate
from genshi.tests.test_utils import doctest_suite
from genshi.tests.utils import doctest_suite

class HTMLFormFillerTestCase(unittest.TestCase):

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from genshi.filters.i18n import Translator, extract
from genshi.input import HTML
from genshi.compat import IS_PYTHON2, StringIO
from genshi.tests.test_utils import doctest_suite
from genshi.tests.utils import doctest_suite


class DummyTranslations(NullTranslations):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from genshi.filters.transform import Transformer, StreamBuffer, ENTER, EXIT, \
OUTSIDE, INSIDE, ATTR, BREAK
import genshi.filters.transform
from genshi.tests.test_utils import doctest_suite
from genshi.tests.utils import doctest_suite


FOO = '<root>ROOT<foo name="foo">FOO</foo></root>'
Expand Down
47 changes: 33 additions & 14 deletions genshi/filters/transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,21 @@
the ``<head>`` of the input document:
>>> from genshi.builder import tag
>>> from genshi.input import HTML
>>> html = HTML('''<html>
... <head><title>Some Title</title></head>
... <body>
... Some <em>body</em> text.
... </body>
... <head><title>Some Title</title></head>
... <body>
... Some <em>body</em> text.
... </body>
... </html>''',
... encoding='utf-8')
>>> print(html | Transformer('body/em').map(six.text_type.upper, TEXT)
... .unwrap().wrap(tag.u))
<html>
<head><title>Some Title</title></head>
<body>
Some <u>BODY</u> text.
</body>
<head><title>Some Title</title></head>
<body>
Some <u>BODY</u> text.
</body>
</html>
The ``Transformer`` support a large number of useful transformations out of the
Expand Down Expand Up @@ -138,6 +139,7 @@ class Transformer(object):
outside a `START`/`END` container (e.g. ``text()``) will yield an `OUTSIDE`
mark.
>>> from genshi.input import HTML
>>> html = HTML('<html><head><title>Some Title</title></head>'
... '<body>Some <em>body</em> text.</body></html>',
... encoding='utf-8')
Expand Down Expand Up @@ -213,6 +215,7 @@ def apply(self, function):
As an example, here is a simple `TEXT` event upper-casing transform:
>>> from genshi.input import HTML
>>> def upper(stream):
... for mark, (kind, data, pos) in stream:
... if mark and kind is TEXT:
Expand All @@ -238,6 +241,7 @@ def select(self, path):
"""Mark events matching the given XPath expression, within the current
selection.
>>> from genshi.input import HTML
>>> html = HTML('<body>Some <em>test</em> text</body>', encoding='utf-8')
>>> print(html | Transformer().select('.//em').trace())
(None, ('START', (QName('body'), Attrs()), (None, 1, 0)))
Expand All @@ -262,6 +266,7 @@ def invert(self):
Specificaly, all marks are converted to null marks, and all null marks
are converted to OUTSIDE marks.
>>> from genshi.input import HTML
>>> html = HTML('<body>Some <em>test</em> text</body>', encoding='utf-8')
>>> print(html | Transformer('//em').invert().trace())
('OUTSIDE', ('START', (QName('body'), Attrs()), (None, 1, 0)))
Expand All @@ -282,6 +287,7 @@ def end(self):
Example:
>>> from genshi.input import HTML
>>> html = HTML('<body>Some <em>test</em> text</body>', encoding='utf-8')
>>> print(html | Transformer('//em').end().trace())
('OUTSIDE', ('START', (QName('body'), Attrs()), (None, 1, 0)))
Expand All @@ -305,6 +311,7 @@ def empty(self):
Example:
>>> from genshi.input import HTML
>>> html = HTML('<html><head><title>Some Title</title></head>'
... '<body>Some <em>body</em> text.</body></html>',
... encoding='utf-8')
Expand All @@ -321,6 +328,7 @@ def remove(self):
Example:
>>> from genshi.input import HTML
>>> html = HTML('<html><head><title>Some Title</title></head>'
... '<body>Some <em>body</em> text.</body></html>',
... encoding='utf-8')
Expand All @@ -339,6 +347,7 @@ def unwrap(self):
Example:
>>> from genshi.input import HTML
>>> html = HTML('<html><head><title>Some Title</title></head>'
... '<body>Some <em>body</em> text.</body></html>',
... encoding='utf-8')
Expand All @@ -353,6 +362,7 @@ def unwrap(self):
def wrap(self, element):
"""Wrap selection in an element.
>>> from genshi.input import HTML
>>> html = HTML('<html><head><title>Some Title</title></head>'
... '<body>Some <em>body</em> text.</body></html>',
... encoding='utf-8')
Expand All @@ -370,6 +380,7 @@ def wrap(self, element):
def replace(self, content):
"""Replace selection with content.
>>> from genshi.input import HTML
>>> html = HTML('<html><head><title>Some Title</title></head>'
... '<body>Some <em>body</em> text.</body></html>',
... encoding='utf-8')
Expand All @@ -389,6 +400,7 @@ def before(self, content):
In this example we insert the word 'emphasised' before the <em> opening
tag:
>>> from genshi.input import HTML
>>> html = HTML('<html><head><title>Some Title</title></head>'
... '<body>Some <em>body</em> text.</body></html>',
... encoding='utf-8')
Expand All @@ -407,6 +419,7 @@ def after(self, content):
Here, we insert some text after the </em> closing tag:
>>> from genshi.input import HTML
>>> html = HTML('<html><head><title>Some Title</title></head>'
... '<body>Some <em>body</em> text.</body></html>',
... encoding='utf-8')
Expand All @@ -425,6 +438,7 @@ def prepend(self, content):
Inserting some new text at the start of the <body>:
>>> from genshi.input import HTML
>>> html = HTML('<html><head><title>Some Title</title></head>'
... '<body>Some <em>body</em> text.</body></html>',
... encoding='utf-8')
Expand All @@ -441,6 +455,7 @@ def prepend(self, content):
def append(self, content):
"""Insert content before the END event of the selection.
>>> from genshi.input import HTML
>>> html = HTML('<html><head><title>Some Title</title></head>'
... '<body>Some <em>body</em> text.</body></html>',
... encoding='utf-8')
Expand All @@ -462,6 +477,7 @@ def attr(self, name, value):
If `value` evaulates to `None` the attribute will be deleted from the
element:
>>> from genshi.input import HTML
>>> html = HTML('<html><head><title>Some Title</title></head>'
... '<body>Some <em class="before">body</em> <em>text</em>.</body>'
... '</html>', encoding='utf-8')
Expand Down Expand Up @@ -505,6 +521,7 @@ def copy(self, buffer, accumulate=False):
be appended to the buffer rather than replacing it.
>>> from genshi.builder import tag
>>> from genshi.input import HTML
>>> buffer = StreamBuffer()
>>> html = HTML('<html><head><title>Some Title</title></head>'
... '<body>Some <em>body</em> text.</body></html>',
Expand Down Expand Up @@ -560,6 +577,7 @@ def cut(self, buffer, accumulate=False):
"""Copy selection into buffer and remove the selection from the stream.
>>> from genshi.builder import tag
>>> from genshi.input import HTML
>>> buffer = StreamBuffer()
>>> html = HTML('<html><head><title>Some Title</title></head>'
... '<body>Some <em>body</em> text.</body></html>',
Expand Down Expand Up @@ -593,6 +611,7 @@ def buffer(self):
For example, to move all <note> elements inside a <notes> tag at the
top of the document:
>>> from genshi.input import HTML
>>> doc = HTML('<doc><notes></notes><body>Some <note>one</note> '
... 'text <note>two</note>.</body></doc>',
... encoding='utf-8')
Expand All @@ -612,6 +631,7 @@ def filter(self, filter):
once for each contiguous block of marked events.
>>> from genshi.filters.html import HTMLSanitizer
>>> from genshi.input import HTML
>>> html = HTML('<html><body>Some text<script>alert(document.cookie)'
... '</script> and some more text</body></html>',
... encoding='utf-8')
Expand All @@ -628,6 +648,7 @@ def map(self, function, kind):
the selection.
>>> import six
>>> from genshi.input import HTML
>>> html = HTML('<html><head><title>Some Title</title></head>'
... '<body>Some <em>body</em> text.</body></html>',
... encoding='utf-8')
Expand All @@ -646,19 +667,14 @@ def substitute(self, pattern, replace, count=1):
Refer to the documentation for ``re.sub()`` for details.
>>> from genshi.input import HTML
>>> html = HTML('<html><body>Some text, some more text and '
... '<b>some bold text</b>\\n'
... '<i>some italicised text</i></body></html>',
... encoding='utf-8')
>>> print(html | Transformer('body/b').substitute('(?i)some', 'SOME'))
<html><body>Some text, some more text and <b>SOME bold text</b>
<i>some italicised text</i></body></html>
>>> tags = tag.html(tag.body('Some text, some more text and\\n',
... Markup('<b>some bold text</b>')))
>>> print(tags.generate() | Transformer('body').substitute(
... '(?i)some', 'SOME'))
<html><body>SOME text, some more text and
<b>SOME bold text</b></body></html>
:param pattern: A regular expression object or string.
:param replace: Replacement pattern.
Expand All @@ -670,6 +686,7 @@ def substitute(self, pattern, replace, count=1):
def rename(self, name):
"""Rename matching elements.
>>> from genshi.input import HTML
>>> html = HTML('<html><body>Some text, some more text and '
... '<b>some bold text</b></body></html>',
... encoding='utf-8')
Expand All @@ -681,6 +698,7 @@ def rename(self, name):
def trace(self, prefix='', fileobj=None):
"""Print events as they pass through the transform.
>>> from genshi.input import HTML
>>> html = HTML('<body>Some <em>test</em> text</body>', encoding='utf-8')
>>> print(html | Transformer('em').trace())
(None, ('START', (QName('body'), Attrs()), (None, 1, 0)))
Expand Down Expand Up @@ -1047,6 +1065,7 @@ class InjectorTransformation(object):
... yield event
... for event in stream:
... yield event
>>> from genshi.input import HTML
>>> html = HTML('<body>Some <em>test</em> text</body>', encoding='utf-8')
>>> print(html | Transformer('.//em').apply(Top('Prefix ')))
Prefix <body>Some <em>test</em> text</body>
Expand Down
20 changes: 10 additions & 10 deletions genshi/template/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@
import unittest

def suite():
from genshi.template.tests import base, directives, eval, interpolation, \
loader, markup, plugin, text
from genshi.template.tests import test_base, test_directives, test_eval, test_interpolation, \
test_loader, test_markup, test_plugin, test_text
suite = unittest.TestSuite()
suite.addTest(base.suite())
suite.addTest(directives.suite())
suite.addTest(eval.suite())
suite.addTest(interpolation.suite())
suite.addTest(loader.suite())
suite.addTest(markup.suite())
suite.addTest(plugin.suite())
suite.addTest(text.suite())
suite.addTest(test_base.suite())
suite.addTest(test_directives.suite())
suite.addTest(test_eval.suite())
suite.addTest(test_interpolation.suite())
suite.addTest(test_loader.suite())
suite.addTest(test_markup.suite())
suite.addTest(test_plugin.suite())
suite.addTest(test_text.suite())
return suite

if __name__ == '__main__':
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -178,18 +178,18 @@ def test_binop_not_contains(self):
'y': (1, 2, 3)}))

def test_binop_is(self):
self.assertEqual(True, Expression("1 is 1").evaluate({}))
self.assertEqual(True, Expression("x is y").evaluate({'x': 1, 'y': 1}))
self.assertEqual(False, Expression("1 is 2").evaluate({}))
self.assertEqual(False, Expression("x is y").evaluate({'x': 1, 'y': 2}))
self.assertEqual(True, Expression("True is True").evaluate({}))
self.assertEqual(True, Expression("x is y").evaluate({'x': True, 'y': True}))
self.assertEqual(False, Expression("True is False").evaluate({}))
self.assertEqual(False, Expression("x is y").evaluate({'x': True, 'y': False}))

def test_binop_is_not(self):
self.assertEqual(True, Expression("1 is not 2").evaluate({}))
self.assertEqual(True, Expression("x is not y").evaluate({'x': 1,
'y': 2}))
self.assertEqual(False, Expression("1 is not 1").evaluate({}))
self.assertEqual(False, Expression("x is not y").evaluate({'x': 1,
'y': 1}))
self.assertEqual(True, Expression("True is not False").evaluate({}))
self.assertEqual(True, Expression("x is not y").evaluate({'x': True,
'y': False}))
self.assertEqual(False, Expression("True is not True").evaluate({}))
self.assertEqual(False, Expression("x is not y").evaluate({'x': True,
'y': True}))

def test_boolop_and(self):
self.assertEqual(False, Expression("True and False").evaluate({}))
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit ca08e57

Please sign in to comment.