1
0
mirror of https://github.com/edgewall/genshi.git synced 2026-02-05 06:45:30 +01:00

Switch tests to pytest. (#86)

* 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.
This commit is contained in:
Simon Cross
2024-08-25 01:08:58 +02:00
committed by GitHub
parent af138557d1
commit ca08e57c90
26 changed files with 109 additions and 59 deletions

View File

@@ -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
@@ -16,10 +25,27 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
- name: Install setuptools
- name: Install genshi
run: |
pip install setuptools
pip install -e .
- name: Install testing requirements
run: |
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

View File

@@ -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">

View File

@@ -15,12 +15,12 @@ import doctest
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__':

View File

@@ -18,7 +18,7 @@ import six
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):

View File

@@ -22,7 +22,7 @@ from genshi.template import MarkupTemplate, Context
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):

View File

@@ -23,7 +23,7 @@ from genshi.core import START, END, TEXT, QName, Attrs
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>'

View File

@@ -26,20 +26,21 @@ For example, the following transformation removes the ``<title>`` element from
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
@@ -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')
@@ -213,6 +215,7 @@ class Transformer(object):
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:
@@ -238,6 +241,7 @@ class Transformer(object):
"""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)))
@@ -262,6 +266,7 @@ class Transformer(object):
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)))
@@ -282,6 +287,7 @@ class Transformer(object):
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)))
@@ -305,6 +311,7 @@ class Transformer(object):
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')
@@ -321,6 +328,7 @@ class Transformer(object):
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')
@@ -339,6 +347,7 @@ class Transformer(object):
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')
@@ -353,6 +362,7 @@ class Transformer(object):
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')
@@ -370,6 +380,7 @@ class Transformer(object):
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')
@@ -389,6 +400,7 @@ class Transformer(object):
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')
@@ -407,6 +419,7 @@ class Transformer(object):
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')
@@ -425,6 +438,7 @@ class Transformer(object):
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')
@@ -441,6 +455,7 @@ class Transformer(object):
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')
@@ -462,6 +477,7 @@ class Transformer(object):
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')
@@ -505,6 +521,7 @@ class Transformer(object):
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>',
@@ -560,6 +577,7 @@ class Transformer(object):
"""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>',
@@ -593,6 +611,7 @@ class Transformer(object):
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')
@@ -612,6 +631,7 @@ class Transformer(object):
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')
@@ -628,6 +648,7 @@ class Transformer(object):
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')
@@ -646,6 +667,7 @@ class Transformer(object):
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>',
@@ -653,12 +675,6 @@ class Transformer(object):
>>> 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.
@@ -670,6 +686,7 @@ class Transformer(object):
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')
@@ -681,6 +698,7 @@ class Transformer(object):
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)))
@@ -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>

View File

@@ -15,17 +15,17 @@ import doctest
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__':

View File

@@ -178,18 +178,18 @@ class ExpressionTestCase(unittest.TestCase):
'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({}))

View File

@@ -15,19 +15,19 @@ import unittest
def suite():
import genshi
from genshi.tests import builder, core, input, output, path, util
from genshi.tests import test_builder, test_core, test_input, test_output, test_path, test_util
from genshi.filters import tests as filters
from genshi.template import tests as template
suite = unittest.TestSuite()
suite.addTest(builder.suite())
suite.addTest(core.suite())
suite.addTest(test_builder.suite())
suite.addTest(test_core.suite())
suite.addTest(filters.suite())
suite.addTest(input.suite())
suite.addTest(output.suite())
suite.addTest(path.suite())
suite.addTest(test_input.suite())
suite.addTest(test_output.suite())
suite.addTest(test_path.suite())
suite.addTest(template.suite())
suite.addTest(util.suite())
suite.addTest(test_util.suite())
return suite
if __name__ == '__main__':

View File

@@ -18,7 +18,7 @@ from genshi import core
from genshi.core import Markup, Attrs, Namespace, QName, escape, unescape
from genshi.input import XML
from genshi.compat import StringIO, BytesIO, IS_PYTHON2
from genshi.tests.test_utils import doctest_suite
from genshi.tests.utils import doctest_suite
class StreamTestCase(unittest.TestCase):

View File

@@ -16,7 +16,7 @@ import unittest
from genshi.core import Attrs, QName, Stream
from genshi.input import XMLParser, HTMLParser, ParseError, ET
from genshi.compat import StringIO, BytesIO
from genshi.tests.test_utils import doctest_suite
from genshi.tests.utils import doctest_suite
from xml.etree import ElementTree
class XMLParserTestCase(unittest.TestCase):

View File

@@ -17,7 +17,7 @@ from genshi.core import Attrs, Markup, QName, Stream
from genshi.input import HTML, XML
from genshi.output import DocType, XMLSerializer, XHTMLSerializer, \
HTMLSerializer, EmptyTagFilter
from genshi.tests.test_utils import doctest_suite
from genshi.tests.utils import doctest_suite
class XMLSerializerTestCase(unittest.TestCase):

View File

@@ -17,7 +17,7 @@ from genshi.core import Attrs, QName
from genshi.input import XML
from genshi.path import Path, PathParser, PathSyntaxError, GenericStrategy, \
SingleStepStrategy, SimplePathStrategy
from genshi.tests.test_utils import doctest_suite
from genshi.tests.utils import doctest_suite
class FakePath(Path):

View File

@@ -14,7 +14,7 @@
import unittest
from genshi import util
from genshi.tests.test_utils import doctest_suite
from genshi.tests.utils import doctest_suite
from genshi.util import LRUCache

3
pytest.ini Normal file
View File

@@ -0,0 +1,3 @@
[pytest]
addopts = --doctest-modules
doctest_optionflags = NORMALIZE_WHITESPACE ALLOW_UNICODE

View File

@@ -1,8 +1,10 @@
[tox]
envlist = py27,py36,py37,py38,py39,py310,pypy,pypy3
envlist = py27,py36,py37,py38,py39,py310,py311,py312,pypy,pypy3
[testenv]
deps=
setuptools
pytest
setenv=
PYTHONHASHSEED=0
commands=
python setup.py test
pytest genshi