Skip to content

Commit

Permalink
Tests and new methods.
Browse files Browse the repository at this point in the history
New method _flagvalidator() and tests - this method is to separate out
the logic for validating command-line flags for future expansion.

New method _proccommandline() and tests - this method is to separate out
the logic for choosing which command-line processor to use. This is to
simplify unit testing.
  • Loading branch information
jimboid committed Dec 5, 2016
1 parent f2d0153 commit c983774
Show file tree
Hide file tree
Showing 3 changed files with 317 additions and 64 deletions.
158 changes: 94 additions & 64 deletions Longbow/corelibs/applications.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,24 +129,17 @@ def processjobs(jobs):
"""
LOG.info("Processing job/s and detecting files that require upload.")

# Get dictionary of executables and their required flags from plug-ins.
appplugins = getattr(apps, "PLUGINEXECS")

# Process each job.
for job in jobs:

app = appplugins[jobs[job]["executable"]]
args = jobs[job]["executableargs"]
execdata = getattr(
apps, app.lower()).EXECDATA[jobs[job]["executable"]]
filelist = []

LOG.debug("Command-line arguments for job '%s' are '%s'",
job, " ".join(args))
job, " ".join(jobs[job]["executableargs"]))

# Check for any files that are located outside the work directory or
# absolute paths.
for arg in args:
for arg in jobs[job]["executableargs"]:

if arg.count(os.path.pardir) > 0 or os.path.isabs(arg):

Expand All @@ -173,81 +166,121 @@ def processjobs(jobs):
"The local job directory '{0}' cannot be found for job '{1}'"
.format(cwd, job))

# Determine the command-line type and call the processor method.
# Start with command-lines of the type exec < input.file.
if args[0] is "<":
filelist, foundflags = _proccommandline(jobs[job], cwd, filelist)

# Command-line type exec < input.file
filelist, foundflags = _proccommandlinetype1(jobs[job], app, cwd,
filelist)
_flagvalidator(jobs[job], foundflags)

elif "-" in args[0]:
# Setup the rysnc upload masks.
if jobs[job]["upload-include"] != "":

# Command-line type exec -i file -c file
filelist, foundflags = _proccommandlinetype2(jobs[job], app, cwd,
filelist)
jobs[job]["upload-include"] = jobs[job]["upload-include"] + ", "

elif "-" not in args[0] and "-" in args[1]:
jobs[job]["upload-include"] = (jobs[job]["upload-include"] +
", ".join(filelist))

# Command-line type exec subexec -i file -c file
filelist, foundflags = _proccommandlinetype3(jobs[job], app, cwd,
filelist)
jobs[job]["upload-exclude"] = "*"

else:
# Replace the input command line with the execution command line.
jobs[job]["executableargs"] = (jobs[job]["executable"] + " " +
" ".join(jobs[job]["executableargs"]))

# Command-line type exec input.file
filelist, foundflags = _proccommandlinetype4(jobs[job], app, cwd,
filelist)
LOG.info("For job '%s' - execution string: %s",
job, jobs[job]["executableargs"])

# Final check for if any required flags are missing.
flags = list(set(execdata["requiredfiles"]) - set(foundflags))
LOG.info("Processing jobs - complete.")

# If there are any missing still then tell the user.
if len(flags) > 0:

# Firstly is this due to it being an either type flag?
for flag in flags:
def _flagvalidator(job, foundflags):
"""Validate that required command-line flags are provided."""
# Initialisation.
appplugins = getattr(apps, "PLUGINEXECS")
app = appplugins[job["executable"]]
execdata = getattr(apps, app.lower()).EXECDATA[job["executable"]]

if "||" in flag:
# Final check for if any required flags are missing.
flags = list(set(execdata["requiredfiles"]) - set(foundflags))

tmpflags = flag.split(" || ")
tmpflag = list(set(tmpflags).intersection(set(foundflags)))
# If there are any missing still then tell the user.
if len(flags) > 0:

if len(tmpflag) > 0:
# Firstly is this due to it being an either type flag?
for flag in flags:

flags.remove(flag)
if "||" in flag:

# If there are any missing still then tell the user.
if len(flags) > 0:
tmpflags = flag.split(" || ")
tmpflag = list(set(tmpflags).intersection(set(foundflags)))

raise exceptions.RequiredinputError(
"In job '{0}' there are missing flags on the command line "
"'{1}'. See user documentation for plug-in '{2}'"
.format(job, flags, app))
if len(tmpflag) > 0:

# Setup the rysnc upload masks.
if jobs[job]["upload-include"] != "":
flags.remove(flag)

jobs[job]["upload-include"] = jobs[job]["upload-include"] + ", "
# If there are any missing still then tell the user.
if len(flags) > 0:

jobs[job]["upload-include"] = (jobs[job]["upload-include"] +
", ".join(filelist))
raise exceptions.RequiredinputError(
"In job '{0}' there are missing flags on the command line '{1}'. "
"See user documentation for plug-in '{2}'"
.format(job["jobname"], flags, app))

jobs[job]["upload-exclude"] = "*"

# Replace the input command line with the execution command line.
jobs[job]["executableargs"] = (jobs[job]["executable"] + " " +
" ".join(jobs[job]["executableargs"]))
def _proccommandline(job, cwd, filelist):
"""Command-line processor.
LOG.info("For job '%s' - execution string: %s",
job, jobs[job]["executableargs"])
This method selects which type of command-line we have.
LOG.info("Processing jobs - complete.")
"""
# Initialisation.
args = job["executableargs"]
appplugins = getattr(apps, "PLUGINEXECS")
app = appplugins[job["executable"]]
foundflags = []

try:

# Determine the command-line type and call the processor method. Start
# with command-lines of the type exec < input.file.
if args[0] == "<":

# Command-line type exec < input.file
filelist, foundflags = _proccommandlinetype1(
job, app, cwd, filelist, foundflags)

def _proccommandlinetype1(job, app, cwd, filelist):
elif len(args) == 1 and args[0] != "<":

# Command-line type exec input.file
filelist, foundflags = _proccommandlinetype4(
job, app, cwd, filelist, foundflags)

elif "-" in args[0]:

# Command-line type exec -i file -c file
filelist, foundflags = _proccommandlinetype2(
job, app, cwd, filelist, foundflags)

elif "-" not in args[0] and "-" in args[1]:

# Command-line type exec subexec -i file -c file
filelist, foundflags = _proccommandlinetype3(
job, app, cwd, filelist, foundflags)

else:

raise ValueError

except (IndexError, ValueError):

raise exceptions.RequiredinputError(
"In job '{0}', the command-line arguments for the application "
"could not be understood. Check the documentation for more "
"information on how to format command-lines."
.format(job["jobname"]))

return filelist, foundflags


def _proccommandlinetype1(job, app, cwd, filelist, foundflags):
"""Processor forcommand-line type 'exec < input.file > output.file'."""
foundflags = []
args = list(job["executableargs"])
initargs = list(job["executableargs"])
substitution = {}
Expand Down Expand Up @@ -318,9 +351,8 @@ def _proccommandlinetype1(job, app, cwd, filelist):
return filelist, foundflags


def _proccommandlinetype2(job, app, cwd, filelist):
def _proccommandlinetype2(job, app, cwd, filelist, foundflags):
"""Processor for command-line type 'exec --input file1 -file file2'."""
foundflags = []
args = list(job["executableargs"])
initargs = list(job["executableargs"])
substitution = {}
Expand Down Expand Up @@ -388,9 +420,8 @@ def _proccommandlinetype2(job, app, cwd, filelist):
return filelist, foundflags


def _proccommandlinetype3(job, app, cwd, filelist):
def _proccommandlinetype3(job, app, cwd, filelist, foundflags):
"""Processor command-line type 'exec subexec --file1 v1 -file2 p2'."""
foundflags = []
args = list(job["executableargs"])
initargs = list(job["executableargs"])
substitution = {}
Expand Down Expand Up @@ -458,9 +489,8 @@ def _proccommandlinetype3(job, app, cwd, filelist):
return filelist, foundflags


def _proccommandlinetype4(job, app, cwd, filelist):
def _proccommandlinetype4(job, app, cwd, filelist, foundflags):
"""Processor for command-line type 'exec input.file'."""
foundflags = []
args = list(job["executableargs"])
initargs = list(job["executableargs"])
substitution = {}
Expand Down
70 changes: 70 additions & 0 deletions Tests/unit/corelibs_applications/test_flagvalidator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Longbow is Copyright (C) of James T Gebbie-Rayet and Gareth B Shannon 2015.
#
# This file is part of the Longbow software which was developed as part of the
# HECBioSim project (http://www.hecbiosim.ac.uk/).
#
# HECBioSim facilitates and supports high-end computing within the UK
# biomolecular simulation community on resources such as ARCHER.
#
# Longbow is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 2 of the License, or (at your option) any later
# version.
#
# Longbow is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# Longbow. If not, see <http://www.gnu.org/licenses/>.

"""
This testing module contains the tests for the applications module methods.
"""

import pytest

import Longbow.corelibs.applications as apps
import Longbow.corelibs.exceptions as ex


def test_flagvalidator_pass():

"""Test that the flag test passes if correct."""

foundflags = ["-c", "-i", "-p"]

job = {
"executable": "pmemd.MPI"
}

apps._flagvalidator(job, foundflags)


def test_flagvalidator_fail():

"""Test that the flag test fails with exception if not correct."""

foundflags = ["-c", "-i"]

job = {
"executable": "pmemd.MPI",
"jobname": "jobone"
}

with pytest.raises(ex.RequiredinputError):

apps._flagvalidator(job, foundflags)


def test_flagvalidator_ortest():

"""Test that cases with -flag || --flag work."""

foundflags = ["-deffnm"]

job = {
"executable": "mdrun"
}

apps._flagvalidator(job, foundflags)
Loading

0 comments on commit c983774

Please sign in to comment.