0

I'm new to Python's Hypothesis library and property based testing in general. I want to generate arbitrarily nested policy expressions with the following grammar:

((A and B) or C)

I'm feeling that the recursive strategy is what I want, but I'm having a hard time understanding how to use it. The code I have only seems to generate one "level" of expression. Here's what I have:

import unittest

from hypothesis import given
from hypothesis.strategies import text, composite, sampled_from, characters, recursive, one_of


def policy_expressions():
    return recursive(attributes(), lambda base_strategy: one_of(base_strategy, policy_expression()))

@composite
def policy_expression(draw):
    left = draw(attributes())
    right = draw(attributes())
    gate = draw(gates())
    return u' '.join((left, gate, right))


def attributes():
    return text(min_size=1, alphabet=characters(whitelist_categories='L', max_codepoint=0x7e))


def gates():
    return sampled_from((u'or', u'and'))


class TestPolicyExpressionSpec(unittest.TestCase):

    @given(policy_expression=policy_expressions())
    def test_policy_expression_spec(self, policy_expression):
        print policy_expression
        assert policy_expression # not empty

How might I generate arbitrarily nested policy expressions using Hypothesis?

doughgle
  • 827
  • 1
  • 9
  • 18

2 Answers2

2

I think this might do what you want.

import unittest

from hypothesis import given
from hypothesis.strategies import text, composite, sampled_from, characters, recursive, one_of


def policy_expressions():
    return one_of(attributes(), policy_expression())

@composite
def policy_expression(draw):
    left = draw(policy_expressions())
    right = draw(policy_expressions())
    gate = draw(gates())
    return u' '.join((left, gate, right))


def attributes():
    return text(min_size=1, alphabet=characters(whitelist_categories='L', max_codepoint=0x7e))


def gates():
    return sampled_from((u'or', u'and'))


class TestPolicyExpressionSpec(unittest.TestCase):

    @given(policy_expression=policy_expressions())
    def test_policy_expression_spec(self, policy_expression):
        print policy_expression
        assert policy_expression # not empty

if __name__ == '__main__':
    unittest.main()
Russell
  • 1,946
  • 1
  • 16
  • 14
2

I believe the correct way is this, to take base_strategy as an argument of policy_expression:

def policy_expressions():
    return recursive(attributes(), policy_expression)

@composite
def policy_expression(draw, base_strategy):
    left = draw(base_strategy)
    right = draw(base_strategy)
    gate = draw(gates())
    return u' '.join((left, gate, right))

The accepted answer not using recursive is likely to run into the problems described in the Generating recursive data post in the Hypothesis blog.

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487