def __init__(self, pav_cfg, tests, _id=None): """Initialize the suite. :param pav_cfg: The pavilion configuration object. :param list tests: The list of test objects that belong to this suite. :param int _id: The test id number. If this is given, it implies that we're regenerating this suite from saved files. """ self.pav_cfg = pav_cfg self.tests = {test.id: test for test in tests} if not tests: raise SuiteError("You cannot create a suite of zero tests.") suites_path = os.path.join(self.pav_cfg.working_dir, 'suites') # We're creating this suite from scratch. if _id is None: # Get the suite id and path. try: self.id, self.path = utils.create_id_dir(suites_path) except (OSError, TimeoutError) as err: raise SuiteError( "Could not get id or suite directory in '{}': {}".format( suites_path, err)) # Create a soft link to the test directory of each test in the # suite. for test in tests: link_path = utils.make_id_path(self.path, test.id) try: os.symlink(test.path, link_path) except OSError as err: raise SuiteError( "Could not link test '{}' in suite at '{}': {}".format( test.path, link_path, err)) # Save the last suite we created to the .pavilion directory # in the user's home dir. Pavilion commands can use this so the # user doesn't actually have to know the suite_id of tests. try: user_pav_dir = os.path.expanduser('~/.pavilion') if not os.path.exists(user_pav_dir): os.mkdir(user_pav_dir) last_suite_fn = os.path.join(user_pav_dir, 'last_suite') with open(last_suite_fn, 'w') as last_suite_file: last_suite_file.write(str(self.id)) except (IOError, OSError): # It's ok if we can't write this file. pass else: self.id = _id self.path = utils.make_id_path(suites_path, self.id) self._logger = logging.getLogger(self.LOGGER_FMT.format(self.id))
def create_id_dir(id_dir): """In the given directory, create the lowest numbered (positive integer) directory that doesn't already exist. :param Path id_dir: Path to the directory that contains these 'id' directories :returns: The id and path to the created directory. :rtype: list(int, Path) :raises OSError: on directory creation failure. :raises TimeoutError: If we couldn't get the lock in time. """ lockfile_path = id_dir / '.lockfile' with lockfile.LockFile(lockfile_path, timeout=1): ids = list(os.listdir(str(id_dir))) # Only return the test directories that could be integers. ids = [id_ for id_ in ids if id_.isdigit()] ids = [id_ for id_ in ids if (id_dir / id_).is_dir()] ids = [int(id_) for id_ in ids] ids.sort() # Find the first unused id. id_ = 1 while id_ in ids: id_ += 1 path = utils.make_id_path(id_dir, id_) path.mkdir() return id_, path
def run(self, pav_cfg, args): """List the run directory for the given run.""" test_dir = pav_cfg.working_dir / 'test_runs' job_dir = utils.make_id_path(test_dir, args.job_id) if os.path.isdir(job_dir.as_posix()) is False: output.fprint("directory '{}' does not exist.".format(job_dir), file=sys.stderr, color=output.RED) return errno.EEXIST if args.path is True: output.fprint(job_dir) return 0 output.fprint(str(job_dir) + ':', file=sys.stdout) if args.tree is True: level = 0 self.tree_(level, job_dir) return 0 if args.subdir: return self.ls_(job_dir / args.subdir[0]) else: return self.ls_(job_dir)
def from_id(cls, pav_cfg, id_): suites_path = os.path.join(pav_cfg.working_dir, 'suites') suite_path = utils.make_id_path(suites_path, id_) if not os.path.exists(suite_path): raise SuiteError("No such suite found: '{}' at '{}'".format( id_, suite_path)) logger = logging.getLogger(cls.LOGGER_FMT.format(id_)) tests = [] for path in os.listdir(suite_path): link_path = os.path.join(suite_path, path) if os.path.islink(link_path) and os.path.isdir(link_path): try: test_id = int(os.path.basename(link_path)) except ValueError: logger.info( "Bad test id in suite from dir '{}'".format(link_path)) continue tests.append(PavTest.from_id(pav_cfg, test_id=test_id)) else: logger.info("Polluted suite directory in suite '{}'".format( suite_path)) raise ValueError(link_path) return cls(pav_cfg, tests, _id=id_)
def from_id(cls, pav_cfg, id_): series_path = pav_cfg.working_dir/'series' series_path = utils.make_id_path(series_path, id_) if not series_path.exists(): raise TestSeriesError("No such series found: '{}' at '{}'" .format(id_, series_path)) logger = logging.getLogger(cls.LOGGER_FMT.format(id_)) tests = [] for path in os.listdir(str(series_path)): link_path = series_path/path if link_path.is_symlink() and link_path.is_dir(): try: test_id = int(link_path.name) except ValueError: logger.info( "Bad test id in series from dir '%s'", link_path) continue tests.append(PavTest.load(pav_cfg, test_id=test_id)) else: logger.info("Polluted series directory in series '%s'", series_path) raise ValueError(link_path) return cls(pav_cfg, tests, _id=id_)
def __init__(self, pav_cfg, tests, _id=None): """Initialize the series. :param pav_cfg: The pavilion configuration object. :param list tests: The list of test objects that belong to this series. :param int _id: The test id number. If this is given, it implies that we're regenerating this series from saved files. """ self.pav_cfg = pav_cfg self.tests = {test.id: test for test in tests} if not tests: raise TestSeriesError("You cannot create a series of zero tests.") series_path = self.pav_cfg.working_dir/'series' # We're creating this series from scratch. if _id is None: # Get the series id and path. try: self._id, self.path = PavTest.create_id_dir(series_path) except (OSError, TimeoutError) as err: raise TestSeriesError( "Could not get id or series directory in '{}': {}" .format(series_path, err)) # Create a soft link to the test directory of each test in the # series. for test in tests: link_path = utils.make_id_path(self.path, test.id) try: link_path.symlink_to(test.path) except OSError as err: raise TestSeriesError( "Could not link test '{}' in series at '{}': {}" .format(test.path, link_path, err)) self._save_series_id() else: self._id = _id self.path = utils.make_id_path(series_path, self._id) self._logger = logging.getLogger(self.LOGGER_FMT.format(self._id))
def run(self, pav_cfg, args): test_dir = pav_cfg.working_dir / 'test_runs' job_dir = utils.make_id_path(test_dir, args.job_id) if os.path.isdir(job_dir.as_posix()) is False: output.fprint("directory '{}' does not exist." .format(job_dir.as_posix()), file=sys.stderr, color=output.RED) return errno.EEXIST level = 0 print_directory(level, job_dir) return 0
def run(self, pav_cfg, args): """Run this command.""" test_dir = pav_cfg.working_dir / 'test_runs' job_dir = utils.make_id_path(test_dir, args.job_id) if os.path.isdir(job_dir.as_posix()) is False: output.fprint("directory '{}' does not exist.".format( job_dir.as_posix()), file=sys.stderr, color=output.RED) return errno.EEXIST return print_file(job_dir / args.file)
def from_id(cls, pav_cfg, test_id): """Load a new PavTest object based on id.""" path = utils.make_id_path(os.path.join(pav_cfg.working_dir, 'tests'), test_id) if not os.path.isdir(path): raise PavTestError("Test directory for test id {} does not exist " "at '{}' as expected." .format(test_id, path)) config = cls._load_config(path) return PavTest(pav_cfg, config, test_id)
def load(cls, pav_cfg, test_id): """Load an old PavTest object given a test id. :param pav_cfg: The pavilion config :param int test_id: The test's id number. """ path = utils.make_id_path(pav_cfg.working_dir / 'tests', test_id) if not path.is_dir(): raise PavTestError("Test directory for test id {} does not exist " "at '{}' as expected.".format(test_id, path)) config = cls._load_config(path) return PavTest(pav_cfg, config, None, _id=test_id)
def from_id(cls, pav_cfg, id_): """Load a series object from the given id, along with all of its associated tests.""" try: id_ = int(id_[1:]) except TypeError as err: pass series_path = pav_cfg.working_dir/'series' series_path = utils.make_id_path(series_path, id_) if not series_path.exists(): raise TestSeriesError("No such series found: '{}' at '{}'" .format(id_, series_path)) logger = logging.getLogger(cls.LOGGER_FMT.format(id_)) tests = [] for path in os.listdir(str(series_path)): link_path = series_path/path if link_path.is_symlink() and link_path.is_dir(): try: test_id = int(link_path.name) except ValueError: logger.info( "Bad test id in series from dir '%s'", link_path) continue try: tests.append(TestRun.load(pav_cfg, test_id=test_id)) except TestRunError as err: logger.info( "Error loading test %s: %s", test_id, err ) else: logger.info("Polluted series directory in series '%s'", series_path) raise ValueError(link_path) return cls(pav_cfg, tests, _id=id_)
def __init__(self, pav_cfg, config, var_man=None, _id=None): """Create an new TestRun object. If loading an existing test instance, use the ``TestRun.from_id()`` method. :param pav_cfg: The pavilion configuration. :param dict config: The test configuration dictionary. :param variables.VariableSetManager var_man: The variable set manager for this test. :param int _id: The test id of an existing test. (You should be using TestRun.load). """ # Just about every method needs this self._pav_cfg = pav_cfg self.load_ok = True # Compute the actual name of test, using the subtitle config parameter. self.name = '.'.join([ config.get('suite', '<unknown>'), config.get('name', '<unnamed>') ]) if 'subtitle' in config and config['subtitle']: self.name = self.name + '.' + config['subtitle'] self.scheduler = config['scheduler'] # Create the tests directory if it doesn't already exist. tests_path = pav_cfg.working_dir / 'test_runs' self.config = config self.id = None # pylint: disable=invalid-name # Get an id for the test, if we weren't given one. if _id is None: self.id, self.path = self.create_id_dir(tests_path) self._save_config() self.var_man = var_man self.var_man.save(self.path / 'variables') else: self.id = _id self.path = utils.make_id_path(tests_path, self.id) if not self.path.is_dir(): raise TestRunNotFoundError( "No test with id '{}' could be found.".format(self.id)) try: self.var_man = variables.VariableSetManager.load(self.path / 'variables') except RuntimeError as err: raise TestRunError(*err.args) # Set a logger more specific to this test. self.logger = logging.getLogger('pav.TestRun.{}'.format(self.id)) # This will be set by the scheduler self._job_id = None # Setup the initial status file. self.status = StatusFile(self.path / 'status') if _id is None: self.status.set(STATES.CREATED, "Test directory and status file created.") self._started = None self._finished = None self.build_path = None # type: Path self.build_name = None self.build_hash = None # type: str self.build_origin = None # type: Path self.run_log = self.path / 'run.log' self.results_path = self.path / 'results.json' build_config = self.config.get('build', {}) # make sure build source_download_name is not set without # source_location try: if build_config['source_download_name'] is not None: if build_config['source_location'] is None: msg = "Test could not be build. Need 'source_location'." fprint(msg) self.status.set( STATES.BUILD_ERROR, "'source_download_name is set without a " "'source_location'") raise TestConfigError(msg) except KeyError: # this is mostly for unit tests that create test configs without a # build section at all pass self.build_script_path = self.path / 'build.sh' # type: Path self.build_path = self.path / 'build' if _id is None: self._write_script(path=self.build_script_path, config=build_config) if _id is None: self.build_hash = self._create_build_hash(build_config) with (self.path / 'build_hash').open('w') as build_hash_file: build_hash_file.write(self.build_hash) else: build_hash_fn = self.path / 'build_hash' if build_hash_fn.exists(): with build_hash_fn.open() as build_hash_file: self.build_hash = build_hash_file.read() if self.build_hash is not None: short_hash = self.build_hash[:self.BUILD_HASH_BYTES * 2] self.build_name = '{hash}'.format(hash=short_hash) self.build_origin = pav_cfg.working_dir / 'builds' / self.build_name run_config = self.config.get('run', {}) self.run_tmpl_path = self.path / 'run.tmpl' self.run_script_path = self.path / 'run.sh' if _id is None: self._write_script(path=self.run_tmpl_path, config=run_config) if _id is None: self.status.set(STATES.CREATED, "Test directory setup complete.") # Checking validity of timeout values. for loc in ['build', 'run']: if loc in config and 'timeout' in config[loc]: try: if config[loc]['timeout'] is None: test_timeout = None else: test_timeout = int(config[loc]['timeout']) if test_timeout < 0: raise ValueError() except ValueError: raise TestRunError( "{} timeout must be a non-negative " "integer or empty. Received {}.".format( loc, config[loc]['timeout'])) else: if loc == 'build': self._build_timeout = test_timeout else: self._run_timeout = test_timeout
def __init__(self, pav_cfg, config, sys_vars, _id=None): """Create an new PavTest object. If loading an existing test instance, use the PavTest.from_id method. :param pav_cfg: The pavilion configuration. :param config: The test configuration dictionary. :param Union(dict, None) sys_vars: System variables. :param _id: The test id of an existing test. (You should be using PavTest.load). """ if _id is None and sys_vars is None: raise RuntimeError("New PavTest objects require a sys_vars dict. ") # Just about every method needs this self._pav_cfg = pav_cfg # Compute the actual name of test, using the subtest config parameter. self.name = config['name'] if 'subtest' in config and config['subtest']: self.name = self.name + '.' + config['subtest'] self.scheduler = config['scheduler'] # Create the tests directory if it doesn't already exist. tests_path = pav_cfg.working_dir / 'tests' self.config = config self.id = None # pylint: disable=invalid-name # Get an id for the test, if we weren't given one. if _id is None: self.id, self.path = self.create_id_dir(tests_path) self._save_config() else: self.id = _id self.path = utils.make_id_path(tests_path, self.id) if not self.path.is_dir(): raise PavTestNotFoundError( "No test with id '{}' could be found.".format(self.id)) # Set a logger more specific to this test. self.logger = logging.getLogger('pav.PavTest.{}'.format(self.id)) # This will be set by the scheduler self._job_id = None # Setup the initial status file. self.status = StatusFile(self.path / 'status') if _id is None: self.status.set(STATES.CREATED, "Test directory and status file created.") self._started = None self._finished = None self.build_path = None # type: Path self.build_name = None self.build_hash = None # type: str self.build_script_path = None # type: Path self.build_origin = None # type: Path self.run_log = self.path / 'run.log' self.results_path = self.path / 'results.json' build_config = self.config.get('build', {}) self.build_script_path = self.path / 'build.sh' if not self.build_script_path.exists(): self._write_script(self.build_script_path, build_config, sys_vars) self.build_path = self.path / 'build' if self.build_path.is_symlink(): build_rp = self.build_path.resolve() self.build_hash = build_rp.name else: self.build_hash = self._create_build_hash(build_config) short_hash = self.build_hash[:self.BUILD_HASH_BYTES * 2] self.build_name = '{hash}'.format(hash=short_hash) self.build_origin = pav_cfg.working_dir / 'builds' / self.build_name run_config = self.config.get('run', {}) if run_config: self.run_tmpl_path = self.path / 'run.tmpl' self.run_script_path = self.path / 'run.sh' if not self.run_tmpl_path.exists(): self._write_script(self.run_tmpl_path, run_config, sys_vars) else: self.run_tmpl_path = None self.run_script_path = None if _id is None: self.status.set(STATES.CREATED, "Test directory setup complete.")
def __init__(self, pav_cfg, config, test_id=None): """Create an new PavTest object. If loading an existing test instance, use the PavTest.from_id method. :param pav_cfg: The pavilion configuration. :param config: The test configuration dictionary. :param test_id: The test id (for an existing test). """ # Just about every method needs this self._pav_cfg = pav_cfg # Compute the actual name of test, using the subtest config parameter. self.name = config['name'] if 'subtest' in config and config['subtest']: self.name = self.name + '.' + config['subtest'] # Create the tests directory if it doesn't already exist. tests_path = os.path.join(pav_cfg.working_dir, 'tests') self.config = config # Get an id for the test, if we weren't given one. if test_id is None: self.id, self.path = utils.create_id_dir(tests_path) self._save_config() else: self.id = test_id self.path = utils.make_id_path(tests_path, self.id) if not os.path.isdir(self.path): raise PavTestNotFoundError( "No test with id '{}' could be found.".format(self.id)) # Set a logger more specific to this test. self.LOGGER = logging.getLogger('pav.PavTest.{}'.format(self.id)) # This will be set by the scheduler self._job_id = None # Setup the initial status file. self.status = StatusFile(os.path.join(self.path, 'status')) self.status.set(STATES.CREATED, "Test directory and status file created.") self.build_path = None self.build_name = None self.build_hash = None self.build_script_path = None build_config = self.config.get('build', {}) if build_config: self.build_path = os.path.join(self.path, 'build') if os.path.islink(self.build_path): build_rp = os.path.realpath(self.build_path) build_fn = os.path.basename(build_rp) self.build_hash = build_fn.split('-')[-1] else: self.build_hash = self._create_build_hash(build_config) short_hash = self.build_hash[:self.BUILD_HASH_BYTES*2] self.build_name = '{hash}'.format(hash=short_hash) self.build_origin = os.path.join(pav_cfg.working_dir, 'builds', self.build_name) self.build_script_path = os.path.join(self.path, 'build.sh') self._write_script(self.build_script_path, build_config) run_config = self.config.get('run', {}) if run_config: self.run_tmpl_path = os.path.join(self.path, 'run.tmpl') self.run_script_path = os.path.join(self.path, 'run.sh') self._write_script(self.run_tmpl_path, run_config) else: self.run_tmpl_path = None self.run_script_path = None self.status.set(STATES.CREATED, "Test directory setup complete.")
def __init__(self, pav_cfg, config, build_tracker=None, var_man=None, _id=None, rebuild=False, build_only=False): """Create an new TestRun object. If loading an existing test instance, use the ``TestRun.from_id()`` method. :param pav_cfg: The pavilion configuration. :param dict config: The test configuration dictionary. :param builder.MultiBuildTracker build_tracker: Tracker for watching and managing the status of multiple builds. :param variables.VariableSetManager var_man: The variable set manager for this test. :param bool build_only: Only build this test run, do not run it. :param bool rebuild: After determining the build name, deprecate it and select a new, non-deprecated build. :param int _id: The test id of an existing test. (You should be using TestRun.load). """ # Just about every method needs this self._pav_cfg = pav_cfg self.load_ok = True self.scheduler = config['scheduler'] # Create the tests directory if it doesn't already exist. tests_path = pav_cfg.working_dir/'test_runs' self.config = config self.id = None # pylint: disable=invalid-name self._attrs = {} # Mark the run to build locally. self.build_local = config.get('build', {}) \ .get('on_nodes', 'false').lower() != 'true' # If a test access group was given, make sure it exists and the # current user is a member. self.group = config.get('group') if self.group is not None: try: group_data = grp.getgrnam(self.group) user = utils.get_login() if self.group != user and user not in group_data.gr_mem: raise TestConfigError( "Test specified group '{}', but the current user '{}' " "is not a member of that group." .format(self.group, user)) except KeyError as err: raise TestConfigError( "Test specified group '{}', but that group does not " "exist on this system. {}" .format(self.group, err)) self.umask = config.get('umask') if self.umask is not None: try: self.umask = int(self.umask, 8) except ValueError: raise RuntimeError( "Invalid umask. This should have been enforced by the " "by the config format.") self.build_only = build_only self.rebuild = rebuild self.suite_path = None if self.config.get('suite_path') is not None: try: self.suite_path = Path(self.config['suite_path']) except ValueError: pass # Get an id for the test, if we weren't given one. if _id is None: self.id, self.path = self.create_id_dir(tests_path) with PermissionsManager(self.path, self.group, self.umask): self._save_config() if var_man is None: var_man = variables.VariableSetManager() self.var_man = var_man self._variables_path = self.path / 'variables' self.var_man.save(self._variables_path) self.save_attributes() else: self.id = _id self.path = utils.make_id_path(tests_path, self.id) self._variables_path = self.path / 'variables' if not self.path.is_dir(): raise TestRunNotFoundError( "No test with id '{}' could be found.".format(self.id)) try: self.var_man = variables.VariableSetManager.load( self._variables_path ) except RuntimeError as err: raise TestRunError(*err.args) self.load_attributes() name_parts = [ self.config.get('suite', '<unknown>'), self.config.get('name', '<unnamed>'), ] subtitle = self.config.get('subtitle') # Don't add undefined or empty subtitles. if subtitle: name_parts.append(subtitle) self.name = '.'.join(name_parts) # Set a logger more specific to this test. self.logger = logging.getLogger('pav.TestRun.{}'.format(self.id)) # This will be set by the scheduler self._job_id = None with PermissionsManager(self.path/'status', self.group, self.umask): # Setup the initial status file. self.status = StatusFile(self.path/'status') if _id is None: self.status.set(STATES.CREATED, "Test directory and status file created.") self.run_timeout = self.parse_timeout( 'run', config.get('run', {}).get('timeout')) self.build_timeout = self.parse_timeout( 'build', config.get('build', {}).get('timeout')) self._attributes = {} self.build_name = None self.run_log = self.path/'run.log' self.results_path = self.path/'results.json' self.build_origin_path = self.path/'build_origin' build_config = self.config.get('build', {}) if (build_config.get('source_path') is None and build_config.get('source_url') is not None): raise TestConfigError( "Build source_url specified, but not a source_path.") self.build_script_path = self.path/'build.sh' # type: Path self.build_path = self.path/'build' if _id is None: self._write_script( 'build', path=self.build_script_path, config=build_config) build_name = None self._build_name_fn = self.path / 'build_name' if _id is not None: build_name = self._load_build_name() try: self.builder = builder.TestBuilder( pav_cfg=pav_cfg, test=self, mb_tracker=build_tracker, build_name=build_name ) except builder.TestBuilderError as err: raise TestRunError( "Could not create builder for test {s.name} (run {s.id}): {err}" .format(s=self, err=err) ) self.save_build_name() run_config = self.config.get('run', {}) self.run_tmpl_path = self.path/'run.tmpl' self.run_script_path = self.path/'run.sh' if _id is None: self._write_script( 'run', path=self.run_tmpl_path, config=run_config) if _id is None: self.status.set(STATES.CREATED, "Test directory setup complete.") self._results = None self._created = None self.skipped = self._get_skipped()
def __init__(self, pav_cfg, config, build_tracker=None, var_man=None, _id=None, **options): """Create an new TestRun object. If loading an existing test instance, use the ``TestRun.from_id()`` method. :param pav_cfg: The pavilion configuration. :param dict config: The test configuration dictionary. :param builder.MultiBuildTracker build_tracker: Tracker for watching and managing the status of multiple builds. :param variables.VariableSetManager var_man: The variable set manager for this test. :param bool build_only: Only build this test run, do not run it. :param bool rebuild: After determining the build name, deprecate it and select a new, non-deprecated build. :param int _id: The test id of an existing test. (You should be using TestRun.load). """ # Just about every method needs this self._pav_cfg = pav_cfg self.load_ok = True # Compute the actual name of test, using the subtitle config parameter. self.name = '.'.join([ config.get('suite', '<unknown>'), config.get('name', '<unnamed>') ]) if 'subtitle' in config and config['subtitle']: self.name = self.name + '.' + config['subtitle'] self.scheduler = config['scheduler'] # Create the tests directory if it doesn't already exist. tests_path = pav_cfg.working_dir / 'test_runs' self.config = config self.id = None # pylint: disable=invalid-name # Mark the run to build locally. self.build_local = config.get('build', {}) \ .get('on_nodes', 'false').lower() != 'true' # Get an id for the test, if we weren't given one. if _id is None: self.id, self.path = self.create_id_dir(tests_path) self._save_config() if var_man is None: var_man = variables.VariableSetManager() self.var_man = var_man self._variables_path = self.path / 'variables' self.var_man.save(self._variables_path) self.opts = TestRunOptions(**options) self.opts.save(self) else: self.id = _id self.path = utils.make_id_path(tests_path, self.id) self._variables_path = self.path / 'variables' if not self.path.is_dir(): raise TestRunNotFoundError( "No test with id '{}' could be found.".format(self.id)) try: self.var_man = variables.VariableSetManager.load( self._variables_path) except RuntimeError as err: raise TestRunError(*err.args) self.opts = TestRunOptions.load(self) # Set a logger more specific to this test. self.logger = logging.getLogger('pav.TestRun.{}'.format(self.id)) # This will be set by the scheduler self._job_id = None # Setup the initial status file. self.status = StatusFile(self.path / 'status') if _id is None: self.status.set(STATES.CREATED, "Test directory and status file created.") self.run_timeout = self.parse_timeout( 'run', config.get('run', {}).get('timeout')) self.build_timeout = self.parse_timeout( 'build', config.get('build', {}).get('timeout')) self._started = None self._finished = None self.build_name = None self.run_log = self.path / 'run.log' self.results_path = self.path / 'results.json' self.build_origin_path = self.path / 'build_origin' build_config = self.config.get('build', {}) # make sure build source_download_name is not set without # source_location try: if build_config['source_download_name'] is not None: if build_config['source_location'] is None: msg = "Test could not be built. Need 'source_location'." self.status.set( STATES.BUILD_ERROR, "'source_download_name is set without a " "'source_location'") raise TestConfigError(msg) except KeyError: # this is mostly for unit tests that create test configs without a # build section at all pass self.build_script_path = self.path / 'build.sh' # type: Path self.build_path = self.path / 'build' if _id is None: self._write_script('build', path=self.build_script_path, config=build_config) build_name = None self._build_name_fn = self.path / 'build_name' if _id is not None: build_name = self._load_build_name() try: self.builder = builder.TestBuilder(pav_cfg=pav_cfg, test=self, mb_tracker=build_tracker, build_name=build_name) except builder.TestBuilderError as err: raise TestRunError( "Could not create builder for test {s.name} (run {s.id}): {err}" .format(s=self, err=err)) self.save_build_name() run_config = self.config.get('run', {}) self.run_tmpl_path = self.path / 'run.tmpl' self.run_script_path = self.path / 'run.sh' if _id is None: self._write_script('run', path=self.run_tmpl_path, config=run_config) if _id is None: self.status.set(STATES.CREATED, "Test directory setup complete.") self._results = None self._created = None