|
|
|
|
===================
|
|
|
|
|
Docutils_ Testing
|
|
|
|
|
===================
|
|
|
|
|
|
|
|
|
|
:Authors: Lea Wiemann <LeWiemann@gmail.com>;
|
|
|
|
|
David Goodger <goodger@python.org>
|
|
|
|
|
:Revision: $Revision: 8051 $
|
|
|
|
|
:Date: $Date: 2017-03-21 16:45:28 +0100 (Di, 21 Mär 2017) $
|
|
|
|
|
:Copyright: This document has been placed in the public domain.
|
|
|
|
|
|
|
|
|
|
.. _Docutils: http://docutils.sourceforge.net/
|
|
|
|
|
|
|
|
|
|
.. contents::
|
|
|
|
|
|
|
|
|
|
When adding new functionality (or fixing bugs), be sure to add test
|
|
|
|
|
cases to the test suite. Practise test-first programming; it's fun,
|
|
|
|
|
it's addictive, and it works!
|
|
|
|
|
|
|
|
|
|
This document describes how to run the Docutils test suite, how the
|
|
|
|
|
tests are organized and how to add new tests or modify existing tests.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Running the Test Suite
|
|
|
|
|
======================
|
|
|
|
|
|
|
|
|
|
Before checking in any changes, run the entire Docutils test suite to
|
|
|
|
|
be sure that you haven't broken anything. From a shell::
|
|
|
|
|
|
|
|
|
|
cd docutils/test
|
|
|
|
|
./alltests.py
|
|
|
|
|
|
|
|
|
|
For Python 3, the tests must be converted with 2to3. To run the tests with
|
|
|
|
|
Python 3, ``cd`` to the package's root directory and do::
|
|
|
|
|
|
|
|
|
|
python3 setup.py build
|
|
|
|
|
python3 test3/alltests.py
|
|
|
|
|
|
|
|
|
|
Python Versions
|
|
|
|
|
===============
|
|
|
|
|
|
|
|
|
|
A docutils release has a commitment to support a minimum version and
|
|
|
|
|
beyond. Before a release is cut, tests must pass in all supported python
|
|
|
|
|
versions.
|
|
|
|
|
|
|
|
|
|
The Docutils 0.10 release supports Python 2.4 or later.
|
|
|
|
|
Versions after 0.12 will drop python 2.4, supporting **2.5** and later.
|
|
|
|
|
|
|
|
|
|
Therefore, you should install python 2.5, as well as the latest Python
|
|
|
|
|
(3.4 at the time of this writing) installed and always run the tests on
|
|
|
|
|
all of them. In a pinch, the edge cases (2.5, and 3.4) should cover most
|
|
|
|
|
of it.
|
|
|
|
|
|
|
|
|
|
Good resources covering the differences between Python versions:
|
|
|
|
|
|
|
|
|
|
* `What's New in Python 2.5`__
|
|
|
|
|
* `What's New in Python 2.6`__
|
|
|
|
|
* `What's New in Python 2.7`__
|
|
|
|
|
* `What's New in Python 3.3`__
|
|
|
|
|
* `What's New in Python 3.4`__
|
|
|
|
|
* `PEP 290 - Code Migration and Modernization`__
|
|
|
|
|
|
|
|
|
|
__ http://www.python.org/doc/2.5.2/whatsnew/whatsnew25.html
|
|
|
|
|
__ http://docs.python.org/whatsnew/2.6.html
|
|
|
|
|
__ http://docs.python.org/whatsnew/2.7.html
|
|
|
|
|
__ https://docs.python.org/3/whatsnew/3.3.html
|
|
|
|
|
__ https://docs.python.org/3/whatsnew/3.4.html
|
|
|
|
|
__ http://www.python.org/peps/pep-0290.html
|
|
|
|
|
|
|
|
|
|
.. _Python Check-in Policies: http://www.python.org/dev/tools.html
|
|
|
|
|
.. _sandbox directory:
|
|
|
|
|
http://docutils.svn.sourceforge.net/svnroot/docutils/trunk/sandbox/
|
|
|
|
|
.. _nightly repository tarball:
|
|
|
|
|
http://svn.berlios.de/svndumps/docutils-repos.gz
|
|
|
|
|
|
|
|
|
|
Testing across multiple python versions
|
|
|
|
|
---------------------------------------
|
|
|
|
|
|
|
|
|
|
`pyenv`_ can be installed and configured (see `installing pyenv`_) to
|
|
|
|
|
test multiple python versions::
|
|
|
|
|
|
|
|
|
|
# assuming your system runs 2.7.x
|
|
|
|
|
pyenv install 2.6.9
|
|
|
|
|
pyenv install 3.3.6
|
|
|
|
|
pyenv install 3.4.3
|
|
|
|
|
pyenv global system 2.5.6 2.6.9 3.3.6 3.4.3
|
|
|
|
|
|
|
|
|
|
# reset your shims
|
|
|
|
|
rm -rf ~/.pyenv/shims && pyenv rehash
|
|
|
|
|
|
|
|
|
|
This will give you ``python2.6``, ``python2.7``, ``python3.3`` and
|
|
|
|
|
``python3.4``. Along with that, ``pip2.6``, ``pip2.7`` and so on.
|
|
|
|
|
|
|
|
|
|
To save time, you can use `tox`_ to test docutils on versions 2.6+. To
|
|
|
|
|
install tox, you can use ``easy_install tox`` or ``pip install tox``.
|
|
|
|
|
From shell::
|
|
|
|
|
|
|
|
|
|
cd docutils
|
|
|
|
|
tox
|
|
|
|
|
|
|
|
|
|
Note: tox and virtualenv only supports python 2.6 and onward.
|
|
|
|
|
|
|
|
|
|
.. _tox: https://tox.readthedocs.org/en/latest/
|
|
|
|
|
.. _pyenv: https://github.com/yyuu/pyenv
|
|
|
|
|
.. _installing pyenv: https://github.com/yyuu/pyenv#installation
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Unit Tests
|
|
|
|
|
==========
|
|
|
|
|
|
|
|
|
|
Unit tests test single functions or modules (i.e. whitebox testing).
|
|
|
|
|
|
|
|
|
|
If you are implementing a new feature, be sure to write a test case
|
|
|
|
|
covering its functionality. It happens very frequently that your
|
|
|
|
|
implementation (or even only a part of it) doesn't work with an older
|
|
|
|
|
(or even newer) Python version, and the only reliable way to detect
|
|
|
|
|
those cases is using tests.
|
|
|
|
|
|
|
|
|
|
Often, it's easier to write the test first and then implement the
|
|
|
|
|
functionality required to make the test pass.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Writing New Tests
|
|
|
|
|
-----------------
|
|
|
|
|
|
|
|
|
|
When writing new tests, it very often helps to see how a similar test
|
|
|
|
|
is implemented. For example, the files in the
|
|
|
|
|
``test_parsers/test_rst/`` directory all look very similar. So when
|
|
|
|
|
adding a test, you don't have to reinvent the wheel.
|
|
|
|
|
|
|
|
|
|
If there is no similar test, you can write a new test from scratch
|
|
|
|
|
using Python's ``unittest`` module. For an example, please have a
|
|
|
|
|
look at the following imaginary ``test_square.py``::
|
|
|
|
|
|
|
|
|
|
#! /usr/bin/env python
|
|
|
|
|
|
|
|
|
|
# $Id: testing.txt 8051 2017-03-21 15:45:28Z milde $
|
|
|
|
|
# Author: Your Name <your_email_address@example.org>
|
|
|
|
|
# Copyright: This module has been placed in the public domain.
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
Test module for docutils.square.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
import unittest
|
|
|
|
|
import docutils.square
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SquareTest(unittest.TestCase):
|
|
|
|
|
|
|
|
|
|
def test_square(self):
|
|
|
|
|
self.assertEqual(docutils.square.square(0), 0)
|
|
|
|
|
self.assertEqual(docutils.square.square(5), 25)
|
|
|
|
|
self.assertEqual(docutils.square.square(7), 49)
|
|
|
|
|
|
|
|
|
|
def test_square_root(self):
|
|
|
|
|
self.assertEqual(docutils.square.sqrt(49), 7)
|
|
|
|
|
self.assertEqual(docutils.square.sqrt(0), 0)
|
|
|
|
|
self.assertRaises(docutils.square.SquareRootError,
|
|
|
|
|
docutils.square.sqrt, 20)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|
unittest.main()
|
|
|
|
|
|
|
|
|
|
For more details on how to write tests, please refer to the
|
|
|
|
|
documentation of the ``unittest`` module.
|
|
|
|
|
|
|
|
|
|
.. Note::
|
|
|
|
|
|
|
|
|
|
Unit tests and functional test should generally set ::
|
|
|
|
|
|
|
|
|
|
settings_overrides['_disable_config'] = True
|
|
|
|
|
|
|
|
|
|
in order to be independent on the users local configuration.
|
|
|
|
|
|
|
|
|
|
.. _functional:
|
|
|
|
|
|
|
|
|
|
Functional Tests
|
|
|
|
|
================
|
|
|
|
|
|
|
|
|
|
The directory ``test/functional/`` contains data for functional tests.
|
|
|
|
|
|
|
|
|
|
Performing functional testing means testing the Docutils system as a
|
|
|
|
|
whole (i.e. blackbox testing).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Directory Structure
|
|
|
|
|
-------------------
|
|
|
|
|
|
|
|
|
|
+ ``functional/`` The main data directory.
|
|
|
|
|
|
|
|
|
|
+ ``input/`` The input files.
|
|
|
|
|
|
|
|
|
|
- ``some_test.txt``, for example.
|
|
|
|
|
|
|
|
|
|
+ ``output/`` The actual output.
|
|
|
|
|
|
|
|
|
|
- ``some_test.html``, for example.
|
|
|
|
|
|
|
|
|
|
+ ``expected/`` The expected output.
|
|
|
|
|
|
|
|
|
|
- ``some_test.html``, for example.
|
|
|
|
|
|
|
|
|
|
+ ``tests/`` The config files for processing the input files.
|
|
|
|
|
|
|
|
|
|
- ``some_test.py``, for example.
|
|
|
|
|
|
|
|
|
|
- ``_default.py``, the `default configuration file`_.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The Testing Process
|
|
|
|
|
-------------------
|
|
|
|
|
|
|
|
|
|
When running ``test_functional.py``, all config files in
|
|
|
|
|
``functional/tests/`` are processed. (Config files whose names begin
|
|
|
|
|
with an underscore are ignored.) The current working directory is
|
|
|
|
|
always Docutils' main test directory (``test/``).
|
|
|
|
|
|
|
|
|
|
For example, ``functional/tests/some_test.py`` could read like this::
|
|
|
|
|
|
|
|
|
|
# Source and destination file names.
|
|
|
|
|
test_source = "some_test.txt"
|
|
|
|
|
test_destination = "some_test.html"
|
|
|
|
|
|
|
|
|
|
# Keyword parameters passed to publish_file.
|
|
|
|
|
reader_name = "standalone"
|
|
|
|
|
parser_name = "rst"
|
|
|
|
|
writer_name = "html"
|
|
|
|
|
settings_overrides['output-encoding'] = 'utf-8'
|
|
|
|
|
# Relative to main ``test/`` directory.
|
|
|
|
|
settings_overrides['stylesheet_path'] = '../docutils/writers/html4css1/html4css1.css'
|
|
|
|
|
|
|
|
|
|
The two variables ``test_source`` and ``test_destination`` contain the
|
|
|
|
|
input file name (relative to ``functional/input/``) and the output
|
|
|
|
|
file name (relative to ``functional/output/`` and
|
|
|
|
|
``functional/expected/``). Note that the file names can be chosen
|
|
|
|
|
arbitrarily. However, the file names in ``functional/output/`` *must*
|
|
|
|
|
match the file names in ``functional/expected/``.
|
|
|
|
|
|
|
|
|
|
If defined, ``_test_more`` must be a function with the following
|
|
|
|
|
signature::
|
|
|
|
|
|
|
|
|
|
def _test_more(expected_dir, output_dir, test_case, parameters):
|
|
|
|
|
|
|
|
|
|
This function is called from the test case to perform tests beyond the
|
|
|
|
|
simple comparison of expected and actual output files.
|
|
|
|
|
|
|
|
|
|
``test_source`` and ``test_destination`` are removed from the
|
|
|
|
|
namespace, as are all variables whose names begin with an underscore
|
|
|
|
|
("_"). The remaining names are passed as keyword arguments to
|
|
|
|
|
``docutils.core.publish_file``, so you can set reader, parser, writer
|
|
|
|
|
and anything else you want to configure. Note that
|
|
|
|
|
``settings_overrides`` is already initialized as a dictionary *before*
|
|
|
|
|
the execution of the config file.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Creating New Tests
|
|
|
|
|
------------------
|
|
|
|
|
|
|
|
|
|
In order to create a new test, put the input test file into
|
|
|
|
|
``functional/input/``. Then create a config file in
|
|
|
|
|
``functional/tests/`` which sets at least input and output file names,
|
|
|
|
|
reader, parser and writer.
|
|
|
|
|
|
|
|
|
|
Now run ``test_functional.py``. The test will fail, of course,
|
|
|
|
|
because you do not have an expected output yet. However, an output
|
|
|
|
|
file will have been generated in ``functional/output/``. Check this
|
|
|
|
|
output file for validity and correctness. Then copy the file to
|
|
|
|
|
``functional/expected/``.
|
|
|
|
|
|
|
|
|
|
If you rerun ``test_functional.py`` now, it should pass.
|
|
|
|
|
|
|
|
|
|
If you run ``test_functional.py`` later and the actual output doesn't
|
|
|
|
|
match the expected output anymore, the test will fail.
|
|
|
|
|
|
|
|
|
|
If this is the case and you made an intentional change, check the
|
|
|
|
|
actual output for validity and correctness, copy it to
|
|
|
|
|
``functional/expected/`` (overwriting the old expected output), and
|
|
|
|
|
commit the change.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.. _default configuration file:
|
|
|
|
|
|
|
|
|
|
The Default Configuration File
|
|
|
|
|
------------------------------
|
|
|
|
|
|
|
|
|
|
The file ``functional/tests/_default.py`` contains default settings.
|
|
|
|
|
It is executed just before the actual configuration files, which has
|
|
|
|
|
the same effect as if the contents of ``_default.py`` were prepended
|
|
|
|
|
to every configuration file.
|