Example #1
0
    def reset(self, identifier, coderoot):
        """Resets the writer to work with a new executable."""
        #A method writer has all the guts needed to generate the executable
        #We are just wrapping it and making sure the variable values get
        #recorded to temporary files for comparison etc.
        self.writer = MethodWriter(identifier, self.parser, self.testgen)
        self.identifier = identifier
        if self.libraryroot is not None:
            self.folders[identifier] = path.abspath(self.libraryroot)
        elif self.writer.group is not None and self.writer.group.staging is not None:
            #Determine the absolute path using the relative path specified in the group
            #staging directory attribute.
            from fortpy.tramp import coderelpath
            self.folders[identifier] = coderelpath(coderoot,
                                                   self.writer.group.staging)
        else:
            raise ValueError(
                "Can't determine the staging directory for running the tests.")

        self._needs = None
        #The calling methods need to know whether the context (i.e. the staging folder)
        #of this executable writer has changed. This variable tracks that.
        newstage = self.folder != self.folders[identifier]
        self.folder = path.join(self.folders[identifier], identifier)

        #Create the directory for the executable files to be copied and written to.
        if not path.exists(self.folder):
            msg.okay("EXEC DIR: create {}".format(self.folder))
            mkdir(self.folder)

        return newstage
Example #2
0
    def reset(self, identifier, coderoot):
        """Resets the writer to work with a new executable."""        
        #A method writer has all the guts needed to generate the executable
        #We are just wrapping it and making sure the variable values get
        #recorded to temporary files for comparison etc.
        self.writer = MethodWriter(identifier, self.parser, self.testgen)
        self.identifier = identifier
        if self.libraryroot is not None:
            self.folders[identifier] = path.abspath(self.libraryroot)
        elif self.writer.group is not None and self.writer.group.staging is not None:
            #Determine the absolute path using the relative path specified in the group
            #staging directory attribute.
            from fortpy.tramp import coderelpath
            self.folders[identifier] = coderelpath(coderoot, self.writer.group.staging)
        else:
            raise ValueError("Can't determine the staging directory for running the tests.")
        
        self._needs = None
        #The calling methods need to know whether the context (i.e. the staging folder)
        #of this executable writer has changed. This variable tracks that.
        newstage = self.folder != self.folders[identifier]
        self.folder = path.join(self.folders[identifier], identifier)

        #Create the directory for the executable files to be copied and written to.
        if not path.exists(self.folder):
            msg.okay("EXEC DIR: create {}".format(self.folder))
            mkdir(self.folder)

        return newstage
Example #3
0
class ExecutableGenerator(object):
    """Generates a fortran executable to perform unit tests for a given
    subroutine or function.

    :arg parser: an instance of the code parser for inter-module access
    :arg folder: the folder in which to generate the code files and execs.
    :arg testgen: the TestGenerator instance that owns this executable generator.
    """
    def __init__(self, parser, folder, testgen):
        #We need the code parser because the pre-reqs specified may be in other
        #modules and may have pre-reqs of their own. This way, we can find
        #them all easily.
        self.parser = parser
        self.libraryroot = folder
        """Specifies a staging directory to override the one specified in the <group>
        tag (if one was specified there)."""
        self.testgen = testgen
        self.folders = {}
        """Dictionary of full paths to the staging directory for each unit test
        being generated by the framework.
        """
        self.folder = None
        """The full path to the *current* staging directory for the executable whose
        tests are being generated.
        """
        self._needs = None

    def needs(self):
        """Returns a list of all the modules that this executable needs to
        run correctly."""
        if self._needs is None:
            self._needs = self._calc_needs()
        return self._needs

    def _calc_needs(self):
        """Calculates a list of all the modules that this executable needs to
        run correctly."""
        result = []
        for modk in self.writer.uses():
            if modk not in result:
                result.append(modk)

        #We also need to look up the dependencies of each of these modules
        recursed = list(result)
        for i in range(len(result)):
            module = result[i]
            self._process_module_needs(module, i, recursed)

        return recursed

    def _process_module_needs(self, module, i, result):
        """Adds the module and its dependencies to the result list."""
        #Some code might decide to use the fortpy module methods for general
        #development, ignore it since we know it will be present in the end.
        if module == "fortpy":
            return

        #See if the parser has alread loaded this module.
        if module not in self.parser.modules:
            self.parser.load_dependency(module, True, True, False)

        #It is possible that the parser couldn't find it, if so
        #we can't create the executable!
        if module in self.parser.modules:
            modneeds = self.parser.modules[module].needs
            for modn in modneeds:
                if modn not in result:
                    #Since this module depends on the other, insert the other
                    #above it in the list.
                    result.insert(i, modn)
                else:
                    x = result.index(modn)
                    if x > i:
                        #We need to move this module higher up in the food chain
                        #because it is needed sooner.
                        result.remove(modn)
                        result.insert(i, modn)

                newi = result.index(modn)
                self._process_module_needs(modn, newi, result)
        else:
            raise ValueError("unable to find module {}.".format(module))

    def reset(self, identifier, coderoot):
        """Resets the writer to work with a new executable."""
        #A method writer has all the guts needed to generate the executable
        #We are just wrapping it and making sure the variable values get
        #recorded to temporary files for comparison etc.
        self.writer = MethodWriter(identifier, self.parser, self.testgen)
        self.identifier = identifier
        if self.libraryroot is not None:
            self.folders[identifier] = path.abspath(self.libraryroot)
        elif self.writer.group is not None and self.writer.group.staging is not None:
            #Determine the absolute path using the relative path specified in the group
            #staging directory attribute.
            from fortpy.tramp import coderelpath
            self.folders[identifier] = coderelpath(coderoot,
                                                   self.writer.group.staging)
        else:
            raise ValueError(
                "Can't determine the staging directory for running the tests.")

        self._needs = None
        #The calling methods need to know whether the context (i.e. the staging folder)
        #of this executable writer has changed. This variable tracks that.
        newstage = self.folder != self.folders[identifier]
        self.folder = path.join(self.folders[identifier], identifier)

        #Create the directory for the executable files to be copied and written to.
        if not path.exists(self.folder):
            msg.okay("EXEC DIR: create {}".format(self.folder))
            mkdir(self.folder)

        return newstage

    def write(self, testid, coderoot):
        """Writes the fortran program file for the executable specified.

        :arg testid: the identifier of the test to construct the executable for.
        """
        lines = []
        identifier = self.writer.tests[testid].identifier

        #First off, we need to start the program and set the module dependencies.
        lines.append("!!<summary>Auto-generated unit test for {}\n".format(
            self.identifier))
        lines.append("!!using FORTPY. Generated on {}.\n".format(
            datetime.now()))
        lines.append("!!{}</summary>\n".format(
            self.writer.tests[testid].description))
        lines.append("PROGRAM UNITTEST_{}\n".format(
            self.writer.finders[testid].executable.name))
        lines.append(self._get_uses(testid))
        lines.append("  implicit none\n")

        #Next add the variable declarations and initializations and the calls
        #to execute the pre-req methods and the one we are trying to test.
        lines.append(self.writer.lines(testid, coderoot))

        lines.append("\nEND PROGRAM UNITTEST_{}".format(
            self.writer.finders[testid].executable.name))

        with open(path.join(self.folder, "{}.f90".format(identifier)),
                  'w') as f:
            f.writelines(lines)

    def makefile(self, identifier):
        """Generates a makefile to create the unit testing executable
        for the specified test identifier.

        :arg identifier: the id of the test that this executable should be made for.
        """
        allneeds = self.needs()
        #We need to see whether to include the pre-compiler directive or not.
        precompile = False
        for needed in allneeds:
            if self.parser.modules[needed].precompile:
                precompile = True
                break

        lines = []
        makepath = path.join(self.folder, "Makefile.{}".format(identifier))
        makefile(identifier,
                 allneeds,
                 makepath,
                 self.identifier,
                 precompile,
                 parser=self.parser)

    def _get_uses(self, testid):
        """Gets a list of use statements to add to the program code."""
        #The writer can extract a dictionary of module dependencies for us.
        alluses = self.writer.uses()
        #Now we just need to generate the code statements.
        uselist = []
        for module in alluses:
            if module == self.writer.finders[testid].module.name:
                uselist.append("use {}".format(module))
            else:
                uselist.append("use {}, only: {}".format(
                    module, ", ".join(alluses[module])))

        #Last of all, we need to append the module that handles interaction with
        #the fortran results and the python testing framework.
        if "fortpy" not in alluses:
            uselist.append("use fortpy\n")

        return self._tabjoin(uselist)

    def _tabjoin(self, values):
        """Joins a list of values with \t\n."""
        return "  {}".format("\n  ".join(values))
Example #4
0
class ExecutableGenerator(object):
    """Generates a fortran executable to perform unit tests for a given
    subroutine or function.

    :arg parser: an instance of the code parser for inter-module access
    :arg folder: the folder in which to generate the code files and execs.
    :arg testgen: the TestGenerator instance that owns this executable generator.
    """
    def __init__(self, parser, folder, testgen):
        #We need the code parser because the pre-reqs specified may be in other
        #modules and may have pre-reqs of their own. This way, we can find
        #them all easily.
        self.parser = parser
        self.libraryroot = folder
        """Specifies a staging directory to override the one specified in the <group>
        tag (if one was specified there)."""
        self.testgen = testgen
        self.folders = {}
        """Dictionary of full paths to the staging directory for each unit test
        being generated by the framework.
        """
        self.folder = None
        """The full path to the *current* staging directory for the executable whose
        tests are being generated.
        """
        self._needs = None

    def needs(self):
        """Returns a list of all the modules that this executable needs to
        run correctly."""
        if self._needs is None:
            self._needs = self._calc_needs()
        return self._needs

    def _calc_needs(self):
        """Calculates a list of all the modules that this executable needs to
        run correctly."""
        result = []        
        for modk in self.writer.uses():
            if modk not in result:
                result.append(modk)

        #We also need to look up the dependencies of each of these modules
        recursed = list(result)
        for i in range(len(result)):
            module = result[i]
            self._process_module_needs(module, i, recursed)

        return recursed

    def _process_module_needs(self, module, i, result):
        """Adds the module and its dependencies to the result list."""
        #Some code might decide to use the fortpy module methods for general
        #development, ignore it since we know it will be present in the end.
        if module == "fortpy":
            return

        #See if the parser has alread loaded this module.
        if module not in self.parser.modules:
            self.parser.load_dependency(module, True, True, False)

        #It is possible that the parser couldn't find it, if so
        #we can't create the executable!
        if module in self.parser.modules:
            modneeds = self.parser.modules[module].needs
            for modn in modneeds:
                if modn not in result:
                    #Since this module depends on the other, insert the other
                    #above it in the list.
                    result.insert(i, modn)
                else:
                    x = result.index(modn)
                    if x > i:
                        #We need to move this module higher up in the food chain
                        #because it is needed sooner.
                        result.remove(modn)
                        result.insert(i, modn)

                newi = result.index(modn)
                self._process_module_needs(modn, newi, result)
        else:
            raise ValueError("unable to find module {}.".format(module))

    def reset(self, identifier, coderoot):
        """Resets the writer to work with a new executable."""        
        #A method writer has all the guts needed to generate the executable
        #We are just wrapping it and making sure the variable values get
        #recorded to temporary files for comparison etc.
        self.writer = MethodWriter(identifier, self.parser, self.testgen)
        self.identifier = identifier
        if self.libraryroot is not None:
            self.folders[identifier] = path.abspath(self.libraryroot)
        elif self.writer.group is not None and self.writer.group.staging is not None:
            #Determine the absolute path using the relative path specified in the group
            #staging directory attribute.
            from fortpy.tramp import coderelpath
            self.folders[identifier] = coderelpath(coderoot, self.writer.group.staging)
        else:
            raise ValueError("Can't determine the staging directory for running the tests.")
        
        self._needs = None
        #The calling methods need to know whether the context (i.e. the staging folder)
        #of this executable writer has changed. This variable tracks that.
        newstage = self.folder != self.folders[identifier]
        self.folder = path.join(self.folders[identifier], identifier)

        #Create the directory for the executable files to be copied and written to.
        if not path.exists(self.folder):
            msg.okay("EXEC DIR: create {}".format(self.folder))
            mkdir(self.folder)

        return newstage
                
    def write(self, testid, coderoot):
        """Writes the fortran program file for the executable specified.

        :arg testid: the identifier of the test to construct the executable for.
        """
        lines = []
        identifier = self.writer.tests[testid].identifier

        #First off, we need to start the program and set the module dependencies.
        lines.append("!!<summary>Auto-generated unit test for {}\n".format(
            self.identifier))
        lines.append("!!using FORTPY. Generated on {}.\n".format(datetime.now()))
        lines.append("!!{}</summary>\n".format(self.writer.tests[testid].description))
        lines.append("PROGRAM UNITTEST_{}\n".format(self.writer.finders[testid].executable.name))
        lines.append(self._get_uses(testid))
        lines.append("  implicit none\n")

        #Next add the variable declarations and initializations and the calls
        #to execute the pre-req methods and the one we are trying to test.
        lines.append(self.writer.lines(testid, coderoot))

        lines.append("\nEND PROGRAM UNITTEST_{}".format(self.writer.finders[testid].executable.name))

        with open(path.join(self.folder, "{}.f90".format(identifier)), 'w') as f:
            f.writelines(lines)
        
    def makefile(self, identifier):
        """Generates a makefile to create the unit testing executable
        for the specified test identifier.

        :arg identifier: the id of the test that this executable should be made for.
        """
        allneeds = self.needs()
        #We need to see whether to include the pre-compiler directive or not.
        precompile = False
        for needed in allneeds:
            if self.parser.modules[needed].precompile:
                precompile = True
                break

        lines = []
        makepath = path.join(self.folder, "Makefile.{}".format(identifier))
        makefile(identifier, allneeds, makepath, self.identifier, precompile, parser=self.parser)
        
    def _get_uses(self, testid):
        """Gets a list of use statements to add to the program code."""
        #The writer can extract a dictionary of module dependencies for us.
        alluses = self.writer.uses()
        #Now we just need to generate the code statements.
        uselist = []
        for module in alluses:
            if module == self.writer.finders[testid].module.name:
                uselist.append("use {}".format(module))
            else:
                uselist.append("use {}, only: {}".format(module, ", ".join(alluses[module])))

        #Last of all, we need to append the module that handles interaction with
        #the fortran results and the python testing framework.
        if "fortpy" not in alluses:
            uselist.append("use fortpy\n")

        return self._tabjoin(uselist)

    def _tabjoin(self, values):
        """Joins a list of values with \t\n."""
        return "  {}".format("\n  ".join(values))
Example #5
0
class ExecutableGenerator(object):
    """Generates a fortran executable to perform unit tests for a given
    subroutine or function.

    :arg parser: an instance of the code parser for inter-module access
    :arg folder: the folder in which to generate the code files and execs.
    :arg testgen: the TestGenerator instance that owns this executable generator.
    """
    def __init__(self, parser, folder, testgen):
        #We need the code parser because the pre-reqs specified may be in other
        #modules and may have pre-reqs of their own. This way, we can find
        #them all easily.
        self.parser = parser
        self.libraryroot = folder
        """Specifies a staging directory to override the one specified in the <group>
        tag (if one was specified there)."""
        self.testgen = testgen
        self.folders = {}
        """Dictionary of full paths to the staging directory for each unit test
        being generated by the framework.
        """
        self.folder = None
        """The full path to the *current* staging directory for the executable whose
        tests are being generated.
        """
        self._needs = None

    def needs(self):
        """Returns a list of all the modules that this executable needs to
        run correctly."""
        if self._needs is None:
            from fortpy.code import order_module_dependencies
            self._needs = order_module_dependencies(self.writer.uses(), self.parser)
        return self._needs

    def reset(self, identifier, coderoot):
        """Resets the writer to work with a new executable."""        
        #A method writer has all the guts needed to generate the executable
        #We are just wrapping it and making sure the variable values get
        #recorded to temporary files for comparison etc.
        self.writer = MethodWriter(identifier, self.parser, self.testgen,
                                   stagedir=self.libraryroot)
        self.identifier = identifier
        if self.libraryroot is not None:
            self.folders[identifier] = path.abspath(self.libraryroot)
        elif self.writer.group is not None and self.writer.group.staging is not None:
            #Determine the absolute path using the relative path specified in the group
            #staging directory attribute.
            from fortpy.tramp import coderelpath
            self.folders[identifier] = coderelpath(coderoot, self.writer.group.staging)
        else:
            raise ValueError("Can't determine the staging directory for running the tests.")
        
        self._needs = None
        #The calling methods need to know whether the context (i.e. the staging folder)
        #of this executable writer has changed. This variable tracks that.
        newstage = self.folder != self.folders[identifier]
        self.folder = path.join(self.folders[identifier], identifier)

        #Create the directory for the executable files to be copied and written to.
        if not path.exists(self.folder):
            msg.okay("EXEC DIR: create {}".format(self.folder))
            mkdir(self.folder)

        return newstage
                
    def write(self, testid, coderoot):
        """Writes the fortran program file for the executable specified.

        :arg testid: the identifier of the test to construct the executable for.
        """
        lines = []
        identifier = self.writer.tests[testid].identifier

        #First off, we need to start the program and set the module dependencies.
        lines.append("!!<summary>Auto-generated unit test for {}\n".format(
            self.identifier))
        lines.append("!!using FORTPY. Generated on {}.\n".format(datetime.now()))
        lines.append("!!{}</summary>\n".format(self.writer.tests[testid].description))
        lines.append("PROGRAM UNITTEST_{}\n".format(self.writer.finders[testid].executable.name))
        lines.append(self._get_uses(testid))
        lines.append("  implicit none\n")

        #Next add the variable declarations and initializations and the calls
        #to execute the pre-req methods and the one we are trying to test.
        lines.append(self.writer.lines(testid, coderoot))

        lines.append("\nEND PROGRAM UNITTEST_{}".format(self.writer.finders[testid].executable.name))

        with open(path.join(self.folder, "{}.f90".format(identifier)), 'w') as f:
            f.writelines(lines)
        
    def makefile(self, identifier):
        """Generates a makefile to create the unit testing executable
        for the specified test identifier.

        :arg identifier: the id of the test that this executable should be made for.
        """
        allneeds = self.needs()

        #We need to see whether to include the pre-compiler directive or not.
        precompile = False
        for needed in allneeds:
            if needed=="fortpy" or needed=="fpy_auxiliary":
                continue
            if self.parser.modules[needed].precompile:
                precompile = True
                break

        lines = []
        #Determine whether the compilation should produce headers on the output screen.
        from fortpy.msg import will_print
        verbose = will_print(2)
        makepath = path.join(self.folder, "Makefile.{}".format(identifier))
        makefile(identifier, allneeds, makepath, self.identifier, precompile,
                 parser=self.parser, inclfpyaux=self.writer.autoclass, verbose=verbose)
        
    def _get_uses(self, testid):
        """Gets a list of use statements to add to the program code."""
        #The writer can extract a dictionary of module dependencies for us.
        alluses = self.writer.uses()
        #Now we just need to generate the code statements.
        uselist = []
        for module in alluses:
            if module == self.writer.finders[testid].module.name:
                uselist.append("use {}".format(module))
            elif module == "fpy_auxiliary":
                uselist.append("use fpy_auxiliary")
            else:
                uselist.append("use {}, only: {}".format(module, ", ".join(alluses[module])))

        #Last of all, we need to append the module that handles interaction with
        #the fortran results and the python testing framework.
        if "fortpy" not in alluses:
            uselist.append("use fortpy\n")

        return self._tabjoin(uselist)

    def _tabjoin(self, values):
        """Joins a list of values with \t\n."""
        return "  {}".format("\n  ".join(values))