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_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_existing_global_object(self): objects = { 'bool': BoolVar(), 'traffic': TrafficLightVar(), } st = Namespace(objects) requested_object = st.get_object("bool") eq_(requested_object, BoolVar())
def test_valid_argument_type(self): """Only arguments that implement the expected datatype are accepted.""" class StronglyTypedFunction(Function): required_arguments = ("arg1", "arg2") argument_types = {'arg1': BooleanType, 'arg2': NumberType} StronglyTypedFunction(BoolVar(), NumVar()) # Now arguments with the wrong types: assert_raises(BadCallError, StronglyTypedFunction, BoolVar(), BoolVar()) assert_raises(BadCallError, StronglyTypedFunction, NumVar(), NumVar()) assert_raises(BadCallError, StronglyTypedFunction, NumVar(), BoolVar())
def test_no_expected_datatype(self): """ All the arguments are accepted if there's no expected type for them. """ class WeaklyTypedFunction(Function): required_arguments = ("arg1", "arg2") WeaklyTypedFunction(BoolVar(), NumVar()) WeaklyTypedFunction(BoolVar(), BoolVar()) WeaklyTypedFunction(NumVar(), NumVar()) WeaklyTypedFunction(NumVar(), BoolVar())
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_evaluation_order(self): """ For efficiency, the second operand must not be evaluated if the first one returns False. """ op1 = BoolVar() op2 = BoolVar() context = {'bool': False} And(op1, op2)(context) ok_(op1.evaluated) assert_false(op2.evaluated)
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_homogeneouos_argument_types(self): """ The argument types can be set just once if it's the same for all the arguments. """ class StronglyTypedFunction(Function): required_arguments = ("arg1", "arg2") argument_types = BooleanType StronglyTypedFunction(BoolVar(), BoolVar()) # Now arguments with the wrong types: assert_raises(BadCallError, StronglyTypedFunction, NumVar(), BoolVar()) assert_raises(BadCallError, StronglyTypedFunction, BoolVar(), NumVar()) assert_raises(BadCallError, StronglyTypedFunction, NumVar(), NumVar())
def test_equivalence(self): """ Two exclusive disjunctions are equivalent if they have the same operands. """ op1 = Xor(BoolVar(), PedestriansCrossingRoad()) op2 = Xor(PedestriansCrossingRoad(), BoolVar()) op3 = Xor(DriversAwaitingGreenLightVar(), BoolVar()) op4 = Xor(DriversAwaitingGreenLightVar(), BoolVar()) op5 = Or(DriversAwaitingGreenLightVar(), BoolVar()) assert_node_equivalence( (op1, op2), (op3, op4), (op5, ), )
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_checking_object_and_subtable_sharing_localized_name(self): """ It's valid for an object and a sub-table to share the localized name. """ st1 = SymbolTable( "global", (Bind("current_day", BoolVar(), es="hoy"), ), SymbolTable("today", (), es="hoy"), ) st2 = SymbolTable( "global", (Bind("current_day", BoolVar(), es="hoy"), ), # This sub-name will be called "hoy" in Spanish too: SymbolTable("hoy", ()), ) eq_(st1.validate_scope(), None) eq_(st2.validate_scope(), None)
def test_checking_object_and_subtable_sharing_global_name(self): """ It's valid for an object and a sub-table to share the global name. """ st = SymbolTable( "global", (Bind("today", BoolVar()), ), SymbolTable("today", ()), ) eq_(st.validate_scope(), None)
def test_equivalence(self): tree1 = EvaluableParseTree(BoolVar()) tree2 = EvaluableParseTree(BoolVar()) tree3 = EvaluableParseTree(PedestriansCrossingRoad()) tree4 = ConvertibleParseTree(PlaceholderVariable("my_variable")) ok_(tree1 == tree2) ok_(tree2 == tree1) ok_(tree1 != None) ok_(tree1 != tree3) ok_(tree1 != tree4) ok_(tree2 != tree3) ok_(tree2 != tree4) ok_(tree3 != tree1) ok_(tree3 != tree2) ok_(tree3 != tree4) ok_(tree4 != tree1) ok_(tree4 != tree2) ok_(tree4 != tree3)
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_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_equivalence(self): tree1 = ConvertibleParseTree(PlaceholderVariable("my_variable")) tree2 = ConvertibleParseTree(PlaceholderVariable("my_variable")) tree3 = ConvertibleParseTree(String("hello")) tree4 = EvaluableParseTree(BoolVar()) ok_(tree1 == tree2) ok_(tree2 == tree1) ok_(tree1 != None) ok_(tree1 != tree3) ok_(tree1 != tree4) ok_(tree2 != tree3) ok_(tree2 != tree4) ok_(tree3 != tree1) ok_(tree3 != tree2) ok_(tree3 != tree4) ok_(tree4 != tree1) ok_(tree4 != tree2) ok_(tree4 != tree3)
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_string(self): tree = EvaluableParseTree(BoolVar()) as_unicode = unicode(tree) expected = "Evaluable parse tree (Anonymous variable [BoolVar])" eq_(as_unicode, expected)
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_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")))
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_representation(self): tree = ConvertibleParseTree(BoolVar()) expected = "<Parse tree (convertible) " \ "<Anonymous variable [BoolVar]>>" eq_(repr(tree), expected)
class StronglyTypedFunction2(Function): required_arguments = ("arg1", ) optional_arguments = {'arg2': BoolVar()} argument_types = {'arg1': BooleanType, 'arg2': BooleanType}
def test_representation(self): tree = EvaluableParseTree(BoolVar()) expected = "<Parse tree (evaluable) <Anonymous variable [BoolVar]>>" eq_(repr(tree), expected)