def test_equivalence(self): """ Two function calls are equivalent not only if they share the same class, but also if their arguments are equivalent. """ class FooFunction(Function): required_arguments = ("abc", ) optional_arguments = {"xyz": LeafNode("123")} class CommutativeFunction(Function): required_arguments = range(2) argument_types = BooleanType is_commutative = True func1 = FooFunction(LeafNode("whatever")) func2 = FooFunction(LeafNode("whatever")) func3 = PermissiveFunction(BranchNode("baz")) func4 = PermissiveFunction(LeafNode("foo")) func5 = FooFunction(LeafNode("something")) func6 = CommutativeFunction(BoolVar(), TrafficLightVar()) func7 = CommutativeFunction(TrafficLightVar(), BoolVar()) assert_node_equivalence( (func1, func2), (func3, ), (func4, ), (func5, ), (func6, func7), )
def test_retrieving_namespace_with_partially_untrastlated_contents(self): """ When a namespace is requested in a locale in which not all items are available, the namespace with global names should be returned instead. """ bool_var = BoolVar() traffic = TrafficLightVar() st = SymbolTable( "global", (Bind("bool", BoolVar(), es="booleano", fr=u"booléen"), Bind("traffic", TrafficLightVar(), es=u"tráfico")), SymbolTable( "foo", ( Bind("bar", bool_var, es="fulano"), Bind("baz", traffic, es="mengano", fr="bonjour"), ), ), ) # Checking the namespace: namespace = st.get_namespace("fr") eq_(len(namespace.subnamespaces), 1) subnamespace = namespace.subnamespaces["foo"] subnamespace_objects = { 'bar': bool_var, 'bonjour': traffic, } eq_(subnamespace.objects, subnamespace_objects) expected_unbound_objects_global = { u'booléen': BoolVar(), 'traffic': TrafficLightVar(), } eq_(namespace.objects, expected_unbound_objects_global)
def test_retrieving_existing_2nd_level_object(self): sub_objects = { 'bool': BoolVar(), 'traffic': TrafficLightVar(), } global_objects = { 'foo': TrafficLightVar(), } sub_namespace = {'sub1': Namespace(sub_objects)} st = Namespace(global_objects, sub_namespace) requested_object = st.get_object("bool", ["sub1"]) eq_(requested_object, BoolVar())
def test_equivalence(self): """Two conjunctions are equivalent if they have the same operands.""" op1 = And(BoolVar(), TrafficLightVar()) op2 = And(TrafficLightVar(), BoolVar()) op3 = And(DriversAwaitingGreenLightVar(), BoolVar()) op4 = And(DriversAwaitingGreenLightVar(), BoolVar()) op5 = Or(DriversAwaitingGreenLightVar(), BoolVar()) assert_node_equivalence( (op1, op2), (op3, op4), (op5, ), )
def test_retrieving_object_in_non_existing_subtable(self): global_objects = { 'foo': TrafficLightVar(), 'traffic-violation': TrafficViolationFunc(String("pedestrians")), } st = Namespace(global_objects, ) assert_raises(ScopeError, st.get_object, "foo", ["doesn't", "exist"])
def test_retrieving_non_existing_object(self): global_objects = { 'foo': TrafficLightVar(), 'traffic-violation': TrafficViolationFunc(String("pedestrians")), } st = Namespace(global_objects, ) assert_raises(ScopeError, st.get_object, "bool")
def test_constructor_with_objects(self): objects = [ Bind("greeting", String("hey")), Bind("traffic", TrafficLightVar()) ] st = SymbolTable("global", objects) eq_(st.objects, set(objects))
def test_evaluation(self): # Setup: traffic_light = TrafficLightVar() operation = Not(traffic_light) # Evaluation: ok_(operation.get_as_boolean(dict(traffic_light=""))) assert_false(operation.get_as_boolean(dict(traffic_light="green")))
def test_retrieving_existing_global_object(self): objects = { 'bool': BoolVar(), 'traffic': TrafficLightVar(), } st = Namespace(objects) requested_object = st.get_object("bool") eq_(requested_object, BoolVar())
def test_duplicate_objects(self): """There must be no duplicate object.""" # In the constructor: objects1 = [ Bind("salutation", String("hey")), Bind("traffic", TrafficLightVar()), Bind("salutation", String("hey")) ] assert_raises(ScopeError, SymbolTable, "global", objects1) # Post-instantiation: objects2 = [ Bind("salutation", String("hey")), Bind("traffic", TrafficLightVar()) ] st = SymbolTable("global", objects2) assert_raises(ScopeError, st.add_object, Bind("salutation", String("hey")))
def test_retrieving_existing_3rd_level_object(self): third_level_objects = { 'bool': BoolVar(), } second_level_objects = { 'traffic': TrafficLightVar(), } global_objects = { 'foo': TrafficLightVar(), 'traffic-violation': TrafficViolationFunc(String("pedestrians")), } third_level_namespaces = {'sub2': Namespace(third_level_objects)} second_level_namespaces = { 'sub1': Namespace(second_level_objects, third_level_namespaces) } st = Namespace(global_objects, second_level_namespaces) requested_object = st.get_object("bool", ["sub1", "sub2"]) eq_(requested_object, BoolVar())
def test_operand(self): """Operands alone can valid evaluable parse trees.""" operand = TrafficLightVar() tree = EvaluableParseTree(operand) # True context = {'traffic_light': "red"} ok_(tree(context)) # False context = {'traffic_light': None} assert_false(tree(context))
class TestEvaluableParser(object): """Tests for the evaluable parser.""" global_objects = { 'bool': BoolVar(), 'message': String("Hello world"), 'foo': PermissiveFunction, } traffic_objects = { 'traffic_light': TrafficLightVar(), 'pedestrians_crossing_road': PedestriansCrossingRoad(), 'drivers_awaiting_green_light': DriversAwaitingGreenLightVar(), 'traffic_violation': TrafficViolationFunc, } root_namespace = Namespace(global_objects, { 'traffic': Namespace(traffic_objects), }) parser = EvaluableParser(Grammar(), root_namespace) #{ Tests for the parse action that makes the variables def test_existing_variable_without_namespace(self): parse_tree = self.parser("~ bool") eq_(parse_tree.root_node, Not(self.global_objects['bool'])) def test_existing_variable_with_namespace(self): parse_tree = self.parser('traffic:traffic_light == "green"') expected_node = Equal(self.traffic_objects['traffic_light'], String("green")) eq_(parse_tree.root_node, expected_node) def test_non_existing_variable_without_namespace(self): assert_raises(ScopeError, self.parser, "~ non_existing_var") def test_non_existing_variable_with_namespace(self): assert_raises(ScopeError, self.parser, "~ traffic:non_existing_var") def test_variable_in_non_existing_namespace(self): assert_raises(ScopeError, self.parser, "~ bar:foo") def test_function_instead_of_variable(self): # "foo" is a function, so it cannot be used as a variable (without # parenthesis): try: self.parser('~ foo') except BadExpressionError, exc: eq_('"foo" represents a function, not a variable', unicode(exc)) else:
def test_unreusable_bindings(self): """ Operand bindings and symbol tables can only be bound to a single parent symbol table. """ # An operand binding: bind = Bind("traffic-light", TrafficLightVar()) SymbolTable("traffic", [bind]) assert_raises(ScopeError, SymbolTable, "baz", [bind]) # A symbol table: st0 = SymbolTable("foo", []) SymbolTable("global", [], st0) assert_raises(ScopeError, SymbolTable, "bar", [], st0)
def test_name_clash_in_grand_children(self): """ The scope must be validated even inside the sub-tables. """ sciences_st1 = SymbolTable( "sciences", (), SymbolTable("maths", (), es=u"matemática"), SymbolTable("computing", ()), SymbolTable("maths", ()), ) sciences_st2 = SymbolTable( "sciences", (), SymbolTable("maths", (), es=u"matemática"), SymbolTable("computing", ()), SymbolTable("mathematics", (), es=u"matemática"), ) sciences_st3 = SymbolTable( "sciences", (), SymbolTable("maths", (), es=u"matemática"), SymbolTable("computing", ()), # This sub-table will be called "matemática" in Spanish too: SymbolTable(u"matemática", ()), ) # Now a name clash at the objects level: sciences_st4 = SymbolTable("global", ( Bind("foo", BoolVar()), Bind("foo", TrafficLightVar(), es="bar"), )) st1 = SymbolTable("global", (), sciences_st1, SymbolTable("society", ())) st2 = SymbolTable("global", (), sciences_st2, SymbolTable("society", ())) st3 = SymbolTable("global", (), sciences_st3, SymbolTable("society", ())) st4 = SymbolTable("global", (), sciences_st4) assert_raises(ScopeError, st1.validate_scope) assert_raises(ScopeError, st2.validate_scope) assert_raises(ScopeError, st3.validate_scope) assert_raises(ScopeError, st4.validate_scope)
def test_checking_valid_table(self): st = SymbolTable( "global", # Bindings/global objects: ( Bind("bool", BoolVar(), es="booleano"), Bind("traffic", TrafficLightVar(), es=u"tráfico"), ), # Sub-tables: SymbolTable( "maths", ( Bind("pi", Number(3.1416)), Bind("e", Number(2.7183)), ), ), ) eq_(st.validate_scope(), None)
def test_retrieving_namespace_without_children(self): """ A namespace shouldn't have sub-namespaces if the original symbol table doesn't have sub-tables. """ bool_var = BoolVar() traffic = TrafficLightVar() st = SymbolTable( "global", (Bind("bool", bool_var, es="booleano"), Bind("traffic", traffic, es=u"tráfico")), ) # Checking the namespace: global_namespace = st.get_namespace() eq_(len(global_namespace.subnamespaces), 0) # Checking the namespace in Castilian: castilian_namespace = st.get_namespace("es") eq_(len(castilian_namespace.subnamespaces), 0)
def test_retrieving_namespace_with_children(self): """ A namespace should have sub-namespaces for the sub-tables of the original symbol table. """ bool_var = BoolVar() traffic = TrafficLightVar() st = SymbolTable( "global", (Bind("bool", bool_var, es="booleano"), Bind("traffic", traffic, es=u"tráfico")), SymbolTable( "foo", ( Bind("bar", bool_var, es="fulano"), Bind("baz", traffic, es="mengano"), ), ), ) # Checking the namespace: global_namespace = st.get_namespace() eq_(len(global_namespace.subnamespaces), 1) global_subnamespace = global_namespace.subnamespaces["foo"] global_subnamespace_objects = { 'bar': bool_var, 'baz': traffic, } eq_(global_subnamespace.objects, global_subnamespace_objects) # Checking the namespace in Castilian: castilian_namespace = st.get_namespace("es") eq_(len(castilian_namespace.subnamespaces), 1) castilian_subnamespace = castilian_namespace.subnamespaces["foo"] castilian_subnamespace_objects = { 'fulano': bool_var, 'mengano': traffic, } eq_(castilian_subnamespace.objects, castilian_subnamespace_objects)
def test_with_mixed_results(self): operation = And(BoolVar(), TrafficLightVar()) assert_false(operation(dict(bool=False, traffic_light="red")))
def test_with_both_results_as_false(self): operation = And(BoolVar(), TrafficLightVar()) assert_false(operation(dict(bool=False, traffic_light="")))
def test_with_both_results_as_true(self): operation = And(BoolVar(), TrafficLightVar()) ok_(operation(dict(bool=True, traffic_light="red")))
def test_equality(self): """Two bindings are equivalent if they have the same names.""" op1 = TrafficLightVar() op2 = String("hey, where's my car?") bind1 = Bind("name1", op1) bind2 = Bind("name2", op2) bind3 = Bind("name2", op1) bind4 = Bind("name1", op2) bind5 = Bind("name1", op1) bind6 = Bind("name2", op2) bind7 = Bind("name1", op1, es="nombre1") bind8 = Bind("name1", op1, es_VE="nombre1", es="nombre1") bind9 = Bind("name1", op1, es="nombre1") ok_(bind1 == bind4) ok_(bind1 == bind5) ok_(bind2 == bind3) ok_(bind2 == bind6) ok_(bind3 == bind2) ok_(bind3 == bind6) ok_(bind4 == bind1) ok_(bind4 == bind5) ok_(bind5 == bind5) ok_(bind5 == bind1) ok_(bind5 == bind4) ok_(bind6 == bind2) ok_(bind6 == bind3) ok_(bind7 == bind9) ok_(bind9 == bind7) ok_(bind1 != None) ok_(bind1 != SymbolTable("name1", [])) ok_(bind1 != bind2) ok_(bind1 != bind3) ok_(bind1 != bind6) ok_(bind1 != bind7) ok_(bind1 != bind8) ok_(bind1 != bind9) ok_(bind2 != bind1) ok_(bind2 != bind4) ok_(bind2 != bind5) ok_(bind2 != bind7) ok_(bind2 != bind8) ok_(bind2 != bind9) ok_(bind3 != bind1) ok_(bind3 != bind4) ok_(bind3 != bind5) ok_(bind3 != bind7) ok_(bind3 != bind8) ok_(bind3 != bind9) ok_(bind4 != bind2) ok_(bind4 != bind3) ok_(bind4 != bind6) ok_(bind4 != bind7) ok_(bind4 != bind8) ok_(bind4 != bind9) ok_(bind5 != bind2) ok_(bind5 != bind3) ok_(bind5 != bind6) ok_(bind5 != bind7) ok_(bind5 != bind8) ok_(bind5 != bind9) ok_(bind6 != bind1) ok_(bind6 != bind4) ok_(bind6 != bind5) ok_(bind6 != bind7) ok_(bind6 != bind8) ok_(bind6 != bind9) ok_(bind7 != bind1) ok_(bind7 != bind2) ok_(bind7 != bind3) ok_(bind7 != bind4) ok_(bind7 != bind5) ok_(bind7 != bind6) ok_(bind7 != bind8) ok_(bind8 != bind1) ok_(bind8 != bind2) ok_(bind8 != bind3) ok_(bind8 != bind4) ok_(bind8 != bind5) ok_(bind8 != bind6) ok_(bind8 != bind7) ok_(bind8 != bind9) ok_(bind9 != bind1) ok_(bind9 != bind2) ok_(bind9 != bind3) ok_(bind9 != bind4) ok_(bind9 != bind5) ok_(bind9 != bind6) ok_(bind9 != bind8)
def test_with_mixed_results(self): operation = Xor(BoolVar(), TrafficLightVar()) ok_(operation(dict(bool=False, traffic_light="red")))
def test_with_both_results_as_true(self): operation = Xor(BoolVar(), TrafficLightVar()) assert_false(operation(dict(bool=True, traffic_light="red")))
def test_hash_nullary(self): """The hash of a nullary function is the hash of the function class.""" func = TrafficLightVar() eq_(hash(func), hash(TrafficLightVar))
class TestEvaluableParseManager(object): """ Tests for the :class:`EvaluableParseManager` with caching disabled. """ # A symbol table to be used across the tests: symbol_table = SymbolTable( "root", ( Bind("boolean", BoolVar(), es="booleano"), Bind("message", String("Hello world"), es="mensaje"), Bind("foo", PermissiveFunction, es="fulano"), ), SymbolTable("traffic", ( Bind("traffic_light", TrafficLightVar(), es=u"semáforo"), Bind("pedestrians_crossing_road", PedestriansCrossingRoad(), es="peatones_cruzando_calle"), Bind("drivers_awaiting_green", DriversAwaitingGreenLightVar(), es="conductores_esperando_verde"), Bind("traffic_violation", TrafficViolationFunc, es=u"infracción"), ), es=u"tráfico"), ) def test_parsing_with_no_localized_grammars(self): mgr = EvaluableParseManager(self.symbol_table, Grammar()) parse_tree1 = mgr.parse('message == "2009-07-13"') parse_tree2 = mgr.parse('message == "2009-07-13"', None) expected_tree = EvaluableParseTree( Equal(String("Hello world"), String("2009-07-13"))) eq_(parse_tree1, parse_tree2) eq_(parse_tree1, expected_tree) def test_parsing_with_localized_grammars(self): castilian_grammar = Grammar(decimal_separator=",", thousands_separator=".") mgr = EvaluableParseManager(self.symbol_table, Grammar(), es=castilian_grammar) parse_tree = mgr.parse(u"tráfico:peatones_cruzando_calle <= 3,00", "es") expected_tree = EvaluableParseTree( LessEqual(PedestriansCrossingRoad(), Number(3.0))) eq_(parse_tree, expected_tree) def test_parsing_with_undefined_grammar_but_available_translations(self): """ When an expression is written in an unsupported grammar, a parser based on the generic grammar must be created and used. The respective translated bindings must be used if available. """ log_handler = LoggingHandlerFixture() mgr = EvaluableParseManager(self.symbol_table, Grammar()) # Castilian grammar is not supported: parse_tree = mgr.parse(u"tráfico:peatones_cruzando_calle <= 3.0", "es") expected_tree = EvaluableParseTree( LessEqual(PedestriansCrossingRoad(), Number(3))) eq_(parse_tree, expected_tree) # Checking the log: info = "Generated parser for unknown grammar 'es'" ok_(info in log_handler.handler.messages['info']) log_handler.undo() def test_parsing_with_undefined_grammar_and_no_translated_bindings(self): """ When an expression is written in an unsupported grammar, a parser based on the generic grammar must be created and used. If there are no translated bindings, the default names must be used. """ log_handler = LoggingHandlerFixture() mgr = EvaluableParseManager(self.symbol_table, Grammar()) # French grammar is not supported: parse_tree = mgr.parse("traffic:pedestrians_crossing_road <= 3.0", "fr") expected_tree = EvaluableParseTree( LessEqual(PedestriansCrossingRoad(), Number(3))) eq_(parse_tree, expected_tree) # Checking the log: info = "Generated parser for unknown grammar 'fr'" ok_(info in log_handler.handler.messages['info']) log_handler.undo() def test_parsing_with_defined_grammar_but_no_available_translations(self): """ When an expression is written in an supported grammar but there are no translated bindings, the default names must be used along with the custom grammar. """ french_grammar = Grammar(decimal_separator=",", thousands_separator=".") mgr = EvaluableParseManager(self.symbol_table, Grammar(), fr=french_grammar) # French grammar is not supported: parse_tree = mgr.parse("traffic:pedestrians_crossing_road <= 3,0", "fr") expected_tree = EvaluableParseTree( LessEqual(PedestriansCrossingRoad(), Number(3))) eq_(parse_tree, expected_tree) def test_adding_grammar(self): """It should be possible to add grammars after instantiation.""" castilian_grammar = Grammar(decimal_separator=",", thousands_separator=".") mgr = EvaluableParseManager(self.symbol_table, Grammar()) mgr.add_parser("es", castilian_grammar) parse_tree = mgr.parse(u'tráfico:peatones_cruzando_calle <= 3,00', "es") expected_tree = EvaluableParseTree( LessEqual(PedestriansCrossingRoad(), Number(3.0))) eq_(parse_tree, expected_tree) def test_adding_existing_grammar(self): """There can't be duplicate/overlapped parsers.""" mgr = EvaluableParseManager(self.symbol_table, Grammar(), es=Grammar()) assert_raises(GrammarError, mgr.add_parser, "es", Grammar()) def test_evaluating_expressions(self): """Managers should be able to evaluate the expressions too.""" mgr = EvaluableParseManager(self.symbol_table, Grammar()) context = {'pedestrians_crossroad': (u"gustavo", u"carla")} evaluation1 = mgr.evaluate("traffic:pedestrians_crossing_road <= 3", None, context) evaluation2 = mgr.evaluate( u'"carla" ∈ traffic:pedestrians_crossing_road', None, context) evaluation3 = mgr.evaluate("traffic:pedestrians_crossing_road > 2", None, context) ok_(evaluation1) ok_(evaluation2) assert_false(evaluation3)
def test_instance(self): """Class instances can be bound.""" instance = TrafficLightVar() bound_instance = Bind("da_variable", instance) eq_(bound_instance.operand, instance)