class RecoverPrompt(Plugin): @property def persist(self): if self._persist is None: self._persist = Persist(backend=MemoryBackend()) return self._persist.root_at("recover_prompt") def register(self, manager): super(RecoverPrompt, self).register(manager) self._persist = None for (rt, rh) in [ ("begin-persist", self.begin_persist), ("prompt-begin", self.prompt_begin), ("prompt-finish", self.prompt_finish)]: self._manager.reactor.call_on(rt, rh) def begin_persist(self, persist): self._persist = persist def prompt_begin(self, interface): if interface.direction == NEXT \ and self.persist.get("recover", False): responses = [RESTART_ANSWER, CONTINUE_ANSWER, RERUN_ANSWER] #The actual responses and default need to be translated... translated_responses = [_(re) for re in responses] translated_default = _(RESTART_ANSWER) #Show_info will return a unicode version of the translated #response. We need to map that to one of our response #constants, this dictionary helps with that: response_map = { _(response):response for response in responses} response = interface.show_info( _("Checkbox did not finish completely.\n" "Do you want to rerun the last test,\n" "continue to the next test, or\n" "restart from the beginning?"), translated_responses, translated_default) self._manager.reactor.fire("begin-recover", response_map[response]) self.persist.set("recover", True) def prompt_finish(self, interface): if interface.direction == NEXT: self.persist.set("recover", False)
class RecoverPrompt(Plugin): @property def persist(self): if self._persist is None: self._persist = Persist(backend=MemoryBackend()) return self._persist.root_at("recover_prompt") def register(self, manager): super(RecoverPrompt, self).register(manager) self._persist = None for (rt, rh) in [("begin-persist", self.begin_persist), ("prompt-begin", self.prompt_begin), ("prompt-finish", self.prompt_finish)]: self._manager.reactor.call_on(rt, rh) def begin_persist(self, persist): self._persist = persist def prompt_begin(self, interface): if interface.direction == NEXT \ and self.persist.get("recover", False): responses = [RESTART_ANSWER, CONTINUE_ANSWER, RERUN_ANSWER] #The actual responses and default need to be translated... translated_responses = [_(re) for re in responses] translated_default = _(RESTART_ANSWER) #Show_info will return a unicode version of the translated #response. We need to map that to one of our response #constants, this dictionary helps with that: response_map = {_(response): response for response in responses} response = interface.show_info( _("Checkbox did not finish completely.\n" "Do you want to rerun the last test,\n" "continue to the next test, or\n" "restart from the beginning?"), translated_responses, translated_default) self._manager.reactor.fire("begin-recover", response_map[response]) self.persist.set("recover", True) def prompt_finish(self, interface): if interface.direction == NEXT: self.persist.set("recover", False)
class RecoverPrompt(Plugin): @property def persist(self): if self._persist is None: self._persist = Persist(backend=MemoryBackend()) return self._persist.root_at("recover_prompt") def register(self, manager): super(RecoverPrompt, self).register(manager) self._persist = None for (rt, rh) in [ ("begin-persist", self.begin_persist), ("prompt-begin", self.prompt_begin), ("prompt-finish", self.prompt_finish), ]: self._manager.reactor.call_on(rt, rh) def begin_persist(self, persist): self._persist = persist def prompt_begin(self, interface): if interface.direction == NEXT and self.persist.get("recover", False): recover = _("Recover") restart = _("Restart") response = interface.show_info( _("Checkbox did not finish completely.\n" "Do you want to recover from the previous run?"), [recover, restart], recover, ) self._manager.reactor.fire("begin-recover", response == recover) self.persist.set("recover", True) def prompt_finish(self, interface): if interface.direction == NEXT: self.persist.set("recover", False)
class JobsPrompt(Plugin): # Directory where messages are stored store_directory = Path(default="%(checkbox_data)s/store") # Maximum number of messages per directory store_directory_size = Int(default=1000) @property def persist(self): if self._persist is None: self._persist = Persist(backend=MemoryBackend()) return self._persist.root_at("jobs_prompt") @property def store(self): if self._store is None: self._store = JobStore(self.persist, self.store_directory, self.store_directory_size) return self._store def register(self, manager): super(JobsPrompt, self).register(manager) self._ignore = [] self._persist = None self._store = None self._fail_current = False for (rt, rh) in [("expose-msgstore", self.expose_msgstore), ("begin-persist", self.begin_persist), ("begin-recover", self.begin_recover), ("ignore-jobs", self.ignore_jobs), ("prompt-job", self.prompt_job), ("prompt-jobs", self.prompt_jobs), ("prompt-finish", self.prompt_finish), ("report", self.report), ("report-job", self.report_job), ("report-jobs", self.report_jobs)]: self._manager.reactor.call_on(rt, rh) #This should fire first thing during the gathering phase. self._manager.reactor.call_on("gather", self.begin_gather, -900) #This should fire last during gathering (i.e. after #all other gathering callbacks are finished) self._manager.reactor.call_on("gather", self.end_gather, 900) def expose_msgstore(self): self._manager.reactor.fire("store-access", self.store) def begin_persist(self, persist): self._persist = persist def begin_recover(self, recover): if recover == RERUN_ANSWER: logging.debug("Recovering from last job") elif recover == CONTINUE_ANSWER: logging.debug("Marking last job failed, starting from next job") self._fail_current = True else: self.store.delete_all_messages() def begin_gather(self): #Speed boost during the gathering phase. Not critical data anyway. self.store.safe_file_closing = False def end_gather(self): #Back to saving data very carefully once gathering is done. self.store.safe_file_closing = True def ignore_jobs(self, jobs): self._ignore = jobs def report_job(self, job): # Update job job.setdefault("status", UNINITIATED) self._manager.reactor.fire("report-%s" % job["plugin"], job) def report_jobs(self, jobs): for job in jobs: self.store.add(job) def prompt_job(self, interface, job): attribute = "description" if job.get("type") == "suite" else "name" if job[attribute] in self._ignore: job["status"] = UNTESTED else: if "depends" in job: offset = self.store.get_pending_offset() self.store.set_pending_offset(0) messages = self.store.get_pending_messages() self.store.set_pending_offset(offset) # Skip if any message in the depends doesn't pass depends = job["depends"] for message in messages: if message["name"] in depends and \ message["status"] != PASS: return self._manager.reactor.fire("prompt-%s" % job["plugin"], interface, job) def prompt_jobs(self, interface): while True: if interface.direction == PREV: if not self.store.remove_pending_offset(): break if self._fail_current: msg_to_fail = self.store.get_pending_messages(1) job_to_fail = msg_to_fail[0] job_to_fail["status"] = "fail" logging.warning("Marking job %s as failed" "at user request" % job_to_fail["name"]) self.store.update(job_to_fail) self.store.add_pending_offset() self._fail_current = False messages = self.store.get_pending_messages(1) if not messages: break done_count = self.store.get_pending_offset() pending_count = self.store.count_pending_messages() progress = (done_count, done_count + pending_count) self._manager.reactor.fire("set-progress", progress) job = messages[0] self._manager.reactor.fire("prompt-job", interface, job) self.store.update(job) if interface.direction == NEXT: self.store.add_pending_offset() def prompt_finish(self, interface): if interface.direction == NEXT: self.store.delete_all_messages() def report(self): self.store.set_pending_offset(0) messages = self.store.get_pending_messages() self.store.add_pending_offset(len(messages)) tests = [m for m in messages if m.get("type") in ("test", "metric")] self._manager.reactor.fire("report-tests", tests) suites = [m for m in messages if m.get("type") == "suite"] self._manager.reactor.fire("report-suites", suites) attachments = [m for m in messages if m.get("type") == "attachment" and "data" in m] self._manager.reactor.fire("report-attachments", attachments)
class SuitesPrompt(Plugin): @property def persist(self): if self._persist is None: self._persist = Persist(backend=MemoryBackend()) return self._persist.root_at("suites_prompt") def register(self, manager): super(SuitesPrompt, self).register(manager) self._depends = {} self._jobs = {} self._statuses = {} self._persist = None self._recover = False for (rt, rh) in [ ("begin-persist", self.begin_persist), ("begin-recover", self.begin_recover), ("report-suite", self.report_suite)]: self._manager.reactor.call_on(rt, rh) for (rt, rh) in [ ("prompt-gather", self.prompt_gather), ("report-suite", self.report_job), ("report-test", self.report_job)]: self._manager.reactor.call_on(rt, rh, 100) def begin_persist(self, persist): self._persist = persist def begin_recover(self, recover): self._recover = recover if not self._recover: self.persist.remove("default") def report_suite(self, suite): suite.setdefault("type", "suite") def report_job(self, job): if job.get("type") == "suite": attribute = "description" else: attribute = "name" if attribute in job: self._jobs[job["name"]] = job[attribute] if "suite" in job: self._depends[job["name"]] = [job["suite"]] if job.get("type") == "test": self._statuses[job["name"]] = job["status"] def prompt_gather(self, interface): # Resolve dependencies resolver = Resolver() for key in self._jobs.iterkeys(): depends = self._depends.get(key, []) resolver.add(key, *depends) # Build options options = {} for job in resolver.get_dependents(): suboptions = options dependencies = resolver.get_dependencies(job) for dependency in dependencies: value = self._statuses.get(dependency, {}) suboptions = suboptions.setdefault(self._jobs[dependency], value) # Build defaults defaults = self.persist.get("default") if defaults is None: defaults = copy.deepcopy(options) # Get results defaults = interface.show_tree(_("Select the suites to test"), options, defaults) self.persist.set("default", defaults) # Get tests to ignore def get_ignore_jobs(options, results): jobs = [] if isinstance(options,dict): for k, v in options.iteritems(): if v == UNINITIATED and k not in results: jobs.append(k) else: jobs.extend(get_ignore_jobs(options[k], results.get(k, {}))) return jobs ignore_jobs = get_ignore_jobs(options, defaults) self._manager.reactor.fire("ignore-jobs", ignore_jobs)
class UserInterface(Plugin): # Module where the user interface implementation is defined. interface_module = String(default="checkbox.user_interface") # Class implementing the UserInterface interface. interface_class = String(default="UserInterface") # HACK: this is only a temporary workaround to internationalize the # user interface title and should be eventually removed. gettext.textdomain("checkbox") # Title of the user interface title = String(default=_("System Testing")) # Path where data files are stored. data_path = Path(required=False) @property def persist(self): if self._persist is None: self._persist = Persist(backend=MemoryBackend()) return self._persist.root_at("user_interface") def register(self, manager): super(UserInterface, self).register(manager) self._persist = None self._manager.reactor.call_on("prompt-begin", self.prompt_begin) self._manager.reactor.call_on("stop", self.save_persist) self._manager.reactor.call_on("begin-persist", self.begin_persist) self._manager.reactor.call_on("run", self.run) self._manager.reactor.call_on("launchpad-report", self.launchpad_report) def begin_persist(self, persist): self._persist = persist def prompt_begin(self, interface): self._interface.ui_flags = self.persist.get("ui_flags", {}) def save_persist(self, *args): self.persist.set("ui_flags", self._interface.ui_flags) self.persist.save() def run(self): interface_module = __import__(self.interface_module, None, None, ['']) interface_class = getattr(interface_module, self.interface_class) interface = interface_class(self.title, self.data_path) self._interface = interface event_types = [ "prompt-begin", "prompt-gather", "prompt-jobs", "prompt-report", "prompt-exchange", "prompt-finish"] index = 0 while index < len(event_types): event_type = event_types[index] self._manager.reactor.fire(event_type, interface) if interface.direction == PREV: if index > 0: index -= 1 else: index += 1 def launchpad_report(self, launchpad_report): self._interface.report_url = "file://%s" % posixpath.abspath(launchpad_report)
class SuitesPrompt(Plugin): deselect_warning = String(default=_("""\ Unselecting a test will invalidate your submission for Ubuntu Friendly. \ If you plan to participate in Ubuntu Friendly, please, select all tests. \ You can always skip individual tests if you don't have the needed equipment.\ """)) @property def persist(self): if self._persist is None: self._persist = Persist(backend=MemoryBackend()) return self._persist.root_at("suites_prompt") def register(self, manager): super(SuitesPrompt, self).register(manager) self._depends = {} self._jobs = {} self._statuses = {} self._persist = None self._recover = False for (rt, rh) in [ ("begin-persist", self.begin_persist), ("begin-recover", self.begin_recover), ("report-suite", self.report_suite), ("store-access", self.store_access)]: self._manager.reactor.call_on(rt, rh) for (rt, rh) in [ ("prompt-gather", self.prompt_gather), ("report-suite", self.report_job), ("report-test", self.report_job)]: self._manager.reactor.call_on(rt, rh, 100) def begin_persist(self, persist): self._persist = persist def begin_recover(self, recover): if recover in [CONTINUE_ANSWER, RERUN_ANSWER]: self._recover = True if not self._recover: self.persist.remove("default") def report_suite(self, suite): suite.setdefault("type", "suite") def store_access(self, store): self.store = store def report_job(self, job): if job.get("type") == "suite": attribute = "description" else: attribute = "name" if attribute in job: self._jobs[job["name"]] = job[attribute] if "suite" in job: self._depends[job["name"]] = [job["suite"]] if job.get("type") == "test": self._statuses[job["name"]] = job["status"] def prompt_gather(self, interface): # Resolve dependencies interface.show_progress_start(_("Gathering information from your system...")) resolver = Resolver() for key in self._jobs.keys(): depends = self._depends.get(key, []) resolver.add(key, *depends) # Build options options = {} self._manager.reactor.fire("expose-msgstore") offset = self.store.get_pending_offset() self.store.set_pending_offset(0) messages = self.store.get_pending_messages() self.store.add_pending_offset(offset) tests = dict([(m["name"], m) for m in messages if m.get("type") in ("test", "metric")]) def walk_dependencies(job, all_dependencies): for dependency in resolver.get_dependencies(job)[:-1]: walk_dependencies(dependency, all_dependencies) all_dependencies.append(job) for job in resolver.get_dependents(): suboptions = options dependencies = [] walk_dependencies(job, dependencies) for dependency in dependencies: if dependency in tests: value = tests[dependency]["status"] else: value = self._statuses.get(dependency, {}) suboptions = suboptions.setdefault(self._jobs[dependency], value) # Build defaults defaults = self.persist.get("default") if defaults is None: defaults = copy.deepcopy(options) # Get results interface.show_progress_stop() defaults = interface.show_tree( _("Choose tests to run on your system:"), options, defaults, self.deselect_warning) self.persist.set("default", defaults) # Get tests to ignore def get_ignore_jobs(options, results): jobs = [] if isinstance(options, dict): for k, v in options.items(): if v == UNINITIATED and k not in results: jobs.append(k) else: jobs.extend(get_ignore_jobs(options[k], results.get(k, {}))) return jobs ignore_jobs = get_ignore_jobs(options, defaults) self._manager.reactor.fire("ignore-jobs", ignore_jobs)
class SuitesPrompt(Plugin): deselect_warning = String(default=_("""\ Unselecting a test will invalidate your submission for Ubuntu Friendly. \ If you plan to participate in Ubuntu Friendly, please, select all tests. \ You can always skip individual tests if you don't have the needed equipment.\ """)) @property def persist(self): if self._persist is None: self._persist = Persist(backend=MemoryBackend()) return self._persist.root_at("suites_prompt") def register(self, manager): super(SuitesPrompt, self).register(manager) self._depends = {} self._jobs = {} self._statuses = {} self._persist = None self._recover = False for (rt, rh) in [("begin-persist", self.begin_persist), ("begin-recover", self.begin_recover), ("report-suite", self.report_suite), ("store-access", self.store_access)]: self._manager.reactor.call_on(rt, rh) for (rt, rh) in [("prompt-gather", self.prompt_gather), ("report-suite", self.report_job), ("report-test", self.report_job)]: self._manager.reactor.call_on(rt, rh, 100) def begin_persist(self, persist): self._persist = persist def begin_recover(self, recover): if recover in [CONTINUE_ANSWER, RERUN_ANSWER]: self._recover = True if not self._recover: self.persist.remove("default") def report_suite(self, suite): suite.setdefault("type", "suite") def store_access(self, store): self.store = store def report_job(self, job): if job.get("type") == "suite": attribute = "description" else: attribute = "name" if attribute in job: self._jobs[job["name"]] = job[attribute] if "suite" in job: self._depends[job["name"]] = [job["suite"]] if job.get("type") == "test": self._statuses[job["name"]] = job["status"] def prompt_gather(self, interface): # Resolve dependencies interface.show_progress_start( _("Gathering information from your system...")) resolver = Resolver() for key in self._jobs.keys(): depends = self._depends.get(key, []) resolver.add(key, *depends) # Build options options = {} self._manager.reactor.fire("expose-msgstore") offset = self.store.get_pending_offset() self.store.set_pending_offset(0) messages = self.store.get_pending_messages() self.store.add_pending_offset(offset) tests = dict([(m["name"], m) for m in messages if m.get("type") in ("test", "metric")]) def walk_dependencies(job, all_dependencies): for dependency in resolver.get_dependencies(job)[:-1]: walk_dependencies(dependency, all_dependencies) all_dependencies.append(job) for job in resolver.get_dependents(): suboptions = options dependencies = [] walk_dependencies(job, dependencies) for dependency in dependencies: if dependency in tests: value = tests[dependency]["status"] else: value = self._statuses.get(dependency, {}) suboptions = suboptions.setdefault(self._jobs[dependency], value) # Build defaults defaults = self.persist.get("default") if defaults is None: defaults = copy.deepcopy(options) # Get results interface.show_progress_stop() defaults = interface.show_tree( _("Choose tests to run on your system:"), options, defaults, self.deselect_warning) self.persist.set("default", defaults) # Get tests to ignore def get_ignore_jobs(options, results): jobs = [] if isinstance(options, dict): for k, v in options.items(): if v == UNINITIATED and k not in results: jobs.append(k) else: jobs.extend( get_ignore_jobs(options[k], results.get(k, {}))) return jobs ignore_jobs = get_ignore_jobs(options, defaults) self._manager.reactor.fire("ignore-jobs", ignore_jobs)
class JobsPrompt(Plugin): # Directory where messages are stored store_directory = Path(default="%(checkbox_data)s/store") # Maximum number of messages per directory store_directory_size = Int(default=1000) @property def persist(self): if self._persist is None: self._persist = Persist(backend=MemoryBackend()) return self._persist.root_at("jobs_prompt") @property def store(self): if self._store is None: self._store = JobStore(self.persist, self.store_directory, self.store_directory_size) return self._store def register(self, manager): super(JobsPrompt, self).register(manager) self._ignore = [] self._persist = None self._store = None for (rt, rh) in [ ("expose-msgstore", self.expose_msgstore), ("begin-persist", self.begin_persist), ("begin-recover", self.begin_recover), ("ignore-jobs", self.ignore_jobs), ("prompt-job", self.prompt_job), ("prompt-jobs", self.prompt_jobs), ("prompt-finish", self.prompt_finish), ("report", self.report), ("report-job", self.report_job)]: self._manager.reactor.call_on(rt, rh) def expose_msgstore(self): self._manager.reactor.fire("store-access", self.store) def begin_persist(self, persist): self._persist = persist def begin_recover(self, recover): if not recover: self.store.delete_all_messages() def ignore_jobs(self, jobs): self._ignore = jobs def report_job(self, job): # Update job job.setdefault("status", UNINITIATED) self._manager.reactor.fire("report-%s" % job["plugin"], job) self.store.add(job) def prompt_job(self, interface, job): attribute = "description" if job.get("type") == "suite" else "name" if job[attribute] in self._ignore: job["status"] = UNTESTED else: self._manager.reactor.fire("prompt-%s" % job["plugin"], interface, job) def prompt_jobs(self, interface): while True: if interface.direction == PREV: if not self.store.remove_pending_offset(): break messages = self.store.get_pending_messages(1) if not messages: break job = messages[0] self._manager.reactor.fire("prompt-job", interface, job) self.store.update(job) if interface.direction == NEXT: self.store.add_pending_offset() def prompt_finish(self, interface): if interface.direction == NEXT: self.store.delete_all_messages() def report(self): self.store.set_pending_offset(0) messages = self.store.get_pending_messages() self.store.add_pending_offset(len(messages)) tests = [m for m in messages if m.get("type") in ("test", "metric")] self._manager.reactor.fire("report-tests", tests) attachments = [m for m in messages if m.get("type") == "attachment" and "data" in m] self._manager.reactor.fire("report-attachments", attachments)
class UserInterface(Plugin): # Module where the user interface implementation is defined. interface_module = String(default="checkbox.user_interface") # Class implementing the UserInterface interface. interface_class = String(default="UserInterface") # HACK: this is only a temporary workaround to internationalize the # user interface title and should be eventually removed. gettext.textdomain("checkbox") # Title of the user interface title = String(default=_("System Testing")) # Path where data files are stored. data_path = Path(required=False) @property def persist(self): if self._persist is None: self._persist = Persist(backend=MemoryBackend()) return self._persist.root_at("user_interface") def register(self, manager): super(UserInterface, self).register(manager) self._persist = None self._manager.reactor.call_on("prompt-begin", self.prompt_begin) self._manager.reactor.call_on("stop", self.save_persist) self._manager.reactor.call_on("begin-persist", self.begin_persist) self._manager.reactor.call_on("run", self.run) self._manager.reactor.call_on("launchpad-report", self.launchpad_report) self._manager.reactor.call_on("set-progress", self.set_progress) self._manager.reactor.call_on("prompt-job", self.update_status, 101) def update_status(self, interface, job): #The UI can choose to implement this method to get #information about each job that completes interface.update_status(job) def begin_persist(self, persist): self._persist = persist def prompt_begin(self, interface): self._interface.ui_flags = self.persist.get("ui_flags", {}) def save_persist(self, *args): self.persist.set("ui_flags", self._interface.ui_flags) self.persist.save() def set_progress(self, progress): self._interface.progress = progress def run(self): interface_module = __import__(self.interface_module, None, None, ['']) interface_class = getattr(interface_module, self.interface_class) interface = interface_class(self.title, self.data_path) self._interface = interface event_types = [ "prompt-begin", "prompt-gather", "prompt-jobs", "prompt-report", "prompt-exchange", "prompt-finish" ] index = 0 while index < len(event_types): event_type = event_types[index] self._manager.reactor.fire(event_type, interface) if interface.direction == PREV: if index > 0: index -= 1 else: index += 1 def launchpad_report(self, launchpad_report): self._interface.report_url = "file://%s" % posixpath.abspath( launchpad_report)