From patchwork Thu Sep 22 18:00:18 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Larson X-Patchwork-Id: 4277 Return-Path: X-Original-To: patchwork@peony.canonical.com Delivered-To: patchwork@peony.canonical.com Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145]) by peony.canonical.com (Postfix) with ESMTP id 55C9E23F98 for ; Thu, 22 Sep 2011 18:00:22 +0000 (UTC) Received: from mail-ey0-f180.google.com (mail-ey0-f180.google.com [209.85.215.180]) by fiordland.canonical.com (Postfix) with ESMTP id 27CD8A18110 for ; Thu, 22 Sep 2011 18:00:22 +0000 (UTC) Received: by eyb6 with SMTP id 6so2427457eyb.11 for ; Thu, 22 Sep 2011 11:00:22 -0700 (PDT) Received: by 10.223.63.8 with SMTP id z8mr3426461fah.84.1316714421772; Thu, 22 Sep 2011 11:00:21 -0700 (PDT) X-Forwarded-To: linaro-patchwork@canonical.com X-Forwarded-For: patch@linaro.org linaro-patchwork@canonical.com Delivered-To: patches@linaro.org Received: by 10.152.18.198 with SMTP id y6cs178688lad; Thu, 22 Sep 2011 11:00:20 -0700 (PDT) Received: by 10.227.8.196 with SMTP id i4mr2539165wbi.41.1316714419541; Thu, 22 Sep 2011 11:00:19 -0700 (PDT) Received: from indium.canonical.com (indium.canonical.com. [91.189.90.7]) by mx.google.com with ESMTPS id gd9si7777749wbb.85.2011.09.22.11.00.18 (version=TLSv1/SSLv3 cipher=OTHER); Thu, 22 Sep 2011 11:00:19 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of bounces@canonical.com designates 91.189.90.7 as permitted sender) client-ip=91.189.90.7; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of bounces@canonical.com designates 91.189.90.7 as permitted sender) smtp.mail=bounces@canonical.com Received: from ackee.canonical.com ([91.189.89.26]) by indium.canonical.com with esmtp (Exim 4.71 #1 (Debian)) id 1R6nZK-00030j-FN for ; Thu, 22 Sep 2011 18:00:18 +0000 Received: from ackee.canonical.com (localhost [127.0.0.1]) by ackee.canonical.com (Postfix) with ESMTP id 62D27E02C7 for ; Thu, 22 Sep 2011 18:00:18 +0000 (UTC) MIME-Version: 1.0 X-Launchpad-Project: lava-test X-Launchpad-Branch: ~linaro-validation/lava-test/trunk X-Launchpad-Message-Rationale: Subscriber X-Launchpad-Branch-Revision-Number: 93 X-Launchpad-Notification-Type: branch-revision To: Linaro Patch Tracker From: noreply@launchpad.net Subject: [Branch ~linaro-validation/lava-test/trunk] Rev 93: Big refactoring branch to make lava-test use lava-tool. Thanks to Message-Id: <20110922180018.25219.21599.launchpad@ackee.canonical.com> Date: Thu, 22 Sep 2011 18:00:18 -0000 Reply-To: noreply@launchpad.net Sender: bounces@canonical.com Errors-To: bounces@canonical.com Precedence: bulk X-Generated-By: Launchpad (canonical.com); Revision="13996"; Instance="initZopeless config overlay" X-Launchpad-Hash: 7418ceefb0b0bca877484ee37ddd96f3184427bc Merge authors: Le Chi Thu le.chi.thu@linaro.org Related merge proposals: https://code.launchpad.net/~le-chi-thu/lava-test/using-lava-tool-2/+merge/75272 proposed by: Le Chi Thu (le-chi-thu) ------------------------------------------------------------ revno: 93 [merge] committer: Paul Larson branch nick: lava-test timestamp: Thu 2011-09-22 12:58:01 -0500 message: Big refactoring branch to make lava-test use lava-tool. Thanks to Zygmunt and ChiThu! removed: abrek/api.py abrek/builtins.py abrek/bundle.py abrek/cache.py abrek/command.py abrek/config.py abrek/dashboard.py abrek/hwprofile.py abrek/providers.py abrek/results.py abrek/swprofile.py abrek/testdef.py bin/ bin/lava-test tests/test_abrekcmd.py tests/test_abrektest.py tests/test_abrektestinstaller.py tests/test_abrektestparser.py tests/test_abrektestrunner.py tests/test_builtins.py tests/test_dashboard.py tests/test_main.py tests/test_results.py added: doc/ doc/changes.rst doc/conf.py doc/index.rst doc/installation.rst doc/reference.rst doc/todo.rst doc/usage.rst lava_test/api/ lava_test/api/__init__.py lava_test/api/core.py lava_test/api/delegates.py lava_test/api/observers.py lava_test/commands.py lava_test/core/ lava_test/core/__init__.py lava_test/core/artifacts.py lava_test/core/config.py lava_test/core/hwprofile.py lava_test/core/installers.py lava_test/core/loader.py lava_test/core/parsers.py lava_test/core/providers.py lava_test/core/runners.py lava_test/core/swprofile.py lava_test/core/tests.py lava_test/extcmd.py tests/test_lavatest_commands.py tests/test_lavatest_test.py tests/test_lavatest_testinstaller.py tests/test_lavatest_testparser.py tests/test_lavatest_testrunner.py renamed: abrek/ => lava_test/ modified: .bzrignore MANIFEST.in README examples/power-management-tests.json examples/stream.json lava_test/__init__.py lava_test/main.py lava_test/test_definitions/bootchart.py lava_test/test_definitions/firefox.py lava_test/test_definitions/glmemperf.py lava_test/test_definitions/gmpbench.py lava_test/test_definitions/gtkperf.py lava_test/test_definitions/ltp.py lava_test/test_definitions/peacekeeper.py lava_test/test_definitions/posixtestsuite.py lava_test/test_definitions/pwrmgmt.py lava_test/test_definitions/pybench.py lava_test/test_definitions/smem.py lava_test/test_definitions/stream.py lava_test/test_definitions/tiobench.py lava_test/test_definitions/x11perf.py lava_test/test_definitions/xrestop.py lava_test/utils.py setup.py tests/__init__.py tests/fixtures.py tests/imposters.py tests/test_hwprofile.py tests/test_swprofile.py --- lp:lava-test https://code.launchpad.net/~linaro-validation/lava-test/trunk You are subscribed to branch lp:lava-test. To unsubscribe from this branch go to https://code.launchpad.net/~linaro-validation/lava-test/trunk/+edit-subscription === modified file '.bzrignore' --- .bzrignore 2011-06-21 19:38:29 +0000 +++ .bzrignore 2011-09-12 09:19:10 +0000 @@ -5,3 +5,4 @@ *~ *.tmp *.py[co] +build === modified file 'MANIFEST.in' --- MANIFEST.in 2011-06-21 19:38:29 +0000 +++ MANIFEST.in 2011-09-12 09:19:10 +0000 @@ -1,4 +1,3 @@ include COPYING include README include .testr.conf -include bin/lava-test \ No newline at end of file === modified file 'README' --- README 2011-08-03 14:11:26 +0000 +++ README 2011-09-12 09:19:10 +0000 @@ -3,20 +3,11 @@ automatically installed, executed, and the results can be parsed and uploaded to an external server. -External dependency -------------------- -The following debian packages are needed: -* python-setuptools -* python-apt -* usbutils -* python-testrepository - for running unit tests - How to install from the source code =================================== 1. Run: ./setup.py install - How to setup from the source code for development ================================================= @@ -32,7 +23,7 @@ 3. Add /.local.bin in your PATH -To install build-in tests +To install built-in tests ========================= 1. Run: lava-test list-tests 2. Run: lava-test install @@ -47,4 +38,5 @@ To install test define with a json file ======================================= 1. Run: lava-test register-test file://localhost/<..>/examples/stream.json -2. Run: lava-test list-tests \ No newline at end of file +2. Run: lava-test list-tests + === removed file 'abrek/api.py' --- abrek/api.py 2011-06-28 12:51:57 +0000 +++ abrek/api.py 1970-01-01 00:00:00 +0000 @@ -1,76 +0,0 @@ -""" -Public API for extending Abrek -""" -from abc import abstractmethod, abstractproperty - -class ITestProvider(object): - """ - Abrek test provider. - - Abstract source of abrek tests. - """ - - @abstractmethod - def __init__(self, config): - """ - Initialize test provider with the specified configuration object. The - configuration object is obtained from the abrek providers registry. - """ - - @abstractmethod - def __iter__(self): - """ - Iterates over instances of ITest exposed by this provider - """ - - @abstractmethod - def __getitem__(self, test_name): - """ - Return an instance of ITest with the specified name - """ - - @abstractproperty - def description(self): - """ - The description string used by abrek list-tests - """ - - -class ITest(object): - """ - Abrek test. - - Something that can be installed and invoked by abre. - """ - - @abstractmethod - def install(self): - """ - Install the test suite. - - This creates an install directory under the user's XDG_DATA_HOME - directory to mark that the test is installed. The installer's - install() method is then called from this directory to complete any - test specific install that may be needed. - """ - - @abstractmethod - def uninstall(self): - """ - Uninstall the test suite. - - Uninstalling just recursively removes the test specific directory under - the user's XDG_DATA_HOME directory. This will both mark the test as - removed, and clean up any files that were downloaded or installed under - that directory. Dependencies are intentionally not removed by this. - """ - - @abstractmethod - def run(self, quiet=False): - # TODO: Document me - pass - - @abstractmethod - def parse(self, resultname): - # TODO: Document me - pass === removed file 'abrek/builtins.py' --- abrek/builtins.py 2011-08-03 14:25:13 +0000 +++ abrek/builtins.py 1970-01-01 00:00:00 +0000 @@ -1,175 +0,0 @@ -# Copyright (c) 2010 Linaro -# -# This program 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 3 of the License, or -# (at your option) any later version. -# -# This program 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 this program. If not, see . - -import os -import sys -from optparse import make_option - -import abrek.command -import abrek.testdef -from abrek.config import get_config - - -class cmd_version(abrek.command.AbrekCmd): - """ - Show the version of abrek - """ - def run(self): - import abrek - print abrek.__version__ - - -class cmd_help(abrek.command.AbrekCmd): - """ Get help on abrek commands - - If the command name is ommited, calling the help command will return a - list of valid commands. - """ - arglist = ['command', 'subcommand'] - def run(self): - if len(self.args) < 1: - print "Available commands:" - for cmd in abrek.command.get_all_cmds(): - print " %s" % cmd - print - print "To access extended help on a command use 'abrek help " \ - "[command]'" - return - command_name = self.args.pop(0) - cmd = abrek.command.get_command(command_name) - if not cmd: - print "No command found for '%s'" % command_name - return - while self.args: - subcommand_name = self.args.pop(0) - cmd = cmd.get_subcommand(subcommand_name) - if not cmd: - print "No sub-command of '%s' found for '%s'" % ( - command_name, subcommand_name) - return - command_name += ' ' + subcommand_name - print cmd.help() - - -class cmd_install(abrek.command.AbrekCmd): - """ - Install a test - """ - arglist = ['*testname'] - - def run(self): - self.checkroot() - if len(self.args) != 1: - print "please specify the name of the test to install" - sys.exit(1) - test = abrek.testdef.testloader(self.args[0]) - try: - test.install() - except RuntimeError as strerror: - print "Test installation error: %s" % strerror - sys.exit(1) - - -class cmd_run(abrek.command.AbrekCmd): - """ - Run tests - """ - arglist = ['*testname'] - options = [make_option('-q', '--quiet', action='store_true', - default=False, dest='quiet'), - make_option('-o', '--output', action='store', - default=None, metavar="FILE", - help="Store processed test output to FILE")] - - def run(self): - self.checkroot() - if len(self.args) != 1: - print "please specify the name of the test to run" - sys.exit(1) - test = abrek.testdef.testloader(self.args[0]) - try: - result_id = test.run(quiet=self.opts.quiet) - if self.opts.output: - from abrek.dashboard import generate_bundle - import json - bundle = generate_bundle(result_id) - with open(self.opts.output, "wt") as stream: - json.dump(bundle, stream) - except Exception as strerror: - print "Test execution error: %s" % strerror - sys.exit(1) - - -class cmd_uninstall(abrek.command.AbrekCmd): - """ - Uninstall a test - """ - arglist = ['*testname'] - - def run(self): - if len(self.args) != 1: - print "please specify the name of the test to uninstall" - sys.exit(1) - test = abrek.testdef.testloader(self.args[0]) - try: - test.uninstall() - except Exception as strerror: - print "Test uninstall error: %s" % strerror - sys.exit(1) - - -class cmd_list_installed(abrek.command.AbrekCmd): - """ - List tests that are currently installed - """ - def run(self): - config = get_config() - print "Installed tests:" - try: - for dir in os.listdir(config.installdir): - print dir - except OSError: - print "No tests installed" - - -class cmd_list_tests(abrek.command.AbrekCmd): - """ - List all known tests - """ - def run(self): - from abrek.testdef import TestLoader - for provider in TestLoader().get_providers(): - print provider.description - for test in provider: - print " - %s" % test - - -class cmd_register_test(abrek.command.AbrekCmd): - """ - Register declarative tests - """ - - arglist = ['test_url'] - - def run(self): - if len(self.args) != 1: - self.parser.error("You need to provide an URL to a test definition file") - test_url = self.args[0] - from abrek.providers import RegistryProvider - try: - RegistryProvider.register_remote_test(test_url) - except ValueError as exc: - print "Unable to register test: %s" % exc - sys.exit(1) === removed file 'abrek/bundle.py' --- abrek/bundle.py 2011-04-19 16:56:01 +0000 +++ abrek/bundle.py 1970-01-01 00:00:00 +0000 @@ -1,44 +0,0 @@ -# Copyright (c) 2011 Linaro -# -# This program 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 3 of the License, or -# (at your option) any later version. -# -# This program 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 this program. If not, see . - -""" -This module attempts to use the linaro-dashboard-bundle package, if possible. - -Using that package adds proper support for loading and saving bundle -documents. In particular it supports loosles decimals, better, more stable -load-modify-write cycles, data validation, transparent migration and many -other features. - -It is not a hard dependency to make it possible to run abrek from a checkout -without having to install (too many) dependencies. -""" - -try: - from linaro_dashboard_bundle import DocumentIO -except ImportError: - import json - - class DocumentIO(object): - """ Bare replacement DocumentIO without any fancy features """ - - @classmethod - def dumps(cls, doc): - return json.dumps(doc, indent=2) - - @classmethod - def loads(cls, text): - doc = json.loads(text) - fmt = doc.get("format") - return fmt, doc \ No newline at end of file === removed file 'abrek/cache.py' --- abrek/cache.py 2011-08-16 19:18:30 +0000 +++ abrek/cache.py 1970-01-01 00:00:00 +0000 @@ -1,73 +0,0 @@ -""" -Cache module for Abrek -""" -import contextlib -import hashlib -import os -import urllib2 - - -class AbrekCache(object): - """ - Cache class for Abrek - """ - - _instance = None - - def __init__(self): - home = os.environ.get('HOME', '/') - basecache = os.environ.get('XDG_CACHE_HOME', - os.path.join(home, '.cache')) - self.cache_dir = os.path.join(basecache, 'abrek') - - @classmethod - def get_instance(cls): - if cls._instance is None: - cls._instance = cls() - return cls._instance - - def open_cached(self, key, mode="r"): - """ - Acts like open() but the pathname is relative to the - abrek-specific cache directory. - """ - if "w" in mode and not os.path.exists(self.cache_dir): - os.makedirs(self.cache_dir) - if os.path.isabs(key): - raise ValueError("key cannot be an absolute path") - try: - stream = open(os.path.join(self.cache_dir, key), mode) - yield stream - finally: - stream.close() - - def _key_for_url(self, url): - return hashlib.sha1(url).hexdigest() - - def _refresh_url_cache(self, key, url): - with contextlib.nested( - contextlib.closing(urllib2.urlopen(url)), - self.open_cached(key, "wb")) as (in_stream, out_stream): - out_stream.write(in_stream.read()) - - @contextlib.contextmanager - def open_cached_url(self, url): - """ - Like urlopen.open() but the content may be cached. - """ - # Do not cache local files, this is not what users would expect - - # workaround - not using cache at all. - # TODO: fix this and use the cache - # if url.startswith("file://"): - if True: - stream = urllib2.urlopen(url) - else: - key = self._key_for_url(url) - try: - stream = self.open_cached(key, "rb") - except IOError as exc: - self._refresh_url_cache(key, url) - stream = self.open_cached(key, "rb") - yield stream - stream.close() === removed file 'abrek/command.py' --- abrek/command.py 2011-08-03 14:25:13 +0000 +++ abrek/command.py 1970-01-01 00:00:00 +0000 @@ -1,171 +0,0 @@ -# Copyright (c) 2010 Linaro -# -# This program 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 3 of the License, or -# (at your option) any later version. -# -# This program 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 this program. If not, see . - -from optparse import OptionParser -import os -import sys - - -class _AbrekOptionParser(OptionParser): - """ - This is just to override the epilog formatter to allow newlines - """ - def format_epilog(self, formatter): - return self.epilog - - -class AbrekCmd(object): - """ Base class for commands that can be passed to Abrek. - - Commands added to abrek should inherit from AbrekCmd. To allow for - autodiscovery, the name of the class should begin with cmd_. - - Arguments allowed by the command can be specified in the 'arglist'. - These arguments will automatically be listed in the help for that - command. Required arguments should begin with a '*'. For example: - arglist = ['*requiredarg', 'optionalarg'] - - Options may also be specified by using the 'options' list. To add - arguments, you must use the make_option() function from optparse. - For example: - options = [make_option("-b", "--bar", dest="bar")] - - Commands also support subcommands. A subcommand is similar to a - command in abrek, and it should also inherit from AbrekCmd. However, - a subcommand class should not begin with cmd_. Instead, it should - be tied to the command that uses it, using the 'subcmds' dict. - For example: - class subcmd_bar(AbrekCmd): - pass - class cmd_foo(AbrekCmd): - subcmds = {'bar':subcmd_bar()} - pass - """ - options = [] - arglist = [] - - def __init__(self, name_prefix=''): - self._name_prefix = name_prefix - self.parser = _AbrekOptionParser(usage=self._usage(), - epilog=self._desc()) - for opt in self.options: - self.parser.add_option(opt) - - def main(self, argv): - (self.opts, self.args) = self.parser.parse_args(argv) - return self.run() - - def name(self): - return self._name_prefix + _convert_command_name(self.__class__.__name__) - - def run(self): - raise NotImplementedError("%s: command defined but not implemented!" % - self.name()) - - def _usage(self): - usagestr = "Usage: lava-test %s" % self.name() - for arg in self.arglist: - if arg[0] == '*': - usagestr += " %s" % arg[1:].upper() - else: - usagestr += " [%s]" % arg.upper() - return usagestr - - def _desc(self): - from inspect import getdoc - docstr = getdoc(self) - if not docstr: - return "" - description = "\nDescription:\n" - description += docstr + "\n" - return description - - def help(self): - #For some reason, format_help includes an extra \n - return self.parser.format_help()[:-1] - - def get_subcommand(self, name): - return None - - def checkroot(self): - if os.getuid() != 0: - print >> sys.stderr, ("**** WARNING: ROOT PERMISSIONS ARE OFTEN" - "REQUIRED FOR THIS OPERATION ****") - - -class AbrekCmdWithSubcommands(AbrekCmd): - - arglist = ['subcommand'] - - def main(self, argv): - if not argv: - print "Missing sub-command." + self._list_subcmds() - else: - subcmd = self.get_subcommand(argv[0]) - if subcmd is None: - # This line might print the help and raise SystemExit if - # --help is passed or if an invalid option was passed. - opts, args = self.parser.parse_args(argv) - # If it didn't, complain. - print "'%s' not found as a sub-command of '%s'" % ( - args[0], self.name()) + self._list_subcmds() - else: - return subcmd.main(argv[1:]) - - def get_subcommand(self, name): - subcmd_cls = getattr(self, 'cmd_' + name.replace('_', '-'), None) - if subcmd_cls is None: - return None - return subcmd_cls(self.name() + ' ') - - def _usage(self): - usagestr = AbrekCmd._usage(self) - usagestr += self._list_subcmds() - return usagestr - - def _list_subcmds(self): - subcmds = [] - for attrname in self.__class__.__dict__.keys(): - if attrname.startswith('cmd_'): - subcmds.append(_convert_command_name(attrname)) - if not subcmds: - return '' - return "\n\nAvailable sub-commands:\n " + "\n ".join(subcmds) - - -def _convert_command_name(cmd): - return cmd[4:].replace('_','-') - - -def _find_commands(module): - cmds = {} - for name, func in module.__dict__.iteritems(): - if name.startswith("cmd_"): - real_name = _convert_command_name(name) - cmds[real_name] = func() - return cmds - - -def get_all_cmds(): - from abrek import builtins, dashboard, results - cmds = _find_commands(builtins) - cmds.update(_find_commands(dashboard)) - cmds.update(_find_commands(results)) - return cmds - - -def get_command(cmd_name): - cmds = get_all_cmds() - return cmds.get(cmd_name) === removed file 'abrek/config.py' --- abrek/config.py 2011-08-03 09:06:20 +0000 +++ abrek/config.py 1970-01-01 00:00:00 +0000 @@ -1,93 +0,0 @@ -# Copyright (c) 2010 Linaro -# -# This program 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 3 of the License, or -# (at your option) any later version. -# -# This program 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 this program. If not, see . - -import os -import json - - -class AbrekConfig(object): - - def __init__(self): - home = os.environ.get('HOME', '/') - baseconfig = os.environ.get('XDG_CONFIG_HOME', - os.path.join(home, '.config')) - basedata = os.environ.get('XDG_DATA_HOME', - os.path.join(home, '.local', 'share')) - self.configdir = os.path.join(baseconfig, 'abrek') - self.installdir = os.path.join(basedata, 'abrek', 'installed-tests') - self.resultsdir = os.path.join(basedata, 'abrek', 'results') - self.registry = self._load_registry() - - @property - def _registry_pathname(self): - return os.path.join(self.configdir, "registry.json") - - def _load_registry(self): - try: - with open(self._registry_pathname) as stream: - return json.load(stream) - except (IOError, ValueError) as exc: - return self._get_default_registry() - - def _save_registry(self): - if not os.path.exists(self.configdir): - os.makedirs(self.configdir) - with open(self._registry_pathname, "wt") as stream: - json.dump(self.registry, stream, indent=2) - - def _get_default_registry(self): - return { - "format": "Abrek Test Registry 1.0 Experimental", - "providers": [ - { - "entry_point": "abrek.providers:BuiltInProvider", - }, - { - "entry_point": "abrek.providers:PkgResourcesProvider", - }, - { - "entry_point": "abrek.providers:RegistryProvider", - "config": { - "entries": [] - } - } - ] - } - - def get_provider_config(self, entry_point_name): - if "providers" not in self.registry: - self.registry["providers"] = [] - for provider_info in self.registry["providers"]: - if provider_info.get("entry_point") == entry_point_name: - break - else: - provider_info = {"entry_point": entry_point_name} - self.registry["providers"].append(provider_info) - if "config" not in provider_info: - provider_info["config"] = {} - return provider_info["config"] - - -_config = None - -def get_config(): - global _config - if _config is not None: - return _config - return AbrekConfig() - -def set_config(config): - global _config - _config = config === removed file 'abrek/dashboard.py' --- abrek/dashboard.py 2011-08-09 09:35:19 +0000 +++ abrek/dashboard.py 1970-01-01 00:00:00 +0000 @@ -1,206 +0,0 @@ -# Copyright (c) 2010 Linaro -# -# This program 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 3 of the License, or -# (at your option) any later version. -# -# This program 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 this program. If not, see . - -import base64 -import os -import socket -import sys -import urllib -import xmlrpclib -from ConfigParser import ConfigParser, NoOptionError -from getpass import getpass -from optparse import make_option - -from abrek.bundle import DocumentIO -from abrek.command import AbrekCmd, AbrekCmdWithSubcommands -from abrek.config import get_config -from abrek.testdef import testloader - - -class DashboardConfig(object): - """ - Read and write dashboard configuration data - """ - - def __init__(self, section="Default Server"): - self.dashboardconf = ConfigParser() - self.section = section - self.config = get_config() - self.path = os.path.join(self.config.configdir, "dashboard.conf") - if os.path.exists(self.path): - self.dashboardconf.read(self.path) - if not (self.section in self.dashboardconf.sections()): - self.dashboardconf.add_section(self.section) - - def set_host(self, host): - self.dashboardconf.set(self.section, 'host', host) - - def get_host(self): - try: - host = self.dashboardconf.get(self.section, 'host') - return host - except NoOptionError: - return "" - - host = property(get_host, set_host) - - def set_user(self, user): - self.dashboardconf.set(self.section, 'user', user) - - def get_user(self): - try: - user = self.dashboardconf.get(self.section, 'user') - return user - except NoOptionError: - return "" - - user = property(get_user, set_user) - - def set_password(self, password): - #Not exactly secure, but better than storing in plaintext - password = base64.encodestring(password).strip() - self.dashboardconf.set(self.section,'password', password) - - def get_password(self): - try: - password = self.dashboardconf.get(self.section, 'password') - return base64.decodestring(password) - except NoOptionError: - return "" - - password = property(get_password, set_password) - - def write(self): - """ - write the dashboard configuration out to the config file - """ - if not os.path.exists(self.config.configdir): - os.makedirs(self.config.configdir) - with open(self.path, "w") as fd: - self.dashboardconf.write(fd) - - -class cmd_dashboard(AbrekCmdWithSubcommands): - """ - Connect to the Launch-control dashboard - """ - - class cmd_setup(AbrekCmd): - """ - Configure information needed to push results to the dashboard - """ - options = [make_option("-u", "--user", dest="user"), - make_option("-p", "--password", dest="password")] - arglist = ["*server"] - - def run(self): - if len(self.args) != 1: - print "You must specify a server" - sys.exit(1) - config = DashboardConfig() - if self.opts.user: - user = self.opts.user - else: - user = raw_input("Username: ") - if self.opts.password: - password = self.opts.password - else: - password = getpass() - config.host = self.args[0] - config.user = user - config.password = password - config.write() - - - class cmd_put(AbrekCmd): - """ - Push the results from a test to the server - The stream name must include slashes (e.g. /anonymous/foo/) - """ - arglist = ["*stream", "*result"] - - def run(self): - if len(self.args) != 2: - print "You must specify a stream and a result" - sys.exit(1) - stream_name = self.args[0] - result_name = self.args[1] - bundle = generate_bundle(result_name) - db_config = DashboardConfig() - hosturl = urllib.basejoin(db_config.host, "xml-rpc/") - try: - server = xmlrpclib.Server(hosturl) - except IOError, e: - print ("Error connecting to server at '%s'. Error was: %s, " - "please run 'lava-test dashboard setup [host]'" % ( - hosturl, e)) - sys.exit(1) - try: - result = server.put(DocumentIO.dumps(bundle), result_name, - stream_name) - print "Bundle successfully uploaded to id: %s" % result - except xmlrpclib.Fault as strerror: - print "Error uploading bundle: %s" % strerror.faultString - sys.exit(1) - except socket.error as strerror: - print "Unable to connect to host: %s" % strerror - sys.exit(1) - except xmlrpclib.ProtocolError as strerror: - print "Connection error: %s" % strerror - sys.exit(1) - - - class cmd_bundle(AbrekCmd): - """ - Print JSON output that can be imported into the dashboard - """ - arglist = ["*result"] - - def run(self): - if len(self.args) != 1: - print "You must specify a result" - sys.exit(1) - bundle = generate_bundle(self.args[0]) - try: - print DocumentIO.dumps(bundle) - except IOError: - pass - - -def generate_bundle(result): - config = get_config() - resultdir = os.path.join(config.resultsdir, result) - if not os.path.exists(resultdir): - # FIXME: UI and sys.exit mixed with internal implementation, yuck - print "Result directory not found" - sys.exit(1) - with open(os.path.join(resultdir, "testdata.json")) as stream: - bundle_text = stream.read() - with open(os.path.join(resultdir, "testoutput.log")) as stream: - output_text = stream.read() - fmt, bundle = DocumentIO.loads(bundle_text) - test = testloader(bundle['test_runs'][0]['test_id']) - test.parse(result) - bundle['test_runs'][0].update(test.parser.results) - bundle['test_runs'][0]["attachments"] = [ - { - "pathname": "testoutput.log", - "mime_type": "text/plain", - "content": base64.standard_b64encode(output_text) - } - ] - return bundle - - === removed file 'abrek/hwprofile.py' --- abrek/hwprofile.py 2011-05-27 02:39:03 +0000 +++ abrek/hwprofile.py 1970-01-01 00:00:00 +0000 @@ -1,216 +0,0 @@ -# Copyright (c) 2010 Linaro -# -# This program 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 3 of the License, or -# (at your option) any later version. -# -# This program 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 this program. If not, see . - -import re -import sys -from subprocess import Popen, PIPE -from utils import read_file, get_machine_type - -INTEL_KEYMAP = { - 'vendor_id': 'cpu_vendor_name', - 'cpu family': 'cpu_family', - 'model': 'cpu_model', - 'model name': 'cpu_model_name', - 'stepping': 'cpu_stepping', - 'cpu MHz': 'cpu_mhz', - 'flags': 'cpu_features', -} - -INTEL_VALMAP = { - 'cpu family': int, - 'model': int, - 'stepping': int, - 'cpu MHz': float, -} - -ARM_KEYMAP = { - 'Processor': 'cpu_model_name', - 'Features': 'cpu_features', - 'CPU implementer': 'cpu_implementer', - 'CPU architecture': 'cpu_architecture', - 'CPU variant': 'cpu_variant', - 'CPU part': 'cpu_part', - 'CPU revision': 'cpu_revision', -} - -ARM_VALMAP = { - 'CPU implementer': lambda value: int(value, 16), - 'CPU architecture': int, - 'CPU variant': lambda value: int(value, 16), - 'CPU part': lambda value: int(value, 16), - 'CPU revision': int, -} - - -def _translate_cpuinfo(keymap, valmap, key, value): - """ - Translate a key and value using keymap and valmap passed in - """ - newkey = keymap.get(key, key) - newval = valmap.get(key, lambda x: x)(value) - return newkey, newval - -def get_cpu_devs(): - """ - Return a list of CPU devices - """ - pattern = re.compile('^(?P.+?)\s*:\s*(?P.*)$') - cpunum = 0 - devices = [] - cpudevs = [] - cpudevs.append({}) - machine = get_machine_type() - if machine in ('i686', 'x86_64'): - keymap, valmap = INTEL_KEYMAP, INTEL_VALMAP - elif machine.startswith('arm'): - keymap, valmap = ARM_KEYMAP, ARM_VALMAP - - try: - cpuinfo = read_file("/proc/cpuinfo") - for line in cpuinfo.splitlines(): - match = pattern.match(line) - if match: - key, value = match.groups() - key, value = _translate_cpuinfo(keymap, valmap, - key, value) - if cpudevs[cpunum].get(key): - cpunum += 1 - cpudevs.append({}) - cpudevs[cpunum][key] = value - for c in range(len(cpudevs)): - device = {} - device['device_type'] = 'device.cpu' - device['description'] = 'Processor #{0}'.format(c) - device['attributes'] = cpudevs[c] - devices.append(device) - except IOError: - print >> sys.stderr, "WARNING: Could not read cpu information" - return devices - - -def get_board_devs(): - """ - Return a list of board devices - """ - devices = [] - attributes = {} - device = {} - machine = get_machine_type() - if machine in ('i686', 'x86_64'): - try: - description = read_file('/sys/class/dmi/id/board_name') or None - vendor = read_file('/sys/class/dmi/id/board_vendor') or None - version = read_file('/sys/class/dmi/id/board_version') or None - if description: - device['description'] = description.strip() - if vendor: - attributes['vendor'] = vendor.strip() - if version: - attributes['version'] = version.strip() - except IOError: - print >> sys.stderr, "WARNING: Could not read board information" - return devices - elif machine.startswith('arm'): - try: - cpuinfo = read_file("/proc/cpuinfo") - if cpuinfo is None: - return devices - pattern = re.compile("^Hardware\s*:\s*(?P.+)$", re.M) - match = pattern.search(cpuinfo) - if match is None: - return devices - device['description'] = match.group('description') - except IOError: - print >> sys.stderr, "WARNING: Could not read board information" - return devices - else: - return devices - if attributes: - device['attributes'] = attributes - device['device_type'] = 'device.board' - devices.append(device) - return devices - -def get_mem_devs(): - """ Return a list of memory devices - - This returns up to two items, one for physical RAM and another for swap - """ - pattern = re.compile('^(?P.+?)\s*:\s*(?P.+) kB$', re.M) - - devices = [] - try: - meminfo = read_file("/proc/meminfo") - for match in pattern.finditer(meminfo): - key, value = match.groups() - if key not in ('MemTotal', 'SwapTotal'): - continue - capacity = int(value) << 10 #Kernel reports in 2^10 units - if capacity == 0: - continue - if key == 'MemTotal': - kind = 'RAM' - else: - kind = 'swap' - description = "{capacity}MiB of {kind}".format( - capacity=capacity >> 20, kind=kind) - device = {} - device['description'] = description - device['attributes'] = {'capacity': str(capacity), 'kind': kind} - device['device_type'] = "device.mem" - devices.append(device) - except IOError: - print >> sys.stderr, "WARNING: Could not read memory information" - return devices - -def get_usb_devs(): - """ - Return a list of usb devices - """ - pattern = re.compile( - "^Bus \d{3} Device \d{3}: ID (?P[0-9a-f]{4}):" - "(?P[0-9a-f]{4}) (?P.*)$") - devices = [] - try: - for line in Popen('lsusb', stdout=PIPE).communicate()[0].splitlines(): - match = pattern.match(line) - if match: - vendor_id, product_id, description = match.groups() - attributes = {} - device = {} - attributes['vendor_id'] = int(vendor_id, 16) - attributes['product_id'] = int(product_id, 16) - device['attributes'] = attributes - device['description'] = description - device['device_type'] = 'device.usb' - devices.append(device) - except OSError: - print >> sys.stderr, "WARNING: Could not read usb device information, \ -unable to run lsusb, please install usbutils package" - return devices - -def get_hardware_context(): - """ - Return a dict with all of the hardware profile information gathered - """ - hardware_context = {} - devices = [] - devices.extend(get_cpu_devs()) - devices.extend(get_board_devs()) - devices.extend(get_mem_devs()) - devices.extend(get_usb_devs()) - hardware_context['devices'] = devices - return hardware_context - === removed file 'abrek/providers.py' --- abrek/providers.py 2011-08-17 13:34:33 +0000 +++ abrek/providers.py 1970-01-01 00:00:00 +0000 @@ -1,145 +0,0 @@ -""" -Test providers. - -Allow listing and loading of tests in a generic way. -""" - -from pkg_resources import working_set - -from abrek.api import ITestProvider -from abrek.cache import AbrekCache -from abrek.config import get_config -from abrek.testdef import AbrekDeclarativeTest - - -class BuiltInProvider(ITestProvider): - """ - Test provider that provides tests shipped in the Abrek source tree - """ - - _builtin_tests = [ - 'bootchart', - 'firefox', - 'glmemperf', - 'gmpbench', - 'gtkperf', - 'ltp', - 'peacekeeper' - 'posixtestsuite', - 'pwrmgmt', - 'pybench', - 'smem', - 'stream', - 'tiobench', - 'x11perf', - 'xrestop', - ] - - def __init__(self, config): - pass - - @property - def description(self): - return "Tests built directly into Abrek:" - - def __iter__(self): - return iter(self._builtin_tests) - - def __getitem__(self, test_name): - try: - module = __import__("abrek.test_definitions.%s" % test_name, fromlist=['']) - except ImportError: - raise KeyError(test_name) - else: - return module.testobj - - -class PkgResourcesProvider(ITestProvider): - """ - Test provider that provides tests declared in pkg_resources working_set - - By default it looks at the 'abrek.test_definitions' name space but it can - be changed with custom 'namespace' configuration entry. - """ - - def __init__(self, config): - self._config = config - - @property - def namespace(self): - return self._config.get("namespace", "abrek.test_definitions") - - @property - def description(self): - return "Tests provided by installed python packages:" - - def __iter__(self): - for entry_point in working_set.iter_entry_points(self.namespace): - yield entry_point.name - - def __getitem__(self, test_name): - for entry_point in working_set.iter_entry_points(self.namespace, test_name): - return entry_point.load().testobj - raise KeyError(test_name) - - -class RegistryProvider(ITestProvider): - """ - Test provider that provides declarative tests listed in the test registry. - """ - def __init__(self, config): - self._config = config - self._cache = None - - @property - def entries(self): - """ - List of URLs to AbrekDeclarativeTest description files - """ - return self._config.get("entries", []) - - @property - def description(self): - return "Tests provided by Abrek registry:" - - @classmethod - def register_remote_test(self, test_url): - config = get_config() # This is a different config object from self._config - provider_config = config.get_provider_config("abrek.providers:RegistryProvider") - if "entries" not in provider_config: - provider_config["entries"] = [] - if test_url not in provider_config["entries"]: - provider_config["entries"].append(test_url) - config._save_registry() - else: - raise ValueError("This test is already registered") - - def _load_remote_test(self, test_url): - """ - Load AbrekDeclarativeTest from a (possibly cached copy of) test_url - """ - cache = AbrekCache.get_instance() - with cache.open_cached_url(test_url) as stream: - return AbrekDeclarativeTest.load_from_stream(stream) - - def _fill_cache(self): - """ - Fill the cache of all remote tests - """ - if self._cache is not None: - return - self._cache = {} - for test_url in self.entries: - test = self._load_remote_test(test_url) - if test.testname in self._cache: - raise ValueError("Duplicate test %s declared" % test.testname) - self._cache[test.testname] = test - - def __iter__(self): - self._fill_cache() - for test_name in self._cache.iterkeys(): - yield test_name - - def __getitem__(self, test_name): - self._fill_cache() - return self._cache[test_name] === removed file 'abrek/results.py' --- abrek/results.py 2010-10-14 13:57:35 +0000 +++ abrek/results.py 1970-01-01 00:00:00 +0000 @@ -1,111 +0,0 @@ -# Copyright (c) 2010 Linaro -# -# This program 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 3 of the License, or -# (at your option) any later version. -# -# This program 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 this program. If not, see . - -import os -import shutil -import sys -from optparse import make_option - -from abrek.command import AbrekCmd, AbrekCmdWithSubcommands -from abrek.config import get_config -from abrek.utils import read_file - - -class cmd_results(AbrekCmdWithSubcommands): - """ - Operate on results of previous test runs stored locally - """ - - class cmd_list(AbrekCmd): - """ - List results of previous runs - """ - def run(self): - config = get_config() - print "Saved results:" - try: - for dir in os.listdir(config.resultsdir): - print dir - except OSError: - print "No results found" - - - class cmd_show(AbrekCmd): - """ - Display the output from a previous test run - """ - arglist = ['*result'] - def run(self): - if len(self.args) != 1: - print "please specify the name of the result dir" - sys.exit(1) - config = get_config() - resultsdir = os.path.join(config.resultsdir, self.args[0]) - testoutput = os.path.join(resultsdir, "testoutput.log") - if not os.path.exists(testoutput): - print "No result found for '%s'" % self.args[0] - sys.exit(1) - try: - print(read_file(testoutput)) - except IOError: - pass - - - class cmd_remove(AbrekCmd): - """ - Remove the results of a previous test run - """ - arglist = ['*result'] - options = [make_option('-f', '--force', action='store_true', - dest='force')] - def run(self): - if len(self.args) != 1: - print "please specify the name of the result dir" - sys.exit(1) - config = get_config() - resultsdir = os.path.join(config.resultsdir, self.args[0]) - if not os.path.exists(resultsdir): - print "No result found for '%s'" % self.args[0] - sys.exit(1) - if not self.opts.force: - print "Delete result '%s' for good? [Y/N]" % self.args[0], - response = raw_input() - if response[0].upper() != 'Y': - sys.exit(0) - shutil.rmtree(resultsdir) - - - class cmd_rename(AbrekCmd): - """ - Rename the results from a previous test run - """ - arglist = ['*source', '*destination'] - - def run(self): - if len(self.args) != 2: - print "please specify the name of the result, and the new name" - sys.exit(1) - config = get_config() - srcdir = os.path.join(config.resultsdir, self.args[0]) - destdir = os.path.join(config.resultsdir, self.args[1]) - if not os.path.exists(srcdir): - print "Result directory not found" - sys.exit(1) - if os.path.exists(destdir): - print "Destination result name already exists" - sys.exit(1) - shutil.move(srcdir, destdir) - - === removed file 'abrek/swprofile.py' --- abrek/swprofile.py 2011-07-22 04:00:04 +0000 +++ abrek/swprofile.py 1970-01-01 00:00:00 +0000 @@ -1,65 +0,0 @@ -# Copyright (c) 2010 Linaro -# -# This program 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 3 of the License, or -# (at your option) any later version. -# -# This program 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 this program. If not, see . - -import apt -import lsb_release -from utils import read_file - -def get_packages(apt_cache=None): - """ Get information about the packages installed - - apt_cache - if not provided, this will be read from the system - """ - if apt_cache == None: - apt_cache = apt.Cache() - packages = [] - for apt_pkg in apt_cache: - if hasattr(apt_pkg, 'is_installed'): - is_installed = apt_pkg.is_installed - else: - is_installed = apt_pkg.isInstalled # old style API - if is_installed: - pkg = {"name":apt_pkg.name, "version":apt_pkg.installed.version} - packages.append(pkg) - return packages - -def get_software_context(apt_cache=None, lsb_information=None): - """ Return dict used for storing software_context information - - test_id - Unique identifier for this test - time_check - whether or not a check was performed to see if - the time on the system was synced with a time server - apt_cache - if not provided, this will be read from the system - lsb_information - if not provided, this will be read from the system - """ - software_context = {} - software_context['image'] = get_image(lsb_information) - software_context['packages'] = get_packages(apt_cache) - return software_context - -def get_image(lsb_information=None): - """ Get information about the image we are running - - If /etc/buildstamp exists, get the image id from that. Otherwise - just use the lsb-release description for a rough idea. - """ - try: - buildstamp = read_file("/etc/buildstamp") - name = buildstamp.splitlines()[1] - except IOError: - if lsb_information == None: - lsb_information = lsb_release.get_distro_information() - name = lsb_information['DESCRIPTION'] - return {"name":name} === removed file 'abrek/testdef.py' --- abrek/testdef.py 2011-08-18 20:09:56 +0000 +++ abrek/testdef.py 1970-01-01 00:00:00 +0000 @@ -1,435 +0,0 @@ -# Copyright (c) 2010 Linaro -# -# This program 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 3 of the License, or -# (at your option) any later version. -# -# This program 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 this program. If not, see . - -import hashlib -import json -import os -import re -import shutil -import decimal -import sys -import time -from commands import getstatusoutput -from datetime import datetime -from uuid import uuid4 - -from abrek import swprofile, hwprofile -from abrek.api import ITest -from abrek.bundle import DocumentIO -from abrek.config import get_config -from abrek.utils import geturl, run_and_log, write_file - - -class AbrekTest(ITest): - """Base class for defining tests. - - This can be used by test definition files to create an object that - contains the building blocks for installing tests, running them, - and parsing the results. - - testname - name of the test or test suite - version - version of the test or test suite - installer - AbrekInstaller instance to use - runner - AbrekRunner instance to use - parser - AbrekParser instance to use - """ - def __init__(self, testname, version="", installer=None, runner=None, - parser=None): - self.testname = testname - self.version = version - self.installer = installer - self.runner = runner - self.parser = parser - self.origdir = os.path.abspath(os.curdir) - - def install(self): - """Install the test suite. - - This creates an install directory under the user's XDG_DATA_HOME - directory to mark that the test is installed. The installer's - install() method is then called from this directory to complete any - test specific install that may be needed. - """ - if not self.installer: - raise RuntimeError("no installer defined for '%s'" % - self.testname) - config = get_config() - installdir = os.path.join(config.installdir, self.testname) - if os.path.exists(installdir): - raise RuntimeError("%s is already installed" % self.testname) - os.makedirs(installdir) - os.chdir(installdir) - try: - self.installer.install() - except Exception as strerror: - self.uninstall() - raise - finally: - os.chdir(self.origdir) - - def uninstall(self): - """Uninstall the test suite. - - Uninstalling just recursively removes the test specific directory - under the user's XDG_DATA_HOME directory. This will both mark - the test as removed, and clean up any files that were downloaded - or installed under that directory. Dependencies are intentionally - not removed by this. - """ - os.chdir(self.origdir) - config = get_config() - path = os.path.join(config.installdir, self.testname) - if os.path.exists(path): - shutil.rmtree(path) - - def _savetestdata(self, analyzer_assigned_uuid): - TIMEFORMAT = '%Y-%m-%dT%H:%M:%SZ' - bundle = { - 'format': 'Dashboard Bundle Format 1.2', - 'test_runs': [ - { - 'test_id': self.testname, - 'analyzer_assigned_date': self.runner.starttime.strftime(TIMEFORMAT), - 'analyzer_assigned_uuid': analyzer_assigned_uuid, - 'time_check_performed': False, - 'hardware_context': hwprofile.get_hardware_context(), - 'software_context': swprofile.get_software_context(), - 'test_results': [] - } - ] - } - filename = os.path.join(self.resultsdir, 'testdata.json') - write_file(DocumentIO.dumps(bundle), filename) - - def run(self, quiet=False): - if not self.runner: - raise RuntimeError("no test runner defined for '%s'" % - self.testname) - config = get_config() - uuid = str(uuid4()) - installdir = os.path.join(config.installdir, self.testname) - resultname = (self.testname + - str(time.mktime(datetime.utcnow().timetuple()))) - self.resultsdir = os.path.join(config.resultsdir, resultname) - os.makedirs(self.resultsdir) - try: - os.chdir(installdir) - self.runner.run(self.resultsdir, quiet=quiet) - self._savetestdata(uuid) - finally: - os.chdir(self.origdir) - result_id = os.path.basename(self.resultsdir) - print("ABREK TEST RUN COMPLETE: Result id is '%s'" % result_id) - return result_id - - def parse(self, resultname): - if not self.parser: - raise RuntimeError("no test parser defined for '%s'" % - self.testname) - config = get_config() - resultsdir = os.path.join(config.resultsdir, resultname) - os.chdir(resultsdir) - self.parser.parse() - os.chdir(self.origdir) - - -class AbrekDeclarativeTest(AbrekTest): - """ - Declarative test is like AbrekTest but cannot contain any python code and - is completely encapsulated in .json file. - """ - - @classmethod - def load_from_stream(cls, stream): - return cls(json.load(stream)) - - def save_to_stream(self, stream): - json.dumps(self.about, stream, indent="2") - - def __init__(self, about): - self.about = about - super(AbrekDeclarativeTest, self).__init__(self.about.get('test_id')) - self.installer = AbrekTestInstaller(**self.about.get('install', {})) - self.runner = AbrekTestRunner(**self.about.get('run', {})) - if self.about.get('parse', {}).get('native', False) is True: - self.parser = AbrekNativeTestParser(self) - else: - self.parser = AbrekForeignTestParser(**self.about.get('parse', {})) - - -class AbrekTestInstaller(object): - """Base class for defining an installer object. - - This class can be used as-is for simple installers, or extended for more - advanced funcionality. - - steps - list of steps to be executed in a shell - deps - list of dependencies to apt-get install before running the steps - url - location from which the test suite should be downloaded - md5 - md5sum to check the integrety of the download - """ - def __init__(self, steps=[], deps=[], url="", md5="", **kwargs): - self.steps = steps - self.deps = deps - self.url = url - self.md5 = md5 - - def _installdeps(self): - if not self.deps: - return 0 - cmd = "sudo apt-get install -y %s" % " ".join(self.deps) - rc, output = getstatusoutput(cmd) - if rc: - raise RuntimeError("Dependency installation failed. %d : %s" %(rc,output)) - - def _download(self): - """Download the file specified by the url and check the md5. - - Returns the path and filename if successful, otherwise return None - """ - if not self.url: - return 0 - filename = geturl(self.url) - #If the file does not exist, then the download was not successful - if not os.path.exists(filename): - return None - if self.md5: - checkmd5 = hashlib.md5() - with open(filename, 'rb') as fd: - data = fd.read(0x10000) - while data: - checkmd5.update(data) - data = fd.read(0x10000) - if checkmd5.hexdigest() != self.md5: - raise RuntimeError("Unexpected md5sum downloading %s" % - filename) - return None - return filename - - def _runsteps(self): - for cmd in self.steps: - rc, output = getstatusoutput(cmd) - if rc: - raise RuntimeError("Run step '%s' failed. %d : %s" %(cmd,rc,output)) - - - def install(self): - self._installdeps() - self._download() - self._runsteps() - - -class AbrekTestRunner(object): - """Base class for defining an test runner object. - - This class can be used as-is for simple execution with the expectation - that the run() method will be called from the directory where the test - was installed. Steps, if used, should handle changing directories from - there to the directory where the test was extracted if necessary. - This class can also be extended for more advanced funcionality. - - steps - list of steps to be executed in a shell - """ - def __init__(self, steps=[]): - self.steps = steps - self.testoutput = [] - - def _runsteps(self, resultsdir, quiet=False): - outputlog = os.path.join(resultsdir, 'testoutput.log') - with open(outputlog, 'a') as fd: - for cmd in self.steps: - run_and_log(cmd, fd, quiet) - - def run(self, resultsdir, quiet=False): - self.starttime = datetime.utcnow() - self._runsteps(resultsdir, quiet=quiet) - self.endtime = datetime.utcnow() - - -class AbrekForeignTestParser(object): - """Base class for defining a test parser - - This class can be used as-is for simple results parsers, but will - likely need to be extended slightly for many. If used as it is, - the parse() method should be called while already in the results - directory and assumes that a file for test output will exist called - testoutput.log. - - pattern - regexp pattern to identify important elements of test output - For example: If your testoutput had lines that look like: - "test01: PASS", then you could use a pattern like this: - "^(?P\w+):\W+(?P\w+)" - This would result in identifying "test01" as testid and - "PASS" as result. Once parse() has been called, - self.results.test_results[] contains a list of dicts of all the - key,value pairs found for each test result - fixupdict - dict of strings to convert test results to standard strings - For example: if you want to standardize on having pass/fail results - in lower case, but your test outputs them in upper case, you could - use a fixupdict of something like: {'PASS':'pass','FAIL':'fail'} - appendall - Append a dict to the test_results entry for each result. - For example: if you would like to add units="MB/s" to each result: - appendall={'units':'MB/s'} - """ - def __init__(self, pattern=None, fixupdict=None, appendall={}): - self.pattern = pattern - self.fixupdict = fixupdict - self.results = {'test_results':[]} - self.appendall = appendall - - def _find_testid(self, id): - for x in self.results['test_results']: - if x['testid'] == id: - return self.results['test_results'].index(x) - - def parse(self): - """Parse test output to gather results - - Use the pattern specified when the class was instantiated to look - through the results line-by-line and find lines that match it. - Results are then stored in self.results. If a fixupdict was supplied - it is used to convert test result strings to a standard format. - """ - filename = "testoutput.log" - try: - pat = re.compile(self.pattern) - except Exception as strerror: - raise RuntimeError( - "AbrekTestParser - Invalid regular expression '%s' - %s" % ( - self.pattern,strerror)) - - with open(filename, 'r') as stream: - for lineno, line in enumerate(stream, 1): - match = pat.search(line) - if not match: - continue - data = match.groupdict() - if 'measurement' in data: - data['measurement'] = decimal.Decimal(data['measurement']) - data["log_filename"] = filename - data["log_lineno"] = lineno - self.results['test_results'].append(data) - if self.fixupdict: - self.fixresults(self.fixupdict) - if self.appendall: - self.appendtoall(self.appendall) - self.fixmeasurements() - self.fixids() - - def append(self, testid, entry): - """Appends a dict to the test_results entry for a specified testid - - This lets you add a dict to the entry for a specific testid - entry should be a dict, updates it in place - """ - index = self._find_testid(testid) - self.results['test_results'][index].update(entry) - - def appendtoall(self, entry): - """Append entry to each item in the test_results. - - entry - dict of key,value pairs to add to each item in the - test_results - """ - for t in self.results['test_results']: - t.update(entry) - - def fixresults(self, fixupdict): - """Convert results to a known, standard format - - pass it a dict of keys/values to replace - For instance: - {"TPASS":"pass", "TFAIL":"fail"} - This is really only used for qualitative tests - """ - for t in self.results['test_results']: - if t.has_key("result"): - t['result'] = fixupdict[t['result']] - - def fixmeasurements(self): - """Measurements are often read as strings, but need to be float - """ - for id in self.results['test_results']: - if id.has_key('measurement'): - id['measurement'] = float(id['measurement']) - - def fixids(self): - """ - Convert spaces to _ in test_case_id and remove illegal characters - """ - badchars = "[^a-zA-Z0-9\._-]" - for id in self.results['test_results']: - if id.has_key('test_case_id'): - id['test_case_id'] = id['test_case_id'].replace(" ", "_") - id['test_case_id'] = re.sub(badchars, "", id['test_case_id']) - - -AbrekTestParser = AbrekForeignTestParser - - -class AbrekNativeTestParser(object): - - def __init__(self, test_def): - self.test_def = test_def - - def parse(self): - raise NotImplementedError(self.parse) - - -class TestLoader(object): - """ - Test loader. - - Encapsulates Abrek's knowledge of available tests. - - Test can be loaded by name with TetsLoader.get_test_by_name. Test can also - be listed by TestLoader.get_providers() and then iterating over tests - returned by each provider. - """ - - def __init__(self): - self._config = get_config() - - def get_providers(self): - """ - Return a generator of available providers - """ - import pkg_resources - for provider_info in self._config.registry.get("providers", []): - entry_point_name = provider_info.get("entry_point") - module_name, attrs = entry_point_name.split(':', 1) - attrs = attrs.split('.') - try: - entry_point = pkg_resources.EntryPoint(entry_point_name, module_name, attrs, - dist=pkg_resources.get_distribution("lava-test")) - provider_cls = entry_point.load() - provider = provider_cls(provider_info.get("config", {})) - yield provider - except pkg_resources.DistributionNotFound as ex: - raise RuntimeError("lava-test is not properly set up. Please read the REAMME file") - - def get_test_by_name(self, test_name): - """ - Lookup a test with the specified name - """ - for provider in self.get_providers(): - try: - return provider[test_name] - except KeyError: - pass - raise ValueError("No such test %r" % test_name) - - -testloader = TestLoader().get_test_by_name === removed directory 'bin' === removed file 'bin/lava-test' --- bin/lava-test 2011-06-10 04:49:54 +0000 +++ bin/lava-test 1970-01-01 00:00:00 +0000 @@ -1,31 +0,0 @@ -#!/usr/bin/python - -# Copyright (c) 2010 Linaro -# -# This program 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 3 of the License, or -# (at your option) any later version. -# -# This program 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 this program. If not, see . - -import os -import sys - -ABREK_BINDIR=os.path.abspath(os.path.dirname(os.path.realpath(sys.argv[0]))) -ABREK_ROOT=os.path.dirname(ABREK_BINDIR) -ABREK_DIR=os.path.join(ABREK_ROOT,'abrek') -if os.path.exists(ABREK_DIR) and ABREK_ROOT not in sys.path: - sys.path.insert(0, ABREK_ROOT) - -import abrek.main - -if __name__ == '__main__': - exit_code = abrek.main.main(sys.argv) - sys.exit(exit_code) === added directory 'doc' === added file 'doc/changes.rst' --- doc/changes.rst 1970-01-01 00:00:00 +0000 +++ doc/changes.rst 2011-09-12 09:19:10 +0000 @@ -0,0 +1,25 @@ +Version History +*************** + +.. _version_0_2: + +Version 0.2 +=========== + +* Rewrite most of the code and deprecate old Abrek interfaces. This allowed us + to clean up the API, rethink some of the design and integrate the code better + with other parts of LAVA. + +* Improved documentation and code reference. LAVA Test should now have + sufficient documentation to help new users and contributors alike. + +* Support for installing and running out-of-tree tests. + +* Ability to define parsers that add new attachments. + +* Unified command line interface with other lava tools thanks to lava-tool. + +Version 0.1 +=========== + +* Initial release (as Abrek) === added file 'doc/conf.py' --- doc/conf.py 1970-01-01 00:00:00 +0000 +++ doc/conf.py 2011-09-12 09:19:10 +0000 @@ -0,0 +1,211 @@ +# -*- coding: utf-8 -*- +# +# Linaro JSON documentation build configuration file, created by +# sphinx-quickstart on Mon Dec 27 16:39:47 2010. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +sys.path.append(os.path.abspath('..')) + +# -- General configuration ----------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.doctest', + 'sphinx.ext.intersphinx', + 'sphinx.ext.todo', + 'sphinx.ext.coverage', + 'sphinx.ext.viewcode'] + +# Configuration for sphinx.ext.todo + +todo_include_todos = True + +# Add any paths that contain templates here, relative to this directory. +templates_path = [] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'LAVA Test' +copyright = u'2010-2011, Linaro Limited' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +import versiontools +import lava_test +version = "%d.%d" % lava_test.__version__[0:2] +# The full version, including alpha/beta/rc tags. +release = versiontools.format_version(lava_test.__version__) + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of documents that shouldn't be included in the build. +#unused_docs = [] + +# List of directories, relative to source directory, that shouldn't be searched +# for source files. +exclude_trees = [] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. Major themes that come with +# Sphinx are currently 'default' and 'sphinxdoc'. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_use_modindex = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = '' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'LAVATestDocumentation' + + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'LAVA Test.tex', u'LAVA Test Documentation', + u'Zygmunt Krynicki', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_use_modindex = True + + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = {'http://docs.python.org/': None} === added file 'doc/index.rst' --- doc/index.rst 1970-01-01 00:00:00 +0000 +++ doc/index.rst 2011-09-12 09:19:10 +0000 @@ -0,0 +1,81 @@ +======================= +LAVA Test Documentation +======================= + +LAVA Test is a wrapper framework exposing unified API and command line +interface for running arbitrary tests and storing the results in a structured +manner. + +LAVA Test is a part of the LAVA stack and can be used with other LAVA +components, most notably the dispatcher (for setting up the test environment +and controlling execution of multiple tests) and the dashboard (for storing + +.. seealso:: To learn more about LAVA see https://launchpad.net/lava + +60 second example +================= + +This example will run on Ubuntu Lucid and beyond:: + + $ sudo add-apt-repository ppa:linaro-validation/ppa + $ sudo apt-get update + $ sudo apt-get install lava-test + $ lava-test install stream + $ lava-test run stream + +.. seealso:: For a more thorough description see :ref:`usage` +.. seealso:: For detailed installation istructions see :ref:`installation` + +Features +======== + +* Ability to enumerate, install, run and remove tests on a Linux-based system. +* Support for benchmarks as well as pass/fail tests. +* Support for capturing environment information such as installed software and + hardware information and recording that in a machine-readable manner. +* Store results in raw form (log files) as well as Linaro Dashboard Bundle + format that can be uploaded to the LAVA Dashboard for archiving and analysis. +* Extensible API for adding new tests (:class:`~lava_test.api.core.ITest`) or even + collections of tests (:class:`~lava_test.api.core.ITestProvider`). +* Ever-growing collection of freely available and generic tests and benchmarks + +.. seealso:: See what's new in :ref:`version_0_2` + + +Latest documentation +==================== + +This documentation my be out of date, we try to make sure that all the latest +and greatest releases are always documented on http://lava-test.readthedocs.org/ + + +Source code, bugs and patches +============================= + +The project is maintained on Launchpad at http://launchpad.net/lava-test/. + +You can get the source code with bazaar using ``bzr branch lp:lava-test``. +Patches can be submitted using Launchpad merge proposals (for introduction to +this and topic see https://help.launchpad.net/Code/Review). + +Please report all bugs at https://bugs.launchpad.net/lava-test/+filebug. + +Most of the team is usually available in ``#linaro`` on ``irc.freenode.net``. +Feel free to drop by to chat and ask questions. + + +Indices and tables +================== + +.. toctree:: + :maxdepth: 2 + + installation.rst + changes.rst + usage.rst + reference.rst + todo.rst + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` === added file 'doc/installation.rst' --- doc/installation.rst 1970-01-01 00:00:00 +0000 +++ doc/installation.rst 2011-09-12 09:19:10 +0000 @@ -0,0 +1,66 @@ + +.. _installation: + +Installation +============ + +Prerequisites +^^^^^^^^^^^^^ + +The following debian packages are needed to use LAVA Test: + +* python-setuptools +* python-apt +* usbutils +* python-testrepository - for running unit tests +* python-sphinx - for building documentation + + +Installation Options +^^^^^^^^^^^^^^^^^^^^ + +There are several installation options available: + + +Using Ubuntu PPAs +----------------- + +For Ubuntu 10.04 onward there is a stable PPA (personal package archive): + +* ppa:linaro-validation/ppa + +To add a ppa to an Ubuntu system use the add-apt-repository command:: + + sudo add-apt-repository ppa:linaro-validation/ppa + +After you add the PPA you need to update your package cache:: + + sudo apt-get update + +Finally you can install the package, it is called `lava-test`:: + + sudo apt-get install lava-test + + +Using Python Package Index +-------------------------- + +This package is being actively maintained and published in the `Python Package +Index `_. You can install it if you have `pip +`_ tool using just one line:: + + pip install lava-test + + +Using source tarball +-------------------- + +To install from source you must first obtain a source tarball from either pypi +or from `Launchpad `_. To install the package unpack the +tarball and run:: + + python setup.py install + +You can pass ``--user`` if you prefer to do a local (non system-wide) +installation. Note that executable programs are placed in ``~/.local/bin/`` and +this directory is not on ``PATH`` by default. === added file 'doc/reference.rst' --- doc/reference.rst 1970-01-01 00:00:00 +0000 +++ doc/reference.rst 2011-09-12 09:19:10 +0000 @@ -0,0 +1,107 @@ +.. _reference: + +========= +Reference +========= + +.. _command_reference: + +Command Reference +================= + +.. automodule:: lava_test.commands + :members: + +.. todo:: + + * Describe basic commands + * Describe arguments and options to each command in detail + +Pathnames and files +=================== + +LAVA Test uses the following files: + +* ``$XDG_CONFIG_HOME/lava_test/`` -- configuration files +* ``$XDG_DATA_HOME/lava_test/installed-tests`` -- installed test programs +* ``$XDG_DATA_HOME/lava_test/results`` -- artifacts of running tests +* ``$XDG_CACHE_HOME/lava_test/`` -- download cache + +.. _code_reference: + +Code reference +============== + +.. todo:: + + * Describe general code layout + * Describe key API integration points (on a separate page if needed for clarity) + * Provide an example test and walk the reader through the meaning of each part + +Abstract Interfaces +^^^^^^^^^^^^^^^^^^^ + +.. automodule:: lava_test.api.core + :members: + +.. automodule:: lava_test.api.delegates + :members: + +.. automodule:: lava_test.api.observers + :members: + +Test definitions and test providers +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: lava_test.core.providers + :members: + +.. automodule:: lava_test.core.tests + :members: + +Test components (installers, runners and parsers) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: lava_test.core.installers + :members: + +.. automodule:: lava_test.core.runners + :members: + +.. automodule:: lava_test.core.parsers + :members: + +Core Modules +^^^^^^^^^^^^ + +.. automodule:: lava_test.core.artifacts + :members: + +.. automodule:: lava_test.core.config + :members: + +Environment Scanners +^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: lava_test.core.hwprofile + :members: + +.. automodule:: lava_test.core.swprofile + :members: + +Utilities +^^^^^^^^^ + +.. automodule:: lava_test.utils + :members: + +.. automodule:: lava_test.extcmd + :members: + + +Abrek compatibility +=================== + +.. automodule:: abrek.testdef + :members: + === added file 'doc/todo.rst' --- doc/todo.rst 1970-01-01 00:00:00 +0000 +++ doc/todo.rst 2011-09-12 09:19:10 +0000 @@ -0,0 +1,4 @@ +List of items that need work +============================ + +.. todolist:: === added file 'doc/usage.rst' --- doc/usage.rst 1970-01-01 00:00:00 +0000 +++ doc/usage.rst 2011-09-12 09:19:10 +0000 @@ -0,0 +1,195 @@ +.. _usage: + +===== +Usage +===== + +Workflow Overview +================= + +LAVA Test can be used in several different ways. Most notably those are +standalone (without the LAVA dispatcher) and managed (when LAVA Test is +installed and controlled by the LAVA dispatcher). + +Standalone usage +^^^^^^^^^^^^^^^^ + +In standalone mode a human operator installs LAVA Test on some device +(development board, laptop or other computer or a virtual machine), installs +the tests that are to be executed and then executes them manually (by manually +running LAVA test, the actual tests are non-interactive). + +Using LAVA to develop and run new tests ++++++++++++++++++++++++++++++++++++++++ + +This mode is useful for test development (adding new tests, developing custom +tests especially tailored for LAVA, etc.). Here the typical cycle depends on +how the tests is wrapped for usage by LAVA and what the test developer is +focusing on. + +While developing the actual test the typical set of commands might look like +this:: + + $ lava-test install my-custom-test + $ lava-test run my-custom-test + $ lava-test uninstall my-custom-test + +Here the developer could observe changes to the test program (that is +presumably compiled and copied somewhere by the install stage). + +Using LAVA to analyze test results +++++++++++++++++++++++++++++++++++ + +Developing the test is only half of the story. The other half is developing +LAVA Test integration code, most importantly the artefact parser / analyzer. +This part has to be implemented in python (unlike the test program that can be +implemented in any language and technology). Here the developer is focusing on +refining the parser to see if the outcome is as indented. Assuming that earlier +the developer ran the test at least once and wrote down the result identifier +the set of commands one might use is:: + + $ lava-test parse my-custom-test my-custom-test.2011-08-19T23:53:21Z | pager + +Here the developer has to pass both the identifier of the test +(``my-custom-test``) as well as the identifier of the actual result. While +currently the result identifier starts with the test identifier we wanted to +avoid magic values like that so both are needed. The test defines which +artefact parser to use. The result id is used to locate leftovers from running +that specific test at some previous point in time. + +By default parse will print the bundle to standard output for inspection. It +should be redirected to a pager for easier verification. + +.. note:: + + While the syntax of the bundle created with `lava-test parse` is always + correct (or, if the parser does something really, really strange, a + detailed error is reported) the actual contents may not be what you + intended it to be. Parsers are ultimately fragile as they mostly deal with + unstructured or semi-structured free-form text that most test programs seem + to produce. The ultimate goal of a developer should be to produce + unambiguous, machine readable format. This level of integration would allow + to wrap a whole class of tests in one go (such as all xUnit-XML speaking + test frameworks). + +Usage with the dispatcher +^^^^^^^^^^^^^^^^^^^^^^^^^ + +The dispatcher is useful for automating LAVA Test environment setup, describing +test scenarios (the list of tests to invoke) and finally storing the results in +the LAVA dashboard. + +Typically this mode is based on the following sequence of commands: + +#. Install lava-test (from PPA or source) along with the required dependencies +#. (optional) for out of tree tests install the additional `test definition` package +#. Install the test or tests that are to be invoked with ``lava-tool install``. +#. Run, parse and store in one go with ``lava-tool run --output=FILE``. + +Here the whole setup is non-interactive and at the end the dispatcher can copy +the output bundle for additional processing. + +Automation considerations +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. _wrapping_existing_test_or_benchmark: + +Wrapping existing test or benchmark +=================================== + +LAVA Test can be extended in several different ways. There is no best method, +each has some pros and cons. In general we welcome any freely redistributable, +generic tests. Those enrich the LAVA ecosystem and by providing useful +out-of-the-box features to our users. + +Technically all tests are hidden behind a set of abstract interfaces that tell +LAVA Test what to do in response to operator or dispatcher actions. The primary +interface is :class:`~lava_test.api.core.ITest` and the three principal +methods: :meth:`~lava_test.api.core.ITest.install`, +:meth:`~lava_test.api.core.ITest.run`, +:meth:`~lava_test.api.core.ITest.parse`. + +In practice it is usually much easier to instantiate our pluggable delegate +test (:class:`lava_test.core.tests.Test`) and define the three delegates that +know how to install, run and parse. Again for each step we have a base class +that can be easily customized or even used directly as is. Those classes are +:class:`~lava_test.core.installers.TestInstaller`, +:class:`~lava_test.core.runners.TestRunner` and +:class:`~lava_test.core.parsers.TestParser`. They all implement well-defined +interfaces (specified in :mod:`lava_test.api.delegates`) so if you wish to +customize them you should become familiar with the API requirements first. + +Contributing new tests to LAVA +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The most direct way to add a new test is to contribute patches to LAVA Test +itself. This method will simply add a new test definition to the collection of +available tests. + +This method is recommended for generic tests that rarely change and are +suitable for wide variety of hardware and software (assuming basic Linux-like +system, Android tests are a special case). + +The advantage is that those tests can be invoked out of the box and will be +maintained by the LAVA team. The disadvantage is that all changes to those +tests need to follow Linaro development work flow, get reviewed and finally +merged. Depending on your situation this may be undesired. + +.. todo:: + + Describe how tests are discovered, loaded and used. It would be + nice to have a tutorial that walks the user through wrapping a + simple pass/fail test. + +Maintaining out-of-tree tests +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +For some kinds of tests (proprietary, non-generic, in rapid development, fused +with application code) contributing their definition to upstream LAVA Test +project would be impractical. + +In such cases the test maintainer can still leverage LAVA to actually run and +process the test without being entangled in the review process or going through +any public channel. + +Because LAVA Test supports pluggable test providers it is easy to add a new +source of test definitions. Fortunately we ship with a very useful generic +out-of-tree test provider based on the python `pkg_resources` system. + +Any python package (that is a module or package and the corresponding setup.py +and .egg_info) can define LAVA Test extensions using the `pkg_resurces` entry +points system. + +To do this write your test program as you normally would, write the LAVA Test +integration code and put this into your integration package setup.py:: + + setup( + ..., + entry_points="""[lava_test.test_definitions] + my_test_id=my_package.my_module + """) + +Here we'd define an entry point in the ``lava_test.test_definitions`` namespace +that LAVA Test searches by default. In that namespace we define one object +``my_test_id`` which points at the module ``my_package.my_module``. LAVA Test +will discover this entry point, import the relevant module and make the test +definition available. + +Maintaining simple declarative tests +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +By registering pure declarative tests at runtime. + +.. todo:: + + Describe how to use declarative tests. It would be a nice + extension of the tutorial once the user feels comfortable with + the initial python-based version. + +Writing new tests from scratch +============================== + +.. todo:: + + Describe considerations for test writers. Using native test + format with human-readable output adapters. === modified file 'examples/power-management-tests.json' --- examples/power-management-tests.json 2011-06-28 12:51:57 +0000 +++ examples/power-management-tests.json 2011-09-12 09:19:10 +0000 @@ -1,5 +1,5 @@ { - "format": "Abrek Test Definition 1.0 Experimental", + "format": "Lava-Test Test Definition 1.0", "test_id": "linaro.pmwg", "install": { "steps": ["bzr get lp:~zkrynicki/+junk/linaro-pm-qa-tests"], === modified file 'examples/stream.json' --- examples/stream.json 2011-06-28 13:31:48 +0000 +++ examples/stream.json 2011-09-12 09:19:10 +0000 @@ -1,5 +1,5 @@ { - "format": "Abrek Test Definition Format 1.0 Experimental", + "format": "LAVA-Test Test Definition Format", "test_id": "stream-json", "install": { "url": "http://www.cs.virginia.edu/stream/FTP/Code/stream.c", === renamed directory 'abrek' => 'lava_test' === modified file 'lava_test/__init__.py' --- abrek/__init__.py 2011-08-20 02:20:37 +0000 +++ lava_test/__init__.py 2011-09-12 09:19:10 +0000 @@ -13,4 +13,4 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -__version__ = "0.2.0" +__version__ = (0, 2, 0, "dev", 0) === added directory 'lava_test/api' === added file 'lava_test/api/__init__.py' --- lava_test/api/__init__.py 1970-01-01 00:00:00 +0000 +++ lava_test/api/__init__.py 2011-09-12 09:19:10 +0000 @@ -0,0 +1,24 @@ +# Copyright (c) 2010, 2011 Linaro +# +# This program 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 3 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see . + +from abc import ABCMeta + + +class _Interface(object): + """ + Interface class for simplifying usage of interface meta-classes + """ + + __metaclass__ = ABCMeta === added file 'lava_test/api/core.py' --- lava_test/api/core.py 1970-01-01 00:00:00 +0000 +++ lava_test/api/core.py 2011-09-12 09:19:10 +0000 @@ -0,0 +1,164 @@ +# Copyright (c) 2010, 2011 Linaro +# +# This program 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 3 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see . + +""" +:mod:`lava_test.api.core` -- Interface classes for core LAVA Test features +========================================================================== + +.. module: lava_test.api.core + + :synopsis: Interface classes for core LAVA Test features +""" + +from abc import abstractmethod, abstractproperty + +from lava_test.api import _Interface + +class ITest(_Interface): + """ + Abstract test definition. + + Test definitions allow lava-test to install, remove, run and parse log + files of automatic tests. While the interface can be implemented directly + you should use :class:`lava_test.core.tests.Test` that implements the core + logic and allow you to customize the parts that are needed by providing + delegates implementing :class:`~lava_test.api.delegates.ITestInstaller`, + :class:`~lava_test.api.delegates.ITestRunner` and + :class:`~lava_test.api.delegates.ITestParser`. + + .. seealso:: :ref:`wrapping_existing_test_or_benchmark` + """ + + @abstractproperty + def is_installed(self): + """ + True if this test is installed + + .. versionadded:: 0.2 + """ + + @abstractmethod + def install(self, observer): + """ + Install the test program. + + This creates an install directory under the user's XDG_DATA_HOME + directory to mark that the test is installed. The installer's + install() method is then called from this directory to complete any + test specific install that may be needed. + + :param observer: + Observer object that makes it possible to monitor the actions + performed by the test installer. + :type observer: :class:`~lava_test.api.observers.ITestInstallerObserver` + + .. versionadded:: 0.2 + """ + + @abstractmethod + def uninstall(self): + """ + Remove the test program + + Recursively remove test specific directory under the user's + ``XDG_DATA_HOME directory``. This will both mark the test as removed, + and clean up any files that were downloaded or installed under that + directory. Dependencies are intentionally not removed by this. + + .. versionadded:: 0.1 + """ + + @abstractmethod + def run(self, observer): + """ + Run the test program and store artifacts. + + :param observer: + Observer object that makes it possible to monitor the actions + performed by the test runner. + :type observer: :class:`~lava_test.api.observers.ITestRunnerObserver` + :return: Test run artifacts + :rtype: :class:`~lava_test.core.artifacts.TestArtifacts`. + + .. versionadded:: 0.2 + """ + + @abstractmethod + def parse(self, artifacts): + """ + Parse the artifacts of an earlier run. + + :param artifacts: Object that describes which files should be parsed. + :type artifacts: :class:`~lava_test.core.artifacts.TestArtifacts` + :return: + A dictionary with all the parsed data. In particular this is a + TestRun part of the dashboard bundle so it should have the + test_results list of all the results parsed from the artifacts. + :rtype: :class:`dict` + + .. versionadded:: 0.2 + """ + + +class ITestProvider(_Interface): + """ + Source of ITest instances. + + Test providers can be used to make lava-test aware of arbitrary collections + of tests that can be installed and invoked. Internally lava-test uses this + class to offer built-in tests (via the + :class:`~lava_test.providers.BuiltInProvider`), out-of-tree tests (via the + :class:`~lava_test.providers.PkgResourcesProvider`) and declarative tests + (via the :class:`~lava_test.providers.RegistryProvider`). + + Normally this is not something you would need to implement. If you have a + large collection of existing tests that can be somehow adapted in bulk, or + you have your own internal registry of tests that could be adapted this way + then you might use this interface to simplify test discovery. + + Test providers need to be registered using pkg-resources entry-point + feature and then added to the lava-test configuration file. See + :class:`lava_test.config.LavaTestConfig` for details. + + .. versionadded:: 0.2 + """ + + @abstractmethod + def __init__(self, config): + """ + Initialize test provider with the specified configuration object. The + configuration object is obtained from the test tool providers registry. + """ + + @abstractmethod + def __iter__(self): + """ + Iterates over instances of ITest exposed by this provider + """ + + @abstractmethod + def __getitem__(self, test_id): + """ + Return an instance of ITest with the specified id + """ + + @abstractproperty + def description(self): + """ + The description string used by `lava-test list-tests` + """ + + +__all__ = ['ITest', 'ITestProvider'] === added file 'lava_test/api/delegates.py' --- lava_test/api/delegates.py 1970-01-01 00:00:00 +0000 +++ lava_test/api/delegates.py 2011-09-12 09:19:10 +0000 @@ -0,0 +1,119 @@ +# Copyright (c) 2010, 2011 Linaro +# +# This program 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 3 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see . + + +""" +:mod:`lava_test.api.delegates` -- Interface classes for test delegates +====================================================================== + +.. module: lava_test.api.delegates + + :synopsis: Interface classes for test delegates +""" + +from abc import abstractmethod, abstractproperty + +from lava_test.api import _Interface + + +class ITestInstaller(_Interface): + """ + Test installer delegate class. + + Wraps the knowledge on how to install a test. It is most helpful with + :class:`~lava_test.core.tests.Test` that delegates actual actions to helper + classes. + + .. versionadded:: 0.2 + """ + + @abstractmethod + def install(self, observer): + """ + Install the test program. + + :param observer: + Observer object that makes it possible to monitor the actions + performed by the test installer. + :type observer: :class:`~lava_test.api.observers.ITestInstallerObserver` + + .. versionadded:: 0.2 + """ + + +class ITestRunner(_Interface): + """ + Test runner delegate. + + Wraps the knowledge on how to run a test. It is most helpful with + :class:`lava_test.core.tests.Test` that delegates actual actions to + helper classes. + + .. versionadded:: 0.2 + """ + + @abstractmethod + def run(self, artifacts, observer): + """ + Run the test and create artifacts (typically log files). + + Artifacts must be created in the directory specified by various methods + and properties of of :class:`lava_test.core.TestArtifacts`. + + :param artifacts: + Object that describes where to store test run artifacts + :type artifacts: :class:`~lava_test.core.artifacts.TestArtifacts`. + :param observer: + Observer object that makes it possible to monitor the actions + performed by the test runner. + :type observer: :class:`~lava_test.api.observers.ITestRunnerObserver` + + .. versionadded:: 0.2 + """ + + +class ITestParser(_Interface): + """ + Test artefact parser delegate. + + Wraps the knowledge on how to parse the artifacts of a previous test run. + It is most helpful with :class:`~lava_test.core.tests.Test` that delegates + actual actions to helper classes. + + .. versionadded:: 0.2 + """ + + @abstractmethod + def parse(self, artifacts): + """ + Parse the artifacts of a previous test run and return a dictionary with + a partial TestRun object. + + :param artifacts: + Object that describes where to find test run artifacts + :type artifacts: :class:`~lava_test.core.artifacts.TestArtifacts`. + + .. versionadded:: 0.2 + """ + + @abstractproperty + def results(self): + """ + Results dictionary to be merged with TestRun object inside the bundle. + + .. seealso:: :meth:`~lava_test.core.artifacts.TestArtifacts.incorporate_parse_results` + + .. versionadded:: 0.1 + """ === added file 'lava_test/api/observers.py' --- lava_test/api/observers.py 1970-01-01 00:00:00 +0000 +++ lava_test/api/observers.py 2011-09-12 09:19:10 +0000 @@ -0,0 +1,120 @@ +# Copyright (c) 2010, 2011 Linaro +# +# This program 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 3 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see . + + +""" +:mod:`lava_test.api.observers` -- Interface classes for observer classes +======================================================================== + +.. module: lava_test.api.observers + :synopsis: Interface classes for observer classes +""" + +from abc import abstractmethod + +from lava_test.api import _Interface + + +class IShellCommandObserver(_Interface): + """ + Shell command runner observer class. + + Allows the caller to observe shell commands that occur during some + operation. It is used by the command line UI. + + .. versionadded:: 0.2 + """ + + @abstractmethod + def about_to_run_shell_command(self, cmd): + """ + Method called when a shell command is about to be invoked by the + observed object. + + .. versionadded:: 0.2 + """ + + @abstractmethod + def did_run_shell_command(self, cmd, returncode): + """ + Method called when a shell command has been invoked by the observed + object. + + .. versionadded:: 0.2 + """ + + @abstractmethod + def display_subprocess_output(self, stream_name, line): + """ + Method called for each line of stdout/stderr as obtained from a + subprocess. + + .. versionadded:: 0.2 + """ + + +class ITestInstallerObserver(IShellCommandObserver): + """ + Test installer observer class. + + Allows the caller to observe interesting actions that occur during + installation process. It is used by the command line UI. + + .. versionadded:: 0.2 + """ + + @abstractmethod + def about_to_install_packages(self, package_list): + """ + Method called when a list of packages is about to be installed by the + installer + + .. versionadded:: 0.2 + """ + + @abstractmethod + def did_install_packages(self, package_list): + """ + Method called when a package has been installed by the installer + + .. versionadded:: 0.2 + """ + + @abstractmethod + def about_to_download_file(self, url): + """ + Method called when a file is about to be downloaded + + .. versionadded:: 0.2 + """ + + @abstractmethod + def did_download_file(self, url): + """ + Method called when a file has been downloaded + + .. versionadded:: 0.2 + """ + + +class ITestRunnerObserver(IShellCommandObserver): + """ + Test runner observer class. + + Allows the caller to observe interesting actions that occur during testing + process. It is used by the command line UI. + + .. versionadded:: 0.2 + """ === added file 'lava_test/commands.py' --- lava_test/commands.py 1970-01-01 00:00:00 +0000 +++ lava_test/commands.py 2011-09-12 09:19:10 +0000 @@ -0,0 +1,383 @@ +# Copyright (c) 2010, 2011 Linaro +# +# This program 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 3 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see . + + +import os +import subprocess + +from lava_tool.interface import Command as LavaCommand +from lava_tool.interface import LavaCommandError +import versiontools + +from lava_test.api.observers import ( + ITestInstallerObserver, + ITestRunnerObserver) +from lava_test.core.artifacts import TestArtifacts +from lava_test.core.config import get_config +from lava_test.core.loader import TestLoader + + +class Command(LavaCommand, ITestInstallerObserver, ITestRunnerObserver): + + def __init__(self, parser, args): + super(Command, self).__init__(parser, args) + self._config = get_config() + self._test_loader = TestLoader(self._config) + + @classmethod + def register_arguments(cls, parser): + parser.add_argument( + "-q", "--quiet", + action="store_true", + default=False, + help="Be less verbose about undertaken actions") + parser.add_argument( + "-Q", "--quiet-subcommands", + action="store_true", + default=False, + help="Hide the output of all sub-commands (including tests)") + + def say(self, text, *args, **kwargs): + print "LAVA:", text.format(*args, **kwargs) + + def about_to_install_packages(self, package_list): + if self.args.quiet: + return + self.say("Installing packages: {0}", ", ".join(package_list)) + + def about_to_run_shell_command(self, cmd): + if self.args.quiet: + return + self.say("Running shell command: {0!r}", cmd) + + def about_to_download_file(self, url): + if self.args.quiet: + return + self.say("Downloading file from: {0!r}", url) + + def did_install_packages(self, package_list): + pass + + def did_run_shell_command(self, cmd, returncode): + if returncode is None: + self.say("Command {0!r} was terminated prematurely", cmd) + elif returncode != 0: + self.say("Command {0!r} returned non-zero exit status {1}", + cmd, returncode) + + def did_download_file(self, url): + pass + + def display_subprocess_output(self, stream_name, line): + if self.args.quiet_subcommands: + return + if stream_name == 'stdout': + self.say('(stdout) {0}', line.rstrip()) + elif stream_name == 'stderr': + self.say('(stderr) {0}', line.rstrip()) + + +class list_tests(Command): + """ + List available tests + + .. program:: lava-test list-tests + + Lists all available tests, grouping them by provider. + """ + + def invoke(self): + for provider in self._test_loader.get_providers(): + test_list = [provider[test_id] for test_id in provider] + if not test_list: + continue + self.say("{0}", provider.description) + for test in test_list: + self.say(" - {test_id}", test_id=test.test_id) + + +class list_installed(Command): + """ + List installed tests + """ + + def invoke(self): + for provider in self._test_loader.get_providers(): + test_list = [provider[test_id] for test_id in provider] + if not test_list: + continue + self.say("{0}", provider.description) + count = 0 + for test in test_list: + if not test.is_installed: + continue + self.say(" - {test_id}", test_id=test.test_id) + count += 1 + if not count: + self.say("No tests installed") + + + +class TestAffectingCommand(Command): + + INSTALL_REQUIRED = False + + @classmethod + def register_arguments(cls, parser): + super(TestAffectingCommand, cls).register_arguments(parser) + parser.add_argument("test_id", + help="Test or test suite identifier") + + def invoke(self): + try: + test = self._test_loader[self.args.test_id] + except KeyError: + raise LavaCommandError("There is no test with the specified ID") + return self.invoke_with_test(test) + + +class install(TestAffectingCommand): + """ + Install a test program + """ + + def invoke_with_test(self, test): + if test.is_installed: + raise LavaCommandError("This test is already installed") + try: + test.install(self) + except (subprocess.CalledProcessError, RuntimeError) as ex: + raise LavaCommandError(str(ex)) + + +class uninstall(TestAffectingCommand): + """ + Uninstall a test program + """ + + def invoke_with_test(self, test): + if not test.is_installed: + raise LavaCommandError("This test is not installed") + test.uninstall() + + +class run(TestAffectingCommand): + """ + Run a previously installed test program + """ + + @classmethod + def register_arguments(cls, parser): + super(run, cls).register_arguments(parser) + group = parser.add_argument_group("initial bundle configuration") + group.add_argument("-S", "--skip-software-context", + default=False, + action="store_true", + help=("Do not store the software context in the" + " initial bundle. Typically this saves OS" + " image name and all the installed software" + " packages.")) + group.add_argument("-H", "--skip-hardware-context", + default=False, + action="store_true", + help=("Do not store the hardware context in the" + " initial bundle. Typically this saves CPU," + " memory and USB device information.")) + group.add_argument("--trusted-time", + default=False, + action="store_true", + help=("Indicate that the real time clock has" + " accurate data. This can differentiate" + " test results created on embedded devices" + " that often have inaccurate real time" + " clock settings.")) + group = parser.add_argument_group("complete bundle configuration") + group.add_argument("-o", "--output", + default=None, + metavar="FILE", + help=("After running the test parse the result" + " artifacts, fuse them with the initial" + " bundle and finally save the complete bundle" + " to the specified FILE.")) + group.add_argument("-A", "--skip-attachments", + default=False, + action="store_true", + help=("Do not store standard output and standard" + " error log files as attachments. This" + " option is only affecting the bundle" + " created with --output, the initial bundle" + " is not affected as it never stores any" + " attachments.")) + + def invoke_with_test(self, test): + if not test.is_installed: + raise LavaCommandError("The specified test is not installed") + try: + artifacts = test.run(self) + except subprocess.CalledProcessError as ex: + if ex.returncode is None: + raise LavaCommandError("Command %r was aborted" % ex.cmd) + else: + raise LavaCommandError(str(ex)) + except RuntimeError as ex: + raise LavaCommandError(str(ex)) + self.say("run complete, result_id is {0!r}", artifacts.result_id) + artifacts.create_initial_bundle( + self.args.skip_software_context, + self.args.skip_hardware_context, + self.args.trusted_time) + artifacts.save_bundle() + if self.args.output: + parse_results = test.parse(artifacts) + artifacts.incorporate_parse_results(parse_results) + if not self.args.skip_attachments: + artifacts.attach_standard_files_to_bundle() + artifacts.save_bundle_as(self.args.output) + + +class parse(TestAffectingCommand): + """ + Parse the results of previous test run + """ + + @classmethod + def register_arguments(cls, parser): + super(parse, cls).register_arguments(parser) + parser.add_argument("result_id", + help="Test run result identifier") + group = parser.add_argument_group("complete bundle configuration") + group.add_argument("-o", "--output", + default=None, + metavar="FILE", + help=("After running the test parse the result" + " artifacts, fuse them with the initial" + " bundle and finally save the complete bundle" + " to the specified FILE.")) + group.add_argument("-A", "--skip-attachments", + default=False, + action="store_true", + help=("Do not store standard output and standard" + " error log files as attachments. This" + " option is only affecting the bundle" + " created with --output, the initial bundle" + " is not affected as it never stores any" + " attachments.")) + + def invoke_with_test(self, test): + artifacts = TestArtifacts( + self.args.test_id, self.args.result_id, self._config) + if not os.path.exists(artifacts.bundle_pathname): + raise LavaCommandError("Specified result does not exist") + artifacts.load_bundle() + parse_results = test.parse(artifacts) + artifacts.incorporate_parse_results(parse_results) + self.say("Parsed {0} test results", + len(artifacts.bundle["test_runs"][0]["test_results"])) + print artifacts.dumps_bundle() + if self.args.output: + if not self.args.skip_attachments: + artifacts.attach_standard_files_to_bundle() + artifacts.save_bundle_as(self.args.output) + + +class show(Command): + """ + Display the output from a previous test run + """ + + @classmethod + def register_arguments(cls, parser): + super(show, cls).register_arguments(parser) + parser.add_argument("result_id", + help="Test run result identifier") + + def invoke(self): + artifacts = TestArtifacts(None, self.args.result_id, self._config) + if not os.path.exists(artifacts.results_dir): + raise LavaCommandError("Specified result does not exist") + if os.path.exists(artifacts.stdout_pathname): + with open(artifacts.stdout_pathname, "rt") as stream: + for line in iter(stream.readline, ''): + self.display_subprocess_output("stdout", line) + if os.path.exists(artifacts.stderr_pathname): + with open(artifacts.stderr_pathname, "rt") as stream: + for line in iter(stream.readline, ''): + self.display_subprocess_output("stderr", line) + + +class version(Command): + """ + Show LAVA Test version + """ + + def invoke(self): + self.say("version details:") + for framework in self._get_frameworks(): + self.say(" - {framework}: {version}", + framework=framework.__name__, + version=versiontools.format_version( + framework.__version__, framework)) + + def _get_frameworks(self): + import lava_tool + import lava_test + import linaro_dashboard_bundle + import linaro_json + return [ + lava_test, + lava_tool, + linaro_dashboard_bundle, + linaro_json] + + +class register_test(Command): + """ + Register remote test + """ + + @classmethod + def register_arguments(cls, parser): + super(register_test, cls).register_arguments(parser) + parser.add_argument("test_url", + help="Url for test definition file") + + def invoke(self): + try: + from lava_test.core.providers import RegistryProvider + RegistryProvider.register_remote_test(self.args.test_url) + except ValueError as exc: + raise LavaCommandError("Unable to register test: %s" % exc) + except KeyError: + raise LavaCommandError("There is no test_url") + +class unregister_test(Command): + """ + Unregister remote test + """ + + @classmethod + def register_arguments(cls, parser): + super(unregister_test, cls).register_arguments(parser) + parser.add_argument("test_url", + help="Url for test definition file") + + def invoke(self): + try: + from lava_test.core.providers import RegistryProvider + RegistryProvider.unregister_remote_test(self.args.test_url) + except ValueError as exc: + raise LavaCommandError("Unable to unregister test: %s" % exc) + except KeyError: + raise LavaCommandError("There is no test_url") === added directory 'lava_test/core' === added file 'lava_test/core/__init__.py' === added file 'lava_test/core/artifacts.py' --- lava_test/core/artifacts.py 1970-01-01 00:00:00 +0000 +++ lava_test/core/artifacts.py 2011-09-12 09:19:10 +0000 @@ -0,0 +1,277 @@ +# Copyright (c) 2010, 2011 Linaro +# +# This program 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 3 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see . + +from __future__ import absolute_import + +import base64 +import datetime +import logging +import os +import uuid + +from linaro_dashboard_bundle.io import DocumentIO + +from lava_test.core import hwprofile, swprofile +from lava_test.utils import merge_dict, mkdir_p + + +class TestArtifacts(object): + """ + Class representing test run artifacts, that is, static leftovers + independent of the wrapper class that encapsulates test handling. + + .. versionadded:: 0.2 + """ + + def __init__(self, test_id, result_id, config): + self._test_id = test_id + self._result_id = result_id + self._config = config + self._bundle = None + + @classmethod + def allocate(cls, test_id, config): + """ + Allocate new test artifacts object that corresponds to the specified + test_id. This constructs a new result_id and creates the corresponding + filesystem directory that holds those artifacts. + + .. versionadded:: 0.2 + """ + result_id = ( + "{test_id}.{time.tm_year:04}-{time.tm_mon:02}-{time.tm_mday:02}T" + "{time.tm_hour:02}:{time.tm_min:02}:{time.tm_sec:02}Z").format( + test_id=test_id, + time=datetime.datetime.utcnow().timetuple()) + self = cls(test_id, result_id, config) + logging.debug("Creating result directory: %r", self.results_dir) + mkdir_p(self.results_dir) + return self + + @property + def test_id(self): + """ + The ID of the test this run is associated with + + .. versionadded:: 0.2 + """ + return self._test_id + + @property + def result_id(self): + """ + The ID of the test run. + + This field is different from analyzer_assigned_uuid at this time but + may change in the future. The purpose of this field is to identify the + test run and be able to locate attachments/log files/bundle on the file + system. + + .. versionadded:: 0.2 + """ + return self._result_id + + @property + def results_dir(self): + """ + Pathname of a directory with test run artifacts (log files, crash + dumps, etc). + + .. versionadded:: 0.2 + """ + return os.path.join(self._config.resultsdir, self.result_id) + + def load_bundle(self): + """ + Load the results bundle from disk. + + The bundle is also validated if linaro-dashboard-bundle library is + installed. + """ + with open(self.bundle_pathname, 'rt') as stream: + self._bundle = DocumentIO.load(stream)[1] + + def dumps_bundle(self): + return DocumentIO.dumps(self._bundle) + + def save_bundle(self): + """ + Save the results bundle to the disk + + The bundle is also validated if linaro-dashboard-bundle library is + installed. + """ + self.save_bundle_as(self.bundle_pathname) + + def save_bundle_as(self, pathname): + """ + Save the results bundle to the specified file on disk. + + The bundle should have been created or loaded earlier + """ + with open(pathname, 'wt') as stream: + DocumentIO.dump(stream, self._bundle) + + @property + def bundle(self): + """ + The deserialized bundle object. + + This can be either created with create_bundle() or loaded + from disk with load_bundle() + """ + return self._bundle + + def create_initial_bundle(self, + skip_software_context=False, + skip_hardware_context=False, + time_check_performed=False): + """ + Create the bundle object. + + This creates a typical bundle structure. Optionally it can also add + software and hardware context information. + + For a complete bundle you may want to add attachments and incorporate + parse results by calling appropriate methods after loading or creating + the initial bundle. + """ + TIMEFORMAT = '%Y-%m-%dT%H:%M:%SZ' + # Generate UUID and analyzer_assigned_date for the test run + analyzer_assigned_uuid = str(uuid.uuid1()) + analyzer_assigned_date = datetime.datetime.utcnow() + # Create basic test run structure + test_run = { + 'test_id': self.test_id, + 'analyzer_assigned_date': analyzer_assigned_date.strftime( + TIMEFORMAT), + 'analyzer_assigned_uuid': analyzer_assigned_uuid, + 'time_check_performed': time_check_performed, + "test_results": [], + "attachments": [], + } + # Store hardware and software context if requested + if not skip_software_context: + test_run['software_context'] = swprofile.get_software_context() + if not skip_hardware_context: + test_run['hardware_context'] = hwprofile.get_hardware_context() + # Create the bundle object + self._bundle = { + 'format': 'Dashboard Bundle Format 1.2', + 'test_runs': [test_run]} + + @property + def test_run(self): + try: + return self._bundle["test_runs"][0] + except KeyError: + raise AttributeError("test_run can be accessed only after you load" + " or create an initial bundle") + + def attach_file(self, real_pathname, stored_pathname, mime_type): + """ + Append an attachment to the test run. + + The file is only attached if real_pathname designates an existing, + nonempty file. If the mime_type starts with 'text/' the file is opened + in text mode, otherwise binary mode is used. + """ + if not os.path.exists(real_pathname): + return + if mime_type.startswith('text/'): + mode = 'rt' + else: + mode = 'rb' + with open(real_pathname, mode) as stream: + data = stream.read() + if not data: + return + self.test_run['attachments'].append({ + "pathname": stored_pathname, + "mime_type": mime_type, + "content": base64.standard_b64encode(data)}) + + def incorporate_parse_results(self, parse_results): + """ + Merge the data returned by the test parser into the current test run. + + Non-overlapping data is simply added. Overlapping data is either merged + (lists are extended, dictionaries are recursively merged) or + overwritten (all other types). + """ + assert isinstance(parse_results, dict) + # Use whatever the parser gave us to improve the results + logging.debug("Using parser data to enrich test run details") + merge_dict(self.test_run, parse_results) + + def attach_standard_files_to_bundle(self): + """ + Attach standard output and standard error log files to the bundle. + + Both files are only attached if exist and non-empty. The attachments + are actually associated with a test run, not a bundle, but the + description is good enough for simplicity. + """ + self.attach_file(self.stdout_pathname, "testoutput.log", "text/plain") + self.attach_file(self.stderr_pathname, "testoutput.err", "text/plain") + + @property + def bundle_pathname(self): + """ + Pathname of the result bundle. + + The bundle contains the snapshot of environment information as well as + test identity and is created when you invoke ITest.run(). + + The bundle file name is always "testdata.json" + + .. versionadded:: 0.2 + """ + return self.get_artefact_pathname("testdata.json") + + @property + def stdout_pathname(self): + """ + Pathname of the log file of the standard output as returned by the test + program. + + The log file name is always "testoutput.log" + + .. versionadded:: 0.2 + """ + return self.get_artefact_pathname("testoutput.log") + + @property + def stderr_pathname(self): + """ + Pathname of the log file of the standard output as returned by the test + program. + + The log file name is always "testoutput.err" + + .. versionadded:: 0.2 + """ + return self.get_artefact_pathname("testoutput.err") + + def get_artefact_pathname(self, artefact_name): + """ + Return a pathname of a test run artefact file. + + This is more useful than hard-coding the path as it allows the test + runner not to worry about the location of the results directory. + + .. versionadded:: 0.2 + """ + return os.path.join(self.results_dir, artefact_name) === added file 'lava_test/core/config.py' --- lava_test/core/config.py 1970-01-01 00:00:00 +0000 +++ lava_test/core/config.py 2011-09-13 20:25:06 +0000 @@ -0,0 +1,97 @@ +# Copyright (c) 2010, 2011 Linaro +# +# This program 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 3 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see . + +import os +import json + + +class LavaTestConfig(object): + + def __init__(self): + home = os.environ.get('HOME', '/') + baseconfig = os.environ.get('XDG_CONFIG_HOME', + os.path.join(home, '.config')) + basedata = os.environ.get('XDG_DATA_HOME', + os.path.join(home, '.local', 'share')) + self.configdir = os.path.join(baseconfig, 'lava_test') + self.installdir = os.path.join(basedata, 'lava_test', 'installed-tests') + self.resultsdir = os.path.join(basedata, 'lava_test', 'results') + self.registry = self._load_registry() + + @property + def _registry_pathname(self): + return os.path.join(self.configdir, "registry.json") + + def _load_registry(self): + try: + with open(self._registry_pathname) as stream: + return json.load(stream) + except (IOError, ValueError): + return self._get_default_registry() + + def _save_registry(self): + if not os.path.exists(self.configdir): + os.makedirs(self.configdir) + with open(self._registry_pathname, "wt") as stream: + json.dump(self.registry, stream, indent=2) + + def _get_default_registry(self): + return { + "format": "Lava-Test Test Registry 1.0", + "providers": [{ + "entry_point": "lava_test.core.providers:BuiltInProvider" + }, { + "entry_point": "lava_test.core.providers:PkgResourcesProvider", + "config": {"namespace": "lava_test.test_definitions" } + }, + { + "entry_point": "lava_test.core.providers:RegistryProvider", + "config": {"entries": [] } + }] + } + + def get_provider_config(self, entry_point_name): + if "providers" not in self.registry: + self.registry["providers"] = [] + for provider_info in self.registry["providers"]: + if provider_info.get("entry_point") == entry_point_name: + break + else: + provider_info = {"entry_point": entry_point_name} + self.registry["providers"].append(provider_info) + if "config" not in provider_info: + provider_info["config"] = {} + return provider_info["config"] + + def get_logging_config_file(self): + logging_file = os.path.join(self.configdir, "logging.conf") + if os.path.exists(logging_file): + return logging_file + else: + return None + +_config = None + + +def get_config(): + global _config + if _config is not None: + return _config + return LavaTestConfig() + + +def set_config(config): + global _config + _config = config === added file 'lava_test/core/hwprofile.py' --- lava_test/core/hwprofile.py 1970-01-01 00:00:00 +0000 +++ lava_test/core/hwprofile.py 2011-09-12 09:19:10 +0000 @@ -0,0 +1,223 @@ +# Copyright (c) 2010, 2011 Linaro +# +# This program 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 3 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see . + +import re +import sys +from subprocess import Popen, PIPE +from lava_test.utils import read_file, get_machine_type + + +INTEL_KEYMAP = { + 'vendor_id': 'cpu_vendor_name', + 'cpu family': 'cpu_family', + 'model': 'cpu_model', + 'model name': 'cpu_model_name', + 'stepping': 'cpu_stepping', + 'cpu MHz': 'cpu_mhz', + 'flags': 'cpu_features', +} + + +INTEL_VALMAP = { + 'cpu family': int, + 'model': int, + 'stepping': int, + 'cpu MHz': float, +} + + +ARM_KEYMAP = { + 'Processor': 'cpu_model_name', + 'Features': 'cpu_features', + 'CPU implementer': 'cpu_implementer', + 'CPU architecture': 'cpu_architecture', + 'CPU variant': 'cpu_variant', + 'CPU part': 'cpu_part', + 'CPU revision': 'cpu_revision', +} + + +ARM_VALMAP = { + 'CPU implementer': lambda value: int(value, 16), + 'CPU architecture': int, + 'CPU variant': lambda value: int(value, 16), + 'CPU part': lambda value: int(value, 16), + 'CPU revision': int, +} + + +def _translate_cpuinfo(keymap, valmap, key, value): + """ + Translate a key and value using keymap and valmap passed in + """ + newkey = keymap.get(key, key) + newval = valmap.get(key, lambda x: x)(value) + return newkey, newval + + +def get_cpu_devs(): + """ + Return a list of CPU devices + """ + pattern = re.compile('^(?P.+?)\s*:\s*(?P.*)$') + cpunum = 0 + devices = [] + cpudevs = [] + cpudevs.append({}) + machine = get_machine_type() + if machine in ('i686', 'x86_64'): + keymap, valmap = INTEL_KEYMAP, INTEL_VALMAP + elif machine.startswith('arm'): + keymap, valmap = ARM_KEYMAP, ARM_VALMAP + + try: + cpuinfo = read_file("/proc/cpuinfo") + for line in cpuinfo.splitlines(): + match = pattern.match(line) + if match: + key, value = match.groups() + key, value = _translate_cpuinfo(keymap, valmap, + key, value) + if cpudevs[cpunum].get(key): + cpunum += 1 + cpudevs.append({}) + cpudevs[cpunum][key] = value + for c in range(len(cpudevs)): + device = {} + device['device_type'] = 'device.cpu' + device['description'] = 'Processor #{0}'.format(c) + device['attributes'] = cpudevs[c] + devices.append(device) + except IOError: + print >> sys.stderr, "WARNING: Could not read cpu information" + return devices + + +def get_board_devs(): + """ + Return a list of board devices + """ + devices = [] + attributes = {} + device = {} + machine = get_machine_type() + if machine in ('i686', 'x86_64'): + try: + description = read_file('/sys/class/dmi/id/board_name') or None + vendor = read_file('/sys/class/dmi/id/board_vendor') or None + version = read_file('/sys/class/dmi/id/board_version') or None + if description: + device['description'] = description.strip() + if vendor: + attributes['vendor'] = vendor.strip() + if version: + attributes['version'] = version.strip() + except IOError: + print >> sys.stderr, "WARNING: Could not read board information" + return devices + elif machine.startswith('arm'): + try: + cpuinfo = read_file("/proc/cpuinfo") + if cpuinfo is None: + return devices + pattern = re.compile("^Hardware\s*:\s*(?P.+)$", re.M) + match = pattern.search(cpuinfo) + if match is None: + return devices + device['description'] = match.group('description') + except IOError: + print >> sys.stderr, "WARNING: Could not read board information" + return devices + else: + return devices + if attributes: + device['attributes'] = attributes + device['device_type'] = 'device.board' + devices.append(device) + return devices + + +def get_mem_devs(): + """ Return a list of memory devices + + This returns up to two items, one for physical RAM and another for swap + """ + pattern = re.compile('^(?P.+?)\s*:\s*(?P.+) kB$', re.M) + + devices = [] + try: + meminfo = read_file("/proc/meminfo") + for match in pattern.finditer(meminfo): + key, value = match.groups() + if key not in ('MemTotal', 'SwapTotal'): + continue + capacity = int(value) << 10 # Kernel reports in 2^10 units + if capacity == 0: + continue + if key == 'MemTotal': + kind = 'RAM' + else: + kind = 'swap' + description = "{capacity}MiB of {kind}".format( + capacity=capacity >> 20, kind=kind) + device = {} + device['description'] = description + device['attributes'] = {'capacity': str(capacity), 'kind': kind} + device['device_type'] = "device.mem" + devices.append(device) + except IOError: + print >> sys.stderr, "WARNING: Could not read memory information" + return devices + + +def get_usb_devs(): + """ + Return a list of usb devices + """ + pattern = re.compile( + "^Bus \d{3} Device \d{3}: ID (?P[0-9a-f]{4}):" + "(?P[0-9a-f]{4}) (?P.*)$") + devices = [] + try: + for line in Popen('lsusb', stdout=PIPE).communicate()[0].splitlines(): + match = pattern.match(line) + if match: + vendor_id, product_id, description = match.groups() + attributes = {} + device = {} + attributes['vendor_id'] = int(vendor_id, 16) + attributes['product_id'] = int(product_id, 16) + device['attributes'] = attributes + device['description'] = description + device['device_type'] = 'device.usb' + devices.append(device) + except OSError: + print >> sys.stderr, "WARNING: Could not read usb device information, \ +unable to run lsusb, please install usbutils package" + return devices + + +def get_hardware_context(): + """ + Return a dict with all of the hardware profile information gathered + """ + hardware_context = {} + devices = [] + devices.extend(get_cpu_devs()) + devices.extend(get_board_devs()) + devices.extend(get_mem_devs()) + devices.extend(get_usb_devs()) + hardware_context['devices'] = devices + return hardware_context === added file 'lava_test/core/installers.py' --- lava_test/core/installers.py 1970-01-01 00:00:00 +0000 +++ lava_test/core/installers.py 2011-09-12 09:19:10 +0000 @@ -0,0 +1,105 @@ +# Copyright (c) 2010, 2011 Linaro +# +# This program 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 3 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see . + +import hashlib +import os + +from lava_test.api.delegates import ITestInstaller +from lava_test.extcmd import ExternalCommandWithDelegate +from lava_test.utils import geturl + + +class TestInstaller(ITestInstaller): + """ + Base class for defining an installer object. + + This class can be used as-is for simple installers, or extended + for more advanced functionality. + + :ivar steps: + List of steps to be executed in a shell + + :ivar deps: + List of Debian or Ubuntu packages to apt-get install before running the + steps. + + :ivar url: + Location from which the test suite should be downloaded. + + :ivar md5: + The md5sum to check the integrety of the download + """ + def __init__(self, steps=None, deps=None, url=None, md5=None, **kwargs): + self.steps = steps or [] + self.deps = deps or [] + self.url = url + self.md5 = md5 + + def __repr__(self): + return "<%s steps=%r deps=%r url=%r md5=%r>" % ( + self.__class__.__name__, + self.steps, self.deps, self.url, self.md5) + + def _run_shell_cmd(self, cmd, observer): + if observer: observer.about_to_run_shell_command(cmd) + extcmd = ExternalCommandWithDelegate(observer) + returncode = extcmd.check_call(cmd, shell=True) + if observer: observer.did_run_shell_command(cmd, returncode) + + def _installdeps(self, observer): + if self.deps: + if observer: observer.about_to_install_packages(self.deps) + # XXX: Possible point of target-specific package installation + cmd = "sudo apt-get install -y " + " ".join(self.deps) + self._run_shell_cmd(cmd, observer) + if observer: observer.did_install_packages(self.deps) + + def _download(self, observer): + """ + Download the file specified by the url and check the md5. + + Returns the path and filename if successful, otherwise return None + """ + if not self.url: + return + if observer: observer.about_to_download_file(self.url) + filename = geturl(self.url) + # If the file does not exist, then the download was not + # successful + if not os.path.exists(filename): + raise RuntimeError( + "Failed to download %r" % self.url) + if observer: observer.did_download_file(self.url) + if self.md5: + checkmd5 = hashlib.md5() + with open(filename, 'rb') as fd: + data = fd.read(0x10000) + while data: + checkmd5.update(data) + data = fd.read(0x10000) + if checkmd5.hexdigest() != self.md5: + raise RuntimeError( + "md5sum mismatch of file %r, got %s expected %s" % ( + filename, checkmd5.hexdigest(), self.md5)) + return filename + + def _runsteps(self, observer): + for cmd in self.steps: + self._run_shell_cmd(cmd, observer) + + def install(self, observer=None): + self._installdeps(observer) + self._download(observer) + self._runsteps(observer) === added file 'lava_test/core/loader.py' --- lava_test/core/loader.py 1970-01-01 00:00:00 +0000 +++ lava_test/core/loader.py 2011-09-12 09:19:10 +0000 @@ -0,0 +1,83 @@ +# Copyright (c) 2010, 2011 Linaro +# +# This program 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 3 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see . + +from __future__ import absolute_import +from lava_test.core.config import get_config + +class TestLoader(object): + """ + Test loader. + + Encapsulates LAVA Test's knowledge of available tests. + + Test can be loaded by name with + :meth:`lava_test.core.loader.TestLoader.__getitem__()`. Test can also be + listed by :meth:`lava_test.core.loader.TestLoader.get_providers()` and then + iterating over tests returned by each provider. + """ + + def __init__(self, config): + self._config = config + + def get_providers(self): + """ + Return a generator of available providers + """ + import pkg_resources + for provider_info in self._config.registry.get("providers", []): + entry_point_name = provider_info.get("entry_point") + module_name, attrs = entry_point_name.split(':', 1) + attrs = attrs.split('.') + try: + entry_point = pkg_resources.EntryPoint( + entry_point_name, module_name, attrs, + dist=pkg_resources.get_distribution("lava-test")) + provider_cls = entry_point.load() + provider = provider_cls(provider_info.get("config", {})) + yield provider + except pkg_resources.DistributionNotFound: + raise RuntimeError( + "lava-test is not properly set up." + " Please read the README file") + except ImportError, err: + print "Couldn't load module : %s . Maybe configuration needs to be updated" % module_name + print "The configuration is stored at %s" %(get_config().configdir) + + + + def __getitem__(self, test_id): + """ + Lookup a test with the specified test_id + """ + for provider in self.get_providers(): + try: + return provider[test_id] + except KeyError: + pass + raise KeyError(test_id) + + def get_test_by_name(self, test_id): + """ + Lookup a test with the specified name + + .. deprecated:: 0.2 + Use __getitem__ instead + """ + for provider in self.get_providers(): + try: + return provider[test_id] + except KeyError: + pass + raise ValueError("No such test %r" % test_id) === added file 'lava_test/core/parsers.py' --- lava_test/core/parsers.py 1970-01-01 00:00:00 +0000 +++ lava_test/core/parsers.py 2011-09-12 09:19:10 +0000 @@ -0,0 +1,147 @@ +# Copyright (c) 2010, 2011 Linaro +# +# This program 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 3 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see . + +import decimal +import os +import re + +from lava_test.api.delegates import ITestParser + + +class TestParser(ITestParser): + """ + Base class for defining a test parser + + This class can be used as-is for simple results parsers, but will likely + need to be extended slightly for many. If used as it is, the parse() + method should be called while already in the results directory and assumes + that a file for test output will exist called testoutput.log. + + :ivar pattern: + regexp pattern to identify important elements of test output For + example: If your testoutput had lines that look like: "test01: PASS" + then you could use a pattern like this: + "^(?P\w+):\W+(?P\w+)" This would result in + identifying "test01" as testid and "PASS" as result. Once parse() + has been called, self.results.test_results[] contains a list of + dicts of all the key,value pairs found for each test result. + + :ivar fixupdict: + Dict of strings to convert test results to standard strings For + example: if you want to standardize on having pass/fail results in + lower case, but your test outputs them in upper case, you could use a + fixupdict of something like: {'PASS':'pass','FAIL':'fail'} + + :ivar appendall: + Append a dict to the test_results entry for each result. + For example: if you would like to add units="MB/s" to each result: + appendall={'units':'MB/s'} + + :ivar results: + Dictionary of data that was scrubbed from the log file for this test + run. Most notably it contains the test_results array. + """ + def __init__(self, pattern=None, fixupdict=None, appendall={}): + if pattern is not None: + try: + re.compile(pattern) + except Exception as ex: + raise ValueError( + "Invalid regular expression %r: %s", pattern, ex) + self._results = {'test_results': []} + self.pattern = pattern + self.fixupdict = fixupdict + self.appendall = appendall + + def __repr__(self): + return "<%s pattern=%r fixupdict=%r appendall=%r>" % ( + self.__class__.__name__, + self.pattern, self.fixupdict, self.appendall) + + @property + def results(self): + return self._results + + def parse(self, artifacts): + if os.path.exists(artifacts.stdout_pathname): + return self.parse_pathname( + artifacts.stdout_pathname, + os.path.relpath(artifacts.stdout_pathname, + artifacts.results_dir)) + if os.path.exists(artifacts.stderr_pathname): + return self.parse_pathname( + artifacts.stderr_pathname, + os.path.relpath(artifacts.stderr_pathname, + artifacts.results_dir)) + + def parse_pathname(self, pathname, relative_pathname=None): + with open(pathname, 'rt') as stream: + for lineno, line in enumerate(stream, 1): + match = re.search(self.pattern, line) + if not match: + continue + data = match.groupdict() + data["log_filename"] = relative_pathname or pathname + data["log_lineno"] = lineno + self._results['test_results'].append( + self.analyze_test_result(data)) + return self.results + + @property + def badchars(self): + return "[^a-zA-Z0-9\._-]" + + def analyze_test_result(self, data): + """ + Analyze sigle match (typically single line) and convert it into a + proper test result object. + + Currently this method does the following transformations: + * measurement is converted to decimal if present + * test_case_id is rewritten to strip badchars + * test_case_id is rewritten to convert spaces to underscores + * result is transformed using fixuptdict, if defined + * appendall information is added, if defined + """ + if 'measurement' in data: + try: + data['measurement'] = decimal.Decimal(data['measurement']) + except decimal.InvalidOperation: + del data['measurement'] + if 'test_case_id' in data: + data['test_case_id'] = re.sub(self.badchars, "", + data['test_case_id']) + data['test_case_id'] = data['test_case_id'].replace(" ", "_") + if 'result' in data and self.fixupdict: + data['result'] = self.fixupdict[data['result']] + if self.appendall: + data.update(self.appendall) + return data + + +class NativeTestParser(ITestParser): + """ + Unfinished native test parser. + + This was meant to be a pass-through for tests that directly create bundles + """ + def __init__(self, test_def): + self.test_def = test_def + + def parse(self, artifacts): + raise NotImplementedError() + + def results(self): + raise NotImplementedError() === added file 'lava_test/core/providers.py' --- lava_test/core/providers.py 1970-01-01 00:00:00 +0000 +++ lava_test/core/providers.py 2011-09-12 09:19:10 +0000 @@ -0,0 +1,165 @@ +# Copyright (c) 2010, 2011 Linaro +# +# This program 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 3 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see . + +from lava_test.api.core import ITestProvider +from lava_test.core.config import get_config +from lava_test.core.tests import DeclarativeTest +from lava_test.utils import Cache + + +class BuiltInProvider(ITestProvider): + """ + Test provider that provides tests shipped in the Lava-Test source tree + """ + + _builtin_tests = [ + 'glmemperf', + 'gmpbench', + 'gtkperf', + 'ltp', + 'posixtestsuite', + 'pwrmgmt', + 'stream', + 'tiobench', + 'x11perf', + ] + + def __init__(self, config): + pass + + @property + def description(self): + return "Tests built directly into LAVA Test:" + + def __iter__(self): + return iter(self._builtin_tests) + + def __getitem__(self, test_id): + if test_id not in self._builtin_tests: + raise KeyError(test_id) + module = __import__("lava_test.test_definitions.%s" % test_id, + fromlist=['']) + return module.testobj + + +class PkgResourcesProvider(ITestProvider): + """ + Test provider that provides tests declared in pkg_resources working_set + + By default it looks at the 'lava_test.test_definitions' name space but it can + be changed with custom 'namespace' configuration entry. + """ + + def __init__(self, config): + self._config = config + + @property + def namespace(self): + return self._config.get("namespace", "lava_test.test_definitions") + + @property + def description(self): + return ("Tests provided by installed python packages" + " (from namespace {0}):").format(self.namespace) + + def __iter__(self): + from pkg_resources import working_set + for entry_point in working_set.iter_entry_points(self.namespace): + yield entry_point.name + + def __getitem__(self, test_name): + from pkg_resources import working_set + for entry_point in working_set.iter_entry_points(self.namespace, + test_name): + return entry_point.load().testobj + raise KeyError(test_name) + + +class RegistryProvider(ITestProvider): + """ + Test provider that provides declarative tests listed in the test registry. + """ + def __init__(self, config): + self._config = config + self._cache = None + + @property + def entries(self): + """ + List of URLs to DeclarativeTest description files + """ + return self._config.get("entries", []) + + @property + def description(self): + return "Tests provided by LAVA Test registry:" + + @classmethod + def register_remote_test(self, test_url): + config = get_config() # This is a different config object from + # self._config + provider_config = config.get_provider_config( + "lava_test.core.providers:RegistryProvider") + if "entries" not in provider_config: + provider_config["entries"] = [] + if test_url not in provider_config["entries"]: + provider_config["entries"].append(test_url) + config._save_registry() + else: + raise ValueError("This test is already registered") + + @classmethod + def unregister_remote_test(self, test_url): + config = get_config() # This is a different config object from + # self._config + provider_config = config.get_provider_config( + "lava_test.core.providers:RegistryProvider") + if "entries" not in provider_config: + provider_config["entries"] = [] + if test_url in provider_config["entries"]: + provider_config["entries"].remove(test_url) + config._save_registry() + else: + raise ValueError("This test is not registered") + + def _load_remote_test(self, test_url): + """ + Load DeclarativeTest from a (possibly cached copy of) test_url + """ + cache = Cache.get_instance() + with cache.open_cached_url(test_url) as stream: + return DeclarativeTest.load_from_stream(stream) + + def _fill_cache(self): + """ + Fill the cache of all remote tests + """ + if self._cache is not None: + return + self._cache = {} + for test_url in self.entries: + test = self._load_remote_test(test_url) + if test.test_id in self._cache: + raise ValueError("Duplicate test %s declared" % test.test_id) + self._cache[test.test_id] = test + + def __iter__(self): + self._fill_cache() + for test_id in self._cache.iterkeys(): + yield test_id + + def __getitem__(self, test_id): + self._fill_cache() + return self._cache[test_id] === added file 'lava_test/core/runners.py' --- lava_test/core/runners.py 1970-01-01 00:00:00 +0000 +++ lava_test/core/runners.py 2011-09-12 09:19:10 +0000 @@ -0,0 +1,66 @@ +# Copyright (c) 2010, 2011 Linaro +# +# This program 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 3 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see . + +import datetime + +from lava_test.api.delegates import ITestRunner +from lava_test.extcmd import (DisplayDelegate, ExternalCommandWithDelegate) + + +class TestRunner(ITestRunner): + """ + Base class for defining an test runner object. + + This class can be used as-is for simple execution with the expectation that + the run() method will be called from the directory where the test was + installed. Steps, if used, should handle changing directories from there to + the directory where the test was extracted if necessary. This class can + also be extended for more advanced functionality. + + :ivar steps: + list of shell commands to execute + """ + def __init__(self, steps=None): + self.steps = steps or [] + self.testoutput = [] # XXX: is this still used? + + def __repr__(self): + return "<%s steps=%r>" % (self.__class__.__name__, self.steps) + + def _run_lava_test_steps(self, artifacts, observer): + stdout = open(artifacts.stdout_pathname, 'at') + stderr = open(artifacts.stderr_pathname, 'at') + delegate = DisplayDelegate(stdout, stderr, observer) + extcmd = ExternalCommandWithDelegate(delegate) + try: + for cmd in self.steps: + if observer: observer.about_to_run_shell_command(cmd) + returncode = extcmd.call(cmd, shell=True) + if observer: observer.did_run_shell_command(cmd, returncode) + finally: + stdout.close() + stderr.close() + + def run(self, artifacts, observer=None): + """ + Run the test program by executing steps in sequence. + + .. seealso:: + + :meth:`~lava_test.api.delegates.TestRunner.run` + """ + self.starttime = datetime.datetime.utcnow() + self._run_lava_test_steps(artifacts, observer) + self.endtime = datetime.datetime.utcnow() === added file 'lava_test/core/swprofile.py' --- lava_test/core/swprofile.py 1970-01-01 00:00:00 +0000 +++ lava_test/core/swprofile.py 2011-09-12 09:19:10 +0000 @@ -0,0 +1,72 @@ +# Copyright (c) 2010, 2011 Linaro +# +# This program 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 3 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see . + +import apt + +from lava_test.utils import read_file + + +def get_packages(apt_cache=None): + """ Get information about the packages installed + + apt_cache - if not provided, this will be read from the system + """ + if apt_cache == None: + apt_cache = apt.Cache() + packages = [] + for apt_pkg in apt_cache: + if hasattr(apt_pkg, 'is_installed'): + is_installed = apt_pkg.is_installed + else: + is_installed = apt_pkg.isInstalled # old style API + if is_installed: + pkg = { + "name": apt_pkg.name, + "version": apt_pkg.installed.version} + packages.append(pkg) + return packages + + +def get_software_context(apt_cache=None, lsb_information=None): + """ Return dict used for storing software_context information + + test_id - Unique identifier for this test + time_check - whether or not a check was performed to see if + the time on the system was synced with a time server + apt_cache - if not provided, this will be read from the system + lsb_information - if not provided, this will be read from the system + """ + software_context = {} + software_context['image'] = get_image(lsb_information) + software_context['packages'] = get_packages(apt_cache) + return software_context + + +def get_image(lsb_information=None): + """ Get information about the image we are running + + If /etc/buildstamp exists, get the image id from that. Otherwise + just use the lsb-release description for a rough idea. + """ + try: + buildstamp = read_file("/etc/buildstamp") + name = buildstamp.splitlines()[1] + except IOError: + import lsb_release + + if lsb_information == None: + lsb_information = lsb_release.get_distro_information() + name = lsb_information['DESCRIPTION'] + return {"name": name} === added file 'lava_test/core/tests.py' --- lava_test/core/tests.py 1970-01-01 00:00:00 +0000 +++ lava_test/core/tests.py 2011-09-12 09:19:10 +0000 @@ -0,0 +1,166 @@ +# Copyright (c) 2010, 2011 Linaro +# +# This program 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 3 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see . + +from __future__ import absolute_import + +import json +import logging +import os +import shutil + +from lava_test.api.core import ITest +from lava_test.core.artifacts import TestArtifacts +from lava_test.core.config import get_config +from lava_test.core.installers import TestInstaller +from lava_test.core.parsers import TestParser, NativeTestParser +from lava_test.core.runners import TestRunner +from lava_test.utils import changed_directory + + +class Test(ITest): + """ + Reusable class for defining tests. + + This class uses composition instead of inheritance. You should be able to + customize the parts you care about by providing delegate objects. This + class can be used by test definition files to create an object that + contains the building blocks for installing tests, running them, and + parsing the results. + + :ivar test_id: + Name of the test or test suite + :ivar test_version: + Version of the test or test suite + :ivar installer: + ITestInstaller instance to use + :ivar runner: + ITestRunner instance to use + :ivar parser: + ITestParser instance to use + """ + + def __init__(self, test_id, test_version=None, + installer=None, runner=None, parser=None): + self._test_id = test_id + self._test_version = test_version + # Delegate objects + self.installer = installer + self.runner = runner + self.parser = parser + # Config instance + self._config = get_config() + + def __repr__(self): + return ("<%s test_id=%r test_version=%r installer=%r runner=%r" + " parser=%r>") % ( + self.__class__.__name__, self.test_id, self.test_version, + self.installer, self.runner, self.parser) + + @property + def test_id(self): + """ + Return the ID of the test. + """ + return self._test_id + + @property + def test_version(self): + """ + Return the version of the test + """ + return self._test_version + + @property + def install_dir(self): + """ + Pathname of a directory with binary and data files installed by the + test. + + .. versionadded:: 0.2 + """ + return os.path.join(self._config.installdir, self.test_id) + + @property + def is_installed(self): + return os.path.exists(self.install_dir) + + def install(self, observer=None): + if self.is_installed: + raise RuntimeError( + "%s is already installed" % self.test_id) + if not self.installer: + raise RuntimeError( + "no installer defined for '%s'" % self.test_id) + with changed_directory(self.install_dir): + try: + logging.debug( + "Invoking %r.install(...)", self.installer) + self.installer.install(observer) + except: + self.uninstall() + raise + + def uninstall(self): + logging.debug("Removing test %r", self.test_id) + if os.path.exists(self.install_dir): + shutil.rmtree(self.install_dir) + + def run(self, observer=None): + if not self.runner: + raise RuntimeError( + "no test runner defined for '%s'" % self.test_id) + artifacts = TestArtifacts.allocate(self.test_id, self._config) + with changed_directory(self.install_dir): + logging.debug( + "Invoking %r.run_and_store_artifacts(...)", + self.runner, observer) + self.runner.run(artifacts, observer) + return artifacts + + def parse(self, artifacts): + if self.parser: + logging.debug("Invoking %r.parse()", self.parser) + with changed_directory(artifacts.results_dir, False): + self.parser.parse(artifacts) + return self.parser.results + + +class DeclarativeTest(Test): + """ + Declaretive ITest implementation. + + Declarative test is like :class:`lava_test.core.tests.Test` but cannot + contain any python code and is completely encapsulated in a .json file. + + The idea is to write .json files that assemble a Test instance using + readily-available TestInstaller, TestRunner and TestParser subclasses. + """ + + def __init__(self, about): + self.about = about + super(DeclarativeTest, self).__init__(self.about.get('test_id')) + self.installer = TestInstaller(**self.about.get('install', {})) + self.runner = TestRunner(**self.about.get('run', {})) + if self.about.get('parse', {}).get('native', False) is True: + self.parser = NativeTestParser(self) + else: + self.parser = TestParser(**self.about.get('parse', {})) + + @classmethod + def load_from_stream(cls, stream): + return cls(json.load(stream)) + + def save_to_stream(self, stream): + json.dumps(self.about, stream, indent="2") === added file 'lava_test/extcmd.py' --- lava_test/extcmd.py 1970-01-01 00:00:00 +0000 +++ lava_test/extcmd.py 2011-09-12 09:19:10 +0000 @@ -0,0 +1,108 @@ +# Copyright (c) 2010, 2011 Linaro +# +# This program 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 3 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see . + +from Queue import Queue +import subprocess +import sys +import threading +try: + import posix +except ImportError: + posix = None + + +class ExternalCommand(object): + + def _popen(self, *args, **kwargs): + if posix: + kwargs['close_fds'] = True + return subprocess.Popen(*args, **kwargs) + + def call(self, *args, **kwargs): + proc = self._popen(*args, **kwargs) + proc.wait() + return proc.returncode + + def check_call(self, *args, **kwargs): + returncode = self.call(*args, **kwargs) + if returncode != 0: + raise subprocess.CalledProcessError( + returncode, kwargs.get("args") or args[0]) + return returncode + + +class ExternalCommandWithDelegate(ExternalCommand): + + def __init__(self, delegate): + self._queue = Queue() + self._delegate = delegate + + def _read_stream(self, stream, stream_name): + for line in iter(stream.readline, ''): + cmd = (stream_name, line) + self._queue.put(cmd) + + def _drain_queue(self): + while True: + args = self._queue.get() + if args is None: + break + self._delegate.display_subprocess_output(*args) + + def call(self, *args, **kwargs): + kwargs['stdout'] = subprocess.PIPE + kwargs['stderr'] = subprocess.PIPE + proc = self._popen(*args, **kwargs) + stdout_reader = threading.Thread( + target=self._read_stream, args=(proc.stdout, "stdout")) + stderr_reader = threading.Thread( + target=self._read_stream, args=(proc.stderr, "stderr")) + ui_printer = threading.Thread( + target=self._drain_queue) + + ui_printer.start() + stdout_reader.start() + stderr_reader.start() + try: + proc.wait() + except KeyboardInterrupt: + proc.kill() + finally: + stdout_reader.join() + stderr_reader.join() + self._queue.put(None) + ui_printer.join() + return proc.returncode + + +class DisplayDelegate(object): + """ + Delegate for displaying command output. + + Perfect companion for ExternalCommandWithDelegate. + """ + + def __init__(self, stdout=None, stderr=None, chain=None): + self.stdout = stdout or sys.stdout + self.stderr = stderr or sys.stderr + self.chain = chain + + def display_subprocess_output(self, stream_name, line): + if stream_name == 'stdout': + self.stdout.write(line) + elif stream_name == 'stderr': + self.stderr.write(line) + if self.chain: + self.chain.display_subprocess_output(stream_name, line) === modified file 'lava_test/main.py' --- abrek/main.py 2011-06-07 11:56:23 +0000 +++ lava_test/main.py 2011-09-13 22:43:20 +0000 @@ -1,4 +1,4 @@ -# Copyright (c) 2010 Linaro +# Copyright (c) 2010, 2011 Linaro # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -13,22 +13,39 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import abrek.command - - -def main(argv): - argv = argv[1:] - if not argv: - argv = ['help'] - cmd = argv.pop(0) - cmd_func = abrek.command.get_command(cmd) - if not cmd_func: - print "command '%s' not found" % cmd - return 1 - return cmd_func.main(argv) +import logging +import logging.config + +from lava_test.core.config import get_config + +from lava_tool.dispatcher import LavaDispatcher, run_with_dispatcher_class + + +class LAVATestDispatcher(LavaDispatcher): + + toolname = 'lava_test' + description = """ + LAVA Test wrapper framework + """ + epilog = """ + Please report all bugs using the Launchpad bug tracker: + http://bugs.launchpad.net/lava-test/+filebug + """ + + +def main(): + + logging_config_file = get_config().get_logging_config_file() + + if logging_config_file != None: + logging.config.fileConfig(logging_config_file) + + run_with_dispatcher_class(LAVATestDispatcher) + if __name__ == '__main__': import os import sys - exit_code = main(sys.argv) - sys.exit(exit_code) \ No newline at end of file + arg_only = sys.argv + arg_only.remove(arg_only[0]) + LAVATestDispatcher().dispatch(arg_only) \ No newline at end of file === modified file 'lava_test/test_definitions/bootchart.py' --- abrek/test_definitions/bootchart.py 2011-08-03 16:23:05 +0000 +++ lava_test/test_definitions/bootchart.py 2011-09-12 09:19:10 +0000 @@ -1,4 +1,4 @@ -# Copyright (c) 2011 Linaro +# Copyright (c) 2010, 2011 Linaro # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -13,16 +13,19 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import abrek.testdef +from lava_test.core.installers import TestInstaller +from lava_test.core.parsers import TestParser +from lava_test.core.runners import TestRunner +from lava_test.core.tests import Test INSTALLSTEPS = ['bzr branch lp:~linaro-foundations/lava-test/bootchartscript'] DEPS = ['bootchart', 'pybootchartgui', 'bzr'] RUNSTEPS = ['./bootchartscript/bootchartscript.sh'] PATTERN = "^(?P\w+):\W+(?P\d+\.\d+)" -bootchartinst = abrek.testdef.AbrekTestInstaller(INSTALLSTEPS, deps=DEPS) -bootchartrun = abrek.testdef.AbrekTestRunner(RUNSTEPS) -bootchartparser = abrek.testdef.AbrekTestParser(PATTERN, +bootchartinst = TestInstaller(INSTALLSTEPS, deps=DEPS) +bootchartrun = TestRunner(RUNSTEPS) +bootchartparser = TestParser(PATTERN, appendall={'units':'sec', 'result':'pass'}) -testobj = abrek.testdef.AbrekTest(testname="bootchart", installer=bootchartinst, +testobj = Test(testname="bootchart", installer=bootchartinst, runner=bootchartrun, parser=bootchartparser) === modified file 'lava_test/test_definitions/firefox.py' --- abrek/test_definitions/firefox.py 2011-06-08 18:33:03 +0000 +++ lava_test/test_definitions/firefox.py 2011-09-12 09:19:10 +0000 @@ -13,16 +13,21 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import abrek.testdef + +from lava_test.core.installers import TestInstaller +from lava_test.core.parsers import TestParser +from lava_test.core.runners import TestRunner +from lava_test.core.tests import Test + INSTALLSTEPS = ['git clone git://github.com/janimo/firefox-startup-timing.git'] DEPS = ['firefox', 'git-core', 'gcalctool'] RUNSTEPS = ['cd firefox-startup-timing; ./firefox_startup_timing.sh'] PATTERN = "^(?P\w+):(?P\d+)" -firefoxinst = abrek.testdef.AbrekTestInstaller(INSTALLSTEPS, deps=DEPS) -firefoxrun = abrek.testdef.AbrekTestRunner(RUNSTEPS) -firefoxparser = abrek.testdef.AbrekTestParser(PATTERN, +firefoxinst = TestInstaller(INSTALLSTEPS, deps=DEPS) +firefoxrun = TestRunner(RUNSTEPS) +firefoxparser = TestParser(PATTERN, appendall={'units':'ms', 'result':'pass'}) -testobj = abrek.testdef.AbrekTest(testname="firefox", installer=firefoxinst, +testobj = Test(test_id="firefox", installer=firefoxinst, runner=firefoxrun, parser=firefoxparser) === modified file 'lava_test/test_definitions/glmemperf.py' --- abrek/test_definitions/glmemperf.py 2010-10-08 14:40:35 +0000 +++ lava_test/test_definitions/glmemperf.py 2011-09-12 09:19:10 +0000 @@ -1,4 +1,4 @@ -# Copyright (c) 2010 Linaro +# Copyright (c) 2010, 2011 Linaro # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -13,16 +13,21 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import abrek.testdef + +from lava_test.core.installers import TestInstaller +from lava_test.core.parsers import TestParser +from lava_test.core.runners import TestRunner +from lava_test.core.tests import Test + RUNSTEPS = ["glmemperf -e shmimage"] PATTERN = "^(?P\w+):\W+(?P\d+) fps" -inst = abrek.testdef.AbrekTestInstaller(deps=["glmemperf"]) -run = abrek.testdef.AbrekTestRunner(RUNSTEPS) -parse = abrek.testdef.AbrekTestParser(PATTERN, +inst = TestInstaller(deps=["glmemperf"]) +run = TestRunner(RUNSTEPS) +parse = TestParser(PATTERN, appendall={'units':'fps', 'result':'pass'}) -testobj = abrek.testdef.AbrekTest(testname="glmemperf", installer=inst, +testobj = Test(test_id="glmemperf", installer=inst, runner=run, parser=parse) === modified file 'lava_test/test_definitions/gmpbench.py' --- abrek/test_definitions/gmpbench.py 2011-07-14 02:46:00 +0000 +++ lava_test/test_definitions/gmpbench.py 2011-09-12 09:19:10 +0000 @@ -1,4 +1,4 @@ -# Copyright (c) 2010 Linaro +# Copyright (c) 2010, 2011 Linaro # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -21,7 +21,12 @@ """ -import abrek.testdef + +from lava_test.core.installers import TestInstaller +from lava_test.core.parsers import TestParser +from lava_test.core.runners import TestRunner +from lava_test.core.tests import Test + VERSION='0.2' URL="ftp://ftp.gmplib.org/pub/misc/gmpbench-%s.tar.bz2" %(VERSION) @@ -36,10 +41,10 @@ PATTERN = "\s*(?PGMPbench\.*\w*\.*\w*):?\s*"\ "(?P\d+.\d+)" -gmpbenchinst = abrek.testdef.AbrekTestInstaller(INSTALLSTEPS, deps=DEPS, +gmpbenchinst = TestInstaller(INSTALLSTEPS, deps=DEPS, url=URL) -gmpbenchrun = abrek.testdef.AbrekTestRunner(RUNSTEPS) -gmpbenchparser = abrek.testdef.AbrekTestParser(PATTERN, +gmpbenchrun = TestRunner(RUNSTEPS) +gmpbenchparser = TestParser(PATTERN, appendall={'units':'operations/s', 'result':'pass'}) -testobj = abrek.testdef.AbrekTest(testname="gmpbench", installer=gmpbenchinst, +testobj = Test(test_id="gmpbench", installer=gmpbenchinst, runner=gmpbenchrun, parser=gmpbenchparser) === modified file 'lava_test/test_definitions/gtkperf.py' --- abrek/test_definitions/gtkperf.py 2011-04-15 16:11:24 +0000 +++ lava_test/test_definitions/gtkperf.py 2011-09-12 09:19:10 +0000 @@ -1,4 +1,4 @@ -# Copyright (c) 2010 Linaro +# Copyright (c) 2010, 2011 Linaro # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -14,14 +14,19 @@ # along with this program. If not, see . import re -import abrek.testdef + +from lava_test.core.installers import TestInstaller +from lava_test.core.parsers import TestParser +from lava_test.core.runners import TestRunner +from lava_test.core.tests import Test + # Run tests automatically, 500 repetitions each gtkperf_options = "-a -c 500" RUNSTEPS = ["LANG=C gtkperf %s" % gtkperf_options] -class GtkTestParser(abrek.testdef.AbrekTestParser): +class GtkTestParser(TestParser): def parse(self): PAT1 = "^(?P\w+) - (?P\w*\W*\w*) - time:\W+(?P\d+\.\d+)" PAT2 = "^(?P\w+) - time:\W+(?P\d+\.\d+)" @@ -46,8 +51,8 @@ self.fixmeasurements() parse = GtkTestParser() -inst = abrek.testdef.AbrekTestInstaller(deps=["gtkperf"]) -run = abrek.testdef.AbrekTestRunner(RUNSTEPS) +inst = TestInstaller(deps=["gtkperf"]) +run = TestRunner(RUNSTEPS) -testobj = abrek.testdef.AbrekTest(testname="gtkperf", installer=inst, +testobj = Test(test_id="gtkperf", installer=inst, runner=run, parser=parse) === modified file 'lava_test/test_definitions/ltp.py' --- abrek/test_definitions/ltp.py 2011-04-13 20:39:16 +0000 +++ lava_test/test_definitions/ltp.py 2011-09-12 09:19:10 +0000 @@ -1,4 +1,4 @@ -# Copyright (c) 2010 Linaro +# Copyright (c) 2010, 2011 Linaro # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,7 +15,12 @@ import re -import abrek.testdef + +from lava_test.core.installers import TestInstaller +from lava_test.core.parsers import TestParser +from lava_test.core.runners import TestRunner +from lava_test.core.tests import Test + VERSION="20100831" URL='http://downloads.sourceforge.net/project/ltp/LTP Source/ltp-%s/ltp-full-%s.bz2' % (VERSION, VERSION) @@ -44,7 +49,7 @@ "TWARN":"unknown"} -class LTPParser(abrek.testdef.AbrekTestParser): +class LTPParser(TestParser): def parse(self): filename = "testoutput.log" pat = re.compile(self.pattern) @@ -67,10 +72,10 @@ self.fixids() -ltpinst = abrek.testdef.AbrekTestInstaller(INSTALLSTEPS, deps=DEPS, url=URL, +ltpinst = TestInstaller(INSTALLSTEPS, deps=DEPS, url=URL, md5=MD5) -ltprun = abrek.testdef.AbrekTestRunner(RUNSTEPS) +ltprun = TestRunner(RUNSTEPS) ltpparser = LTPParser(PATTERN, fixupdict = FIXUPS) -testobj = abrek.testdef.AbrekTest(testname="ltp", version=VERSION, +testobj = Test(test_id="ltp", test_version=VERSION, installer=ltpinst, runner=ltprun, parser=ltpparser) === modified file 'lava_test/test_definitions/peacekeeper.py' --- abrek/test_definitions/peacekeeper.py 2011-06-08 12:38:38 +0000 +++ lava_test/test_definitions/peacekeeper.py 2011-09-12 09:19:10 +0000 @@ -1,4 +1,4 @@ -# Copyright (c) 2010 Linaro +# Copyright (c) 2010, 2011 Linaro # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -20,7 +20,12 @@ http://clients.futuremark.com/peacekeeper/index.action """ -import abrek.testdef + +from lava_test.core.installers import TestInstaller +from lava_test.core.parsers import TestParser +from lava_test.core.runners import TestRunner +from lava_test.core.tests import Test + import os curdir = os.path.realpath(os.path.dirname(__file__)) @@ -29,13 +34,13 @@ RUNSTEPS = ['python peacekeeper_runner.py firefox'] DEPS = ['python-ldtp','firefox'] -my_installer = abrek.testdef.AbrekTestInstaller(INSTALLSTEPS, deps=DEPS) -my_runner = abrek.testdef.AbrekTestRunner(RUNSTEPS) +my_installer = TestInstaller(INSTALLSTEPS, deps=DEPS) +my_runner = TestRunner(RUNSTEPS) PATTERN = "^(?P\w+): Score = (?P\d+)" -my_parser = abrek.testdef.AbrekTestParser(PATTERN, +my_parser = TestParser(PATTERN, appendall={'units':'point'}) -testobj = abrek.testdef.AbrekTest(testname="peacekeeper", installer=my_installer, +testobj = Test(test_id="peacekeeper", installer=my_installer, runner=my_runner, parser=my_parser) === modified file 'lava_test/test_definitions/posixtestsuite.py' --- abrek/test_definitions/posixtestsuite.py 2011-07-14 02:46:00 +0000 +++ lava_test/test_definitions/posixtestsuite.py 2011-09-12 09:19:10 +0000 @@ -1,4 +1,4 @@ -# Copyright (c) 2010 Linaro +# Copyright (c) 2010, 2011 Linaro # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -23,7 +23,12 @@ """ import re -import abrek.testdef + +from lava_test.core.installers import TestInstaller +from lava_test.core.parsers import TestParser +from lava_test.core.runners import TestRunner +from lava_test.core.tests import Test + VERSION="20100831" URL= "http://downloads.sourceforge.net/project/ltp/LTP Source/ltp-%s/"\ @@ -45,7 +50,7 @@ } -class PosixParser(abrek.testdef.AbrekTestParser): +class PosixParser(TestParser): def parse(self): filename = "testoutput.log" pat = re.compile(self.pattern) @@ -60,10 +65,10 @@ if self.fixupdict: self.fixresults(self.fixupdict) -posix_inst = abrek.testdef.AbrekTestInstaller(INSTALLSTEPS, deps=DEPS, +posix_inst = TestInstaller(INSTALLSTEPS, deps=DEPS, url=URL, md5=MD5) -posix_run = abrek.testdef.AbrekTestRunner(RUNSTEPS) +posix_run = TestRunner(RUNSTEPS) posixparser = PosixParser(PATTERN, fixupdict = FIXUPS) -testobj = abrek.testdef.AbrekTest(testname="posixtestsuite", version=VERSION, +testobj = Test(test_id="posixtestsuite", test_version=VERSION, installer=posix_inst, runner=posix_run, parser=posixparser) === modified file 'lava_test/test_definitions/pwrmgmt.py' --- abrek/test_definitions/pwrmgmt.py 2011-09-02 14:28:19 +0000 +++ lava_test/test_definitions/pwrmgmt.py 2011-09-12 09:19:10 +0000 @@ -1,4 +1,4 @@ -# Copyright (c) 2010 Linaro +# Copyright (c) 2010, 2011 Linaro # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -13,33 +13,29 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import abrek.testdef - - -INSTALLSTEPS = ['git clone git://git.linaro.org/tools/pm-qa.git', + +from lava_test.core.installers import TestInstaller +from lava_test.core.parsers import TestParser +from lava_test.core.runners import TestRunner +from lava_test.core.tests import Test + + + +INSTALLSTEPS = ['git clone git://git.linaro.org/people/torez/pm-qa.git', 'cd pm-qa && make clean && make all'] -RUNSTEPS = ['cd pm-qa && make check'] -DEPS = ['git-core', 'make', 'linux-libc-dev'] - -pwrmgmtinst = abrek.testdef.AbrekTestInstaller(INSTALLSTEPS, deps=DEPS) -pwrmgmtrun = abrek.testdef.AbrekTestRunner(RUNSTEPS) - -# test case name is before ":" , the test log is between ":" and "...", the result is after "..." -# Each test case is separated with a test description beginning with "#" - -# Example: -#### -#### cpufreq_02: -#### test the cpufreq framework is available for governor -#### -#cpufreq_02.0/cpu0: checking scaling_available_governors exists... pass -#cpufreq_02.1/cpu0: checking scaling_governor exists... pass -#cpufreq_02.0/cpu1: checking scaling_available_governors exists... pass -#cpufreq_02.1/cpu1: checking scaling_governor exists... pass - -PATTERN = "^(?P[\w/\.]+):\s+(?P.+)\.\.\.\s+(?P\w+)" - -pwrmgmtparser = abrek.testdef.AbrekTestParser(PATTERN) - -testobj = abrek.testdef.AbrekTest(testname="pwrmgmt", installer=pwrmgmtinst, +RUNSTEPS = ['cd pm-qa && awk -f testcases.awk run_template'] +DEPS = ['git-core', 'make', 'alsa-utils', 'pulseaudio-utils', 'lame', 'festival', 'wget'] + +pwrmgmtinst = TestInstaller(INSTALLSTEPS, deps=DEPS) +pwrmgmtrun = TestRunner(RUNSTEPS) + +# test case name is between "pm-qa-" and ":" and results and/or +# measurements are rest of the line +PATTERN = "^pm-qa-(?P\w+):\s+(?P.*)" + + +pwrmgmtparser = TestParser(PATTERN, + appendall={'result':'pass'}) + +testobj = Test(test_id="pwrmgmt", installer=pwrmgmtinst, runner=pwrmgmtrun, parser=pwrmgmtparser) === modified file 'lava_test/test_definitions/pybench.py' --- abrek/test_definitions/pybench.py 2011-04-16 13:13:04 +0000 +++ lava_test/test_definitions/pybench.py 2011-09-12 09:19:10 +0000 @@ -1,4 +1,4 @@ -# Copyright (c) 2010 Linaro +# Copyright (c) 2010, 2011 Linaro # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -20,7 +20,12 @@ standardized way to measure the performance of Python implementations. """ -import abrek.testdef + +from lava_test.core.installers import TestInstaller +from lava_test.core.parsers import TestParser +from lava_test.core.runners import TestRunner +from lava_test.core.tests import Test + VERSION='r27' URL="http://svn.python.org/projects/python/tags/%s/Tools/pybench/" %(VERSION) @@ -29,8 +34,8 @@ RUNSTEPS = ['python pybench/pybench.py'] DEPS = ['subversion'] -my_installer = abrek.testdef.AbrekTestInstaller(INSTALLSTEPS, deps=DEPS) -my_runner = abrek.testdef.AbrekTestRunner(RUNSTEPS) +my_installer = TestInstaller(INSTALLSTEPS, deps=DEPS) +my_runner = TestRunner(RUNSTEPS) # test case name is first column and measurement is average column # @@ -40,9 +45,9 @@ PATTERN = "^\s+(?P\w+):\s+(\d+)ms\s+(?P\d+)ms" -my_parser = abrek.testdef.AbrekTestParser(PATTERN, +my_parser = TestParser(PATTERN, appendall={'units':'ms', 'result':'pass'}) -testobj = abrek.testdef.AbrekTest(testname="pybench", installer=my_installer, +testobj = Test(test_id="pybench", installer=my_installer, runner=my_runner, parser=my_parser) === modified file 'lava_test/test_definitions/smem.py' --- abrek/test_definitions/smem.py 2011-08-03 16:23:05 +0000 +++ lava_test/test_definitions/smem.py 2011-09-12 09:19:10 +0000 @@ -1,4 +1,4 @@ -# Copyright (c) 2011 Linaro +# Copyright (c) 2010, 2011 Linaro # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -13,15 +13,19 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import abrek.testdef +from lava_test.core.installers import TestInstaller +from lava_test.core.parsers import TestParser +from lava_test.core.runners import TestRunner +from lava_test.core.tests import Test + RUNSTEPS = ['smem -w | tail -n 3'] PATTERN = "^(?P(\w+\s)+)\s\s+(?P\d+)" DEPS = ['smem'] -smeminst = abrek.testdef.AbrekTestInstaller(deps=DEPS) -smemrun = abrek.testdef.AbrekTestRunner(RUNSTEPS) -smemparser = abrek.testdef.AbrekTestParser(PATTERN, +smeminst = TestInstaller(deps=DEPS) +smemrun = TestRunner(RUNSTEPS) +smemparser = TestParser(PATTERN, appendall={'units':'KB', 'result':'pass'}) -testobj = abrek.testdef.AbrekTest(testname="smem", installer=smeminst, +testobj = Test(testname="smem", installer=smeminst, runner=smemrun, parser=smemparser) === modified file 'lava_test/test_definitions/stream.py' --- abrek/test_definitions/stream.py 2011-05-04 06:01:58 +0000 +++ lava_test/test_definitions/stream.py 2011-09-12 09:19:10 +0000 @@ -1,4 +1,4 @@ -# Copyright (c) 2010 Linaro +# Copyright (c) 2010, 2011 Linaro # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -13,7 +13,10 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import abrek.testdef +from lava_test.core.installers import TestInstaller +from lava_test.core.parsers import TestParser +from lava_test.core.runners import TestRunner +from lava_test.core.tests import Test URL="http://www.cs.virginia.edu/stream/FTP/Code/stream.c" INSTALLSTEPS = ['cc stream.c -O2 -fopenmp -o stream'] @@ -21,9 +24,11 @@ RUNSTEPS = ['./stream'] PATTERN = "^(?P\w+):\W+(?P\d+\.\d+)" -streaminst = abrek.testdef.AbrekTestInstaller(INSTALLSTEPS, deps=DEPS, url=URL) -streamrun = abrek.testdef.AbrekTestRunner(RUNSTEPS) -streamparser = abrek.testdef.AbrekTestParser(PATTERN, +streaminst = TestInstaller(INSTALLSTEPS, deps=DEPS, url=URL) +streamrun = TestRunner(RUNSTEPS) +streamparser = TestParser(PATTERN, appendall={'units':'MB/s', 'result':'pass'}) -testobj = abrek.testdef.AbrekTest(testname="stream", installer=streaminst, +testobj = Test(test_id="stream", installer=streaminst, runner=streamrun, parser=streamparser) + + === modified file 'lava_test/test_definitions/tiobench.py' --- abrek/test_definitions/tiobench.py 2011-04-15 16:11:24 +0000 +++ lava_test/test_definitions/tiobench.py 2011-09-12 09:19:10 +0000 @@ -1,4 +1,4 @@ -# Copyright (c) 2010 Linaro +# Copyright (c) 2010, 2011 Linaro # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -21,7 +21,12 @@ random read, sequential write, and random write. """ import re -import abrek.testdef + +from lava_test.core.installers import TestInstaller +from lava_test.core.parsers import TestParser +from lava_test.core.runners import TestRunner +from lava_test.core.tests import Test + VERSION="0.3.3" URL="http://prdownloads.sourceforge.net/tiobench/tiobench-%s.tar.gz" %(VERSION) @@ -33,7 +38,7 @@ "--numruns=2" % (VERSION)] -class TIObenchTestParser(abrek.testdef.AbrekTestParser): +class TIObenchTestParser(TestParser): def parse(self): # Pattern to match the test case name pattern1="(?P^(Sequential|Random) (Writes|Reads))" @@ -63,9 +68,9 @@ self.appendtoall(self.appendall) self.fixmeasurements() -tiobench_inst = abrek.testdef.AbrekTestInstaller(INSTALLSTEPS, url=URL, +tiobench_inst = TestInstaller(INSTALLSTEPS, url=URL, md5=MD5) -tiobench_run = abrek.testdef.AbrekTestRunner(RUNSTEPS) +tiobench_run = TestRunner(RUNSTEPS) parse = TIObenchTestParser(appendall={'units':'MB/s', 'result':'pass'}) -testobj = abrek.testdef.AbrekTest(testname="tiobench", version=VERSION, +testobj = Test(test_id="tiobench", test_version=VERSION, installer=tiobench_inst, runner=tiobench_run, parser=parse) === modified file 'lava_test/test_definitions/x11perf.py' --- abrek/test_definitions/x11perf.py 2010-10-18 16:03:50 +0000 +++ lava_test/test_definitions/x11perf.py 2011-09-12 09:19:10 +0000 @@ -1,4 +1,4 @@ -# Copyright (c) 2010 Linaro +# Copyright (c) 2010, 2011 Linaro # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -14,7 +14,12 @@ # along with this program. If not, see . # -import abrek.testdef + +from lava_test.core.installers import TestInstaller +from lava_test.core.parsers import TestParser +from lava_test.core.runners import TestRunner +from lava_test.core.tests import Test + x11perf_options = "-repeat 3" @@ -44,11 +49,11 @@ RUNSTEPS = ["x11perf %s %s" % (x11perf_options, " ".join(x11perf_tests))] PATTERN = "trep @.*\(\W*(?P\d+.\d+)/sec\):\W+(?P.+)" -inst = abrek.testdef.AbrekTestInstaller(deps=["x11-apps"]) -run = abrek.testdef.AbrekTestRunner(RUNSTEPS) -parse = abrek.testdef.AbrekTestParser(PATTERN, +inst = TestInstaller(deps=["x11-apps"]) +run = TestRunner(RUNSTEPS) +parse = TestParser(PATTERN, appendall={'units':'reps/s', 'result':'pass'}) -testobj = abrek.testdef.AbrekTest(testname="x11perf", installer=inst, +testobj = Test(test_id="x11perf", installer=inst, runner=run, parser=parse) === modified file 'lava_test/test_definitions/xrestop.py' --- abrek/test_definitions/xrestop.py 2011-08-17 13:28:09 +0000 +++ lava_test/test_definitions/xrestop.py 2011-09-12 09:19:10 +0000 @@ -1,4 +1,4 @@ -# Copyright (c) 2010 Linaro +# Copyright (c) 2010, 2011 Linaro # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -13,15 +13,18 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import abrek.testdef +from lava_test.core.installers import TestInstaller +from lava_test.core.parsers import TestParser +from lava_test.core.runners import TestRunner +from lava_test.core.tests import Test INSTALLSTEPS = ['bzr branch lp:~linaro-foundations/lava-test/xrestopscript'] RUNSTEPS = ["./xrestopscript/xrestop.sh"] PATTERN = "^(?P\w+):\W+(?P\d+)" -xrestopinst = abrek.testdef.AbrekTestInstaller(INSTALLSTEPS, deps=["xrestop"]) -xrestoprun = abrek.testdef.AbrekTestRunner(RUNSTEPS) -xrestopparser = abrek.testdef.AbrekTestParser(PATTERN, +xrestopinst = TestInstaller(INSTALLSTEPS, deps=["xrestop"]) +xrestoprun = TestRunner(RUNSTEPS) +xrestopparser = TestParser(PATTERN, appendall={'units':'KB', 'result':'pass'}) -testobj = abrek.testdef.AbrekTest(testname="xrestop", installer=xrestopinst, +testobj = Test(testname="xrestop", installer=xrestopinst, runner=xrestoprun, parser=xrestopparser) === modified file 'lava_test/utils.py' --- abrek/utils.py 2011-07-19 16:00:42 +0000 +++ lava_test/utils.py 2011-09-12 09:19:10 +0000 @@ -1,4 +1,4 @@ -# Copyright (c) 2010 Linaro +# Copyright (c) 2010, 2011 Linaro # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -13,10 +13,11 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import contextlib +import hashlib +import logging import os import shutil -import subprocess -import sys import urllib2 import urlparse @@ -25,25 +26,6 @@ _fake_machine = None -class Tee(file): - """ A file-like object that optionally mimics tee functionality. - - By default, output will go to both stdout and the file specified. - Optionally, quiet=True can be used to mute the output to stdout. - """ - def __init__(self, *args, **kwargs): - try: - self.quiet = kwargs.pop('quiet') - except KeyError: - self.quiet = False - super(Tee, self).__init__(*args, **kwargs) - - def write(self, data): - super(Tee, self).write(data) - if self.quiet is False: - sys.stdout.write(data) - - def geturl(url, path=""): urlpath = urlparse.urlsplit(url).path filename = os.path.basename(urlpath) @@ -75,9 +57,8 @@ if _fake_paths is not None: if path in _fake_paths: path = _fake_paths[path] - with open(path) as fd: - data = fd.read() - return data + with open(path, 'rb') as stream: + return stream.read() def fake_file(path, data=None, newpath=None): @@ -119,21 +100,6 @@ _fake_machine = None -def run_and_log(cmd, fd, quiet=False): - """ - Run a command and log the output to fd - """ - proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, shell=True) - while proc.returncode == None: - proc.poll() - data = proc.stdout.readline() - fd.write(data) - if quiet is False: - sys.stdout.write(data) - return proc.returncode - - def get_machine_type(): """ Return the machine type @@ -142,3 +108,123 @@ if _fake_machine is None: return os.uname()[-1] return _fake_machine + + +def mkdir_p(dirname): + if not os.path.exists(dirname): + os.makedirs(dirname) + + +@contextlib.contextmanager +def changed_directory(dirname, make_if_needed=True): + """ + A context manager for running a piece of code in another + directory. The directory is created if needed (by default, can + be changed with make_if_needed). + """ + orig_dir = os.getcwd() + if make_if_needed: + mkdir_p(dirname) + logging.info("Changing directory to %r", dirname) + os.chdir(dirname) + try: + yield + finally: + logging.info("Changing directory to %r", orig_dir) + os.chdir(orig_dir) + + +def merge_dict(merge_into, merge_from): + """ + Merge two dictionaries recursively: + + 1) Simple values are overwritten with a logging.warning() message + 2) Lists are appended + 3) Dictionaries are merged recursively + """ + assert isinstance(merge_into, dict) + assert isinstance(merge_from, dict) + for key in merge_from.iterkeys(): + if key in merge_into: + if (isinstance(merge_from[key], dict) + and isinstance(merge_into[key], dict)): + merge_dict(merge_into[key], merge_from[key]) + elif (isinstance(merge_from[key], list) + and isinstance(merge_into[key], list)): + merge_into[key].extend(merge_from[key]) + else: + logging.warning( + "Overwriting existing value of %r:" + "%r overwritten with %r", + key, merge_into[key], merge_from[key]) + merge_into[key] = merge_from[key] + else: + merge_into[key] = merge_from[key] + + +class Cache(object): + """ + Simple open-cached-URL class + """ + + _instance = None + + def __init__(self): + home = os.environ.get('HOME', '/') + basecache = os.environ.get('XDG_CACHE_HOME', + os.path.join(home, '.cache')) + self.cache_dir = os.path.join(basecache, 'lava_test') + + @classmethod + def get_instance(cls): + if cls._instance is None: + cls._instance = cls() + return cls._instance + + def open_cached(self, key, mode="r"): + """ + Acts like open() but the pathname is relative to the + lava_test-specific cache directory. + """ + if "w" in mode and not os.path.exists(self.cache_dir): + os.makedirs(self.cache_dir) + if os.path.isabs(key): + raise ValueError("key cannot be an absolute path") + try: + stream = open(os.path.join(self.cache_dir, key), mode) + yield stream + finally: + stream.close() + + def _key_for_url(self, url): + return hashlib.sha1(url).hexdigest() + + def _refresh_url_cache(self, key, url): + with contextlib.nested( + contextlib.closing(urllib2.urlopen(url)), + self.open_cached(key, "wb")) as (in_stream, out_stream): + out_stream.write(in_stream.read()) + + @contextlib.contextmanager + def open_cached_url(self, url): + """ + Like urlopen.open() but the content may be cached. + """ + # Do not cache local files, this is not what users would expect + + # workaround - not using cache at all. + # TODO: fix this and use the cache + # if url.startswith("file://"): + if True: + stream = urllib2.urlopen(url) + else: + key = self._key_for_url(url) + try: + stream = self.open_cached(key, "rb") + except IOError: + self._refresh_url_cache(key, url) + stream = self.open_cached(key, "rb") + try: + yield stream + finally: + stream.close() === modified file 'setup.py' --- setup.py 2011-07-21 16:26:12 +0000 +++ setup.py 2011-09-12 09:19:10 +0000 @@ -16,21 +16,34 @@ # along with this program. If not, see . from setuptools import setup, find_packages -from abrek import __version__ as version setup( name='lava-test', - version=version, + version=":versiontools:lava_test:", author='Linaro Validation Team', author_email='linaro-dev@lists.linaro.org', url='https://launchpad.net/lava-test', - description='Lava test execution framework', + description='LAVA test execution framework', long_description=open("README").read(), packages=find_packages(exclude=['tests']), license="GNU GPLv3", test_suite='tests.test_suite', - scripts = ['bin/lava-test'], + entry_points=""" + [console_scripts] + lava-test=lava_test.main:main + [lava_test.commands] + version=lava_test.commands:version + list-tests=lava_test.commands:list_tests + list-installed=lava_test.commands:list_installed + install=lava_test.commands:install + uninstall=lava_test.commands:uninstall + run=lava_test.commands:run + parse=lava_test.commands:parse + show=lava_test.commands:show + register_test=lava_test.commands:register_test + unregister_test=lava_test.commands:unregister_test + """, classifiers=[ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", @@ -39,6 +52,13 @@ "Programming Language :: Python :: 2.6", "Topic :: Software Development :: Testing", ], + install_requires=[ + 'lava-tool >= 0.2', + 'versiontools >= 1.4', + 'linaro_dashboard_bundle', + ], + setup_requires=[ + 'versiontools >= 1.4' + ], zip_safe=False, include_package_data=True) - === modified file 'tests/__init__.py' --- tests/__init__.py 2010-10-15 15:05:18 +0000 +++ tests/__init__.py 2011-09-12 09:19:10 +0000 @@ -1,4 +1,4 @@ -# Copyright (c) 2010 Linaro +# Copyright (c) 2010, 2011 Linaro # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -16,16 +16,12 @@ import unittest def test_suite(): - module_names = ['tests.test_abrekcmd', - 'tests.test_abrektest', - 'tests.test_abrektestinstaller', - 'tests.test_abrektestparser', - 'tests.test_abrektestrunner', - 'tests.test_builtins', - 'tests.test_dashboard', + module_names = ['tests.test_lavatest_commands', + 'tests.test_lavatest_test', + 'tests.test_lavatest_testinstaller', + 'tests.test_lavatest_testparser', + 'tests.test_lavatest_testrunner', 'tests.test_hwprofile', - 'tests.test_main', - 'tests.test_results', 'tests.test_swprofile'] loader = unittest.TestLoader() suite = loader.loadTestsFromNames(module_names) === modified file 'tests/fixtures.py' --- tests/fixtures.py 2010-09-10 17:09:14 +0000 +++ tests/fixtures.py 2011-09-12 09:19:10 +0000 @@ -1,4 +1,4 @@ -# Copyright (c) 2010 Linaro +# Copyright (c) 2010, 2011 Linaro # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by === modified file 'tests/imposters.py' --- tests/imposters.py 2011-06-28 12:51:57 +0000 +++ tests/imposters.py 2011-09-12 09:19:10 +0000 @@ -1,4 +1,4 @@ -# Copyright (c) 2010 Linaro +# Copyright (c) 2010, 2011 Linaro # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -19,7 +19,7 @@ import tempfile import StringIO -from abrek.config import set_config +from lava_test.core.config import set_config class OutputImposter(object): @@ -43,16 +43,17 @@ self.installdir = os.path.join(basedir, "install") self.resultsdir = os.path.join(basedir, "results") self.registry = { - "format": "Abrek Test Registry 1.0 Experimental", + "format": "LAVA Test Test Registry 1.0", "providers": [ { - "entry_point": "abrek.providers:BuiltInProvider", - }, - { - "entry_point": "abrek.providers:PkgResourcesProvider", - }, - { - "entry_point": "abrek.providers:RegistryProvider", + "entry_point": "lava_test.core.providers:BuiltInProvider" + }, + { + "entry_point": "lava_test.core.providers:PkgResourcesProvider", + "config": {"namespace": "lava_test.test_definitions" } + }, + { + "entry_point": "lava_test.core.providers:RegistryProvider", "config": { "entries": [] } === removed file 'tests/test_abrekcmd.py' --- tests/test_abrekcmd.py 2011-08-03 14:23:20 +0000 +++ tests/test_abrekcmd.py 1970-01-01 00:00:00 +0000 @@ -1,137 +0,0 @@ -# Copyright (c) 2010 Linaro -# -# This program 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 3 of the License, or -# (at your option) any later version. -# -# This program 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 this program. If not, see . - -import unittest -from optparse import make_option -from abrek.command import ( - AbrekCmd, - AbrekCmdWithSubcommands, - get_command, - get_all_cmds, - ) - - -class testAbrekCmd(unittest.TestCase): - def test_empty_run(self): - cmd = AbrekCmd() - self.assertRaises(NotImplementedError, cmd.run) - - def test_name(self): - class cmd_test_name(AbrekCmd): - pass - cmd = cmd_test_name() - self.assertEqual("test-name", cmd.name()) - - def test_help(self): - class cmd_test_help(AbrekCmd): - """Test Help""" - pass - expected_str = 'Usage: lava-test test-help\n\nOptions:\n -h, ' + \ - '--help show this help message and exit\n\n' + \ - 'Description:\nTest Help' - cmd = cmd_test_help() - self.assertEqual(expected_str, cmd.help()) - - def test_no_help(self): - class cmd_test_no_help(AbrekCmd): - pass - expected_str = 'Usage: lava-test test-no-help\n\nOptions:\n -h, ' + \ - '--help show this help message and exit' - cmd = cmd_test_no_help() - self.assertEqual(expected_str, cmd.help()) - - def test_get_command(self): - cmd = get_command("install") - self.assertTrue(isinstance(cmd, AbrekCmd)) - - def test_get_all_cmds(self): - cmds = get_all_cmds() - self.assertTrue("install" in cmds) - - def test_arglist(self): - expected_str = 'Usage: lava-test arglist FOO' - class cmd_arglist(AbrekCmd): - arglist = ['*foo'] - pass - cmd = cmd_arglist() - self.assertTrue(expected_str in cmd.help()) - - def test_options(self): - expected_str = '-b BAR, --bar=BAR' - class cmd_options(AbrekCmd): - options = [make_option("-b", "--bar", dest="bar")] - pass - cmd = cmd_options() - self.assertTrue(expected_str in cmd.help()) - - def test_subcmds(self): - expected_str = 'Available sub-commands:\n foo' - - class cmd_test_subcmds(AbrekCmdWithSubcommands): - """Help for test-subcmds.""" - class cmd_foo(AbrekCmd): - pass - cmd = cmd_test_subcmds() - self.assertTrue( - expected_str in cmd.help() - and 'Help for test-subcmds.' in cmd.help()) - - def test_subcmds_run(self): - expected_str = "subcmd test str" - - class cmd_test_subcmds(AbrekCmdWithSubcommands): - class cmd_foo(AbrekCmd): - def run(self): - return expected_str - cmd = cmd_test_subcmds() - argv = ['foo'] - self.assertEqual(expected_str, cmd.main(argv)) - - def test_subcmds_name(self): - expected_str = "subcmd test str" - - class cmd_test_subcmds(AbrekCmdWithSubcommands): - class cmd_foo(AbrekCmd): - def run(self): - return expected_str - cmd = cmd_test_subcmds().get_subcommand('foo') - self.assertEqual('test-subcmds foo', cmd.name()) - - def test_subcmds_help(self): - expected_str = "subcmd test str" - - class cmd_test_subcmds(AbrekCmdWithSubcommands): - class cmd_foo(AbrekCmd): - """Help for foo.""" - def run(self): - return expected_str - cmd = cmd_test_subcmds().get_subcommand('foo') - self.assertTrue( - 'test-subcmds foo' in cmd.help() - and 'Help for foo.' in cmd.help()) - - def test_subcmd_strip_argv(self): - """ - Make sure that the argv list is stripped after calling the subcmd - """ - - class cmd_test_subcmds(AbrekCmdWithSubcommands): - class cmd_foo(AbrekCmd): - def main(self, argv): - return len(argv) - cmd = cmd_test_subcmds() - argv = ['foo'] - self.assertEqual(0, cmd.main(argv)) - === removed file 'tests/test_abrektest.py' --- tests/test_abrektest.py 2011-06-07 21:18:06 +0000 +++ tests/test_abrektest.py 1970-01-01 00:00:00 +0000 @@ -1,48 +0,0 @@ -# Copyright (c) 2010 Linaro -# -# This program 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 3 of the License, or -# (at your option) any later version. -# -# This program 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 this program. If not, see . - -import re - -from abrek.testdef import AbrekTest, AbrekTestInstaller, AbrekTestRunner -from imposters import OutputImposter, ConfigImposter -from fixtures import TestCaseWithFixtures - -def maketest(name="foo", version="", installer=None, runner=None, parser=None): - if installer is None: - installer = makeinstaller() - return AbrekTest(name, version, installer, runner, parser) - -def makerunner(**kwargs): - return AbrekTestRunner(**kwargs) - -def makeinstaller(**kwargs): - return AbrekTestInstaller(**kwargs) - -class AbrekTestConfigOutput(TestCaseWithFixtures): - def setUp(self): - super(AbrekTestConfigOutput, self).setUp() - self.config = self.add_fixture(ConfigImposter()) - self.out = self.add_fixture(OutputImposter()) - - def test_run(self): - testrunner = makerunner(steps=["echo foo"]) - test = maketest(name="foo", runner=testrunner) - test.install() - test.run() - self.assertEqual("foo", self.out.getvalue().splitlines()[0]) - completion_message = self.out.getvalue().splitlines()[1] - completion_pattern = "ABREK TEST RUN COMPLETE: Result id is 'foo\d+\.0'" - self.assertTrue(re.match(completion_pattern, completion_message)) - === removed file 'tests/test_abrektestinstaller.py' --- tests/test_abrektestinstaller.py 2010-09-10 17:09:14 +0000 +++ tests/test_abrektestinstaller.py 1970-01-01 00:00:00 +0000 @@ -1,60 +0,0 @@ -# Copyright (c) 2010 Linaro -# -# This program 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 3 of the License, or -# (at your option) any later version. -# -# This program 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 this program. If not, see . - -import hashlib -import os -import shutil -import tempfile -import unittest - -from abrek.testdef import AbrekTestInstaller - - -class testAbrekTestInstaller(unittest.TestCase): - def setUp(self): - self.origdir = os.path.abspath(os.curdir) - self.tmpdir = tempfile.mkdtemp() - self.filename = os.path.abspath(__file__) - os.chdir(self.tmpdir) - - def tearDown(self): - os.chdir(self.origdir) - shutil.rmtree(self.tmpdir) - - def makeinstaller(self,**kwargs): - return AbrekTestInstaller(**kwargs) - - def test_bad_download(self): - url = "file:///xxxyyyzzz" - installer = self.makeinstaller(url=url) - self.assertRaises(RuntimeError, installer._download) - - def test_bad_md5(self): - url = "file://%s" % self.filename - installer = self.makeinstaller(url=url, md5='foo') - self.assertRaises(RuntimeError, installer._download) - - def test_good_md5(self): - url = "file://%s" % self.filename - md5 = hashlib.md5(file(self.filename).read()).hexdigest() - installer = self.makeinstaller(url=url, md5=md5) - location = installer._download() - self.assertTrue(os.path.exists(location)) - - def test_runsteps(self): - steps = ["echo test > foo"] - installer = self.makeinstaller(steps=steps) - installer._runsteps() - self.assertTrue(os.path.exists("./foo")) === removed file 'tests/test_abrektestparser.py' --- tests/test_abrektestparser.py 2010-09-10 17:09:14 +0000 +++ tests/test_abrektestparser.py 1970-01-01 00:00:00 +0000 @@ -1,65 +0,0 @@ -# Copyright (c) 2010 Linaro -# -# This program 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 3 of the License, or -# (at your option) any later version. -# -# This program 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 this program. If not, see . - -import os -import shutil -import tempfile -import unittest - -from abrek.testdef import AbrekTestParser - - -class testAbrekTestParser(unittest.TestCase): - def setUp(self): - self.origdir = os.path.abspath(os.curdir) - self.tmpdir = tempfile.mkdtemp() - self.filename = os.path.abspath(__file__) - os.chdir(self.tmpdir) - - def tearDown(self): - os.chdir(self.origdir) - shutil.rmtree(self.tmpdir) - - def makeparser(self, *args, **kwargs): - return AbrekTestParser(*args, **kwargs) - - def writeoutputlog(self, str): - with open("testoutput.log", "a") as fd: - fd.write(str) - - def test_parse(self): - pattern = "^(?P\w+):\W+(?P\w+)" - self.writeoutputlog("test001: pass") - parser = self.makeparser(pattern) - parser.parse() - self.assertTrue(parser.results["test_results"][0]["testid"] == "test001" and - parser.results["test_results"][0]["result"] == "pass") - - def test_fixupdict(self): - pattern = "^(?P\w+):\W+(?P\w+)" - fixup = {"pass":"PASS"} - self.writeoutputlog("test001: pass") - parser = self.makeparser(pattern, fixupdict=fixup) - parser.parse() - self.assertEquals("PASS", parser.results["test_results"][0]["result"]) - - def test_appendall(self): - pattern = "^(?P\w+):\W+(?P\w+)" - append = {"units":"foo/s"} - self.writeoutputlog("test001: pass") - parser = self.makeparser(pattern, appendall=append) - parser.parse() - self.assertEqual("foo/s", parser.results["test_results"][0]["units"]) - === removed file 'tests/test_abrektestrunner.py' --- tests/test_abrektestrunner.py 2010-09-21 22:39:58 +0000 +++ tests/test_abrektestrunner.py 1970-01-01 00:00:00 +0000 @@ -1,104 +0,0 @@ -# Copyright (c) 2010 Linaro -# -# This program 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 3 of the License, or -# (at your option) any later version. -# -# This program 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 this program. If not, see . - -import os -import shutil -import tempfile -import unittest -from datetime import datetime - -from abrek.testdef import AbrekTestRunner -from imposters import OutputImposter -from fixtures import TestCaseWithFixtures - -def makerunner(**kwargs): - return AbrekTestRunner(**kwargs) - -class testAbrekTestRunner(unittest.TestCase): - def setUp(self): - self.origdir = os.path.abspath(os.curdir) - self.tmpdir = tempfile.mkdtemp() - self.filename = os.path.abspath(__file__) - os.chdir(self.tmpdir) - - def tearDown(self): - os.chdir(self.origdir) - shutil.rmtree(self.tmpdir) - - def test_starttime(self): - runner = makerunner() - runner.run(self.tmpdir) - self.assertTrue(isinstance(runner.starttime, datetime)) - - def test_endtime(self): - runner = makerunner() - runner.run(self.tmpdir) - self.assertTrue(isinstance(runner.endtime, datetime)) - - def test_timediff(self): - steps = ['sleep 2'] - runner = makerunner(steps=steps) - runner.run(self.tmpdir) - self.assertNotEqual(runner.starttime, runner.endtime) - - def test_runsteps(self): - steps = ["echo test > foo"] - runner = makerunner(steps=steps) - runner._runsteps(self.tmpdir) - self.assertTrue(os.path.exists("./foo")) - - def test_logoutput(self): - steps = ["echo test > foo"] - runner = makerunner(steps=steps) - runner._runsteps(self.tmpdir) - self.assertTrue(os.path.exists("./testoutput.log")) - -class testAbrekTestRunnerVerbosity(TestCaseWithFixtures): - def setUp(self): - super(testAbrekTestRunnerVerbosity, self).setUp() - self.origdir = os.path.abspath(os.curdir) - self.tmpdir = tempfile.mkdtemp() - self.filename = os.path.abspath(__file__) - os.chdir(self.tmpdir) - self.out = self.add_fixture(OutputImposter()) - - def tearDown(self): - super(testAbrekTestRunnerVerbosity, self).tearDown() - os.chdir(self.origdir) - shutil.rmtree(self.tmpdir) - - def test_runsteps_quiet_true(self): - steps = ["echo test"] - runner = makerunner(steps=steps) - runner._runsteps(self.tmpdir, quiet=True) - self.assertEqual("", self.out.getvalue().strip()) - - def test_runsteps_quiet_false(self): - steps = ["echo test"] - runner = makerunner(steps=steps) - runner._runsteps(self.tmpdir, quiet=False) - self.assertEqual("test", self.out.getvalue().strip()) - - def test_run_quiet_true(self): - steps = ["echo test"] - runner = makerunner(steps=steps) - runner.run(self.tmpdir, quiet=True) - self.assertEqual("", self.out.getvalue().strip()) - - def test_run_quiet_false(self): - steps = ["echo test"] - runner = makerunner(steps=steps) - runner.run(self.tmpdir, quiet=False) - self.assertEqual("test", self.out.getvalue().strip()) === removed file 'tests/test_builtins.py' --- tests/test_builtins.py 2010-10-12 02:42:16 +0000 +++ tests/test_builtins.py 1970-01-01 00:00:00 +0000 @@ -1,66 +0,0 @@ -# Copyright (c) 2010 Linaro -# -# This program 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 3 of the License, or -# (at your option) any later version. -# -# This program 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 this program. If not, see . - -import os - -import abrek.builtins -from imposters import ConfigImposter, OutputImposter -from fixtures import TestCaseWithFixtures - - -class ListKnown(TestCaseWithFixtures): - def test_list_tests(self): - out = self.add_fixture(OutputImposter()) - cmd = abrek.builtins.cmd_list_tests() - cmd.run() - self.assertTrue("stream" in out.getvalue()) - -class ListInstalled(TestCaseWithFixtures): - def test_list_installed(self): - config = self.add_fixture(ConfigImposter()) - out = self.add_fixture(OutputImposter()) - test_name="test_list_installed000" - os.makedirs(os.path.join(config.installdir, test_name)) - cmd = abrek.builtins.cmd_list_installed() - cmd.run() - self.assertTrue(test_name in out.getvalue()) - -class TestHelp(TestCaseWithFixtures): - - def test_command_help(self): - out = self.add_fixture(OutputImposter()) - abrek.builtins.cmd_help().main(['results']) - self.assertEqual( - abrek.results.cmd_results().help() + '\n', out.getvalue()) - - def test_subcommand_help(self): - out = self.add_fixture(OutputImposter()) - abrek.builtins.cmd_help().main(['results', 'list']) - self.assertEqual( - abrek.results.cmd_results().get_subcommand('list').help() + '\n', - out.getvalue()) - - def test_bad_command(self): - out = self.add_fixture(OutputImposter()) - abrek.builtins.cmd_help().main(['foo']) - self.assertEqual( - "No command found for 'foo'\n", out.getvalue()) - - def test_bad_subcommand(self): - out = self.add_fixture(OutputImposter()) - abrek.builtins.cmd_help().main(['results', 'foo']) - self.assertEqual( - "No sub-command of 'results' found for 'foo'\n", - out.getvalue()) === removed file 'tests/test_dashboard.py' --- tests/test_dashboard.py 2011-08-17 22:32:50 +0000 +++ tests/test_dashboard.py 1970-01-01 00:00:00 +0000 @@ -1,213 +0,0 @@ -# Copyright (c) 2010 Linaro -# -# This program 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 3 of the License, or -# (at your option) any later version. -# -# This program 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 this program. If not, see . - -import json -import os -from uuid import uuid4 -from abrek.dashboard import ( - DashboardConfig, - cmd_dashboard, - ) -from imposters import ConfigImposter, OutputImposter -from fixtures import TestCaseWithFixtures - - -class SetupTests(TestCaseWithFixtures): - def setUp(self): - super(SetupTests, self).setUp() - self.config = self.add_fixture(ConfigImposter()) - - def test_dashboard_setup(self): - host, user, passwd = setup_dashboard() - conf = DashboardConfig() - self.assertEqual(host, conf.host) - self.assertEqual(user, conf.user) - self.assertEqual(passwd, conf.password) - - -class SetupOutputTests(TestCaseWithFixtures): - def setUp(self): - super(SetupOutputTests, self).setUp() - self.out = self.add_fixture(OutputImposter()) - - def test_dashboard_setup_noserver(self): - errmsg = "You must specify a server" - cmd = cmd_dashboard.cmd_setup() - self.assertRaises(SystemExit, cmd.main, argv=[]) - self.assertEqual(errmsg, self.out.getvalue().strip()) - - -class BundleOutputTests(TestCaseWithFixtures): - def setUp(self): - super(BundleOutputTests, self).setUp() - self.out = self.add_fixture(OutputImposter()) - - def test_dashboard_bundle_badresult(self): - errmsg = "Result directory not found" - cmd = cmd_dashboard.cmd_bundle() - self.assertRaises(SystemExit, cmd.main, argv=['badresult']) - self.assertEqual(errmsg, self.out.getvalue().strip()) - - def test_dashboard_bundle_noresult(self): - errmsg = "You must specify a result" - cmd = cmd_dashboard.cmd_bundle() - self.assertRaises(SystemExit, cmd.main, argv=[]) - self.assertEqual(errmsg, self.out.getvalue().strip()) - -class BundleConfigOutputTests(TestCaseWithFixtures): - def setUp(self): - super(BundleConfigOutputTests, self).setUp() - self.config = self.add_fixture(ConfigImposter()) - self.out = self.add_fixture(OutputImposter()) - - def test_dashboard_bundle_good(self): - cmd = cmd_dashboard.cmd_bundle() - (testname, testuuid) = make_stream_result(self.config) - expected_dict = { - "format": "Dashboard Bundle Format 1.2", - "test_runs": [{ - "analyzer_assigned_date": "2010-10-10T00:00:00Z", - "analyzer_assigned_uuid": testuuid, - "hardware_context": { - "devices": [] - }, - "software_context": {}, - "test_id": "stream", - "test_results": [{ - "measurement": 1111.1111, - "result": "pass", - "test_case_id": "Copy", - "units": "MB/s", - "log_filename": "testoutput.log", - "log_lineno": 3 - }, - { - "measurement": 2222.2222, - "result": "pass", - "test_case_id": "Scale", - "units": "MB/s", - "log_filename": "testoutput.log", - "log_lineno": 4 - }, - { - "measurement": 3333.3333, - "result": "pass", - "test_case_id": "Add", - "units": "MB/s", - "log_filename": "testoutput.log", - "log_lineno": 5 - }, - { - "measurement": 4444.4444, - "result": "pass", - "test_case_id": "Triad", - "units": "MB/s", - "log_filename": "testoutput.log", - "log_lineno": 6 - }], - "time_check_performed": False, - "attachments": [ - { - "mime_type": "text/plain", - "pathname": "testoutput.log", - "content": "CkZ1bmN0aW9uICAgICAgUmF0ZSAoTUIvcykgICBBdmcgdGltZSAgICAgTWluIHRpbWUgICAgIE1heCB0aW1lCkNvcHk6ICAgICAgICAxMTExLjExMTEgICAgICAgMC4wMTgwICAgICAgIDAuMDExMiAgICAgICAwLjAyNDIKU2NhbGU6ICAgICAgIDIyMjIuMjIyMiAgICAgICAwLjAxOTggICAgICAgMC4wMTIyICAgICAgIDAuMDI0MwpBZGQ6ICAgICAgICAgMzMzMy4zMzMzICAgICAgIDAuMDIwMSAgICAgICAwLjAxNzYgICAgICAgMC4wMjIzClRyaWFkOiAgICAgICA0NDQ0LjQ0NDQgICAgICAgMC4wMTk3ICAgICAgIDAuMDEzOCAgICAgICAwLjAyMjMK" - }] - }] - } - cmd.main(argv=[testname]) - returned_dict = json.loads(self.out.getvalue()) - self.assertEqual(expected_dict, returned_dict) - - -class PutConfigOutputTests(TestCaseWithFixtures): - def setUp(self): - super(PutConfigOutputTests, self).setUp() - self.config = self.add_fixture(ConfigImposter()) - self.out = self.add_fixture(OutputImposter()) - - def test_put_nosetup(self): - testname, testuuid = make_stream_result(self.config) - errmsg = "Error connecting to server, please run 'abrek dashboard " \ - "setup [host]'" - args = ["put", "somestream", testname] - cmd = cmd_dashboard() - self.assertRaises(SystemExit, cmd.main, argv=args) - self.assertEqual(errmsg, self.out.getvalue().strip()) - - def test_put_badhost(self): - testname, testuuid = make_stream_result(self.config) - host, user, passwd = setup_dashboard(host = "http://badhost.foo") - errmsg = "Unable to connect to host: [Errno -2] Name or service " \ - "not known" - args = ["put", "somestream", testname] - cmd = cmd_dashboard() - self.assertRaises(SystemExit, cmd.main, argv=args) - self.assertEqual(errmsg, self.out.getvalue().strip()) - - -class PutOutputTests(TestCaseWithFixtures): - def setUp(self): - super(PutOutputTests, self).setUp() - self.out = self.add_fixture(OutputImposter()) - - def test_put_noargs(self): - errmsg = "You must specify a stream and a result" - cmd = cmd_dashboard() - self.assertRaises(SystemExit, cmd.main, argv=["put"]) - self.assertEqual(errmsg, self.out.getvalue().strip()) - - -def make_stream_result(config): - """ - Make a fake set of test results for the stream test - """ - testname = "stream000" - testuuid = str(uuid4()) - testdata_data = """ -{ -"format": "Dashboard Bundle Format 1.2", -"test_runs": [{ - "analyzer_assigned_date": "2010-10-10T00:00:00Z", - "analyzer_assigned_uuid": "%s", - "hardware_context": { - "devices": [] - }, - "software_context": {}, - "test_id": "stream", - "time_check_performed": false, - "test_results": [] - }] -} -""" % testuuid - testoutput_data = """ -Function Rate (MB/s) Avg time Min time Max time -Copy: 1111.1111 0.0180 0.0112 0.0242 -Scale: 2222.2222 0.0198 0.0122 0.0243 -Add: 3333.3333 0.0201 0.0176 0.0223 -Triad: 4444.4444 0.0197 0.0138 0.0223 -""" - result_dir = os.path.join(config.resultsdir, testname) - os.makedirs(result_dir) - with open(os.path.join(result_dir, "testdata.json"), "w") as fd: - fd.write(testdata_data) - with open(os.path.join(result_dir, "testoutput.log"), "w") as fd: - fd.write(testoutput_data) - return (testname, testuuid) - -def setup_dashboard(host="http://localhost:8080", user="foo", passwd="baz"): - args = ["setup", host, "-u", user, "-p", passwd] - cmd = cmd_dashboard() - cmd.main(argv=args) - return host, user, passwd === modified file 'tests/test_hwprofile.py' --- tests/test_hwprofile.py 2011-04-07 05:26:43 +0000 +++ tests/test_hwprofile.py 2011-09-12 09:19:10 +0000 @@ -1,4 +1,4 @@ -# Copyright (c) 2010 Linaro +# Copyright (c) 2010, 2011 Linaro # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -16,10 +16,10 @@ import os import unittest -import abrek.hwprofile -from abrek.utils import fake_file, clear_fakes, fake_machine, clear_fake_machine -from imposters import OutputImposter -from fixtures import TestCaseWithFixtures +import lava_test.core.hwprofile +from lava_test.utils import fake_file, clear_fakes, fake_machine, clear_fake_machine +from tests.imposters import OutputImposter +from tests.fixtures import TestCaseWithFixtures class AptCache: @@ -81,7 +81,7 @@ def test_get_cpu_devs_arm(self): fake_file('/proc/cpuinfo', ARM_CPUINFO_FILE) fake_machine('arm') - devs = abrek.hwprofile.get_cpu_devs() + devs = lava_test.core.hwprofile.get_cpu_devs() clear_fake_machine() cpuinfo = { 'attributes': { @@ -113,7 +113,7 @@ 'vendor': 'YYYYYYY'}, 'description': 'XXXXXXX', 'device_type': 'device.board'} - devs = abrek.hwprofile.get_board_devs() + devs = lava_test.core.hwprofile.get_board_devs() clear_fake_machine() self.assertEqual(boardinfo, devs[0]) @@ -123,13 +123,13 @@ boardinfo = { 'description': 'OMAP3 Beagle Board', 'device_type': 'device.board'} - devs = abrek.hwprofile.get_board_devs() + devs = lava_test.core.hwprofile.get_board_devs() clear_fake_machine() self.assertEqual(boardinfo, devs[0]) def test_get_mem_devs(self): fake_file('/proc/meminfo', FAKE_MEMINFO_FILE) - devs = abrek.hwprofile.get_mem_devs() + devs = lava_test.core.hwprofile.get_mem_devs() meminfo = { 'attributes': { 'kind': 'RAM', @@ -139,7 +139,7 @@ self.assertEqual(meminfo, devs[0]) def test_get_usb_devs(self): - devs = abrek.hwprofile.get_usb_devs() + devs = lava_test.core.hwprofile.get_usb_devs() self.assertEqual('device.usb', devs[0]['device_type']) @@ -156,7 +156,7 @@ def test_bad_cpuinfo(self): errmsg = "WARNING: Could not read cpu information\n" fake_file('/proc/cpuinfo', newpath='/foo/bar') - devs = abrek.hwprofile.get_cpu_devs() + devs = lava_test.core.hwprofile.get_cpu_devs() self.assertEqual([], devs) self.assertEqual(errmsg, self.out.getvalue()) @@ -165,7 +165,7 @@ errmsg = "WARNING: Could not read board information\n" fake_file('/sys/class/dmi/id/board_name', newpath='/foo/bar') fake_file('/proc/cpuinfo', newpath='/foo/bar') - devs = abrek.hwprofile.get_board_devs() + devs = lava_test.core.hwprofile.get_board_devs() clear_fake_machine() self.assertEqual([], devs) self.assertEqual(errmsg, self.out.getvalue()) @@ -173,7 +173,7 @@ def test_bad_meminfo(self): errmsg = "WARNING: Could not read memory information\n" fake_file('/proc/meminfo', newpath='/foo/bar') - devs = abrek.hwprofile.get_mem_devs() + devs = lava_test.core.hwprofile.get_mem_devs() self.assertEqual([], devs) self.assertEqual(errmsg, self.out.getvalue()) === added file 'tests/test_lavatest_commands.py' --- tests/test_lavatest_commands.py 1970-01-01 00:00:00 +0000 +++ tests/test_lavatest_commands.py 2011-09-12 09:19:10 +0000 @@ -0,0 +1,62 @@ +# Copyright (c) 2010, 2011 Linaro +# +# This program 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 3 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see . + +import os, re + +from tests.imposters import ConfigImposter, OutputImposter +from tests.fixtures import TestCaseWithFixtures +from lava_test.main import LAVATestDispatcher + +class LavaTestCommandTestCase(TestCaseWithFixtures): + def setUp(self): + self.config = self.add_fixture(ConfigImposter()) + self.out = self.add_fixture(OutputImposter()) + + def _runLavaTest(self, cmds): + LAVATestDispatcher().dispatch(cmds) + +class BadCommand(LavaTestCommandTestCase): + def test_bad_cmd(self): + # Running an unknown command that does not exist of a command that does + # gives a nice error message. + errmsg = "invalid choice: 'results'" + + self.assertRaises(SystemExit, LAVATestDispatcher().dispatch, ['results', 'foo']) + self.assertNotEqual(None, re.search(errmsg, self.out.getvalue()), re.MULTILINE) + self.assertTrue(errmsg in self.out.getvalue()) + +class ListKnown(LavaTestCommandTestCase): + def test_list_tests(self): + self._runLavaTest(['list-tests']) + self.assertTrue("stream" in self.out.getvalue()) + +class ListInstalled(LavaTestCommandTestCase): + def test_list_installed(self): + # test_name must be in the BuiltInProvider._builtin_tests + test_name="ltp" + os.makedirs(os.path.join(self.config.installdir, test_name)) + self._runLavaTest(['list-installed']) + self.assertTrue(test_name in self.out.getvalue()) + + def test_run_command_test_not_exist(self): + self._runLavaTest(['run','abc']) + self.assertTrue("There is no test with the specified ID" in self.out.getvalue()) + +class TestHelp(LavaTestCommandTestCase): + + def test_command_help(self): + self.assertRaises(SystemExit, LAVATestDispatcher().dispatch, ['--help']) + self.assertTrue("--help" in self.out.getvalue()) + === added file 'tests/test_lavatest_test.py' --- tests/test_lavatest_test.py 1970-01-01 00:00:00 +0000 +++ tests/test_lavatest_test.py 2011-09-12 09:19:10 +0000 @@ -0,0 +1,55 @@ +# Copyright (c) 2010, 2011 Linaro +# +# This program 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 3 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see . + +import re + +from lava_test.core.installers import TestInstaller +from lava_test.core.tests import Test +from lava_test.core.runners import TestRunner +from tests.imposters import OutputImposter, ConfigImposter +from tests.fixtures import TestCaseWithFixtures + +def maketest(name="foo", version="", installer=None, runner=None, parser=None): + if installer is None: + installer = makeinstaller() + return Test(name, version, installer, runner, parser) + +def makerunner(**kwargs): + return TestRunner(**kwargs) + +def makeinstaller(**kwargs): + return TestInstaller(**kwargs) + +class TestConfigOutput(TestCaseWithFixtures): + def setUp(self): + super(TestConfigOutput, self).setUp() + self.config = self.add_fixture(ConfigImposter()) + + def test_run(self): + testrunner = makerunner(steps=["echo foo"]) + test = maketest(name="foo", runner=testrunner) + + self.assertFalse(test.is_installed) + test.install() + self.assertTrue(test.is_installed) + artifacts = test.run() + std_out = open(artifacts.stdout_pathname).read() + self.assertTrue("foo" in std_out) + + result_id_pattern = "foo\.\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z" + self.assertTrue(re.match(result_id_pattern, artifacts.result_id)) + + test.uninstall() + self.assertFalse(test.is_installed) === added file 'tests/test_lavatest_testinstaller.py' --- tests/test_lavatest_testinstaller.py 1970-01-01 00:00:00 +0000 +++ tests/test_lavatest_testinstaller.py 2011-09-12 09:19:10 +0000 @@ -0,0 +1,63 @@ +# Copyright (c) 2010, 2011 Linaro +# +# This program 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 3 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see . + +import hashlib +import os +import shutil +import tempfile +import unittest + +from lava_test.core.installers import TestInstaller + + +class testTestInstaller(unittest.TestCase): + def setUp(self): + self.origdir = os.path.abspath(os.curdir) + self.tmpdir = tempfile.mkdtemp() + self.filename = os.path.abspath(__file__) + os.chdir(self.tmpdir) + + def tearDown(self): + os.chdir(self.origdir) + shutil.rmtree(self.tmpdir) + + def makeinstaller(self,**kwargs): + return TestInstaller(**kwargs) + + def test_bad_download(self): + url = "file:///xxxyyyzzz" + installer = self.makeinstaller(url=url) + self.assertRaises(RuntimeError, installer._download, None) + + def test_bad_md5(self): + url = "file://%s" % self.filename + installer = self.makeinstaller(url=url, md5='foo') + self.assertRaises(RuntimeError, installer._download, None) + + def test_good_md5(self): + url = "file://%s" % self.filename + md5 = hashlib.md5(file(self.filename).read()).hexdigest() + installer = self.makeinstaller(url=url, md5=md5) + location = installer._download(observer=None) + self.assertTrue(os.path.exists(location)) + + def test_runsteps(self): + self.assertFalse(os.path.exists("./foo")) + steps = ["echo test > foo"] + installer = self.makeinstaller(steps=steps) + installer._runsteps(observer=None) + self.assertTrue(os.path.exists("./foo")) + self.assertTrue("test" in open("./foo").read()) + === added file 'tests/test_lavatest_testparser.py' --- tests/test_lavatest_testparser.py 1970-01-01 00:00:00 +0000 +++ tests/test_lavatest_testparser.py 2011-09-12 09:19:10 +0000 @@ -0,0 +1,70 @@ +# Copyright (c) 2010, 2011 Linaro +# +# This program 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 3 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see . + +import os +import shutil +import tempfile +import unittest + +from lava_test.core.parsers import TestParser +from tests.fixtures import TestCaseWithFixtures +from lava_test.core.artifacts import TestArtifacts +from tests.imposters import OutputImposter, ConfigImposter + +class testTestParser(TestCaseWithFixtures): + def setUp(self): + self.config = self.add_fixture(ConfigImposter()) + self.out = self.add_fixture(OutputImposter()) + self.origdir = os.path.abspath(os.curdir) + self.filename = os.path.abspath(__file__) + self.test_id = "ABC" + self.artifacts = TestArtifacts.allocate(self.test_id, self.config) + os.chdir(self.artifacts.results_dir) + + def tearDown(self): + os.chdir(self.origdir) + shutil.rmtree(self.artifacts.results_dir) + + def makeparser(self, *args, **kwargs): + return TestParser(*args, **kwargs) + + def writeoutputlog(self, str): + with open("testoutput.log", "a") as fd: + fd.write(str) + + def test_parse(self): + pattern = "^(?P\w+):\W+(?P\w+)" + self.writeoutputlog("test001: pass") + parser = self.makeparser(pattern) + parser.parse(self.artifacts) + self.assertTrue(parser.results["test_results"][0]["testid"] == "test001" and + parser.results["test_results"][0]["result"] == "pass") + + def test_fixupdict(self): + pattern = "^(?P\w+):\W+(?P\w+)" + fixup = {"pass":"PASS"} + self.writeoutputlog("test001: pass") + parser = self.makeparser(pattern, fixupdict=fixup) + parser.parse(self.artifacts) + self.assertEquals("PASS", parser.results["test_results"][0]["result"]) + + def test_appendall(self): + pattern = "^(?P\w+):\W+(?P\w+)" + append = {"units":"foo/s"} + self.writeoutputlog("test001: pass") + parser = self.makeparser(pattern, appendall=append) + parser.parse(self.artifacts) + self.assertEqual("foo/s", parser.results["test_results"][0]["units"]) + === added file 'tests/test_lavatest_testrunner.py' --- tests/test_lavatest_testrunner.py 1970-01-01 00:00:00 +0000 +++ tests/test_lavatest_testrunner.py 2011-09-12 09:19:10 +0000 @@ -0,0 +1,73 @@ +# Copyright (c) 2010, 2011 Linaro +# +# This program 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 3 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see . + +import os +import shutil +import tempfile +import unittest +from datetime import datetime + +from lava_test.core.runners import TestRunner +from lava_test.core.artifacts import TestArtifacts +from tests.imposters import OutputImposter, ConfigImposter +from tests.fixtures import TestCaseWithFixtures + +def makerunner(**kwargs): + return TestRunner(**kwargs) + +class testTestRunner(TestCaseWithFixtures): + def setUp(self): + self.config = self.add_fixture(ConfigImposter()) + self.out = self.add_fixture(OutputImposter()) + self.origdir = os.path.abspath(os.curdir) + self.tmpdir = tempfile.mkdtemp() + self.filename = os.path.abspath(__file__) + os.chdir(self.tmpdir) + self.test_id = "ABC" + self.artifacts = TestArtifacts.allocate(self.test_id, self.config) + + def tearDown(self): + os.chdir(self.origdir) + shutil.rmtree(self.tmpdir) + + def test_starttime(self): + runner = makerunner() + runner.run(self.artifacts) + self.assertTrue(isinstance(runner.starttime, datetime)) + + def test_endtime(self): + runner = makerunner() + runner.run(self.artifacts) + self.assertTrue(isinstance(runner.endtime, datetime)) + + def test_timediff(self): + steps = ['sleep 2'] + runner = makerunner(steps=steps) + runner.run(self.artifacts) + self.assertNotEqual(runner.starttime, runner.endtime) + + def test_runsteps(self): + steps = ["echo test > foo"] + runner = makerunner(steps=steps) + runner._run_lava_test_steps(self.artifacts, observer = None) + self.assertTrue(os.path.exists("./foo")) + + def test_logoutput(self): + steps = ["echo test > foo"] + runner = makerunner(steps=steps) + runner._run_lava_test_steps(self.artifacts, observer = None) + self.assertTrue(os.path.exists(self.artifacts.stdout_pathname)) + self.assertTrue(os.path.exists(self.artifacts.stderr_pathname)) + === removed file 'tests/test_main.py' --- tests/test_main.py 2010-10-12 02:02:35 +0000 +++ tests/test_main.py 1970-01-01 00:00:00 +0000 @@ -1,32 +0,0 @@ -# Copyright (c) 2010 Linaro -# -# This program 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 3 of the License, or -# (at your option) any later version. -# -# This program 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 this program. If not, see . - -from abrek.main import main -from imposters import OutputImposter -from fixtures import TestCaseWithFixtures - - -class testMain(TestCaseWithFixtures): - def setUp(self): - super(testMain, self).setUp() - self.out = self.add_fixture(OutputImposter()) - - def test_bad_subcmd(self): - # Running a subcommand that does not exist of a command that does - # gives a nice error message. - errmsg = "'foo' not found as a sub-command of 'results'" - main(['./abrek', 'results', 'foo']) - self.assertEqual(errmsg, self.out.getvalue().splitlines()[0]) - === removed file 'tests/test_results.py' --- tests/test_results.py 2010-10-12 00:24:57 +0000 +++ tests/test_results.py 1970-01-01 00:00:00 +0000 @@ -1,117 +0,0 @@ -# Copyright (c) 2010 Linaro -# -# This program 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 3 of the License, or -# (at your option) any later version. -# -# This program 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 this program. If not, see . - -import os - -from abrek.results import cmd_results -from abrek.utils import write_file -from imposters import ConfigImposter, OutputImposter -from fixtures import TestCaseWithFixtures - - -class ResultsTests(TestCaseWithFixtures): - def setUp(self): - super(ResultsTests, self).setUp() - self.config = self.add_fixture(ConfigImposter()) - self.out = self.add_fixture(OutputImposter()) - - def test_results_list(self): - result_name = "test_results_list000" - os.makedirs(os.path.join(self.config.resultsdir, result_name)) - cmd = cmd_results.cmd_list() - cmd.run() - self.assertTrue(result_name in self.out.getvalue()) - - def test_results_list_nodir(self): - errmsg = "No results found" - cmd = cmd_results.cmd_list() - cmd.run() - self.assertTrue(errmsg in self.out.getvalue()) - - def test_results_show(self): - result_name = "test_results_show000" - result_output = "test result output" - result_dir = os.path.join(self.config.resultsdir, result_name) - os.makedirs(result_dir) - outputfile = os.path.join(result_dir, 'testoutput.log') - write_file(result_output, outputfile) - cmd = cmd_results.cmd_show() - cmd.main(argv=[result_name]) - self.assertEqual(result_output, self.out.getvalue().strip()) - - def test_results_show_noarg(self): - errmsg = "please specify the name of the result dir" - cmd = cmd_results.cmd_show() - self.assertRaises(SystemExit, cmd.main, argv=[]) - self.assertEqual(errmsg, self.out.getvalue().strip()) - - def test_results_show_nodir(self): - testname = "foo" - errmsg = "No result found for '%s'" % testname - cmd = cmd_results.cmd_show() - self.assertRaises(SystemExit, cmd.main, argv=[testname]) - self.assertEqual(errmsg, self.out.getvalue().strip()) - - def test_results_remove(self): - result_name = "test_results_remove000" - result_dir = os.path.join(self.config.resultsdir, result_name) - os.makedirs(result_dir) - cmd = cmd_results.cmd_remove() - cmd.main(argv=[result_name, '-f']) - self.assertFalse(os.path.exists(result_dir)) - - def test_results_remove_noarg(self): - errmsg = "please specify the name of the result dir" - cmd = cmd_results.cmd_remove() - self.assertRaises(SystemExit, cmd.main, argv=[]) - self.assertEqual(errmsg, self.out.getvalue().strip()) - - def test_results_remove_nodir(self): - testname = "foo" - errmsg = "No result found for '%s'" % testname - cmd = cmd_results.cmd_remove() - self.assertRaises(SystemExit, cmd.main, argv=[testname]) - self.assertEqual(errmsg, self.out.getvalue().strip()) - - def test_results_rename(self): - result_src = "test_results_old" - result_dest = "test_results_new" - result_srcdir = os.path.join(self.config.resultsdir, result_src) - result_destdir = os.path.join(self.config.resultsdir, result_dest) - os.makedirs(result_srcdir) - cmd = cmd_results.cmd_rename() - cmd.main(argv=[result_src, result_dest]) - self.assertFalse(os.path.exists(result_srcdir)) - self.assertTrue(os.path.exists(result_destdir)) - - def test_results_rename_badsrc(self): - errmsg = "Result directory not found" - result_src = "test_results_old" - result_dest = "test_results_new" - cmd = cmd_results.cmd_rename() - self.assertRaises(SystemExit, cmd.main, argv=[result_src, result_dest]) - self.assertEqual(errmsg, self.out.getvalue().strip()) - - def test_results_rename_baddest(self): - errmsg = "Destination result name already exists" - result_src = "test_results_old" - result_dest = "test_results_new" - result_srcdir = os.path.join(self.config.resultsdir, result_src) - result_destdir = os.path.join(self.config.resultsdir, result_dest) - os.makedirs(result_srcdir) - os.makedirs(result_destdir) - cmd = cmd_results.cmd_rename() - self.assertRaises(SystemExit, cmd.main, argv=[result_src, result_dest]) - self.assertEqual(errmsg, self.out.getvalue().strip()) === modified file 'tests/test_swprofile.py' --- tests/test_swprofile.py 2011-04-07 05:26:43 +0000 +++ tests/test_swprofile.py 2011-09-12 09:19:10 +0000 @@ -1,4 +1,4 @@ -# Copyright (c) 2010 Linaro +# Copyright (c) 2010, 2011 Linaro # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -16,8 +16,8 @@ import os import unittest -import abrek.swprofile -from abrek.utils import fake_file, clear_fakes +import lava_test.core.swprofile +from lava_test.utils import fake_file, clear_fakes class Version: @@ -49,7 +49,7 @@ cache = self.cache if info == None: info = self.lsb_information - return abrek.swprofile.get_software_context(apt_cache=cache, + return lava_test.core.swprofile.get_software_context(apt_cache=cache, lsb_information=info) def test_pkg_name(self):