class CommandHost(local_host.LocalHost): """A CommandHost runs an executable through a command. The 'command' parameter specifies the command to use, while 'command_args' contains a set of arguments to be passed to the command.""" command = TextField(description="Name of the command" " used to run the executable.") command_args = SetField( TextField(description="Set of arguments" " passed to the command.")) def Run(self, path, arguments, environment=None, timeout=-1, relative=False): if (relative or (not os.path.isabs(path) and (path.find(os.path.sep) != -1 or (os.path.altsep and path.find(os.path.altsep) != -1)))): path = os.path.join(os.curdir, path) arguments = self.command_args + [path] + arguments return local_host.LocalHost.Run(self, self.command, arguments, environment, timeout)
class RemoteHost(ssh_host.SSHHost): # The wrapper script name to be used to invoke # remote programs wrapper = TextField() wrapper_args = SetField(TextField()) def Run(self, path, arguments, environment=None, timeout=-1, relative=False): if self.wrapper: path = os.path.join(os.curdir, os.path.basename(path)) arguments = [path] + arguments[:] path = self.wrapper if self.wrapper_args: arguments = self.wrapper_args + arguments[:] return super(RemoteHost, self).Run(path, arguments, environment, timeout, relative) def DeleteFile(self, remote_file): if self.default_dir: remote_file = os.path.join(self.default_dir, remote_file) super(RemoteHost, self).Run('rm', [remote_file])
class RSHHost(SSHHost): """An 'RSHHost' is an 'SSHHost' that uses 'rsh' instead of 'ssh'. The reason that 'RSHHost' is a separate class is that (a) that makes it easier for users to construct an 'SSHHost', and (b) 'rsh' does not return the exit code of the remote program, so 'Run' requires adjustment.""" # Override the default values. ssh_program = TextField( default_value = "rsh", description = """The path to the remote shell program.""" ) scp_program = TextField( default_value = "rcp", description = """The path to the remote copy program.""" ) def Run(self, path, arguments, environment = None, timeout = -1): status, output = \ super(RSHHost, self).Run(path, arguments, environment, timeout) # The exit status of 'rsh' is not the exit status of the # remote program. The exit status of the remote program is # unavailable. return (None, output)
class ExplicitSuite(qm.test.suite.Suite): """An 'ExplicitSuite' stores all of the test and suite ids explicitly.""" arguments = [ SetField( TextField( name="test_ids", title="Test Names", description="""The the tests contained in this suite.""")), SetField( TextField( name="suite_ids", title="Suite Names", description="""The the suites contained in this suite.""")), BooleanField(name="is_implicit", title="Implicit?", description="""\ True if this test is implicitly generated by QMTest.""", default_value="false"), ] def IsImplicit(self): return self.is_implicit == "true" def GetTestIds(self): return self.test_ids def GetSuiteIds(self): return self.suite_ids
class PreviousTestRun(ExpectationDatabase): """A 'PreviousTestRun' uses test results as expectations. A 'PreviousTestRun' uses a ResultsFile such as generated by 'qmtest run' to determine the expected outcome for the current test run.""" file_name = TextField(description="The name of the results file.") results_file = PythonField("The results file.") def __init__(self, **args): super(PreviousTestRun, self).__init__(**args) if not self.results_file: self.results_file = open(self.file_name, "rb") results = load_results(self.results_file, self.test_database) self._results = {} for r in results: # Keep test results only. if r.GetKind() == Result.TEST: self._results[r.GetId()] = r def Lookup(self, test_id): return self._results.get(test_id) or Result(Result.TEST, test_id)
class FileResultReader(ResultReader): """A 'FileResultReader' gets its input from a file. A 'FileResultReader' is an abstract base class for other result reader classes that read results from a single file. The file from which results should be read can be specified using either the 'filename' argument or the 'file' argument. The latter is for use by QMTest internally.""" class InvalidFile(QMException): """An 'InvalidFile' exception indicates an incorrect file format. If the constructor for a 'FileResultStream' detects an invalid file, it must raise an instance of this exception.""" pass arguments = [ TextField(name="filename", title="File Name", description="""The name of the file. All results will be read from the file indicated. If no filename is specified, or the filename specified is "-", the standard input will be used.""", verbatim="true", default_value=""), PythonField(name="file"), ] _is_binary_file = 0 """If true, results are stored in a binary format. This flag can be overridden by derived classes.""" def __init__(self, arguments=None, **args): """Construct a new 'FileResultReader'. 'arguments' -- As for 'ResultReader'. If the file provided is not in the input format expected by this result reader, the derived class '__init__' function must raise an 'InvalidStream' exception.""" super(FileResultReader, self).__init__(arguments, **args) if not self.file: if self.filename and self.filename != "-": if self._is_binary_file: mode = "rb" else: mode = "r" self.file = open(self.filename, mode, 0) else: self.file = sys.stdin
class Simulator(LocalHost): """A 'Simulator' is a semi-hosted simulation environment. The local file system is shared with the simulated machine. A simulator is used to execute programs.""" simulator = TextField( description = """The simulation program.""" ) # Any arguments that must be provided to the simulator. simulator_args = SetField( TextField(description = """Arguments to the simulation program.""")) def Run(self, path, arguments, environment = None, timeout = -1): arguments = self.simulator_args + [path] + arguments return super(Simulator, self.Run(self.simulator, arguments, environment, timeout))
class RemoteHost(ssh_host.SSHHost): # The wrapper script name to be used to invoke # remote programs wrapper = TextField() def Run(self, path, arguments, environment=None, timeout=-1, relative=False): if self.wrapper: arguments = [path] + arguments[:] path = self.wrapper return super(RemoteHost, self).Run(path, arguments, environment, timeout, relative)
class XMLExpectationDatabase(ExpectationDatabase): """An 'XMLExpectationDatabase' reads expectations from an XML file.""" file_name = TextField() def __init__(self, **args): super(XMLExpectationDatabase, self).__init__(**args) self._expectations = [] document = load_xml(open(self.file_name)) root = document.documentElement for e in root.getElementsByTagName('expectation'): test_id = e.getAttribute('test_id') outcome = { 'pass': Result.PASS, 'fail': Result.FAIL, }[e.getAttribute('outcome')] filters = {} for a in e.getElementsByTagName('annotation'): filters[a.getAttribute('name')] = a.getAttribute('value') description = e.getElementsByTagName('description') if description: description = get_dom_text(description[0]) self._expectations.append((test_id, outcome, filters, description)) def Lookup(self, test_id): outcome, description = Result.PASS, '' for rule_id, rule_outcome, rule_annotations, rule_description in self._expectations: if re.match(rule_id, test_id): match = True for a in rule_annotations.iteritems(): if (a[0] not in self.testrun_parameters or not re.match(a[1], self.testrun_parameters[a[0]])): match = False if match: outcome = rule_outcome description = rule_description return Result(Result.TEST, test_id, outcome, annotations={'description': description})
class SSHHost(Host): """An 'SSHHost' is accessible via 'ssh' or a similar program.""" # If not empty, the name of the remote host. host_name = TextField() # The path to "ssh". ssh_program = TextField( default_value = "ssh", description = """The path to the remote shell program.""" ) # Any arguments that must be provided to "ssh". ssh_args = SetField( TextField(description = """The arguments to the remote shell program.""")) # The path to "scp". scp_program = TextField( default_value = "scp", description = """The path to the remote copy program.""" ) # Any arguments that must be provided to "scp". scp_args = SetField( TextField(description = """The arguments to the remote copy program.""")) # The default directory on the remote system. default_dir = TextField( description = """The default directory on the remote system.""" ) nfs_dir = TextField( description = """The default directory, as seen from the local host. If not empty, 'nfs_dir' is a directory on the local machine that is equivalent to the default directory on the remote machine. In that case, files will be copied to and from this directory on the local machine, rather than by using 'scp'.""" ) user_name = TextField( description = """The user name on the remote host. If not empty, the user name that should be used when connecting to the remote host.""" ) def Run(self, path, arguments, environment = None, timeout = -1, relative = False): default_dir = self.default_dir if not default_dir: default_dir = os.curdir if (relative or (not os.path.isabs(path) and (path.find(os.path.sep) != -1 or (os.path.altsep and path.find(os.path.altsep) != -1)))): path = os.path.join(default_dir, path) path, arguments = self._FormSSHCommandLine(path, arguments, environment) return super(SSHHost, self).Run(path, arguments, None, timeout) def UploadFile(self, local_file, remote_file = None): if remote_file is None: remote_file = os.path.basename(local_file) if self.nfs_dir: remote_file = os.path.join(self.nfs_dir, remote_file) super(SSHHost, self).UploadFile(local_file, remote_file) else: if self.default_dir: remote_file = os.path.join(self.default_dir, remote_file) command = self._FormSCPCommandLine(True, local_file, remote_file) executable = self.Executable() status = executable.Run(command) if ((sys.platform != "win32" and (not os.WIFEXITED(status) or os.WEXITSTATUS(status) != 0)) or (sys.platform == "win32" and status != 0)): raise qm.common.QMException("could not upload file") def DownloadFile(self, remote_file, local_file = None): if local_file is None: local_file = os.path.basename(remote_file) if self.nfs_dir: remote_file = os.path.join(self.nfs_dir, remote_file) super(SSHHost, self).DownloadFile(remote_file, local_file) else: if self.default_dir: remote_file = os.path.join(self.default_dir, remote_file) command = self._FormSCPCommandLine(False, local_file, remote_file) executable = self.Executable() executable.Run(command) def DeleteFile(self, remote_file): if self.default_dir: remote_file = os.path.join(self.default_dir, remote_file) return self.Run("rm", [remote_file]) def _FormSSHCommandLine(self, path, arguments, environment = None): """Form the 'ssh' command line. 'path' -- The remote command, in the same format expected by 'Run'. 'arguments' -- The arguments to the remote command. 'environment' -- As for 'Run'. returns -- A pair '(path, arguments)' describing the command to run on the local machine that will execute the remote command.""" command = self.ssh_args + [self.host_name] if self.user_name: command += ["-l", self.user_name] if environment is not None: command.append("env") for (k, v) in environment.iteritems(): command.append("%s=%s" % (k, v)) command.append(path) command += arguments return self.ssh_program, command def _FormSCPCommandLine(self, upload, local_file, remote_file): """Form the 'scp' command line. 'upload' -- True iff the 'local_file' should be copied to the remote host. 'local_file' -- The path to the local file. 'remote_file' -- The path to the remote file. returns -- The list of arguments for a command to run on the local machine that will perform the file copy.""" if self.default_dir: remote_file = os.path.join(self.default_dir, remote_file) remote_file = self.host_name + ":" + remote_file if self.user_name: remote_file = self.user_name + "@" + remote_file command = [self.scp_program] + self.scp_args if upload: command += [local_file, remote_file] else: command += [remote_file, local_file] return command