def _get_matching_job_list(self, ns, job_list): logger.debug("_get_matching_job_list(%r, %r)", ns, job_list) qualifier_list = [] # Add whitelists for whitelist_file in ns.whitelist: qualifier = self.get_whitelist_from_file( whitelist_file.name, whitelist_file) if qualifier is not None: qualifier_list.append(qualifier) # Add all the --include jobs for pattern in ns.include_pattern_list: try: qualifier = RegExpJobQualifier( '^{}$'.format(pattern), inclusive=True) except Exception as exc: logger.warning( _("Incorrect pattern %r: %s"), pattern, exc) else: qualifier_list.append(qualifier) # Add all the --exclude jobs for pattern in ns.exclude_pattern_list: try: qualifier = RegExpJobQualifier( '^{}$'.format(pattern), inclusive=False) except Exception as exc: logger.warning( _("Incorrect pattern %r: %s"), pattern, exc) else: qualifier_list.append(qualifier) logger.debug("select_jobs(%r, %r)", job_list, qualifier_list) return select_jobs(job_list, qualifier_list)
def _get_matching_job_list(self, ns, job_list): logger.debug("_get_matching_job_list(%r, %r)", ns, job_list) qualifier_list = [] # Add whitelists for whitelist_file in ns.whitelist: qualifier = self.get_whitelist_from_file(whitelist_file.name, whitelist_file) if qualifier is not None: qualifier_list.append(qualifier) # Add all the --include jobs for pattern in ns.include_pattern_list: try: qualifier = RegExpJobQualifier('^{}$'.format(pattern), inclusive=True) except Exception as exc: logger.warning(_("Incorrect pattern %r: %s"), pattern, exc) else: qualifier_list.append(qualifier) # Add all the --exclude jobs for pattern in ns.exclude_pattern_list: try: qualifier = RegExpJobQualifier('^{}$'.format(pattern), inclusive=False) except Exception as exc: logger.warning(_("Incorrect pattern %r: %s"), pattern, exc) else: qualifier_list.append(qualifier) logger.debug("select_jobs(%r, %r)", job_list, qualifier_list) return select_jobs(job_list, qualifier_list)
def _run_jobs_with_session(self, ns, manager, runner): # TODO: run all resource jobs concurrently with multiprocessing # TODO: make local job discovery nicer, it would be best if # desired_jobs could be managed entirely internally by SesionState. In # such case the list of jobs to run would be changed during iteration # but would be otherwise okay). if self._local_only: print("[ Loading Jobs Definition ]".center(80, '=')) else: print("[ Running All Jobs ]".center(80, '=')) again = True while again: again = False for job in manager.state.run_list: # Skip jobs that already have result, this is only needed when # we run over the list of jobs again, after discovering new # jobs via the local job output if (manager.state.job_state_map[job.id].result.outcome is not None): continue self._run_single_job_with_session(ns, manager, runner, job) manager.checkpoint() if job.plugin == "local": # After each local job runs rebuild the list of matching # jobs and run everything again desired_job_list = select_jobs(manager.state.job_list, self.whitelists) if self._local_only: desired_job_list = [ job for job in desired_job_list if job.plugin == 'local' ] self._update_desired_job_list(manager, desired_job_list) again = True break
def _run_jobs_with_session(self, ns, manager, runner): # TODO: run all resource jobs concurrently with multiprocessing # TODO: make local job discovery nicer, it would be best if # desired_jobs could be managed entirely internally by SesionState. In # such case the list of jobs to run would be changed during iteration # but would be otherwise okay). if self._local_only: print("[ Loading Jobs Definition ]".center(80, '=')) else: print("[ Running All Jobs ]".center(80, '=')) again = True while again: again = False for job in manager.state.run_list: # Skip jobs that already have result, this is only needed when # we run over the list of jobs again, after discovering new # jobs via the local job output if (manager.state.job_state_map[job.id].result.outcome is not None): continue self._run_single_job_with_session(ns, manager, runner, job) manager.checkpoint() if job.plugin == "local": # After each local job runs rebuild the list of matching # jobs and run everything again desired_job_list = select_jobs(manager.state.job_list, self.whitelists) if self._local_only: desired_job_list = [ job for job in desired_job_list if job.plugin == 'local'] self._update_desired_job_list(manager, desired_job_list) again = True break
def get_matching_job_list(job_list, qualifier): """ Get a list of jobs that are designated by the specified qualifier. This is intended to be used with :class:`CompositeQualifier` but works with any :class:`IJobQualifier` subclass. """ return select_jobs(job_list, [qualifier])
def _add_test_plan_sheet(self, workbook, plan, job_list): """A sheet for a given test plan.""" # Create a sheet for this test plan sheet = workbook.add_worksheet(_('{}').format(plan.tr_name()[0:30])) # Define cell formatting fmt_header = workbook.add_format({ 'bold': True, 'font_color': '#ffffff', 'bg_color': '#77216f', # Light Aubergine }) fmt_code = workbook.add_format({ 'font_name': 'courier', }) fmt_info = workbook.add_format({ 'font_color': '#dd4814', # Ubuntu Orange 'font_name': 'Ubuntu', 'font_size': 16, }) # Create a section with static information sheet.write('A2', _("Test Plan Name"), fmt_info) sheet.write('B2', plan.tr_name()) sheet.write('A3', _("Test Plan ID"), fmt_info) sheet.write('B3', plan.id, fmt_code) sheet.merge_range('A4:B4', 'TIP: plainbox run -T {}'.format(plan.id), fmt_code) # We can add anything we want to all the rows in range(INFO_OFFSET) INFO_OFFSET = 5 # Find what is the effective run list of this test plan state = SessionState(job_list) state.update_desired_job_list( select_jobs(job_list, [plan.get_qualifier()])) def max_of(callback): """Get the maximum of some function applied to each job.""" return max((callback(job) for job in state.run_list), default=0) COL_ID, COL_SUMMARY = range(2) # Add columns: id sheet.write(INFO_OFFSET, COL_ID, _("Test Case ID"), fmt_header) sheet.write(INFO_OFFSET, COL_SUMMARY, _("Summary"), fmt_header) sheet.set_column( COL_ID, COL_ID, max_of(lambda job: nr_cols_of(job.partial_id)) * WIDTH_FACTOR + WIDTH_PADDING) sheet.set_column( COL_SUMMARY, COL_SUMMARY, max_of(lambda job: nr_cols_of(job.tr_summary())) * WIDTH_FACTOR + WIDTH_PADDING) # Add the information about each job as a separate row for index, job in enumerate(state.run_list, INFO_OFFSET + 1): sheet.set_row(index, HEIGHT_FACTOR + HEIGHT_PADDING) sheet.write(index, COL_ID, job.partial_id, fmt_code) sheet.write(index, COL_SUMMARY, job.tr_summary()) # Make sure the sheet is read only sheet.protect()
def test_select_jobs__inclusion(self): """ verify that select_jobs() honors qualifier ordering """ job_a = JobDefinition({'id': 'a'}) job_b = JobDefinition({'id': 'b'}) job_c = JobDefinition({'id': 'c'}) qual_a = JobIdQualifier("a", self.origin) qual_c = JobIdQualifier("c", self.origin) for job_list in permutations([job_a, job_b, job_c], 3): # Regardless of how the list of job is ordered the result # should be the same, depending on the qualifier list self.assertEqual(select_jobs(job_list, [qual_a, qual_c]), [job_a, job_c])
def test_select_jobs__inclusion(self): """ verify that select_jobs() honors qualifier ordering """ job_a = JobDefinition({'id': 'a'}) job_b = JobDefinition({'id': 'b'}) job_c = JobDefinition({'id': 'c'}) qual_a = JobIdQualifier("a") qual_c = JobIdQualifier("c") for job_list in permutations([job_a, job_b, job_c], 3): # Regardless of how the list of job is ordered the result # should be the same, depending on the qualifier list self.assertEqual( select_jobs(job_list, [qual_a, qual_c]), [job_a, job_c])
def interactively_pick_jobs_to_run(self): print(self.C.header(_("Selecting Jobs For Execution"))) self._update_desired_job_list(select_jobs( self.manager.state.job_list, self._qualifier_list)) if self.launcher.skip_test_selection or not self.is_interactive: return tree = SelectableJobTreeNode.create_tree( self.manager.state, self.manager.state.run_list) title = _('Choose tests to run on your system:') self.display.run(ScrollableTreeNode(tree, title)) # NOTE: tree.selection is correct but ordered badly. To retain # the original ordering we should just treat it as a mask and # use it to filter jobs from desired_job_list. wanted_set = frozenset(tree.selection + tree.resource_jobs) job_list = [job for job in self.manager.state.run_list if job in wanted_set] self._update_desired_job_list(job_list)
def _get_matching_job_list(self, ns, job_list): logger.debug("_get_matching_job_list(%r, %r)", ns, job_list) qualifier_list = [] # Add the test plan if ns.test_plan is not None: # Uh, dodgy, recreate a list of providers from the list of jobs we # know about here. This code needs to be re-factored to use the # upcoming provider store class. for provider in {job.provider for job in job_list}: for unit in provider.id_map[ns.test_plan]: if unit.Meta.name == 'test plan': qualifier_list.append(unit.get_qualifier()) break else: logger.debug(_("There is no test plan: %s"), ns.test_plan) # Add whitelists for whitelist_file in ns.whitelist: qualifier = self.get_whitelist_from_file(whitelist_file.name, whitelist_file) if qualifier is not None: qualifier_list.append(qualifier) # Add all the --include jobs for pattern in ns.include_pattern_list: origin = Origin(CommandLineTextSource('-i', pattern), None, None) try: qualifier = RegExpJobQualifier('^{}$'.format(pattern), origin, inclusive=True) except Exception as exc: logger.warning(_("Incorrect pattern %r: %s"), pattern, exc) else: qualifier_list.append(qualifier) # Add all the --exclude jobs for pattern in ns.exclude_pattern_list: origin = Origin(CommandLineTextSource('-x', pattern), None, None) try: qualifier = RegExpJobQualifier('^{}$'.format(pattern), origin, inclusive=False) except Exception as exc: logger.warning(_("Incorrect pattern %r: %s"), pattern, exc) else: qualifier_list.append(qualifier) logger.debug("select_jobs(%r, %r)", job_list, qualifier_list) return select_jobs(job_list, qualifier_list)
def select_local_jobs(self): print(self.C.header(_("Selecting Job Generators"))) # Create a qualifier list that will pick all local jobs out of the # subset of jobs also enumerated by the whitelists we've already # picked. # # Since each whitelist is a qualifier that selects jobs enumerated # within, we only need to and an exclusive qualifier that deselects # non-local jobs and we're done. qualifier_list = [] qualifier_list.extend(self._qualifier_list) origin = Origin.get_caller_origin() qualifier_list.append(FieldQualifier( 'plugin', OperatorMatcher(operator.ne, 'local'), origin, inclusive=False)) local_job_list = select_jobs( self.manager.state.job_list, qualifier_list) self._update_desired_job_list(local_job_list)
def test_select_jobs__exclusion(self): """ verify that select_jobs() honors qualifier ordering """ job_a = JobDefinition({'id': 'a'}) job_b = JobDefinition({'id': 'b'}) job_c = JobDefinition({'id': 'c'}) qual_all = CompositeQualifier([ JobIdQualifier("a"), JobIdQualifier("b"), JobIdQualifier("c"), ]) qual_not_c = JobIdQualifier("c", inclusive=False) for job_list in permutations([job_a, job_b, job_c], 3): # Regardless of how the list of job is ordered the result # should be the same, depending on the qualifier list self.assertEqual(select_jobs(job_list, [qual_all, qual_not_c]), [job_a, job_b])
def run(self): ns = self.ns job_list = self.get_job_list(ns) previous_session_file = SessionStorageRepository().get_last_storage() resume_in_progress = False if previous_session_file: if self.is_interactive: if self.ask_for_resume(): resume_in_progress = True manager = SessionManager.load_session( job_list, previous_session_file) self._maybe_skip_last_job_after_resume(manager) else: resume_in_progress = True manager = SessionManager.load_session(job_list, previous_session_file) if not resume_in_progress: # Create a session that handles most of the stuff needed to run # jobs try: manager = SessionManager.create_with_job_list(job_list, legacy_mode=True) except DependencyDuplicateError as exc: # Handle possible DependencyDuplicateError that can happen if # someone is using plainbox for job development. print("The job database you are currently using is broken") print("At least two jobs contend for the name {0}".format( exc.job.id)) print("First job defined in: {0}".format(exc.job.origin)) print("Second job defined in: {0}".format( exc.duplicate_job.origin)) raise SystemExit(exc) manager.state.metadata.title = " ".join(sys.argv) if self.is_interactive: if self.display is None: self.display = get_display() if self.settings['welcome_text']: self.display.run(ShowWelcome( self.settings['welcome_text'])) if not self.whitelists: whitelists = [] for p in self.provider_list: if p.name in self.settings['default_providers']: whitelists.extend( [w.name for w in p.get_builtin_whitelists()]) selection = self.display.run( ShowMenu("Suite selection", whitelists)) if not selection: raise SystemExit('No whitelists selected, aborting...') for s in selection: self.whitelists.append( get_whitelist_by_name(self.provider_list, whitelists[s])) else: self.whitelists.append( get_whitelist_by_name(self.provider_list, self.settings['default_whitelist'])) manager.checkpoint() if self.is_interactive and not resume_in_progress: # Pre-run all local jobs desired_job_list = select_jobs(manager.state.job_list, [ CompositeQualifier(self.whitelists + [NonLocalJobQualifier(inclusive=False)]) ]) self._update_desired_job_list(manager, desired_job_list) # Ask the password before anything else in order to run local jobs # requiring privileges if self._auth_warmup_needed(manager): print("[ Authentication ]".center(80, '=')) return_code = authenticate_warmup() if return_code: raise SystemExit(return_code) self._local_only = True self._run_jobs(ns, manager) self._local_only = False if not resume_in_progress: # Run the rest of the desired jobs desired_job_list = select_jobs(manager.state.job_list, self.whitelists) self._update_desired_job_list(manager, desired_job_list) if self.is_interactive: # Ask the password before anything else in order to run jobs # requiring privileges if self._auth_warmup_needed(manager): print("[ Authentication ]".center(80, '=')) return_code = authenticate_warmup() if return_code: raise SystemExit(return_code) tree = SelectableJobTreeNode.create_tree( manager.state.run_list, legacy_mode=True) title = 'Choose tests to run on your system:' if self.display is None: self.display = get_display() self.display.run(ScrollableTreeNode(tree, title)) self._update_desired_job_list(manager, tree.selection) estimated_duration_auto, estimated_duration_manual = \ manager.state.get_estimated_duration() if estimated_duration_auto: print( "Estimated duration is {:.2f} " "for automated jobs.".format(estimated_duration_auto)) else: print("Estimated duration cannot be " "determined for automated jobs.") if estimated_duration_manual: print("Estimated duration is {:.2f} " "for manual jobs.".format(estimated_duration_manual)) else: print("Estimated duration cannot be " "determined for manual jobs.") self._run_jobs(ns, manager) manager.destroy() # FIXME: sensible return value return 0
def run(self): ns = self.ns job_list = self.get_job_list(ns) previous_session_file = SessionStorageRepository().get_last_storage() resume_in_progress = False if previous_session_file: if self.is_interactive: if self.ask_for_resume(): resume_in_progress = True manager = SessionManager.load_session( job_list, previous_session_file) self._maybe_skip_last_job_after_resume(manager) else: resume_in_progress = True manager = SessionManager.load_session( job_list, previous_session_file) if not resume_in_progress: # Create a session that handles most of the stuff needed to run # jobs try: manager = SessionManager.create_with_job_list( job_list, legacy_mode=True) except DependencyDuplicateError as exc: # Handle possible DependencyDuplicateError that can happen if # someone is using plainbox for job development. print("The job database you are currently using is broken") print("At least two jobs contend for the name {0}".format( exc.job.id)) print("First job defined in: {0}".format(exc.job.origin)) print("Second job defined in: {0}".format( exc.duplicate_job.origin)) raise SystemExit(exc) manager.state.metadata.title = " ".join(sys.argv) if self.is_interactive: if self.display is None: self.display = get_display() if self.settings['welcome_text']: self.display.run( ShowWelcome(self.settings['welcome_text'])) if not self.whitelists: whitelists = [] for p in self.provider_list: if p.name in self.settings['default_providers']: whitelists.extend( [w.name for w in p.get_builtin_whitelists()]) selection = self.display.run(ShowMenu("Suite selection", whitelists)) if not selection: raise SystemExit('No whitelists selected, aborting...') for s in selection: self.whitelists.append( get_whitelist_by_name(self.provider_list, whitelists[s])) else: self.whitelists.append( get_whitelist_by_name( self.provider_list, self.settings['default_whitelist'])) manager.checkpoint() if self.is_interactive and not resume_in_progress: # Pre-run all local jobs desired_job_list = select_jobs( manager.state.job_list, [CompositeQualifier( self.whitelists + [NonLocalJobQualifier(inclusive=False)] )]) self._update_desired_job_list(manager, desired_job_list) # Ask the password before anything else in order to run local jobs # requiring privileges if self._auth_warmup_needed(manager): print("[ Authentication ]".center(80, '=')) return_code = authenticate_warmup() if return_code: raise SystemExit(return_code) self._local_only = True self._run_jobs(ns, manager) self._local_only = False if not resume_in_progress: # Run the rest of the desired jobs desired_job_list = select_jobs(manager.state.job_list, self.whitelists) self._update_desired_job_list(manager, desired_job_list) if self.is_interactive: # Ask the password before anything else in order to run jobs # requiring privileges if self._auth_warmup_needed(manager): print("[ Authentication ]".center(80, '=')) return_code = authenticate_warmup() if return_code: raise SystemExit(return_code) tree = SelectableJobTreeNode.create_tree( manager.state.run_list, legacy_mode=True) title = 'Choose tests to run on your system:' if self.display is None: self.display = get_display() self.display.run(ScrollableTreeNode(tree, title)) self._update_desired_job_list(manager, tree.selection) estimated_duration_auto, estimated_duration_manual = \ manager.state.get_estimated_duration() if estimated_duration_auto: print( "Estimated duration is {:.2f} " "for automated jobs.".format(estimated_duration_auto)) else: print( "Estimated duration cannot be " "determined for automated jobs.") if estimated_duration_manual: print( "Estimated duration is {:.2f} " "for manual jobs.".format(estimated_duration_manual)) else: print( "Estimated duration cannot be " "determined for manual jobs.") self._run_jobs(ns, manager) manager.destroy() # FIXME: sensible return value return 0