def test_script_invalid_content_runtime(): # pylint: disable=invalid-name ''' checks that generator.py raises an appropriate error when a script file contains valid python syntactically but produces a runtime exception. ''' with pytest.raises(GenerationError): _, _ = generate(os.path.join(BASE_PATH, "dynamo0p3", "1_single_invoke.f90"), api="dynamo0.3", script_name=os.path.join(BASE_PATH, "dynamo0p3", "runtime_error.py"))
def test_multi_kernel_named_invoke(): ''' Test that we correctly handle a named invoke call that contains more than one kernel ''' alg, _ = generate(os.path.join(os.path.dirname(os.path.abspath(__file__)), "test_files", "dynamo0p3", "4.9_named_multikernel_invokes.f90"), api="dynamo0.3") gen = str(alg) assert "USE multikernel_invokes_7_psy, ONLY: invoke_some_name" in gen assert ("CALL invoke_some_name(a, b, c, istp, rdt, d, ascalar, f, g, e)" in gen)
def test_script_file_not_found_relative(): # pylint: disable=invalid-name ''' checks that generator.py raises an appropriate error when a script file is supplied that can't be found in the Python path. In this case the script path is not supplied so must be found via the PYTHONPATH variable''' with pytest.raises(GenerationError): _, _ = generate(os.path.join(BASE_PATH, "dynamo0p3", "1_single_invoke.f90"), api="dynamo0.3", script_name="non_existant.py")
def test_multi_deref_derived_type_args(): ''' Test the case where a given kernel argument is specified using different derived types in the same invoke. ''' alg, _ = generate(os.path.join( os.path.dirname(os.path.abspath(__file__)), "test_files", "dynamo0p3", "1.6.3_single_invoke_multiple_derived_types.f90"), api="dynamo0.3") gen = str(alg) print(gen) assert ("CALL invoke_0(f1, obj_a % iflag, f2, m1, m2, obj_b % iflag, " "obj_a % obj_b, obj_b % obj_a)" in gen)
def test_vector_field_arg_deref(): ''' Test that we generate a correct invoke call when a kernel argument representing a field vector is obtained by de-referencing a derived type ''' alg, _ = generate(os.path.join(os.path.dirname(os.path.abspath(__file__)), "test_files", "dynamo0p3", "8.1_vector_field_deref.f90"), api="dynamo0.3") gen = str(alg) print(gen) assert "CALL invoke_0_testkern_chi_type(f1, box % chi, f2)" in gen
def test_single_function_named_invoke(self): ''' Test that we correctly handle a named invoke call ''' alg, _ = generate( os.path.join(os.path.dirname(os.path.abspath(__file__)), "test_files", "dynamo0p3", "1.0.1_single_named_invoke.f90"), api="dynamo0.3") gen = str(alg) print gen assert "USE single_invoke_psy, ONLY: invoke_important_invoke" in gen assert "CALL invoke_important_invoke(a, f1, f2, m1, m2)" in gen
def test_multi_function_multi_invokes(): ''' two invokes, each containing multiple functions ''' alg, _ = generate(os.path.join(BASE_PATH, "3.1_multi_functions_multi_invokes.f90"), api="dynamo0.3") gen = str(alg) print(gen) assert "USE multi_functions_multi_invokes_psy, ONLY: invoke_1" in gen assert "USE multi_functions_multi_invokes_psy, ONLY: invoke_0" in gen assert "CALL invoke_0(a, f1, f2, m1, m2, istp, qr)" in gen assert "CALL invoke_1(f1, f2, m1, a, m2, istp, qr)" in gen
def test_op_and_scalar_and_qr_derived_type_args(): ''' Test the case where the operator, scalar and qr arguments to a kernel are all supplied by de-referencing derived types ''' alg, _ = generate(os.path.join( os.path.dirname(os.path.abspath(__file__)), "test_files", "dynamo0p3", "10.6.1_operator_no_field_scalar_deref.f90"), api="dynamo0.3") gen = str(alg) print gen assert ("CALL invoke_0_testkern_operator_nofield_scalar_type(" "opbox%my_mapping, box%b(1), qr%get_instance(qr3, 9, 3))" in gen)
def test_single_function_multi_invokes(): ''' three invokes, each containing a single function ''' alg, _ = generate(os.path.join(BASE_PATH, "3_multi_invokes.f90"), api="dynamo0.3") gen = str(alg) assert "USE testkern, ONLY: testkern_type" in gen assert "USE testkern_qr, ONLY: testkern_qr_type" in gen assert "CALL invoke_0_testkern_type(a, f1, f2, m1, m2)" in gen assert "CALL invoke_2_testkern_type(a, f1, f2, m1, m2)" in gen assert ("CALL invoke_1_testkern_qr_type(f1, f2, m1, a, m2, istp, qr)" in gen)
def test_single_invoke_dynamo0p1(): ''' Test for correct code transformation for a single function specified in an invoke call for the dynamo0.1 API. We use the generate function as parse and PSyFactory need to be called before AlgGen so it is simpler to use this. ''' alg, _ = generate(os.path.join(os.path.dirname(os.path.abspath(__file__)), "test_files", "dynamo0p1", "algorithm", "1_single_function.f90"), api="dynamo0.1") gen = str(alg) assert "USE psy_single_function, ONLY: invoke_0_testkern_type" in gen assert "CALL invoke_0_testkern_type(f1, f2, m1)" in gen
def test_invoke_named_invoke(self): ''' Test that we correctly handle a named invoke call where the naming string already begins with "invoke_" ''' alg, _ = generate( os.path.join(os.path.dirname(os.path.abspath(__file__)), "test_files", "dynamo0p3", "1.0.5_invoke_named_invoke.f90"), api="dynamo0.3") gen = str(alg) print gen assert "USE single_invoke_psy, ONLY: invoke_important" in gen assert "CALL invoke_important(a, f1, f2, m1, m2)" in gen
def test_script_attr_error(): ''' checks that generator.py raises an appropriate error when a script file contains a trans() function which raises an attribute error. This is what we previously used to check for a script file not containing a trans() function.''' with pytest.raises(GenerationError) as excinfo: _, _ = generate(os.path.join(BASE_PATH, "dynamo0p3", "1_single_invoke.f90"), api="dynamo0.3", script_name=os.path.join(BASE_PATH, "dynamo0p3", "error_trans.py")) assert 'object has no attribute' in str(excinfo.value)
def test_op_and_scalar_and_qr_derived_type_args(): ''' Test the case where the operator, scalar and qr arguments to a kernel are all supplied by de-referencing derived types. ''' alg, _ = generate(os.path.join( os.path.dirname(os.path.abspath(__file__)), "test_files", "dynamo0p3", "10.6.1_operator_no_field_scalar_deref.f90"), api="dynamo0.3") gen = str(alg) assert ("CALL invoke_0_testkern_operator_nofield_scalar_type(" "opbox % my_mapping, box % b(1), qr % init_quadrature_symmetrical" "(3_i_def, qrl_gauss))" in gen)
def test_deref_derived_type_args(): ''' Test the case where a kernel argument is specified as both a component of a derived type and as the result of a call to a type-bound procedure ''' alg, _ = generate(os.path.join( os.path.dirname(os.path.abspath(__file__)), "test_files", "dynamo0p3", "1.6.2_single_invoke_1_int_from_derived_type.f90"), api="dynamo0.3") gen = str(alg) print(gen) assert ("CALL invoke_0(f1, my_obj%iflag, f2, m1, m2, my_obj%get_flag(), " "my_obj%get_flag(switch), my_obj%get_flag(int_wrapper%data))" in gen)
def test_script_null_trans_relative(): ''' checks that generator.py works correctly when the trans() function in a valid script file does no transformations (it simply passes input to output). In this case the valid script file contains no path and must therefore be found via the PYTHOPATH path list. ''' alg1, psy1 = generate(os.path.join(BASE_PATH, "dynamo0p3", "1_single_invoke.f90"), api="dynamo0.3") # set up the python path so that null_trans.py can be found os.sys.path.append(os.path.join(BASE_PATH, "dynamo0p3")) alg2, psy2 = generate(os.path.join(BASE_PATH, "dynamo0p3", "1_single_invoke.f90"), api="dynamo0.3", script_name="null_trans.py") # remove imported module so we do not affect any following tests delete_module("null_trans") os.sys.path.pop() # we need to remove the first line before comparing output as # this line is an instance specific header assert '\n'.join(str(alg1).split('\n')[1:]) == \ '\n'.join(str(alg2).split('\n')[1:]) assert str(psy1) == str(psy2)
def test_long_line_continuator(): '''Tests that an input algorithm file with long lines of a type not recognised by FortLineLength (assignments in this case), which already have continuators to make the code conform to the line length limit, does not cause an error. ''' alg, _ = generate(os.path.join(os.path.dirname(os.path.abspath(__file__)), "test_files", "dynamo0p3", "13.2_alg_long_line_continuator.f90"), api="dynamo0.3") input_string = str(alg) fll = FortLineLength() _ = fll.process(input_string)
def test_script_null_trans(): ''' checks that generator.py works correctly when the trans() function in a valid script file does no transformations (it simply passes input to output). In this case the valid script file has an explicit path and must therefore exist at this location. ''' alg1, psy1 = generate(os.path.join(BASE_PATH, "dynamo0p3", "1_single_invoke.f90"), api="dynamo0.3") alg2, psy2 = generate(os.path.join(BASE_PATH, "dynamo0p3", "1_single_invoke.f90"), api="dynamo0.3", script_name=os.path.join(BASE_PATH, "dynamo0p3", "null_trans.py")) # remove module so we do not affect any following tests delete_module("null_trans") # we need to remove the first line before comparing output as # this line is an instance specific header assert '\n'.join(str(alg1).split('\n')[1:]) == \ '\n'.join(str(alg2).split('\n')[1:]) assert '\n'.join(str(psy1).split('\n')[1:]) == \ '\n'.join(str(psy2).split('\n')[1:])
def test_invalid_kern_naming(): ''' Check that we raise the expected error if an invalid kernel-renaming scheme is supplied. ''' alg_filename = (os.path.join(os.path.dirname(os.path.abspath(__file__)), "test_files", "dynamo0p3", "1_single_invoke.f90")) # Simply supplying the wrong value on the command line is picked up # by the argparse module so we call generate() directly with an # incorrect value with pytest.raises(GenerationError) as err: _, _ = generate(alg_filename, api="dynamo0.3", kern_naming="not-a-scheme") assert "Invalid kernel-renaming scheme supplied" in str(err.value) assert "but got 'not-a-scheme'" in str(err.value)
def test_named_multi_invokes(self): ''' Check that we generate correct code when we have more than one named invoke in an Algorithm file ''' alg, _ = generate(os.path.join( BASE_PATH, "3.2_multi_functions_multi_named_invokes.f90"), api="dynamo0.3") gen = str(alg) assert "USE testkern, ONLY: testkern_type" in gen assert "USE testkern_qr, ONLY: testkern_qr_type" in gen assert ("USE multi_functions_multi_invokes_psy, ONLY: " "invoke_my_first" in gen) assert ("USE multi_functions_multi_invokes_psy, ONLY: " "invoke_my_second" in gen) assert "CALL invoke_my_first(a, f1, f2," in gen assert "CALL invoke_my_second(f1, f2, m1, a, m2" in gen
def test_multi_position_named_invoke(): ''' Test that we correctly handle a named invoke call that contains more than one kernel when the name= clause appears at different points in the Invoke argument list ''' alg, _ = generate(os.path.join(os.path.dirname(os.path.abspath(__file__)), "test_files", "dynamo0p3", "4.10_multi_position_named_invokes.f90"), api="dynamo0.3") gen = str(alg) assert "USE multikernel_invokes_7_psy, ONLY: invoke_name_first" in gen assert "USE multikernel_invokes_7_psy, ONLY: invoke_name_middle" in gen assert "USE multikernel_invokes_7_psy, ONLY: invoke_name_last" in gen assert ("CALL invoke_name_first(a, b, istp, rdt, d, e, ascalar, f, c, " "g, qr)") in gen assert ("CALL invoke_name_middle(a, b, istp, rdt, d, e, ascalar, f, c, " "g, qr)") in gen assert ("CALL invoke_name_last(a, b, istp, rdt, d, e, ascalar, f, c, " "g, qr)") in gen
def test_script_trans(): ''' checks that generator.py works correctly when a transformation is provided as a script, i.e. it applies the transformations correctly. We use loop fusion as an example.''' from psyclone.parse import parse from psyclone.psyGen import PSyFactory from psyclone.transformations import LoopFuseTrans root_path = os.path.dirname(os.path.abspath(__file__)) base_path = os.path.join(root_path, "test_files", "dynamo0p3") # first loop fuse explicitly (without using generator.py) parse_file = os.path.join(base_path, "4_multikernel_invokes.f90") _, invoke_info = parse(parse_file, api="dynamo0.3") psy = PSyFactory("dynamo0.3").create(invoke_info) invoke = psy.invokes.get("invoke_0") schedule = invoke.schedule # remove unecessary halos between loops. At the moment we have no # intra halo analysis so we add them before all loops just in # case. del schedule.children[4:7] loop1 = schedule.children[3] loop2 = schedule.children[4] trans = LoopFuseTrans() schedule.view() schedule, _ = trans.apply(loop1, loop2) invoke.schedule = schedule generated_code_1 = psy.gen schedule.view() # second loop fuse using generator.py and a script _, generated_code_2 = generate(parse_file, api="dynamo0.3", script_name=os.path.join( base_path, "loop_fuse_trans.py")) # remove module so we do not affect any following tests delete_module("loop_fuse_trans") # third - check that the results are the same ... assert str(generated_code_1) == str(generated_code_2)
def test_invalid_api(): ''' checks that alg_gen raises appropriate error when an invalid api is supplied ''' with pytest.raises(GenerationError): generate(os.path.join(BASE_PATH, "dynamo0p1", "algorithm", "1_single_function.f90"), api="invalid")
def test_non_existant_filename(): ''' checks that alg_gen raises appropriate error when a non-existant filename is supplied ''' with pytest.raises(IOError): generate("non_existant_file.f90")
''' Script to demonstrate the use on the line wrapping support in PSyclone''' from __future__ import print_function from psyclone.generator import generate from psyclone.line_length import FortLineLength # long_lines=True checks whether the input fortran conforms to the 132 line # length limit ALG, PSY = generate( "longlines.f90", kernel_path="../../src/psyclone/tests/test_files/dynamo0p3", line_length=True) LINE_LENGTH = FortLineLength() ALG_STR = LINE_LENGTH.process(str(ALG)) print(ALG_STR) PSY_STR = LINE_LENGTH.process(str(PSY)) print(PSY_STR)