class Runner(ModelRunner): """ Standard test runner for behave: * setup paths * loads environment hooks * loads step definitions * select feature files, parses them and creates model (elements) """ def __init__(self, config): super(Runner, self).__init__(config) self.path_manager = PathManager() self.base_dir = None def setup_paths(self): # pylint: disable=too-many-branches, too-many-statements if self.config.paths: if self.config.verbose: print("Supplied path:", \ ", ".join('"%s"' % path for path in self.config.paths)) first_path = self.config.paths[0] if hasattr(first_path, "filename"): # -- BETTER: isinstance(first_path, FileLocation): first_path = first_path.filename base_dir = first_path if base_dir.startswith("@"): # -- USE: behave @features.txt base_dir = base_dir[1:] file_locations = self.feature_locations() if file_locations: base_dir = os.path.dirname(file_locations[0].filename) base_dir = os.path.abspath(base_dir) # supplied path might be to a feature file if os.path.isfile(base_dir): if self.config.verbose: print("Primary path is to a file so using its directory") base_dir = os.path.dirname(base_dir) else: if self.config.verbose: print('Using default path "./features"') base_dir = os.path.abspath("features") # Get the root. This is not guaranteed to be "/" because Windows. root_dir = path_getrootdir(base_dir) new_base_dir = base_dir steps_dir = self.config.steps_dir environment_file = self.config.environment_file while True: if self.config.verbose: print("Trying base directory:", new_base_dir) if os.path.isdir(os.path.join(new_base_dir, steps_dir)): break if os.path.isfile(os.path.join(new_base_dir, environment_file)): break if new_base_dir == root_dir: break new_base_dir = os.path.dirname(new_base_dir) if new_base_dir == root_dir: if self.config.verbose: if not self.config.paths: print('ERROR: Could not find "%s" directory. '\ 'Please specify where to find your features.' % \ steps_dir) else: print('ERROR: Could not find "%s" directory in your '\ 'specified path "%s"' % (steps_dir, base_dir)) message = 'No %s directory in %r' % (steps_dir, base_dir) raise ConfigError(message) base_dir = new_base_dir self.config.base_dir = base_dir for dirpath, dirnames, filenames in os.walk(base_dir): if [fn for fn in filenames if fn.endswith(".feature")]: break else: if self.config.verbose: if not self.config.paths: print('ERROR: Could not find any "<name>.feature" files. '\ 'Please specify where to find your features.') else: print('ERROR: Could not find any "<name>.feature" files '\ 'in your specified path "%s"' % base_dir) raise ConfigError('No feature files in %r' % base_dir) self.base_dir = base_dir self.path_manager.add(base_dir) if not self.config.paths: self.config.paths = [base_dir] if base_dir != os.getcwd(): self.path_manager.add(os.getcwd()) def before_all_default_hook(self, context): """ Default implementation for :func:`before_all()` hook. Setup the logging subsystem based on the configuration data. """ # pylint: disable=no-self-use context.config.setup_logging() def load_hooks(self, filename=None): filename = filename or self.config.environment_file hooks_path = os.path.join(self.base_dir, filename) if os.path.exists(hooks_path): exec_file(hooks_path, self.hooks) if "before_all" not in self.hooks: self.hooks["before_all"] = self.before_all_default_hook def load_step_definitions(self, extra_step_paths=None): if extra_step_paths is None: extra_step_paths = [] # -- Allow steps to import other stuff from the steps dir # NOTE: Default matcher can be overridden in "environment.py" hook. steps_dir = os.path.join(self.base_dir, self.config.steps_dir) step_paths = [steps_dir] + list(extra_step_paths) load_step_modules(step_paths) def feature_locations(self): return collect_feature_locations(self.config.paths) def run(self): with self.path_manager: self.setup_paths() return self.run_with_paths() def run_with_paths(self): self.context = Context(self) self.load_hooks() self.load_step_definitions() # -- ENSURE: context.execute_steps() works in weird cases (hooks, ...) # self.setup_capture() # self.run_hook("before_all", self.context) # -- STEP: Parse all feature files (by using their file location). feature_locations = [filename for filename in self.feature_locations() if not self.config.exclude(filename)] features = parse_features(feature_locations, language=self.config.lang) self.features.extend(features) # -- STEP: Run all features. stream_openers = self.config.outputs self.formatters = make_formatters(self.config, stream_openers) return self.run_model()
class Runner(ModelRunner): """Standard test runner for behave: * setup paths * loads environment hooks * loads step definitions * select feature files, parses them and creates model (elements) """ def __init__(self, config): super(Runner, self).__init__(config) self.path_manager = PathManager() self.base_dir = None def setup_paths(self): # pylint: disable=too-many-branches, too-many-statements if self.config.paths: if self.config.verbose: print("Supplied path:", \ ", ".join('"%s"' % path for path in self.config.paths)) first_path = self.config.paths[0] if hasattr(first_path, "filename"): # -- BETTER: isinstance(first_path, FileLocation): first_path = first_path.filename base_dir = first_path if base_dir.startswith("@"): # -- USE: behave @features.txt base_dir = base_dir[1:] file_locations = self.feature_locations() if file_locations: base_dir = os.path.dirname(file_locations[0].filename) base_dir = os.path.abspath(base_dir) # supplied path might be to a feature file if os.path.isfile(base_dir): if self.config.verbose: print("Primary path is to a file so using its directory") base_dir = os.path.dirname(base_dir) else: if self.config.verbose: print('Using default path "./features"') base_dir = os.path.abspath("features") # Get the root. This is not guaranteed to be "/" because Windows. root_dir = path_getrootdir(base_dir) new_base_dir = base_dir steps_dir = self.config.steps_dir environment_file = self.config.environment_file while True: if self.config.verbose: print("Trying base directory:", new_base_dir) if os.path.isdir(os.path.join(new_base_dir, steps_dir)): break if os.path.isfile(os.path.join(new_base_dir, environment_file)): break if new_base_dir == root_dir: break new_base_dir = os.path.dirname(new_base_dir) if new_base_dir == root_dir: if self.config.verbose: if not self.config.paths: print('ERROR: Could not find "%s" directory. '\ 'Please specify where to find your features.' % \ steps_dir) else: print('ERROR: Could not find "%s" directory in your '\ 'specified path "%s"' % (steps_dir, base_dir)) message = 'No %s directory in %r' % (steps_dir, base_dir) raise ConfigError(message) base_dir = new_base_dir self.config.base_dir = base_dir for dirpath, dirnames, filenames in os.walk(base_dir, followlinks=True): if [fn for fn in filenames if fn.endswith(".feature")]: break else: if self.config.verbose: if not self.config.paths: print('ERROR: Could not find any "<name>.feature" files. '\ 'Please specify where to find your features.') else: print('ERROR: Could not find any "<name>.feature" files '\ 'in your specified path "%s"' % base_dir) raise ConfigError('No feature files in %r' % base_dir) self.base_dir = base_dir self.path_manager.add(base_dir) if not self.config.paths: self.config.paths = [base_dir] if base_dir != os.getcwd(): self.path_manager.add(os.getcwd()) def before_all_default_hook(self, context): """ Default implementation for :func:`before_all()` hook. Setup the logging subsystem based on the configuration data. """ # pylint: disable=no-self-use context.config.setup_logging() def load_hooks(self, filename=None): filename = filename or self.config.environment_file hooks_path = os.path.join(self.base_dir, filename) if os.path.exists(hooks_path): exec_file(hooks_path, self.hooks) if "before_all" not in self.hooks: self.hooks["before_all"] = self.before_all_default_hook def load_step_definitions(self, extra_step_paths=None): if extra_step_paths is None: extra_step_paths = [] # -- Allow steps to import other stuff from the steps dir # NOTE: Default matcher can be overridden in "environment.py" hook. steps_dir = os.path.join(self.base_dir, self.config.steps_dir) step_paths = [steps_dir] + list(extra_step_paths) load_step_modules(step_paths) def feature_locations(self): return collect_feature_locations(self.config.paths) def run(self): with self.path_manager: self.setup_paths() return self.run_with_paths() def run_with_paths(self): self.context = Context(self) self.load_hooks() self.load_step_definitions() # -- ENSURE: context.execute_steps() works in weird cases (hooks, ...) # self.setup_capture() # self.run_hook("before_all", self.context) # -- STEP: Parse all feature files (by using their file location). feature_locations = [ filename for filename in self.feature_locations() if not self.config.exclude(filename) ] features = parse_features(feature_locations, language=self.config.lang) self.features.extend(features) # -- STEP: Run all features. stream_openers = self.config.outputs self.formatters = make_formatters(self.config, stream_openers) return self.run_model()
class Runner(ModelRunner): """ Standard test runner for behave: * setup paths * loads environment hooks * loads step definitions * select feature files, parses them and creates model (elements) """ def __init__(self, config): super(Runner, self).__init__(config) self.path_manager = PathManager() self.base_dir = None def setup_paths(self): # pylint: disable=too-many-branches, too-many-statements if self.config.paths: if self.config.verbose: print("Supplied path:", \ ", ".join('"%s"' % path for path in self.config.paths)) first_path = self.config.paths[0] if hasattr(first_path, "filename"): # -- BETTER: isinstance(first_path, FileLocation): first_path = first_path.filename base_dir = first_path if base_dir.startswith("@"): # -- USE: behave @features.txt base_dir = base_dir[1:] file_locations = self.feature_locations() if file_locations: base_dir = os.path.dirname(file_locations[0].filename) base_dir = os.path.abspath(base_dir) # supplied path might be to a feature file if os.path.isfile(base_dir): if self.config.verbose: print("Primary path is to a file so using its directory") base_dir = os.path.dirname(base_dir) else: if self.config.verbose: print('Using default path "./features"') base_dir = os.path.abspath("features") # Get the root. This is not guaranteed to be "/" because Windows. root_dir = path_getrootdir(base_dir) new_base_dir = base_dir steps_dir = self.config.steps_dir environment_file = self.config.environment_file while True: if self.config.verbose: print("Trying base directory:", new_base_dir) if os.path.isdir(os.path.join(new_base_dir, steps_dir)): break if os.path.isfile(os.path.join(new_base_dir, environment_file)): break if new_base_dir == root_dir: break new_base_dir = os.path.dirname(new_base_dir) if new_base_dir == root_dir: if self.config.verbose: if not self.config.paths: print('ERROR: Could not find "%s" directory. '\ 'Please specify where to find your features.' % \ steps_dir) else: print('ERROR: Could not find "%s" directory in your '\ 'specified path "%s"' % (steps_dir, base_dir)) message = 'No %s directory in %r' % (steps_dir, base_dir) raise ConfigError(message) base_dir = new_base_dir self.config.base_dir = base_dir for dirpath, dirnames, filenames in os.walk(base_dir): if [fn for fn in filenames if fn.endswith(".feature")]: break else: if self.config.verbose: if not self.config.paths: print('ERROR: Could not find any "<name>.feature" files. '\ 'Please specify where to find your features.') else: print('ERROR: Could not find any "<name>.feature" files '\ 'in your specified path "%s"' % base_dir) raise ConfigError('No feature files in %r' % base_dir) self.base_dir = base_dir self.path_manager.add(base_dir) if not self.config.paths: self.config.paths = [base_dir] if base_dir != os.getcwd(): self.path_manager.add(os.getcwd()) def before_all_default_hook(self, context): """ Default implementation for :func:`before_all()` hook. Setup the logging subsystem based on the configuration data. """ # pylint: disable=no-self-use context.config.setup_logging() def load_hooks(self, filename=None): filename = filename or self.config.environment_file hooks_path = os.path.join(self.base_dir, filename) if os.path.exists(hooks_path): exec_file(hooks_path, self.hooks) if "before_all" not in self.hooks: self.hooks["before_all"] = self.before_all_default_hook def load_step_definitions(self, extra_step_paths=None): if extra_step_paths is None: extra_step_paths = [] # -- Allow steps to import other stuff from the steps dir # NOTE: Default matcher can be overridden in "environment.py" hook. steps_dir = os.path.join(self.base_dir, self.config.steps_dir) step_paths = [steps_dir] + list(extra_step_paths) load_step_modules(step_paths) def feature_locations(self): return collect_feature_locations(self.config.paths) def run(self): with self.path_manager: self.setup_paths() return self.run_with_paths() def run_with_paths(self): self.context = Context(self) self.load_hooks() self.load_step_definitions() # -- ENSURE: context.execute_steps() works in weird cases (hooks, ...) self.setup_capture() self.run_hook("before_all", self.context) # -- STEP: Parse all feature files (by using their file location). feature_locations = [ filename for filename in self.feature_locations() if not self.config.exclude(filename) ] features = parse_features(feature_locations, language=self.config.lang) self.features.extend(features) # -- STEP: Run all features. stream_openers = self.config.outputs self.formatters = make_formatters(self.config, stream_openers) if self.context.config.userdata.has_key( "runMode" ) and self.context.config.userdata["runMode"] == "serial": return self.run_model() return self.run_multiproc() def run_multiproc(self): if not multiprocessing: print("ERROR: Cannot import multiprocessing module.") return 1 self.config.format = ['plain'] self.parallel_element = 'feature' # -- Prevent context warnings. # def do_nothing(obj2, obj3): # pass # self.context._emit_warning = do_nothing self.joblist_index_queue = multiprocessing.Manager().JoinableQueue() self.resultsqueue = multiprocessing.Manager().JoinableQueue() self.joblist = [] scenario_count = 0 feature_count = 0 serial_features = [] for feature in self.features: if 'serial' in feature.tags: serial_features.append(feature) continue if self.parallel_element == 'feature': self.joblist.append(feature) self.joblist_index_queue.put(feature_count + scenario_count) feature_count += 1 continue # for scenario in feature.scenarios: # if scenario.type == 'scenario': # self.joblist.append(scenario) # self.joblist_index_queue.put( # feature_count + scenario_count) # scenario_count += 1 # else: # for subscenario in scenario.scenarios: # self.joblist.append(subscenario) # self.joblist_index_queue.put( # feature_count + scenario_count) # scenario_count += 1 proc_count = 8 procs = [] for i in range(proc_count): p = multiprocessing.Process(target=self.worker, args=(i, )) procs.append(p) p.start() [p.join() for p in procs] self.joblist = [] feature_count = 0 for feature in serial_features: self.joblist.append(feature) self.joblist_index_queue.put(feature_count) feature_count += 1 continue p = multiprocessing.Process(target=self.worker, args=(0, )) p.start() p.join() self.run_hook('after_all', self.context) return self.multiproc_fullreport() def worker(self, proc_number): self.context.procnum = proc_number while 1: try: joblist_index = self.joblist_index_queue.get_nowait() except Exception, e: break current_job = self.joblist[joblist_index] writebuf = StringIO.StringIO() self.setfeature(current_job) self.config.outputs = [] self.config.outputs.append(StreamOpener(stream=writebuf)) stream_openers = self.config.outputs self.formatters = make_formatters(self.config, stream_openers) for formatter in self.formatters: formatter.uri(current_job.filename) if self.step_registry is None: self.step_registry = the_step_registry start_time = time.strftime("%Y-%m-%d %H:%M:%S") # if current_job.type == 'scenario' and getattr(self.config, # 'parallel_scenario_feature_hooks'): # self.run_hook('before_feature', self.context, current_job) current_job.run(self) # if current_job.type == 'scenario' and getattr(self.config, # 'parallel_scenario_feature_hooks'): # self.run_hook('after_feature', self.context, current_job) end_time = time.strftime("%Y-%m-%d %H:%M:%S") sys.stderr.write("%s: %s \n" % (current_job.filename, current_job.status.name)) if current_job.type == 'feature': for reporter in self.config.reporters: reporter.feature(current_job) job_report_text = self.generatereport(proc_number, current_job, start_time, end_time, writebuf) if job_report_text: results = {} results['steps_passed'] = 0 results['steps_failed'] = 0 results['steps_skipped'] = 0 results['steps_undefined'] = 0 results['steps_untested'] = 0 results['jobtype'] = current_job.type results['reportinginfo'] = job_report_text results['status'] = str(current_job.status.name) if current_job.type != 'feature': results['uniquekey'] = \ current_job.filename + current_job.feature.name else: results['scenarios_passed'] = 0 results['scenarios_failed'] = 0 results['scenarios_skipped'] = 0 results['scenarios_untested'] = 0 # results['scenarios_undefined'] = 0 # results['scenarios_executing'] = 0 self.countscenariostatus(current_job, results) self.countstepstatus(current_job, results) if current_job.type != 'feature' and \ getattr(self.config, 'junit'): results['junit_report'] = \ self.generate_junit_report(current_job, writebuf) self.resultsqueue.put(results)