Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: Xion/pyqcy
base: 52a4253a0b
...
head fork: Xion/pyqcy
compare: f73e1a5120
Checking mergeability… Don't worry, you can still create the pull request.
  • 4 commits
  • 10 files changed
  • 0 commit comments
  • 1 contributor
View
89 docs/arbitraries.rst
@@ -1,16 +1,71 @@
Using generators
================
+.. currentmodule:: pyqcy.arbitraries
+
+To provide test data for your properties, *pyqcy* has a set of generators
+for all common types and use cases, including Python's
+:ref:`scalar types <scalar-arbitraries>`
+and :ref:`collections <collection-arbitraries>`. It is also easy
+to :ref:`combine <arbitrary-combinators>` several generators into
+one - up to creating :ref:`complex data structures <data-arbitrary>`
+on the fly.
+
+Still, if those are not enough, you can always define your own generator.
+This is especially handy for custom classes, as it enables you to write
+properties that should be true for their instances.
+To create a custom generator, simply define a function that returns
+an appropriate random object and decorate it with the :func:`arbitrary`
+decorator::
+
+ from pyqcy import *
+
+ @arbitrary(MyClass)
+ def my_class():
+ obj = MyClass()
+ obj.some_field = next(int_(min=0, max=1024))
+ obj.other_field = next(str_(max_length=64))
+ return obj
+
+Now you can write properties which use the new generator::
+
+ @qc
+ def forbs_correctly(obj=MyClass):
+ assert obj.forb() >= obj.some_field * len(obj.other_field)
+
+Because we have passed a type argument to :func:`arbitrary`,
+we can use our class name (`MyClass`) in place of generator name
+(`my_class`) - although the latter is of course still possible.
+
+.. autofunction:: arbitrary(type_=None)
+
Built-in types
**************
.. currentmodule:: pyqcy.arbitraries.standard
+Most Python types are conveniently supported by *pyqcy*
+and generators for them are readily available. They should
+cover a vast majority of typical use cases.
+
+
+.. _scalar-arbitraries:
Scalar types
------------
+Numeric and string types have parametrized generators that allow
+for setting desired range of produces values. But if we are fine
+with the defaults, we can simply use the types directly, as seen
+in this example:
+
+.. code-block:: python
+
+ @qc
+ def vec2d_length_is_positive(x=float, y=float):
+ return vec2d_len(x, y) >= 0.0
+
.. autofunction:: int_(min, max)
.. autofunction:: float_(min, max)
@@ -20,9 +75,15 @@ Scalar types
.. autofunction:: str_(of, min_length, max_length)
+
Tuples
------
+Tuples can be produced by combining several generators together
+through :func:`tuple_` function. There are also handy shortcuts
+for pairs, triplers and quadruples that consists of values
+from the same source.
+
.. autofunction:: tuple_(*generators, of, n)
.. autofunction:: two(of)
@@ -32,19 +93,36 @@ Tuples
.. autofunction:: four(of)
+.. _collection-arbitraries:
+
Collections
-----------
+Lists and dictionaries can be generated by giving their minimum
+and maximum size, as well as a generator for their elements.
+For dictionaries, you can either specify a separate generators
+for keys and values, or a single generator that outputs
+2-element tuples.
+
.. autofunction:: list_(of, min_length, max_length)
.. autofunction:: dict_(keys, values, items, min_length, max_length)
+.. _arbitrary-combinators:
+
Combinators
***********
.. currentmodule:: pyqcy.arbitraries.combinators
+If you want to have a generator that produces values of more than one type,
+use the simple :func:`one_of` function or the more sophisticated
+:func:`frequency` combinator.
+
+For a simpler task of always choosing a value from a predefined
+set of objects, the :func:`elements` function will come handy.
+
.. autofunction:: one_of(*generators)
.. autofunction:: frequency(*distribution)
@@ -52,13 +130,24 @@ Combinators
.. autofunction:: elements(*list)
+.. _data-arbitrary:
+
Data structures
---------------
+For testing higher level code, it is often required to prepare more complex
+input data and not just simple, uniform collections of elements. Even then,
+it can be possible to avoid writing a custom generator if we use the
+:func:`data` function.
+
.. autofunction:: data
Applying functions
------------------
+Yet another way of combining generators is to use them as building blocks
+for whole object *pipelines*. This is possible thanks to :func:`apply`
+combinator.
+
.. autofunction:: apply
View
7 docs/conf.py
@@ -20,6 +20,8 @@
# -- General configuration -----------------------------------------------------
+import pyqcy
+
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
@@ -48,9 +50,10 @@
# built documents.
#
# The short X.Y version.
-version = '0.4'
+import re
+version = re.search(r"\d+\.\d+", pyqcy.__version__).group()
# The full version, including alpha/beta/rc tags.
-release = '0.4'
+release = pyqcy.__version__
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
View
2  docs/index.rst
@@ -61,5 +61,5 @@ Learn more
Defining properties <properties>
Using generators <arbitraries>
- Gathering statistics <statistics>
Running the tests <running>
+ Gathering statistics <statistics>
View
12 docs/statistics.rst
@@ -1,13 +1,21 @@
Gathering statistics
====================
+.. currentmodule:: pyqcy.statistics
+
+As your tests :doc:`are ran <running>`, you may want to gain some insight
+into what test cases are actually generated in order to verify your properties.
+Usually, however, there will be hundreds or thousands of them, so you
+certainly don't want to wade through them all.
+
+To consolidate this data into more useful information, *pyqcy* provides you
+with statistical functions: :func:`collect` and `classify`.
+
.. warning::
All statistical functions described below must be `yield` from within
test properties to be recorded.
-.. currentmodule:: pyqcy.statistics
-
.. autofunction:: collect
.. autofunction:: classify
View
5 pyqcy/__init__.py
@@ -1,6 +1,11 @@
"""
pyqcy :: QuickCheck-like testing framework for Python
"""
+__version__ = "0.3.1"
+__author__ = "Karol Kuczmarski"
+__license__ = "BSD"
+
+
from .arbitraries import *
from .properties import *
from .statistics import *
View
31 pyqcy/arbitraries/__init__.py
@@ -9,15 +9,35 @@
@optional_args
class arbitrary(object):
- """@arbitrary decorator which can be applied on functions
- that are intended to output "random" (arbitrary) values
+ """Decorator to be applied on functions in order to turn
+ them into generators of arbitrary ("random") values
of given type.
+
+ :param type_: Type of values generated by the function
+
+ The `type_` argument is optional. If it's provided,
+ then objects returned by the function will be checked against
+ this type. Furthermore, it will be possible to use the type
+ directly when defining properties, e.g.::
+
+ from pyqcy import *
+
+ @arbitrary(MyClass)
+ def my_class():
+ return MyClass()
+
+ @qc
+ def my_class_works(obj=MyClass):
+ assert obj.is_valid()
"""
# Dictionary mapping types into
# generators of arbitrary values for those types
registry = {}
def __init__(self, type_=None):
+ if type_ is not None and not isinstance(type_, type):
+ raise TypeError("%r (a `%s`) is not a type" % (
+ type_, type(type_).__name__))
self.type_ = type_
def __call__(self, func):
@@ -67,12 +87,17 @@ def wrapper(*args, **kwargs):
return wrapper
- def __coerce_to_arbitraries(self, args=[], kwargs={}):
+ def __coerce_to_arbitraries(self, args=None, kwargs=None):
"""Ensures given list and dictionary of positional and keyword
arguments contains appropriate arbitrary values' generators.
+
Elements that cannot be reasonably coerced into arbitraries
are left unchanged.
+
+ Returns a tuple of (args, kwargs) with coerced arguments.
"""
+ args = args or []
+ kwargs = kwargs or {}
args = [to_arbitrary(arg) if is_arbitrary(arg) else arg
for arg in args]
kwargs = dict((k, to_arbitrary(v) if is_arbitrary(v) else v)
View
2  pyqcy/arbitraries/combinators.py
@@ -57,7 +57,7 @@ def generator():
def data(schema):
"""Generator that outputs data structures conforming to given schema.
- :param schema: A list of dictionary that contains either
+ :param schema: A list or dictionary that contains either
immediate values or other generators.
.. note::
View
2  pyqcy/arbitraries/standard.py
@@ -130,7 +130,7 @@ def tuple_(*args, **kwargs):
@arbitrary
def list_(of, min_length=0, max_length=1024):
- """Generators for arbitrary lists.
+ """Generator for arbitrary lists.
Parameters for this generator allow for adjusting the length
of resulting list and elements they contain.
View
26 pyqcy/statistics.py
@@ -38,10 +38,20 @@ def collect(value):
@qc
def sort_works(
- l=list_(int, min_length=1, max_length=64)
+ l=list_(int, min_length=1, max_length=100)
):
yield collect(len(l))
assert list(sorted(l))[0] = min(l)
+
+ Checking the above property will produce output similar to this:
+
+ .. code-block:: console
+
+ sort_works: passed 100 tests.
+ 1.00%: 1
+ 1.00%: 2
+ ...
+ 1.00%: 100
"""
return Tag(value)
@@ -64,10 +74,22 @@ def classify(condition, label):
.. code-block:: python
@qc
- def sort_preserves_length(l=list_(int, max_length=64)):
+ def sort_preserves_length(
+ l=list_(int, min_length=1, max_length=100)
+ ):
yield classify(len(l) == 0, "empty list")
yield classify(len(l) < 10, "short list")
assert len(list(sorted(l))) == len(l)
+
+ Checking the above property will produce
+ something like the following output:
+
+ .. code-block:: console
+
+ sort_preserves_length: passed 100 tests.
+ 1.00%: empty list, short list
+ 9.00%: short list
+ 90.00%: <rest>
"""
satisfied = condition() if callable(condition) else bool(condition)
return Tag(label) if satisfied else None
View
4 setup.py
@@ -63,10 +63,12 @@ def addition_actually_works(
"""
from setuptools import setup, find_packages
+import pyqcy
+
setup(
name="pyqcy",
- version="0.3.1",
+ version=pyqcy.__version__,
description="QuickCheck-like testing framework for Python",
long_description=__doc__,
author='Karol Kuczmarski "Xion"',

No commit comments for this range

Something went wrong with that request. Please try again.