def test_imp_none_in_subroutine(): ''' test that implicit none can be added to a subroutine ''' module = ModuleGen(name="testmodule") subroutine = SubroutineGen(module, name="testsubroutine") module.add(subroutine) subroutine.add(ImplicitNoneGen(subroutine)) assert 'IMPLICIT NONE' in str(subroutine.root)
def test_progunitgen_multiple_generic_use(): '''Check that we correctly handle the case where duplicate use statements are added''' module = ModuleGen(name="testmodule") sub = SubroutineGen(module, name="testsubroutine") module.add(sub) sub.add(UseGen(sub, name="fred")) sub.add(UseGen(sub, name="fred")) assert count_lines(sub.root, "USE fred") == 1
def test_imp_none_in_subroutine_already_exists(): ''' test that implicit none is not added to a subroutine when one already exists''' module = ModuleGen(name="testmodule") sub = SubroutineGen(module, name="testsubroutine", implicitnone=True) module.add(sub) sub.add(ImplicitNoneGen(sub)) count = count_lines(sub.root, "IMPLICIT NONE") assert count == 1, \ "There should only be one instance of IMPLICIT NONE"
def test_imp_none_exception_if_wrong_parent(): ''' test that an exception is thrown if implicit none is added and the parent is not a module or a subroutine ''' module = ModuleGen(name="testmodule") sub = SubroutineGen(module, name="testsubroutine") module.add(sub) dogen = DoGen(sub, "i", "1", "10") sub.add(dogen) with pytest.raises(Exception): dogen.add(ImplicitNoneGen(dogen))
def test_typedeclgen_names(): ''' Check that the names method of TypeDeclGen works as expected ''' module = ModuleGen(name="testmodule") sub = SubroutineGen(module, name="testsubroutine") module.add(sub) dgen = TypeDeclGen(sub, datatype="my_type", entity_decls=["type1"]) sub.add(dgen) names = dgen.names assert len(names) == 1 assert names[0] == "type1"
def test_do_loop_with_increment(): ''' Test that we correctly generate code for a do loop with non-unit increment ''' module = ModuleGen(name="testmodule") sub = SubroutineGen(module, name="testsub") module.add(sub) dogen = DoGen(sub, "it", "1", "10", step="2") sub.add(dogen) count = count_lines(sub.root, "DO it=1,10,2") assert count == 1
def test_basegen_previous_loop_no_loop(): '''Check that we raise an error when requesting the position of the previous loop if we don't have a loop ''' module = ModuleGen(name="testmodule") sub = SubroutineGen(module, name="testsubroutine") module.add(sub) # Request the position of the last loop # even though we haven't got one with pytest.raises(RuntimeError) as err: sub.previous_loop() assert "no loop found - there is no previous loop" in str(err)
def test_basegen_last_declaration_no_vars(): '''Check that we raise an error when requesting the position of the last variable declaration if we don't have any variables''' module = ModuleGen(name="testmodule") sub = SubroutineGen(module, name="testsubroutine") module.add(sub) # Request the position of the last variable declaration # even though we haven't got any with pytest.raises(RuntimeError) as err: sub.last_declaration() assert "no variable declarations found" in str(err)
def test_adduse_default_funcnames(): ''' Test that the adduse module method works correctly when we do not specify a list of funcnames ''' module = ModuleGen(name="testmodule") sub = SubroutineGen(module, name="testsubroutine") module.add(sub) call = CallGen(sub, name="testcall", args=["a", "b"]) sub.add(call) from psyclone.f2pygen import adduse adduse("fred", call.root) gen = str(sub.root) expected = (" SUBROUTINE testsubroutine()\n" " USE fred\n") assert expected in gen
def test_imp_none_in_subroutine_with_use_and_decs(): ''' test that implicit none is added after any use statements and before any declarations in a subroutine when auto (the default) is used for insertion''' module = ModuleGen(name="testmodule") sub = SubroutineGen(module, name="testsubroutine") module.add(sub) sub.add(DeclGen(sub, datatype="integer", entity_decls=["var1"])) sub.add(TypeDeclGen(sub, datatype="my_type", entity_decls=["type1"])) sub.add(UseGen(sub, "fred")) sub.add(ImplicitNoneGen(sub)) in_idx = line_number(sub.root, "IMPLICIT NONE") assert in_idx == 2
def test_adduse(): ''' Test that the adduse module method works correctly when we use a call object as our starting point ''' module = ModuleGen(name="testmodule") sub = SubroutineGen(module, name="testsubroutine") module.add(sub) call = CallGen(sub, name="testcall", args=["a", "b"]) sub.add(call) from psyclone.f2pygen import adduse adduse("fred", call.root, only=True, funcnames=["astaire"]) gen = str(sub.root) expected = (" SUBROUTINE testsubroutine()\n" " USE fred, ONLY: astaire\n") assert expected in gen
def test_typeselectiongen(): ''' Check that SelectionGen works as expected for a type ''' from psyclone.f2pygen import SelectionGen module = ModuleGen(name="testmodule") sub = SubroutineGen(module, name="testsubroutine") module.add(sub) sgen = SelectionGen(sub, expr="my_var=>another_var", typeselect=True) sub.add(sgen) agen = AssignGen(sgen, lhs="happy", rhs=".TRUE.") sgen.addcase("fspace", [agen]) sgen.adddefault() gen = str(sub.root) print gen assert "SELECT TYPE ( my_var=>another_var )" in gen assert "TYPE IS ( fspace )" in gen
def test_subroutine_var_with_implicit_none(): ''' test that a variable is added after an implicit none statement in a subroutine''' module = ModuleGen(name="testmodule") subroutine = SubroutineGen(module, name="testsubroutine", implicitnone=True) module.add(subroutine) subroutine.add( DeclGen(subroutine, datatype="integer", entity_decls=["var1"])) idx_var = line_number(subroutine.root, "INTEGER var1") idx_imp_none = line_number(subroutine.root, "IMPLICIT NONE") print str(module.root) assert idx_var - idx_imp_none == 1, \ "variable declation must be after implicit none"
def test_do_loop_add_after(): ''' Test that we correctly generate code for a do loop when adding a child to it with position *after* ''' module = ModuleGen(name="testmodule") sub = SubroutineGen(module, name="testsub") module.add(sub) dogen = DoGen(sub, "it", "1", "10", step="2") sub.add(dogen) assign1 = AssignGen(dogen, lhs="happy", rhs=".TRUE.") dogen.add(assign1) assign2 = AssignGen(dogen, lhs="sad", rhs=".FALSE.") dogen.add(assign2, position=["before", assign1.root]) a1_line = line_number(sub.root, "happy = ") a2_line = line_number(sub.root, "sad = ") assert a1_line > a2_line
def test_lfricfields_stub_err(): ''' Check that the LFRicFields constructor raises the expected internal error if it encounters an unrecognised intrinsic type of a field argument when generating a kernel stub. ''' fparser.logging.disable(fparser.logging.CRITICAL) ast = fpapi.parse(FIELD_CODE, ignore_comments=False) metadata = DynKernMetadata(ast) kernel = DynKern() kernel.load_meta(metadata) # Create an empty Kernel stub module and subroutine objects psy_module = ModuleGen("testkern_2qr_int_field_mod") sub_stub = SubroutineGen(psy_module, name="testkern_2qr_int_field_code", implicitnone=True) # Sabotage the field argument to make it have an invalid intrinsic type fld_arg = kernel.arguments.args[1] fld_arg.descriptor._data_type = "gh_invalid_type" print(fld_arg.descriptor._data_type) with pytest.raises(InternalError) as err: LFRicFields(kernel)._stub_declarations(sub_stub) assert ("Found an unsupported data type 'gh_invalid_type' in " "kernel stub declarations for the field argument 'field_2'. " "Supported types are {0}.".format( LFRicArgDescriptor.VALID_FIELD_DATA_TYPES) in str(err.value))
def test_selectiongen_addcase(): ''' Check that SelectionGen.addcase() works as expected when no content is supplied''' from psyclone.f2pygen import SelectionGen module = ModuleGen(name="testmodule") sub = SubroutineGen(module, name="testsubroutine") module.add(sub) sgen = SelectionGen(sub, expr="my_var") sub.add(sgen) sgen.addcase("1") gen = str(sub.root) print gen expected = (" SELECT CASE ( my_var )\n" " CASE ( 1 )\n" " END SELECT") assert expected in gen
def test_subgen_implicit_none_default(): ''' test that implicit none is not added to the subroutine by default ''' module = ModuleGen(name="testmodule") sub = SubroutineGen(module, name="testsubroutine") module.add(sub) count = count_lines(sub.root, "IMPLICIT NONE") assert count == 0, "IMPLICIT NONE SHOULD NOT EXIST BY DEFAULT"
def test_subgen_implicit_none_false(): ''' test that implicit none is not added to the subroutine if not requested ''' module = ModuleGen(name="testmodule") sub = SubroutineGen(module, name="testsubroutine", implicitnone=False) module.add(sub) count = count_lines(sub.root, "IMPLICIT NONE") assert count == 0, "IMPLICIT NONE SHOULD NOT EXIST"
def test_subgen_implicit_none_true(): ''' test that implicit none is added to the subroutine if requested ''' module = ModuleGen(name="testmodule") sub = SubroutineGen(module, name="testsubroutine", implicitnone=True) module.add(sub) count = count_lines(sub.root, "IMPLICIT NONE") assert count == 1, "IMPLICIT NONE SHOULD EXIST"
def test_declgen_wrong_type(): ''' Check that we raise an appropriate error if we attempt to create a DeclGen for an unsupported type ''' module = ModuleGen(name="testmodule") sub = SubroutineGen(module, name="testsubroutine") module.add(sub) with pytest.raises(RuntimeError) as err: _ = DeclGen(sub, datatype="complex", entity_decls=["rvar1"]) assert "Only integer and real are currently supported" in str(err)
def gen_code(self, parent): ''' Generates Dynamo specific invocation code (the subroutine called by the associated invoke call in the algorithm layer). This consists of the PSy invocation subroutine and the declaration of its arguments.''' from psyclone.f2pygen import SubroutineGen, TypeDeclGen # create the subroutine invoke_sub = SubroutineGen(parent, name=self.name, args=self.psy_unique_var_names) self.schedule.gen_code(invoke_sub) parent.add(invoke_sub) # add the subroutine argument declarations my_typedecl = TypeDeclGen(invoke_sub, datatype="field_type", entity_decls=self.psy_unique_var_names, intent="inout") invoke_sub.add(my_typedecl)
def test_typedeclgen_missing_names(): ''' Check that we raise an error if we attempt to create TypeDeclGen without naming the variables ''' module = ModuleGen(name="testmodule") sub = SubroutineGen(module, name="testsubroutine") module.add(sub) with pytest.raises(RuntimeError) as err: _ = TypeDeclGen(sub, datatype="my_type") assert ("Cannot create a declaration of a derived-type variable " "without specifying" in str(err))
def test_declgen_missing_names(): ''' Check that we raise an error if we attempt to create a DeclGen without naming the variable(s) ''' module = ModuleGen(name="testmodule") sub = SubroutineGen(module, name="testsubroutine") module.add(sub) with pytest.raises(RuntimeError) as err: _ = DeclGen(sub, datatype="integer") assert ("Cannot create a variable declaration without specifying " "the name" in str(err))
def test_modulegen_add_wrong_parent(): ''' Check that attempting to add an object to a ModuleGen fails if the object's parent is not that ModuleGen ''' module = ModuleGen(name="testmodule") module_wrong = ModuleGen(name="another_module") sub = SubroutineGen(module_wrong, name="testsubroutine") with pytest.raises(RuntimeError) as err: module.add(sub) print str(err) assert "because it is not a descendant of it or of any of" in str(err)
def test_basegen_start_parent_loop_dbg(capsys): '''Check the debug option to the start_parent_loop method''' module = ModuleGen(name="testmodule") sub = SubroutineGen(module, name="testsubroutine") module.add(sub) loop = DoGen(sub, "it", "1", "10") sub.add(loop) call = CallGen(loop, "testcall") loop.add(call) call.start_parent_loop(debug=True) out, _ = capsys.readouterr() print out expected = ("Parent is a do loop so moving to the parent\n" "The type of the current node is now <class " "'fparser.one.block_statements.Do'>\n" "The type of parent is <class " "'fparser.one.block_statements.Subroutine'>\n" "Finding the loops position in its parent ...\n" "The loop's index is 0\n") assert expected in out
def test_basegen_append_default(): ''' Check if no position argument is supplied to BaseGen.add() then it defaults to appending ''' from psyclone.f2pygen import BaseGen module = ModuleGen(name="testmodule") sub = SubroutineGen(module, name="testsubroutine") module.add(sub) BaseGen.add(sub, DeclGen(sub, datatype="integer", entity_decls=["var1"])) BaseGen.add(sub, CommentGen(sub, " hello")) cindex = line_number(sub.root, "hello") assert cindex == 3
def test_selectiongen(): ''' Check that SelectionGen works as expected ''' from psyclone.f2pygen import SelectionGen module = ModuleGen(name="testmodule") sub = SubroutineGen(module, name="testsubroutine") module.add(sub) sgen = SelectionGen(sub, expr="my_var") sub.add(sgen) agen = AssignGen(sgen, lhs="happy", rhs=".TRUE.") sgen.addcase("1", [agen]) # TODO how do we specify what happens in the default case? sgen.adddefault() gen = str(sub.root) print gen expected = ("SELECT CASE ( my_var )\n" "CASE ( 1 )\n" " happy = .TRUE.\n" "CASE DEFAULT\n" " END SELECT") assert expected in gen assert False
def test_adduse_empty_only(): ''' Test that the adduse module method works correctly when we specify that we want it to be specific but then don't provide a list of entities for the only qualifier ''' module = ModuleGen(name="testmodule") sub = SubroutineGen(module, name="testsubroutine") module.add(sub) from psyclone.f2pygen import adduse # Add a use statement with only=True but an empty list of entities adduse("fred", sub.root, only=True, funcnames=[]) assert count_lines(sub.root, "USE fred") == 1 assert count_lines(sub.root, "USE fred, only") == 0
def gen_code(self, parent): ''' Generates GOcean specific invocation code (the subroutine called by the associated invoke call in the algorithm layer). This consists of the PSy invocation subroutine and the declaration of its arguments.''' from psyclone.f2pygen import SubroutineGen, DeclGen, TypeDeclGen # create the subroutine invoke_sub = SubroutineGen(parent, name=self.name, args=self.psy_unique_var_names) parent.add(invoke_sub) self.schedule.gen_code(invoke_sub) # add the subroutine argument declarations for arrays if len(self.unique_args_arrays) > 0: my_decl_arrays = DeclGen(invoke_sub, datatype="REAL", intent="inout", kind="go_wp", entity_decls=self.unique_args_arrays, dimension=":,:") invoke_sub.add(my_decl_arrays) # add the subroutine argument declarations for scalars if len(self.unique_args_scalars) > 0: my_decl_scalars = \ TypeDeclGen(invoke_sub, datatype="scalar_field_type", entity_decls=self.unique_args_scalars, intent="inout") invoke_sub.add(my_decl_scalars)
def test_basegen_start_parent_loop_omp_end_dbg(capsys): '''Check the debug option to the start_parent_loop method when we have an OpenMP end directive''' module = ModuleGen(name="testmodule") sub = SubroutineGen(module, name="testsubroutine") module.add(sub) dgen = DirectiveGen(sub, "omp", "end", "do", "") sub.add(dgen) loop = DoGen(sub, "it", "1", "10") sub.add(loop) call = CallGen(loop, "testcall") loop.add(call) call.start_parent_loop(debug=True) out, _ = capsys.readouterr() print out expected = ("Parent is a do loop so moving to the parent\n" "The type of the current node is now <class " "'fparser.one.block_statements.Do'>\n" "The type of parent is <class " "'fparser.one.block_statements.Subroutine'>\n" "Finding the loops position in its parent ...\n" "The loop's index is 1\n" "The type of the object at the index is <class " "'fparser.one.block_statements.Do'>\n" "If preceding node is a directive then move back one\n" "preceding node is a directive so find out what type ...\n") assert expected in out