Skip to content

Commit

Permalink
Merge pull request #12 from Gab-km/for_all
Browse files Browse the repository at this point in the history
Introduce `for_all` function
  • Loading branch information
futoase committed Jan 1, 2015
2 parents a77c030 + 26b3dcd commit e68135a
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 3 deletions.
2 changes: 1 addition & 1 deletion lib/pyqcheck/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

from multiprocessing import Process

from prop import Prop, PropRunner
from prop import Prop, PropRunner, for_all, may_throw
from arbitrary import Arbitrary, ArbitraryAbstraction
from pyqworker import PyQWorker
from util import PrettyPrinter
Expand Down
20 changes: 20 additions & 0 deletions lib/pyqcheck/prop.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import sys
import traceback
import marshal
from arbitrary import Arbitrary


class PropResult:
Expand Down Expand Up @@ -53,6 +54,25 @@ def execute(self):
return PropResult.fail(func.__name__, inputs, error)


def for_all(arbitraries, label, func):
arb = Arbitrary(*arbitraries)
if type(func) == PossibleFuncToThrow:
return Prop(arb, func.to_function, label, *(func.exceptions))
else:
return Prop(arb, func, label)


class PossibleFuncToThrow:
def __init__(self, func, should_all_throw, *exception):
self.to_function = func
self.should_all_throw = should_all_throw
self.exceptions = exception


def may_throw(func, *exception):
return PossibleFuncToThrow(func, False, *exception)


class RunningResult:
def __init__(self, label, func_name, func_code, success, failure, exceptions, results):
self.label = label
Expand Down
2 changes: 1 addition & 1 deletion lib/test/_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
if INCLUDE_PATH not in sys.path:
sys.path.insert(0, INCLUDE_PATH)

from pyqcheck import PyQCheck, Arbitrary, ArbitraryAbstraction, set_arbitrary, Prop, PropRunner, PrettyPrinter
from pyqcheck import PyQCheck, Arbitrary, ArbitraryAbstraction, set_arbitrary, Prop, for_all, may_throw, PropRunner, PrettyPrinter
from pyqcheck.arbitraries.pq_string import PyQString
from pyqcheck.arbitraries.pq_integer import PyQInteger
from pyqcheck.arbitraries.pq_number import PyQNumber
Expand Down
66 changes: 66 additions & 0 deletions lib/test/case_for_all.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# -*- coding: utf-8 -*-

from _import import PyQCheck, Arbitrary, for_all, may_throw, PropRunner


describe "for_all test":

before each:
PyQCheck.TEST_STACK = []

it "should succeed all tests when for_all creates an all true property":
test_label = '!(x || y) == !x && !y'
test_func = lambda x, y: (not(x or y)) == ((not x) and (not y))

result = (
PropRunner(1000).run(
for_all(
('boolean', 'boolean'),
test_label,
test_func
)
).test_result
)

assert result.label == test_label
assert result.success == 1000
assert result.failure == 0

it "should succeed all tests when for_all creates an all true property with limitations":
test_label = 'x + y == y + x'
test_func = lambda x, y: x + y == y + x

result = (
PropRunner(1000).run(
for_all(
(('number', {"min":5, "max":10}), ('number', {"min":5, "max":10})),
test_label,
test_func
)
).test_result
)

assert result.label == test_label
assert result.success == 1000
assert result.failure == 0

it "should catch errors when for_all creates a fragile property":
test_label = 'n <= 10 == True'
test_number = 1000
def ten_or_less(n):
if n > 10:
raise ValueError
return True

result = (
PropRunner(test_number).run(
for_all(
(('integer', {"max":30}),),
test_label,
may_throw(ten_or_less, ValueError)
)
).test_result
)

assert result.label == test_label
assert result.success == test_number or not (result.exceptions.get("ValueError") is None)
36 changes: 35 additions & 1 deletion lib/test/case_pyqcheck.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# -*- coding:utf-8 -*-

from _import import PyQCheck, Arbitrary, set_arbitrary
from _import import PyQCheck, Arbitrary, set_arbitrary, for_all
from decimal import Decimal, getcontext

getcontext().prec = 60
Expand Down Expand Up @@ -122,3 +122,37 @@ def length_10(chars):
assert test_3.label == 'len(chars) <= 10'
assert test_3.success == TEST_COUNT
assert test_3.failure == 0

it "can register 2 tests and should succeed when it runs them":
TEST_COUNT = 100
TEST_LABEL_1 = '!(x || y) == !x && !y'
TEST_LABEL_2 = 'x * y == y * x and x + y == y + x'

def eq(x,y):
return x * y == y * x and x + y == y + x

results = PyQCheck().add(
for_all(
('boolean', 'boolean'),
TEST_LABEL_1,
lambda x, y: (not(x or y)) == ((not x) and (not y))
)
).add(
for_all(
('integer', 'integer'),
TEST_LABEL_2,
eq
)
).run(TEST_COUNT).results

assert len(results) == 2

result_1 = results[0]
result_2 = results[1]

assert result_1.label == TEST_LABEL_1
assert result_1.success == TEST_COUNT
assert result_1.failure == 0
assert result_2.label == TEST_LABEL_2
assert result_2.success == TEST_COUNT
assert result_2.failure == 0

0 comments on commit e68135a

Please sign in to comment.