def test_access_info(): '''Test the AccessInfo class. ''' location = 12 access_info = AccessInfo(AccessType.READ, location, Node()) assert access_info.access_type == AccessType.READ assert access_info.location == location assert access_info.indices is None assert str(access_info) == "READ(12)" access_info.change_read_to_write() assert str(access_info) == "WRITE(12)" assert access_info.access_type == AccessType.WRITE with pytest.raises(InternalError) as err: access_info.change_read_to_write() assert "Trying to change variable to 'WRITE' which does not have "\ "'READ' access." in str(err.value) access_info.indices = ["i"] assert access_info.indices == ["i"] access_info = AccessInfo(AccessType.UNKNOWN, location, Node()) assert access_info.access_type == AccessType.UNKNOWN assert access_info.location == location assert access_info.indices is None access_info = AccessInfo(AccessType.UNKNOWN, location, Node(), ["i", "j"]) assert access_info.access_type == AccessType.UNKNOWN assert access_info.location == location assert access_info.indices == ["i", "j"]
def test_variable_access_info_read_write(): '''Test the handling of READWRITE accesses. A READWRITE indicates both a read and a write access, but if a variable as a READ and a WRITE access, this is not one READWRITE access. A READWRITE access is only used in subroutine calls (depending on kernel metadata) ''' vai = VariableAccessInfo("var_name") assert vai.has_read_write() is False # Add a READ and WRITE access at the same location, and make sure it # is not reported as READWRITE access node = Node() vai.add_access(AccessType.READ, 2, node) assert vai[0].node == node assert vai[0].location == 2 vai.add_access(AccessType.WRITE, 2, Node()) assert vai.has_read_write() is False vai.add_access(AccessType.READWRITE, 2, Node()) assert vai.has_read_write() # Create a new instance, and add only one READWRITE access: vai = VariableAccessInfo("var_name") vai.add_access(AccessType.READWRITE, 2, Node()) assert vai.has_read_write() assert vai.is_read() assert vai.is_written()
def test_variables_access_info(): '''Test the implementation of VariablesAccessInfo, a class that manages a list of variables, each with a list of accesses. ''' var_accesses = VariablesAccessInfo() node1 = Node() var_accesses.add_access(Signature("read"), AccessType.READ, node1) node2 = Node() var_accesses.add_access(Signature("written"), AccessType.WRITE, node2) assert str(var_accesses) == "read: READ, written: WRITE" var_accesses.next_location() node = Node() var_accesses.add_access(Signature("written"), AccessType.WRITE, node) var_accesses.next_location() var_accesses.add_access(Signature("read_written"), AccessType.WRITE, node) var_accesses.add_access(Signature("read_written"), AccessType.READ, node) assert str(var_accesses) == "read: READ, read_written: READ+WRITE, "\ "written: WRITE" assert set(var_accesses.all_signatures) == set( [Signature("read"), Signature("written"), Signature("read_written")]) all_accesses = var_accesses[Signature("read")].all_accesses assert all_accesses[0].node == node1 written_accesses = var_accesses[Signature("written")].all_accesses assert written_accesses[0].location == 0 assert written_accesses[1].location == 1 # Check that the location pointer is pointing to the next statement: assert var_accesses.location == 2 # Create a new instance var_accesses2 = VariablesAccessInfo() var_accesses2.add_access(Signature("new_var"), AccessType.READ, node) var_accesses2.add_access(Signature("written"), AccessType.READ, node) # Now merge the new instance with the previous instance: var_accesses.merge(var_accesses2) assert str(var_accesses) == "new_var: READ, read: READ, " \ "read_written: READ+WRITE, written: READ+WRITE" with pytest.raises(KeyError): _ = var_accesses[Signature("does_not_exist")] with pytest.raises(KeyError): var_accesses.is_read(Signature("does_not_exist")) with pytest.raises(KeyError): var_accesses.is_written(Signature("does_not_exist")) assert "READWRITE" not in str(var_accesses) var_accesses.add_access(Signature("readwrite"), AccessType.READWRITE, node) assert "READWRITE" in str(var_accesses) with pytest.raises(InternalError) as err: var_accesses.add_access("no-signature", AccessType.READWRITE, node) assert "Got 'no-signature' of type 'str' but expected it to be of type " \ "psyclone.core.Signature." in str(err.value)
def test_omploop_no_collapse(): ''' Check that the OMPLoopTrans.directive() method rejects the collapse argument ''' from psyclone.transformations import OMPLoopTrans trans = OMPLoopTrans() pnode = Node() cnode = Node() with pytest.raises(NotImplementedError) as err: _ = trans._directive(pnode, cnode, collapse=2) assert ("The COLLAPSE clause is not yet supported for '!$omp do' " "directives" in str(err.value))
def test_malformed_extract_node(monkeypatch): ''' Check that we raise the expected error if a ReadOnlyVerifyNode does not have a single Schedule node as its child. ''' read_node = ReadOnlyVerifyNode() monkeypatch.setattr(read_node, "_children", []) with pytest.raises(InternalError) as err: _ = read_node.read_only_verify_body assert "malformed or incomplete. It should have a " in str(err.value) monkeypatch.setattr(read_node, "_children", [Node(), Node()]) with pytest.raises(InternalError) as err: _ = read_node.read_only_verify_body assert "malformed or incomplete. It should have a " in str(err.value)
def test_malformed_profile_node(monkeypatch): ''' Check that we raise the expected error if a ProfileNode does not have a single Schedule node as its child. ''' pnode = ProfileNode() monkeypatch.setattr(pnode, "_children", []) with pytest.raises(InternalError) as err: _ = pnode.profile_body assert "malformed or incomplete. It should have a " in str(err.value) monkeypatch.setattr(pnode, "_children", [Node(), Node()]) with pytest.raises(InternalError) as err: _ = pnode.profile_body assert "malformed or incomplete. It should have a " in str(err.value)
def test_variable_access_info_is_array(): '''Test that the SingleVariableAccesInfo class handles arrays as expected. ''' vai = SingleVariableAccessInfo("var_name") # Add non array-like access: vai.add_access_with_location(AccessType.READ, 1, Node()) assert not vai.is_array() # Add array access: vai.add_access_with_location(AccessType.READ, 1, Node(), [[Node()]]) assert vai.is_array()
def test_variables_access_info(): '''Test the implementation of VariablesAccessInfo, a class that manages a list of variables, each with a list of accesses. ''' var_accesses = VariablesAccessInfo() node1 = Node() var_accesses.add_access("read", AccessType.READ, node1) node2 = Node() var_accesses.add_access("written", AccessType.WRITE, node2) assert str(var_accesses) == "read: READ, written: WRITE" var_accesses.next_location() node = Node() var_accesses.add_access("written", AccessType.WRITE, node) var_accesses.next_location() var_accesses.add_access("read_written", AccessType.WRITE, node) var_accesses.add_access("read_written", AccessType.READ, node) assert str(var_accesses) == "read: READ, read_written: READ+WRITE, "\ "written: WRITE" assert set(var_accesses.all_vars) == set( ["read", "written", "read_written"]) all_accesses = var_accesses["read"].all_accesses assert all_accesses[0].node == node1 written_accesses = var_accesses["written"].all_accesses assert written_accesses[0].location == 0 assert written_accesses[1].location == 1 # Check that the location pointer is pointing to the next statement: assert var_accesses.location == 2 # Create a new instance var_accesses2 = VariablesAccessInfo() var_accesses2.add_access("new_var", AccessType.READ, node) var_accesses2.add_access("written", AccessType.READ, node) # Now merge the new instance with the previous instance: var_accesses.merge(var_accesses2) assert str(var_accesses) == "new_var: READ, read: READ, " \ "read_written: READ+WRITE, written: READ+WRITE" with pytest.raises(KeyError): _ = var_accesses["does_not_exist"] with pytest.raises(KeyError): var_accesses.is_read("does_not_exist") with pytest.raises(KeyError): var_accesses.is_written("does_not_exist") assert "READWRITE" not in str(var_accesses) var_accesses.add_access("readwrite", AccessType.READWRITE, node) assert "READWRITE" in str(var_accesses)
def test_access_info_exceptions(): '''Test that the right exceptions are raised. ''' location = 12 with pytest.raises(InternalError) as err: _ = AccessInfo(AccessType.READ, location, Node(), component_indices=123) assert "component_indices in add_access must be a list of lists or " \ "None, got '123'" in str(err.value) with pytest.raises(InternalError) as err: _ = AccessInfo(AccessType.READ, location, Node(), component_indices=[[], 123]) assert "component_indices in add_access must be a list of lists or None, "\ "got '[[], 123]'" in str(err.value)
def test_get_node_list(): '''Test for valid parameters to get_node_list.''' my_rt = MyRegionTrans() # 1) Provide a schedule # --------------------- sched = Schedule() # get_node_list returns a copy of the list, so it must be a list # with the same content, but NOT the same list: node_list = my_rt.get_node_list(sched) assert sched.children == node_list assert node_list is not sched.children # 2) Provide a single node # ------------------------ node = Node() node_list = my_rt.get_node_list(node) assert node_list == [node] # 3) Provide a node list # ---------------------- # We use the previously returned node list, and make sure # that we get a copy of that list. node_list2 = my_rt.get_node_list(node_list) assert node_list2 == node_list assert node_list2 is not node_list
def test_check_intergrid(): ''' Test that the check_intergrid utility does not raise an error if the supplied node has no children. ''' from psyclone.psyir.nodes import Node from psyclone.transformations import check_intergrid tnode = Node() check_intergrid(tnode)
def test_psyirvisitor_visit_no_method2(): '''Check that an exception is not raised if the method for the Node class does not exist and skip_nodes is set to True. ''' visitor = PSyIRVisitor(skip_nodes=True) result = visitor(Node()) assert result is None
def test_kernelfunctor_parent(): '''Check that the optional parent argument to a KernelFunctor class constructor is stored correctly. ''' parent = Node() symbol = DataTypeSymbol("hello", StructureType()) klr = KernelFunctor(symbol, parent=parent) assert klr.parent == parent
def test_variable_access_info(): '''Test the VariableAccesInfo class, i.e. the class that manages a list of VariableInfo instances for one variable ''' vai = VariableAccessInfo("var_name") assert vai.var_name == "var_name" assert str(vai) == "var_name:" assert vai.is_written() is False assert vai.is_read() is False assert vai.all_accesses == [] vai.add_access(AccessType.READ, 2, Node()) assert str(vai) == "var_name:READ(2)" assert vai.is_read() assert vai.is_read_only() vai.change_read_to_write() assert not vai.is_read() assert vai.is_written() assert not vai.is_read_only() # Now we have one write access, which we should not be able to # change to write again: with pytest.raises(InternalError) as err: vai.change_read_to_write() assert "Trying to change variable 'var_name' to 'WRITE' which "\ "does not have 'READ' access." in str(err.value) assert vai.all_accesses[0] == vai[0] with pytest.raises(IndexError) as err: _ = vai[1] # Add a READ access - now we should not be able to # change read to write anymore: vai.add_access(AccessType.READ, 1, Node()) with pytest.raises(InternalError) as err: vai.change_read_to_write() assert "Variable 'var_name' had 2 accesses listed, "\ "not one in change_read_to_write." in str(err.value) # And make sure the variable is not read_only if a write is added vai.add_access(AccessType.WRITE, 3, Node()) assert vai.is_read_only() is False
def test_psyirvisitor_visit_no_method1(): '''Check that an exception is raised if the method for the Node class does not exist. ''' visitor = PSyIRVisitor() with pytest.raises(VisitorError) as excinfo: visitor(Node()) assert ("Visitor Error: Unsupported node 'Node' found: method names " "attempted were ['node_node']." in str(excinfo.value))
def test_instance_args(): '''Check that the parent and variable arguments are stored correctly when values are provided. ''' variable = DataSymbol("nemo_symbol", INTEGER_TYPE) parent = Node() nemo_loop = NemoLoop(parent=parent, variable=variable) assert nemo_loop.parent is parent assert nemo_loop._variable is variable
def test_accloop(): ''' Generic tests for the ACCLoopTrans transformation class ''' from psyclone.transformations import ACCLoopTrans from psyclone.psyGen import ACCLoopDirective trans = ACCLoopTrans() assert trans.name == "ACCLoopTrans" assert str(trans) == "Adds an 'OpenACC loop' directive to a loop" pnode = Node() cnode = Statement() tdir = trans._directive(pnode, [cnode]) assert isinstance(tdir, ACCLoopDirective)
def test_lfricalgorithminvokecall_options(): '''Check that an instance of LFRicAlgorithmInvokeCall can be created with optional arguments and that these optional arguments are stored as expected. ''' node = Node() routine = RoutineSymbol("hello") call = LFRicAlgorithmInvokeCall( routine, 0, description="describing an invoke", parent=node) assert call._description == "describing an invoke" assert call.parent is node
def test_variables_access_info_merge(): '''Tests the merge operation of VariablesAccessInfo. ''' # First create one instance representing for example: # a=b; c=d var_accesses1 = VariablesAccessInfo() node = Node() var_accesses1.add_access("b", AccessType.READ, node) var_accesses1.add_access("a", AccessType.WRITE, node) var_accesses1.next_location() var_accesses1.add_access("d", AccessType.READ, node) var_accesses1.add_access("c", AccessType.WRITE, node) c_accesses = var_accesses1["c"] assert len(c_accesses.all_accesses) == 1 assert c_accesses[0].access_type == AccessType.WRITE # First create one instance representing for example: # e=f; g=h var_accesses2 = VariablesAccessInfo() var_accesses2.add_access("f", AccessType.READ, node) var_accesses2.add_access("e", AccessType.WRITE, node) var_accesses2.next_location() var_accesses2.add_access("h", AccessType.READ, node) var_accesses2.add_access("g", AccessType.WRITE, node) # Now merge the second instance into the first one var_accesses1.merge(var_accesses2) # The e=f access pattern should have the same location # as the c=d (since there is no next_location after # adding the b=a access): c_accesses = var_accesses1["c"] e_accesses = var_accesses1["e"] assert c_accesses[0].access_type == AccessType.WRITE assert e_accesses[0].access_type == AccessType.WRITE assert c_accesses[0].location == e_accesses[0].location # Test that the g=h part has a higher location than the # c=d data. This makes sure that merge() increases the # location number of accesses when merging. c_accesses = var_accesses1["c"] g_accesses = var_accesses1["g"] h_accesses = var_accesses1["h"] assert c_accesses[0].location < g_accesses[0].location assert g_accesses[0].location == h_accesses[0].location # Also make sure that the access location was properly increased # Originally we had locations 0,1. Then we merged accesses with # location 0,1 in - the one at 0 is merged with the current 1, # and the new location 1 increases the current location from # 1 to 2: assert var_accesses1.location == 2
def test_validate(): '''Test that the validate method in the ArrayRange2LoopTrans class raises the expected exceptions. ''' trans = ArrayRange2LoopTrans() with pytest.raises(TransformationError) as info: trans.validate(Node()) assert ( "Error in ArrayRange2LoopTrans transformation. The supplied node " "argument should be a PSyIR Assignment, but found 'Node'." in str(info.value)) with pytest.raises(TransformationError) as info: trans.validate(Assignment.create(DataNode(), DataNode())) assert ( "Error in ArrayRange2LoopTrans transformation. The lhs of the " "supplied Assignment node should be a PSyIR ArrayReference, but found " "'DataNode'." in str(info.value)) array_symbol = DataSymbol("x", ArrayType(INTEGER_TYPE, [10, 10])) one = Literal("1", INTEGER_TYPE) array_assignment = ArrayReference.create(array_symbol, [one, one.copy()]) with pytest.raises(TransformationError) as info: trans.validate(Assignment.create(array_assignment, DataNode())) assert ( "Error in ArrayRange2LoopTrans transformation. The lhs of the " "supplied Assignment node should be a PSyIR ArrayReference with at " "least one " "of its dimensions being a Range, but found None in " "'ArrayReference[name:'x']\\nLiteral[value:'1', " "Scalar<INTEGER, UNDEFINED>]\\nLiteral[value:'1', Scalar<INTEGER, " "UNDEFINED>]\\n'." in str(info.value)) array_x = create_array_x(SymbolTable()) assignment = Assignment.create( create_array_x(SymbolTable()), array_x) trans.validate(assignment) array_x.children[0].step = Literal("2", INTEGER_TYPE) with pytest.raises(TransformationError) as info: trans.validate(assignment) assert ( "The ArrayRange2LoopTrans transformation only supports ranges that " "are known to be the same as each other but array access 'x' " "dimension 0 and 'x' dimension 0 are either different or can't be " "determined in the assignment 'Assignment[]\\n" "ArrayReference[name:'x']\\nRange[]\\n" "ArrayReference[name:'x']\\nRange[]\\n'." in str(info.value))
def test_get_literal_precision_type(monkeypatch): '''Make sure the get_literal_precision function in fparser2.py behaves as expected when an unsupported datatype is found ''' monkeypatch.setattr(fparser2, "CONSTANT_TYPE_MAP", {}) code = "x=0.0" reader = FortranStringReader(code) astmt = Fortran2003.Assignment_Stmt(reader) fparser2_literal = astmt.children[2] with pytest.raises(NotImplementedError) as excinfo: _ = get_literal_precision(fparser2_literal, Node()) assert ("Could not process Real_Literal_Constant. Only 'real', 'integer', " "'logical' and 'character' intrinsic types are supported." in str(excinfo.value))
def test_range_init(parser): ''' Check that the Range constructor behaves as intended. ''' from fparser.common.readfortran import FortranStringReader # When no arguments are provided erange = Range() assert not erange.children # Children list is empty assert erange.parent is None assert erange.annotations == [] assert erange.ast is None # When arguments are provided parent = Node() reader = FortranStringReader("program hello\nend program hello\n") prog = parser(reader) erange2 = Range(ast=prog, parent=parent) assert erange2.parent is parent assert erange2.ast is prog with pytest.raises(InternalError) as err: _ = Range(annotations=["was-where"]) assert "unrecognised annotation 'was-where'" in str(err.value)
def test_psyirvisitor_visit_attribute_error(): '''Check that an Attribute Error is raised if the method for the Node class does exist and the method itself raises an Attribute Error. This is checked because AttributeError is used to catch a method not existing. Therefore an AttributeError raised by a method that does exist could be mistaken as meaning that the method does not exist. ''' class MyPSyIRVisitor(PSyIRVisitor): '''Subclass PSyIRVisitor to make the node method exist but it itself raises an attribute error.''' def node_node(self, _): ''' Raise an AttributeError for testing purposes ''' raise AttributeError("Error") visitor = MyPSyIRVisitor() with pytest.raises(AttributeError) as excinfo: _ = visitor(Node()) assert str(excinfo.value) == "Error"
def test_range_create(): ''' Check that the Range.create() method behaves as intended. ''' parent = Node() start = Literal("1", INTEGER_SINGLE_TYPE) stop = Literal("10", INTEGER_SINGLE_TYPE) # No parent and no step erange = Range.create(start, stop) assert erange.children[0] is start assert erange.children[1] is stop assert erange.parent is None # Parent but no step erange2 = Range.create(start, stop, parent=parent) assert erange2.parent is parent assert erange2.children[2].value == "1" # Parent and step supplied erange3 = Range.create(start, stop, step=Literal("5", INTEGER_SINGLE_TYPE), parent=parent) assert erange3.parent is parent assert erange3.children[2].value == "5"
def test_string_compare(): '''Check that the string_compare utility function in ArrayRange2LoopTrans works as expected. ''' with pytest.raises(TypeError) as info: ArrayRange2LoopTrans.string_compare(None, None) assert ("The first argument to the string_compare method should be a Node " "but found 'NoneType'." in str(info.value)) with pytest.raises(TypeError) as info: ArrayRange2LoopTrans.string_compare(Node(), None) assert ( "The second argument to the string_compare method should be a Node " "but found 'NoneType'." in str(info.value)) node1 = Literal("1.0", REAL_TYPE) node2 = BinaryOperation.create(BinaryOperation.Operator.MUL, node1, node1) node3 = BinaryOperation.create(BinaryOperation.Operator.MAX, node2, node2) assert ArrayRange2LoopTrans.string_compare(node3, node3) is True assert ArrayRange2LoopTrans.string_compare(node3, node2) is False
def test_check_intergrid(): ''' Test that the check_intergrid utility does not raise an error if the supplied node has no children. ''' tnode = Node() check_intergrid(tnode)
'''We can't create an instance of RegionTrans since it is abstract, so create a simple class that can be instantiated by adding dummy implementations of the missing methods. ''' excluded_node_types = () def apply(self, node, options=None): '''Dummy only to make this not abstract.''' @property def name(self): '''Dummy only to make this not abstract.''' # ----------------------------------------------------------------------------- @pytest.mark.parametrize("node_list", [5, [1], [1, Node()], [Node(), 1], [Node(), Node(), 1], [Node(), 1, Node()]]) def test_get_node_list_errors(node_list): '''Test incorrect parameters to get_node_list. ''' my_rt = MyRegionTrans() with pytest.raises(TransformationError) as err: my_rt.get_node_list(node_list) assert "Argument must be a single Node in a Schedule, a Schedule or a "\ "list of Nodes in a Schedule" in str(err.value) # Test for more specific error message for the first test case: # node_list = 5 if isinstance(node_list, int): # Python 3 reports 'class', python 2 'type' - so just check for both