def _set_correct(wizard, parameter, target): """Sets the current model output for the target and parameter as correct so that it will be used for future unit test comparisons. """ casepath = _get_casepath(wizard) #We don't worry about updating the XML tag. That is done by a calling #procedure. The job here is to copy model output to the global output's #folder specified by the user for the current module, and then return the name #of the file in that folder. #As such, we need to choose a naming convention for the auto-assigned model #outputs. The safest thing to do is use the #module.executable.parameter.testid.caseid as the file name. But that is #*really* long and if the test folder is already buried pretty deep, it can #become unwieldly... On the other hand, if multiple unit tests want to compare #to the same model output, then we don't want to duplicate that, especially #considering the size of some of the files in scientific computing. from os import path varfile = path.join(casepath, target.varfile) newname = "" dst = path.join(wizard.folders[wizard.xauto.module], newname) if target.autoclass: if path.isdir(varfile): from fortpy.utility import copytree copytree(varfile, dst) else: if path.isfile(varfile): from fortpy.utility import copyfile copyfile(varfile, dst) return (newname, None)
def compile_general(folder, compiler=None, identifier=None, debug=False, profile=False, quiet=False, moptions=None, inclfortpy=True, vupdates=None, strict=False): """Same as for compile() but the folder is assumed to be generic and a copy of it is made for the specific compiler that we are dealing with. """ #Because we often run the tests for multiple compiler versions, we need #a copy of the execution directory that was setup for the testing. from fortpy.utility import copytree from os import path target = replace(folder + ".[c]", compiler) copytree(folder, target) code, success = compile(target, compiler, identifier, debug, profile, quiet, moptions, inclfortpy) return (code, success, target)
def compile_general(folder, compiler=None, identifier=None, debug=False, profile=False, quiet=False, moptions=None, inclfortpy=True, vupdates=None, strict=False): """Same as for compile() but the folder is assumed to be generic and a copy of it is made for the specific compiler that we are dealing with. """ #Because we often run the tests for multiple compiler versions, we need #a copy of the execution directory that was setup for the testing. from fortpy.utility import copytree from os import path target = replace(folder + ".[c]", compiler) copytree(folder, target) code, success = compile(target, compiler, identifier, debug, profile, quiet, moptions, inclfortpy, vupdates, strict) return (code, success, target)
def _run_compile(self, identifier, testid): """Compiles the executable that was created for the specified identifier, returns True if the compile was successful.""" #Because we often run the tests for multiple compiler versions, we need #a copy of the execution directory that was setup for the testing. from fortpy.testing.compilers import replace, executor, family from fortpy.utility import copytree from os import path source = path.join(self.libraryroot(identifier), identifier) target = replace(source + ".[c]", self.compiler) copytree(source, target) #Before we compile, we need to make sure we have the fortpy.o and fortpy.mod #files for the specific compiler. tversion = self.template_version("fortpy.f90") for sdfile in ["fortpy.o", "fortpy.mod"]: fdfile = replace(sdfile + ".[c]", self.compiler) ftarget = path.join(target, sdfile) dversion = self.get_fortpy_version(ftarget) if not path.isfile(ftarget) or dversion != tversion: from shutil import copy source = path.join(self.fortpy_templates, fdfile) sversion = self.get_fortpy_version(source) if not path.isfile(source) or sversion != tversion: self._compile_fortpyf90(tversion) msg.info(" COPY: {}".format(source)) copy(source, ftarget) #If the file is a binary, we need to save a .v with version #information as well for the next time we want to copy it. pre, ext = path.splitext(ftarget) if ext in [".o", ".so", ".mod"]: with open(ftarget + '.v', 'w') as f: f.write("# <fortpy version=\"{}\" />".format('.'.join(map(str, tversion)))) #Find the target folder that has the executables etc then run #make and check the exit code. msg.blank() options = "" if self.debug: options += " DEBUG=true" if self.profile: options += " GPROF=true" codestr = "cd {}; make -f 'Makefile.{}' F90='{}' FAM='{}'" + options #If we are running in quiet mode, we don't want the compile information #to post to stdout; only errors should be redirected. This means we need #to wrap the execution in a subprocess and redirect the std* to PIPE from os import waitpid from subprocess import Popen, PIPE command = codestr.format(target, testid, executor(self.compiler), family(self.compiler)) pcompile = Popen(command, shell=True, executable="/bin/bash", stdout=PIPE, stderr=PIPE) waitpid(pcompile.pid, 0) if not self.quiet: output = [x.decode('utf8') for x in pcompile.stdout.readlines()] msg.std(''.join(output)) #else: #We don't need to get these lines since we are purposefully redirecting them. error = pcompile.stderr.readlines() code = len(error) if code != 0: msg.err(''.join(error)) #It turns out that the compiler still returns a code of zero, even if the compile #failed because the actual compiler didn't fail; it did its job properly. We need to #check for the existence of errors in the 'compile.log' file. lcount = 0 errors = [] log = path.join(target, "compile.log") with open(log) as f: for line in f: lcount += 1 if lcount > 21 and lcount < 32: errors.append(line) elif lcount > 21: break if len(errors) > 0: #There are 21 lines in the compile.log file when everything runs correctly #Overwrite code with a bad exit value since we have some other problems. code = 1 #We want to write the first couple of errors to the console and give them the #option to still execute if the compile only generated warnings. msg.warn("compile generated some errors or warnings:") msg.blank() msg.info(''.join(errors)) #If the executable exists, we could still prompt them to run it (in case the #additional lines were just warnings). exe = path.join(target, "{}.x".format(testid)) if path.isfile(exe): choice = input("\nWould you still like to run the executable? ").lower() code = 0 if "y" in choice else code if "n" in choice: msg.err("Unit testing terminated by user.") exit(0) else: msg.err("Could not compile executable {}.x".format(testid)) exit(-1) return code == 0, target