예제 #1
0
def test_parser_updateargtomodulemap_invalid():
    '''Test that if the statement argument to the
    update_arg_to_module_map method is not a use statement that the
    appropriate exception is raised.'''
    tmp = Parser()
    with pytest.raises(InternalError) as excinfo:
        tmp.update_arg_to_module_map("invalid")
    assert "Expected a use statement but found instance of" \
        in str(excinfo.value)
예제 #2
0
def test_parser_createinvokecall_error():
    '''Test that if an argument to an invoke call is not what is expected
    then the appropriate exception is raised.

    '''
    statement = Call_Stmt("call invoke(0.0)")
    tmp = Parser()
    with pytest.raises(ParseError) as excinfo:
        _ = tmp.create_invoke_call(statement)
    assert ("Expecting argument to be of the form 'name=xxx' or a Kernel call "
            "but found '0.0' in file 'None'.") in str(excinfo.value)
예제 #3
0
def test_parser_parse_nemo():
    '''Check that the parse() method in the Parser() class returns the
    expected results (None, and an fparser2 ast) when using the NEMO
    API. We actually use an LFRic algorithm file here but it does not
    matter as we are only parsing the code.

    '''
    parser = Parser(api="nemo")
    res1, res2 = parser.parse(os.path.join(
        LFRIC_TEST_PATH, "1_single_invoke.f90"))
    assert res1 is None
    assert isinstance(res2, Program)
    assert "PROGRAM single_invoke" in str(res2)
예제 #4
0
def test_parser_invokeinfo_containers(tmpdir, code, name):
    '''Check that the invoke_info() method in the Parser() class works
    with program, module, subroutine and function.

    '''
    parser = Parser()
    alg_filename = str(tmpdir.join("container.f90"))
    with open(alg_filename, "w") as ffile:
        ffile.write(code)
    alg_parse_tree = parse_fp2(alg_filename)
    res = parser.invoke_info(alg_parse_tree)
    assert isinstance(res, FileInfo)
    assert res.name == name
예제 #5
0
def test_parser_parse(tmpdir):
    '''Test that if no relevant code is found in the algorithm file then
    the appropriate exception is raised.

    '''
    tmp = Parser()
    filename = str(tmpdir.join("empty.f90"))
    ffile = open(filename, "w")
    ffile.write("")
    ffile.close()
    with pytest.raises(ParseError) as excinfo:
        _ = tmp.parse(filename)
    assert ("Program, module, function or subroutine not found in parse tree "
            "for file") in str(excinfo.value)
예제 #6
0
def test_parser_invokeinfo_nocode(tmpdir):
    '''Check that the invoke_info() method in the Parser() class raises
    the expected exception if no relevant code (subroutine, module
    etc.) is found in the supplied fparser2 tree.

    '''
    parser = Parser()
    alg_filename = str(tmpdir.join("empty.f90"))
    with open(alg_filename, "w") as ffile:
        ffile.write("")
    alg_parse_tree = parse_fp2(alg_filename)
    with pytest.raises(ParseError) as info:
        parser.invoke_info(alg_parse_tree)
    assert ("Program, module, function or subroutine not found in fparser2 "
            "parse tree.") in str(info.value)
예제 #7
0
def test_parser_parse():
    '''Check that the parse() method in the Parser() class returns the
    expected results (fparser2 ast and a FileInfo instance) when using
    an API other than the NEMO API. Also test that the filename is
    stored in _alg_filename.

    '''
    parser = Parser(api="dynamo0.3")
    assert parser._alg_filename is None
    res1, res2 = parser.parse(os.path.join(
        LFRIC_TEST_PATH, "1_single_invoke.f90"))
    assert "1_single_invoke.f90" in parser._alg_filename
    assert isinstance(res1, Program)
    assert "PROGRAM single_invoke" in str(res1)
    assert isinstance(res2, FileInfo)
    assert res2.name == "single_invoke"
예제 #8
0
def test_parser_createinvokecall():
    '''Test that valid invoke calls are created without an
    exception. Limit this to builtins as kernel calls fail as there is
    no associated use statement declared. Test with names, real
    scalars, integer scalars and structure references, including ones
    to self, as the parser represents these in different ways so the
    create_invoke_call needs to deal with the different
    representations.

    '''
    statement = Call_Stmt(
        "call invoke(name=\"dummy\", setval_c(a,1.0), setval_c(a,1), "
        "setval_c(a,b), setval_c(a%c, b), setval_c(self%a, 1.0), "
        "setval_c(self%a, b))")
    parse = Parser()
    _ = parse.create_invoke_call(statement)
예제 #9
0
def test_parser_invokeinfo_datatypes_self():
    '''Test that the invoke_info method in the Parser class captures the
    required datatype information when the argument is part of a class
    and is referenced via self.

    '''
    alg_filename = os.path.join(
        LFRIC_TEST_PATH, "26.2_mixed_precision_self.f90")
    parser = Parser(kernel_path=LFRIC_TEST_PATH)
    alg_parse_tree = parse_fp2(alg_filename)
    info = parser.invoke_info(alg_parse_tree)
    args = info.calls[0].kcalls[0].args
    assert args[0]._datatype == ("r_solver_operator_type", None)
    assert args[1]._datatype == ("r_solver_field_type", None)
    assert args[2]._datatype == ("real", "r_solver")
    assert args[3]._datatype == ("quadrature_xyoz_type", None)
예제 #10
0
def test_parser_invokeinfo_first(tmpdir):
    '''Check that the invoke_info() method in the Parser() class evaluates
    the first subroutine, module, etc if more than one exist in the
    supplied fparser2 tree.

    '''
    parser = Parser()
    alg_filename = str(tmpdir.join("two_routines.f90"))
    with open(alg_filename, "w") as ffile:
        ffile.write(
            "subroutine first()\n"
            "end subroutine first\n"
            "subroutine second()\n"
            "end subroutine second\n")
    alg_parse_tree = parse_fp2(alg_filename)
    res = parser.invoke_info(alg_parse_tree)
    assert isinstance(res, FileInfo)
    assert res.name == "first"
예제 #11
0
def test_parser_invokeinfo_internalerror():
    '''Test that the invoke_info method in the Parser class raises the
    expected exception if an unexpected child of Type_Declaration_Stmt
    or Data_Component_Def_Stmt is found.

    '''
    alg_filename = os.path.join(
        LFRIC_TEST_PATH, "26.1_mixed_precision.f90")
    parser = Parser(kernel_path=LFRIC_TEST_PATH)
    alg_parse_tree = parse_fp2(alg_filename)
    # Modify parse tree to make it invalid
    alg_parse_tree.children[0].children[1].children[9].items = ["hello"]
    with pytest.raises(InternalError) as info:
        parser.invoke_info(alg_parse_tree)
    assert (
        "Expected first child of Type_Declaration_Stmt or "
        "Data_Component_Def_Stmt to be Declaration_Type_Spec or "
        "Intrinsic_Type_Spec but found 'str'" in str(info.value))
예제 #12
0
def test_parser_invokeinfo_datatypes():
    '''Test that the invoke_info method in the Parser class
    captures the required datatype information for "standard" fields,
    operators and scalars i.e. defined as field_type, operator_type
    and r_def respectively. We also capture the datatype of quadrature
    but don't care. field_type is actually a vector which shows that
    the code works with arrays as well as individual types.

    '''
    alg_filename = os.path.join(LFRIC_TEST_PATH, "10_operator.f90")
    parser = Parser(kernel_path=LFRIC_TEST_PATH)
    alg_parse_tree = parse_fp2(alg_filename)
    info = parser.invoke_info(alg_parse_tree)
    args = info.calls[0].kcalls[0].args
    assert args[0]._datatype == ("operator_type", None)
    assert args[1]._datatype == ("field_type", None)
    assert args[2]._datatype == ("real", "r_def")
    assert args[3]._datatype == ("quadrature_xyoz_type", None)
예제 #13
0
def test_parser_invokeinfo_use_error():
    '''Test that the invoke_info method in the Parser class
    provides None as the datatype to the associated Arg class if an
    argument to an invoke comes from a use statement (as we then do
    not know its datatype). Also check for the same behaviour if the
    variable is not declared at all i.e. is included via a wildcard
    use statement, or implicit none is not specified.

    '''
    alg_filename = os.path.join(
        LFRIC_TEST_PATH, "26.4_mixed_precision_use.f90")
    parser = Parser(kernel_path=LFRIC_TEST_PATH)
    alg_parse_tree = parse_fp2(alg_filename)
    info = parser.invoke_info(alg_parse_tree)
    args = info.calls[0].kcalls[0].args
    assert args[0]._datatype is None
    assert args[1]._datatype == ("r_solver_field_type", None)
    assert args[2]._datatype is None
    assert args[3]._datatype == ("quadrature_xyoz_type", None)
예제 #14
0
def test_parser_invokeinfo_datatypes_clash():
    '''Test that the invoke_info method in the Parser class
    allows multiple symbols with the same name and type but raises an
    exception if a symbol has the same name but a different type. This
    is simply a limitation of the current implementation as we do not
    capture the context of a symbol so do not deal with variable
    scope. This limitation will disapear when the PSyIR is used to
    determine datatypes, see issue #753.

    '''
    alg_filename = os.path.join(
        LFRIC_TEST_PATH, "26.3_mixed_precision_error.f90")
    parser = Parser(kernel_path=LFRIC_TEST_PATH)
    alg_parse_tree = parse_fp2(alg_filename)
    with pytest.raises(NotImplementedError) as info:
        parser.invoke_info(alg_parse_tree)
    assert ("The same symbol 'a' is used for different datatypes, 'real, "
            "r_solver' and 'real, None'. This is not currently supported."
            in str(info.value))
예제 #15
0
def test_parser_invokeinfo_structure_error():
    '''Test that the invoke_info method in the Parser class
    provides None as the datatype to the associated Arg class if an
    argument to an invoke is a structure that comes from a use
    statement (as we then do not know its datatype), but that the
    datatype for a structure is found if the structure is declared
    within the code.

    '''
    alg_filename = os.path.join(
        LFRIC_TEST_PATH, "26.5_mixed_precision_structure.f90")
    parser = Parser(kernel_path=LFRIC_TEST_PATH)
    alg_parse_tree = parse_fp2(alg_filename)
    info = parser.invoke_info(alg_parse_tree)
    args = info.calls[0].kcalls[0].args
    assert args[0]._datatype is None
    assert args[1]._datatype == ("r_solver_field_type", None)
    assert args[2]._datatype == ("real", "r_def")
    assert args[3]._datatype == ("quadrature_xyoz_type", None)
예제 #16
0
def test_parser_caseinsensitive2(monkeypatch):
    '''Check that the test for the existance of a kernel call in a use
    statement is case insensitive.

    '''
    parser = Parser()
    use = Use_Stmt("use testkern_mod, only : TESTKERN_TYPE")
    parser.update_arg_to_module_map(use)

    def dummy_func(arg1, arg2, arg3, arg4):
        '''A dummy function used by monkeypatch to override the get_kernel_ast
        function. We don't care about the arguments as we just want to
        raise an exception.

        '''
        raise NotImplementedError("test_parser_caseinsensitive2")
    monkeypatch.setattr("psyclone.parse.algorithm.get_kernel_ast", dummy_func)
    with pytest.raises(NotImplementedError) as excinfo:
        # We have monkeypatched the function 'get_kernel_ast' to
        # return 'NotImplementedError' with a string associated with
        # this test so we know that we have got to this function if
        # this exception is raised. The case insensitive test we
        # really care about is before this function is called (and it
        # raises a ParseError) so we know that if we don't get a
        # ParseError then all is well.
        parser.create_coded_kernel_call("TestKern_Type", None)
    # Sanity check that the exception is the monkeypatched one.
    assert str(excinfo.value) == "test_parser_caseinsensitive2"
예제 #17
0
def test_parser_caseinsensitive2(monkeypatch):
    '''Check that the test for the existance of a kernel call in a use
    statement is case insensitive.

    '''
    def dummy_func(arg1, arg2, arg3, arg4):
        '''A dummy function used by monkeypatch to override the get_kernel_ast
        function. We don't care about the arguments as we just want to
        raise an exception.

        '''
        # pylint: disable=unused-argument
        raise NotImplementedError("test_parser_caseinsensitive2")

    monkeypatch.setattr("psyclone.parse.kernel.get_kernel_ast", dummy_func)
    from fparser.two import Fortran2003 as f2003
    from fparser.two.parser import ParserFactory
    ParserFactory().create(std="f2003")
    parser = Parser()
    use = f2003.Use_Stmt("use my_mod, only : MY_KERN")
    parser.update_arg_to_module_map(use)
    with pytest.raises(NotImplementedError) as excinfo:
        # We have monkeypatched the function 'get_kernel_ast' to
        # return 'NotImplementedError' with a string associated with
        # this test so we know that we have got to this function if
        # this exception is raised. The case insensitive test we
        # really care about is before this function is called (and it
        # raises a ParseError) so we know that if we don't get a
        # ParseError then all is well.
        parser.create_coded_kernel_call("My_Kern", None)
    # Sanity check that the exception is the monkeypatched one.
    assert str(excinfo.value) == "test_parser_caseinsensitive2"
예제 #18
0
def test_parser_invokeinfo_datatypes_mixed():
    '''Test that the invoke_info method in the Parser class captures the
    required datatype information with mixed-precision fields,
    operators and scalars e.g. defined as r_solver_field_type,
    r_solver_operator_type and r_solver respectively.

    Also tests that the datatype information is always lower case
    irrespective of the case of the declaration and argument. This
    covers the situation where the variable is declared and used with
    different case e.g. real a\n call invoke(kern(A))

    '''
    alg_filename = os.path.join(
        LFRIC_TEST_PATH, "26.1_mixed_precision.f90")
    parser = Parser(kernel_path=LFRIC_TEST_PATH)
    alg_parse_tree = parse_fp2(alg_filename)
    info = parser.invoke_info(alg_parse_tree)
    args = info.calls[0].kcalls[0].args
    assert args[0]._datatype == ("r_solver_operator_type", None)
    assert args[1]._datatype == ("r_solver_field_type", None)
    assert args[2]._datatype == ("real", "r_solver")
    assert args[3]._datatype == ("quadrature_xyoz_type", None)
예제 #19
0
def test_parser_parse_linelength():
    '''Check that the parse() method in the Parser() class raises an
    exception if one or more of the lines is too long (>132
    characters) in the supplied input file when _line_length is set to
    True and does not raise an exception by default.

    '''
    parser = Parser()
    parser.parse(os.path.join(LFRIC_TEST_PATH, "13_alg_long_line.f90"))

    parser = Parser(line_length=True)
    with pytest.raises(ParseError) as info:
        parser.parse(os.path.join(LFRIC_TEST_PATH, "13_alg_long_line.f90"))
    assert ("the file does not conform to the specified 132 line length "
            "limit" in str(info.value))
예제 #20
0
def test_parser_caseinsensitive1():
    '''Check that the test for the existance of a builtin call in a use
    statement is case insensitive.

    '''
    parser = Parser()
    use = Use_Stmt("use my_mod, only : SETVAL_X")
    parser.update_arg_to_module_map(use)
    with pytest.raises(ParseError) as excinfo:
        parser.create_builtin_kernel_call("SetVal_X", None)
    assert "A built-in cannot be named in a use statement" \
        in str(excinfo.value)
예제 #21
0
def test_parser_caseinsensitive1():
    '''Check that the test for the existance of a builtin call in a use
    statement is case insensitive.

    '''
    from fparser.two import Fortran2003 as f2003
    from fparser.two.parser import ParserFactory
    ParserFactory().create(std="f2003")
    parser = Parser()
    use = f2003.Use_Stmt("use my_mod, only : SETVAL_X")
    parser.update_arg_to_module_map(use)
    with pytest.raises(ParseError) as excinfo:
        parser.create_builtin_kernel_call("SetVal_X", None)
    assert "A built-in cannot be named in a use statement" \
        in str(excinfo.value)