def test_fw_gen_declaration(): '''Check the FortranWriter class gen_declaration method produces the expected declarations. ''' fvisitor = FortranWriter() # Basic entry symbol = Symbol("dummy1", "integer") result = fvisitor.gen_declaration(symbol) assert result == "integer(i_def) :: dummy1\n" # Array with intent symbol = Symbol("dummy2", "integer", shape=[2, None, 2], interface=Symbol.Argument(access=Symbol.Access.READ)) result = fvisitor.gen_declaration(symbol) assert result == "integer(i_def), dimension(2,:,2), intent(in) :: dummy2\n" # Array with unknown intent symbol = Symbol("dummy2", "integer", shape=[2, None, 2], interface=Symbol.Argument(access=Symbol.Access.UNKNOWN)) result = fvisitor.gen_declaration(symbol) assert result == "integer(i_def), dimension(2,:,2) :: dummy2\n" # Constant symbol = Symbol("dummy3", "integer", constant_value=10) result = fvisitor.gen_declaration(symbol) assert result == "integer(i_def), parameter :: dummy3 = 10\n"
def test_symtab_implementation_for_opencl(): ''' Tests that the GOcean specialised Symbol Table implements the abstract properties needed to generate OpenCL. ''' kschedule = GOKernelSchedule('test') # Test symbol table without any kernel argument with pytest.raises(GenerationError) as err: _ = kschedule.symbol_table.iteration_indices assert ("GOcean 1.0 API kernels should always have at least two " "arguments representing the iteration indices but the Symbol " "Table for kernel 'test' has only 0 argument(s).") in str(err) # Test symbol table with 1 kernel argument arg1 = Symbol("arg1", "integer", [], interface=Symbol.Argument(access=Symbol.Access.READ)) kschedule.symbol_table.add(arg1) kschedule.symbol_table.specify_argument_list([arg1]) with pytest.raises(GenerationError) as err: _ = kschedule.symbol_table.iteration_indices assert ("GOcean 1.0 API kernels should always have at least two " "arguments representing the iteration indices but the Symbol " "Table for kernel 'test' has only 1 argument(s).") in str(err) # Test symbol table with 2 kernel argument arg2 = Symbol("arg2", "integer", shape=[], interface=Symbol.Argument(access=Symbol.Access.READ)) kschedule.symbol_table.add(arg2) kschedule.symbol_table.specify_argument_list([arg1, arg2]) iteration_indices = kschedule.symbol_table.iteration_indices assert iteration_indices[0] is arg1 assert iteration_indices[1] is arg2 # Test symbol table with 3 kernel argument arg3 = Symbol("buffer1", "real", shape=[10, 10], interface=Symbol.Argument(access=Symbol.Access.READ)) kschedule.symbol_table.add(arg3) kschedule.symbol_table.specify_argument_list([arg1, arg2, arg3]) iteration_indices = kschedule.symbol_table.iteration_indices data_args = kschedule.symbol_table.data_arguments assert iteration_indices[0] is arg1 assert iteration_indices[1] is arg2 assert data_args[0] is arg3 # Test gen_ocl with wrong iteration indices types and shapes. arg1._datatype = "real" with pytest.raises(GenerationError) as err: _ = kschedule.symbol_table.iteration_indices assert ("GOcean 1.0 API kernels first argument should be a scalar integer" " but got a scalar of type 'real' for kernel 'test'.")\ in str(err) arg1._datatype = "integer" # restore arg2._shape = [None] with pytest.raises(GenerationError) as err: _ = kschedule.symbol_table.iteration_indices assert ("GOcean 1.0 API kernels second argument should be a scalar integer" " but got an array of type 'integer' for kernel 'test'.")\ in str(err)
def test_gen_dims(): '''Check the gen_dims function produces the expected dimension strings. ''' arg = Symbol("arg", "integer", interface=Symbol.Argument(access=Symbol.Access.UNKNOWN)) symbol = Symbol("dummy", "integer", shape=[arg, 2, None], interface=Symbol.Argument(access=Symbol.Access.UNKNOWN)) assert gen_dims(symbol) == ["arg", "2", ":"]
def test_oclw_gen_declaration(): '''Check the OpenCLWriter class gen_declaration method produces the expected declarations. ''' oclwriter = OpenCLWriter() # Basic entry - Scalar are passed by value and don't have additional # qualifiers. symbol = Symbol("dummy1", "integer") result = oclwriter.gen_declaration(symbol) assert result == "int dummy1" # Array argument has a memory qualifier (only __global for now) symbol = Symbol("dummy2", "integer", shape=[2, None, 2]) result = oclwriter.gen_declaration(symbol) assert result == "__global int * restrict dummy2" # Array with unknown intent symbol = Symbol("dummy2", "integer", shape=[2, None, 2], interface=Symbol.Argument(access=Symbol.Access.UNKNOWN)) result = oclwriter.gen_declaration(symbol) assert result == "__global int * restrict dummy2"
def test_gen_intent(): '''Check the gen_intent function produces the expected intent strings. ''' symbol = Symbol("dummy", "integer", interface=Symbol.Argument(access=Symbol.Access.UNKNOWN)) assert gen_intent(symbol) is None symbol = Symbol("dummy", "integer", interface=Symbol.Argument(Symbol.Access.READ)) assert gen_intent(symbol) == "in" symbol = Symbol("dummy", "integer", interface=Symbol.Argument(Symbol.Access.WRITE)) assert gen_intent(symbol) == "out" symbol = Symbol("dummy", "integer", interface=Symbol.Argument(Symbol.Access.READWRITE)) assert gen_intent(symbol) == "inout"
def test_oclw_kernelschedule(): '''Check the OpenCLWriter class kernelschedule_node visitor produces the expected C code. ''' # The kernelschedule OpenCL Backend relies on abstrct methods that # need to be implemented by the APIs. A generic kernelschedule will # produce a NotImplementedError. oclwriter = OpenCLWriter() kschedule = KernelSchedule("kname") with pytest.raises(NotImplementedError) as error: _ = oclwriter(kschedule) assert "Abstract property. Which symbols are data arguments is " \ "API-specific." in str(error) # Mock abstract properties. (pytest monkeypatch does not work # with properties, used sub-class instead) class MockSymbolTable(SymbolTable): ''' Mock needed abstract methods of the Symbol Table ''' @property def iteration_indices(self): return self.argument_list[:2] @property def data_arguments(self): return self.argument_list[2:] kschedule.symbol_table.__class__ = MockSymbolTable # Create a sample symbol table and kernel schedule interface = Symbol.Argument(access=Symbol.Access.UNKNOWN) i = Symbol('i', 'integer', interface=interface) j = Symbol('j', 'integer', interface=interface) data1 = Symbol('data1', 'real', [10, 10], interface=interface) data2 = Symbol('data2', 'real', [10, 10], interface=interface) kschedule.symbol_table.add(i) kschedule.symbol_table.add(j) kschedule.symbol_table.add(data1) kschedule.symbol_table.add(data2) kschedule.symbol_table.specify_argument_list([i, j, data1, data2]) kschedule.addchild(Return(parent=kschedule)) result = oclwriter(kschedule) print(result) assert result == "" \ "__kernel void kname(\n" \ " __global double * restrict data1,\n" \ " __global double * restrict data2\n" \ " ){\n" \ " int data1LEN1 = get_global_size(0);\n" \ " int data1LEN2 = get_global_size(1);\n" \ " int data2LEN1 = get_global_size(0);\n" \ " int data2LEN2 = get_global_size(1);\n" \ " int i = get_global_id(0);\n" \ " int j = get_global_id(1);\n" \ " return;\n" \ "}\n"
def test_gen_kind(): '''Check the gen_kind function produces the expected kind values. Note these are currently hardcoded to support the LFRic API. Issue #375 captures this problem. ''' int_symbol = Symbol( "dummy1", "integer", interface=Symbol.Argument(access=Symbol.Access.UNKNOWN)) real_symbol = Symbol( "dummy2", "real", interface=Symbol.Argument(access=Symbol.Access.UNKNOWN)) logical_symbol = Symbol( "dummy3", "boolean", interface=Symbol.Argument(access=Symbol.Access.UNKNOWN)) assert gen_kind(int_symbol) == "i_def" assert gen_kind(real_symbol) == "r_def" assert gen_kind(logical_symbol) is None
def test_gen_dims_error(monkeypatch): '''Check the gen_dims function raises an exception if a symbol shape entry is not supported. ''' symbol = Symbol("dummy", "integer", interface=Symbol.Argument(access=Symbol.Access.UNKNOWN)) monkeypatch.setattr(symbol, "_shape", ["invalid"]) with pytest.raises(NotImplementedError) as excinfo: _ = gen_dims(symbol) assert "unsupported gen_dims index 'invalid'" in str(excinfo)
def test_gen_intent_error(monkeypatch): '''Check the gen_intent function raises an exception if an unsupported access type is found. ''' symbol = Symbol("dummy", "integer", interface=Symbol.Argument(access=Symbol.Access.UNKNOWN)) monkeypatch.setattr(symbol.interface, "_access", "UNSUPPORTED") with pytest.raises(VisitorError) as excinfo: _ = gen_intent(symbol) assert "Unsupported access ''UNSUPPORTED'' found." in str(excinfo)
def test_fw_gen_vardecl(fort_writer): '''Check the FortranWriter class gen_vardecl method produces the expected declarations. Also check that an exception is raised if the symbol does not describe a variable declaration statement. ''' # Basic entry symbol = Symbol("dummy1", "integer") result = fort_writer.gen_vardecl(symbol) assert result == "integer :: dummy1\n" # Array with intent symbol = Symbol("dummy2", "integer", shape=[2, None, 2], interface=Symbol.Argument(access=Symbol.Access.READ)) result = fort_writer.gen_vardecl(symbol) assert result == "integer, dimension(2,:,2), intent(in) :: dummy2\n" # Array with unknown intent symbol = Symbol("dummy2", "integer", shape=[2, None, 2], interface=Symbol.Argument(access=Symbol.Access.UNKNOWN)) result = fort_writer.gen_vardecl(symbol) assert result == "integer, dimension(2,:,2) :: dummy2\n" # Constant symbol = Symbol("dummy3", "integer", constant_value=10) result = fort_writer.gen_vardecl(symbol) assert result == "integer, parameter :: dummy3 = 10\n" # Use statement symbol = Symbol("dummy1", "deferred", interface=Symbol.FortranGlobal("my_module")) with pytest.raises(VisitorError) as excinfo: _ = fort_writer.gen_vardecl(symbol) assert ("gen_vardecl requires the symbol 'dummy1' to be a local " "declaration or an argument declaration, but found scope " "'global' and interface 'FortranGlobal'." in str(excinfo.value))
def test_cw_gen_declaration(): '''Check the CWriter class gen_declaration method produces the expected declarations. ''' cwriter = CWriter() # Basic entries symbol = Symbol("dummy1", "integer") result = cwriter.gen_declaration(symbol) assert result == "int dummy1" symbol = Symbol("dummy1", "character") result = cwriter.gen_declaration(symbol) assert result == "char dummy1" symbol = Symbol("dummy1", "boolean") result = cwriter.gen_declaration(symbol) assert result == "bool dummy1" # Array argument symbol = Symbol("dummy2", "real", shape=[2, None, 2], interface=Symbol.Argument(access=Symbol.Access.READ)) result = cwriter.gen_declaration(symbol) assert result == "double * restrict dummy2" # Array with unknown intent symbol = Symbol("dummy2", "integer", shape=[2, None, 2], interface=Symbol.Argument(access=Symbol.Access.UNKNOWN)) result = cwriter.gen_declaration(symbol) assert result == "int * restrict dummy2" # Check invalid datatype produces and error symbol._datatype = "invalid" with pytest.raises(NotImplementedError) as error: _ = cwriter.gen_declaration(symbol) assert "Could not generate the C definition for the variable 'dummy2', " \ "type 'invalid' is currently not supported." in str(error)
def test_fw_container_3(fort_writer, monkeypatch): '''Check the FortranWriter class raises an exception when a Container node contains a symbol table with an argument declaration (as this does not make sense). ''' # Generate fparser2 parse tree from Fortran code. code = ("module test\n" "real :: a\n" "contains\n" "subroutine tmp()\n" "end subroutine tmp\n" "end module test") schedule = create_schedule(code, "tmp") container = schedule.root symbol = container.symbol_table.symbols[0] assert symbol.name == "a" monkeypatch.setattr(symbol, "_interface", Symbol.Argument()) with pytest.raises(VisitorError) as excinfo: _ = fort_writer(container) assert ("Arguments are not allowed in this context but this symbol table " "contains argument(s): '['a']'." in str(excinfo))
def test_gen_decls(fort_writer): '''Check the FortranWriter class gen_decls method produces the expected declarations. Also check that an exception is raised if an 'argument' symbol exists in the supplied symbol table and the optional argument 'args_allowed' is set to False. ''' symbol_table = SymbolTable() use_statement = Symbol("my_use", "deferred", interface=Symbol.FortranGlobal("my_module")) symbol_table.add(use_statement) argument_variable = Symbol("arg", "integer", interface=Symbol.Argument()) symbol_table.add(argument_variable) local_variable = Symbol("local", "integer") symbol_table.add(local_variable) result = fort_writer.gen_decls(symbol_table) assert (result == "use my_module, only : my_use\n" "integer :: arg\n" "integer :: local\n") with pytest.raises(VisitorError) as excinfo: _ = fort_writer.gen_decls(symbol_table, args_allowed=False) assert ("Arguments are not allowed in this context but this symbol table " "contains argument(s): '['arg']'." in str(excinfo.value))