def run(self): args = self.args location = args.location console = self.console if args.output is not None: from moya.console import Console console = Console(html=True) def write_output(): if args.output is not None: from moya import consolehtml html = console.get_text() html = consolehtml.render_console_html(html) with open(args.output, 'wb') as f: f.write(html.encode('utf-8')) test_libs = [] for location in args.location: archive, lib = build.build_lib(location, tests=True) lib_name = lib.long_name if archive.failed_documents: sys.stderr.write('library build failed\n') build.render_failed_documents(archive, console) write_output() return -1 test_libs.append(lib) for lib_name in test_libs: if not lib.load_tests(): sys.stderr.write('no tests for {}\n'.format(lib)) return -1 archive.finalize() if archive.failed_documents: sys.stderr.write('tests build failed\n') build.render_failed_documents(archive, console) write_output() return -1 if args.automated: archive.suppress_breakpoints = True all_results = [] for lib in test_libs: suites = list(lib.get_elements_by_type((namespaces.test, 'suite'))) for suite_no, suite in enumerate(suites, 1): try: suite_description = suite.description except: suite_description = suite.libid if args.quick and suite.slow: with self.console.progress("{} {} (skipped slow test)".format(lib, suite_description), 0): pass continue if args.exclude and suite.group is not None and suite.group in args.exclude: with self.console.progress("{} {} (excluded group)".format(lib, suite_description), 0): pass continue if args.group and suite.group not in args.group: with self.console.progress("{} {} (not in group)".format(lib, suite_description), 0): pass continue steps = 0 setup = suite.get_child((namespaces.test, 'setup')) teardown = suite.get_child((namespaces.test, 'teardown')) if setup is not None: setup_callable = archive.get_callable_from_element(setup) steps += 1 if teardown is not None: teardown_callable = archive.get_callable_from_element(teardown) steps += 1 tests = list(suite.children((namespaces.test, 'case'))) steps += len(tests) test_runner = {} context = Context({'_test_runner': test_runner}) results = TestResults(context, lib, suite.description) results.group = suite._definition all_results.append(results) context['._test_results'] = results test_info = "({} of {})".format(suite_no, len(suites)) progress_text = "{} {} {}".format(lib, suite_description, test_info) with self.console.progress(progress_text, steps) as progress: if steps == 0: progress.step() else: try: with TestRunner(context, results): if setup is not None: setup_callable(context) progress.step() except Exception as e: results.add_setup_error(setup, e) progress.step('setup failed') else: with TestRunner(context, results): for test in tests: results.case = test test_callable = archive.get_callable_from_element(test) try: test_return = test_callable(context) except Exception as e: results.add_error(test, e) else: if test_return != 'fail': results.add_pass(test) finally: progress.step() try: with TestRunner(context, results): if teardown is not None: teardown_callable(context) progress.step() except Exception as e: results.add_teardown_error(teardown, e) all_totals = {'pass': 0, 'fail': 0, 'error': 0} for result in all_results: _pass, fail, error = result.stats all_totals['pass'] += _pass all_totals['fail'] += fail all_totals['error'] += error summary = "{fail} fail(s), {error} error(s), {pass} pass(es)".format(**all_totals) if args.quick: console.div("Test results (quick) {}".format(datetime.now().ctime())) else: console.div("Test results {}".format(datetime.now().ctime())) test_count = sum(len(r.tests) for r in all_results) console.text('Ran {} test(s) in {} test suite(s) - {}'.format(test_count, len(all_results), summary)) for results in all_results: results.report(console, show_trace=not args.summary, show_passes=args.verbose or args.summary, verbose=args.verbose) header = ['lib', 'suite', 'passes', 'fails', 'errors'] table = [] passes = 0 fails = 0 errors = 0 for result in all_results: _pass, fail, error = result.stats passes += _pass fails += fail errors += error _pass = Cell(_pass, fg="green" if _pass else 'white', bold=True) fail = Cell(fail, fg="red" if fail else "green", bold=True) error = Cell(error, fg="red" if error else "green", bold=True) table.append([result.lib.long_name, result.suite, _pass, fail, error]) if not args.summary or args.verbose: console.nl() console.table(table, header_row=header) summary = "{fails} fail(s), {errors} error(s), {passes} pass(es)".format(fails=fails, errors=errors, passes=passes) if errors or fails: console.text(summary, fg="red", bold=True) else: console.text(summary, fg="green", bold=True) write_output() return fails
class MoyaSrv(object): """Moya Service""" def get_argparse(self): parser = argparse.ArgumentParser(prog="moya-srv", description=self.__doc__) parser.add_argument('-d', '--debug', dest="debug", action="store_true", help="enable debug information (show tracebacks)") parser.add_argument('--home', dest="home", metavar="PATH", default=None, help="moya service directory") subparsers = parser.add_subparsers(title="available sub-commands", dest="subcommand", help="sub-command help") list_parser = subparsers.add_parser( 'list', help="list projects", description="list enabled projects") list_parser where_parser = subparsers.add_parser( 'where', help='find project directory', description="output the location of the project") where_parser.add_argument(dest="name", metavar="PROJECT", help="name of a project") restart_parser = subparsers.add_parser('restart', help='restart a project server', description="restart a server") restart_parser.add_argument(dest="name", metavar="PROJECT", help="name of a project") install_parser = subparsers.add_parser( 'install', help='install moya service', description="install moya service") install_parser.add_argument( '--home', dest="home", metavar="PATH", default=DEFAULT_HOME_DIR, help="where to install service conf", ) install_parser.add_argument('--force', dest="force", action="store_true", help="overwrite files that exist") install_parser return parser def error(self, msg, code=-1): sys.stderr.write(msg + '\n') sys.exit(code) def run(self): parser = self.get_argparse() self.args = args = parser.parse_args(sys.argv[1:]) self.console = Console() if args.subcommand not in ['install']: self.home_dir = args.home or os.environ.get( 'MOYA_SERVICE_HOME', None) or DEFAULT_HOME_DIR settings_path = os.path.join(self.home_dir, 'moya.conf') try: with io.open(settings_path, 'rt') as f: self.settings = SettingsContainer.read_from_file(f) except IOError: self.error('unable to read {}'.format(settings_path)) return -1 method_name = "run_" + args.subcommand.replace('-', '_') try: return getattr(self, method_name)() or 0 except CommandError as e: self.error(text_type(e)) except Exception as e: if args.debug: raise self.error(text_type(e)) def _get_projects(self): project_paths = self.settings.get_list('projects', 'read') paths = [] cwd = os.getcwd() try: os.chdir(self.home_dir) for path in project_paths: glob_paths = glob.glob(path) paths.extend([os.path.abspath(p) for p in glob_paths]) finally: os.chdir(cwd) return paths def project_exists(self, name): for path in self._get_projects(): settings = self.read_project(path) if settings.get('service', 'name', None) == name: return True return False def read_project(self, path): settings = SettingsContainer.read_os(path) return settings def run_list(self): table = [] for path in self._get_projects(): settings = self.read_project(path) location = settings.get('service', 'location', '?') name = settings.get('service', 'name', '?') domains = "\n".join(settings.get_list('service', 'domains', "")) table.append([name, domains, path, location]) table.sort(key=lambda row: row[0].lower()) self.console.table( table, header_row=['name', 'domain(s)', 'conf', 'location']) def run_where(self): name = self.args.name location = None for path in self._get_projects(): settings = self.read_project(path) if settings.get('service', 'name', None) == name: location = settings.get('service', 'location', None) if location is None: self.error("no project '{}'".format(name)) return -1 sys.stdout.write(location) return 0 def run_restart(self): name = self.args.name if not self.project_exists(name): self.error("no project '{}'".format(name)) temp_dir = os.path.join( self.settings.get('service', 'temp_dir', tempfile.gettempdir()), 'moyasrv') try: os.makedirs(temp_dir) except OSError: pass change_path = os.path.join(temp_dir, "{}.changes".format(name)) try: with open(change_path, 'a'): os.utime(change_path, None) except IOError as e: sys.stderr.write("{}\n".format(text_type(e))) return -1 def run_install(self): home_dir = self.args.home or DEFAULT_HOME_DIR def create_dir(_path): path = os.path.join(home_dir, _path) try: if not os.path.exists(path): os.makedirs(path) sys.stdout.write("created '{}'\n".format(path)) except OSError as e: if e.errno != 17: raise for dirpath in ["", "sites-enabled", "sites-available"]: try: create_dir(dirpath) except OSError as e: if e.errno == 13: sys.stderr.write('permission denied (do you need sudo)?\n') return -1 raise def write_file(_path, contents): path = os.path.join(home_dir, _path) if not self.args.force and os.path.exists(path): sys.stdout.write( "not overwriting '{}' (use --force to overwrite)\n".format( path)) return with open(path, 'wt') as f: f.write(contents) sys.stdout.write("wrote '{}'\n".format(path)) for path, contents in [('moya.conf', DEFAULT_CONF), ('logging.ini', DEFAULT_LOGGING), ('bashtools', BASH_TOOLS)]: try: write_file(path, contents) except IOError as e: if e.errno == 13: sys.stdout.write( "permission denied writing '{}' (do you need sudo)?\n". format(path)) return -1 else: raise TOOLS_PATH = "~/.bashrc" bashtools_path = os.path.join(home_dir, 'bashtools') try: cmd = b'\n# Added by moya-srv install\nsource {}\n'.format( bashtools_path) bashrc_path = os.path.expanduser(TOOLS_PATH) if os.path.exists(bashrc_path): with open(bashrc_path, 'rb') as f: bashrc = f.read() else: bashrc = b'' if cmd not in bashrc: with open(bashrc_path, 'ab') as f: f.write(cmd) except Exception as e: sys.stdout.write( 'unable to add moya service bash tools ({})\n'.format(e)) else: sys.stdout.write( 'Added Moya service bash tools to {}\n'.format(TOOLS_PATH)) sys.stdout.write( "Tools will be available when you next log in (or run 'source {})\n" .format(bashtools_path)) sys.stdout.write('Moya service was installed in {}\n'.format(home_dir))
def run(self): args = self.args location = args.location console = self.console if args.output is not None: from moya.console import Console console = Console(html=True) def write_output(): if args.output is not None: from moya import consolehtml html = console.get_text() html = consolehtml.render_console_html(html) with open(args.output, 'wb') as f: f.write(html.encode('utf-8')) test_libs = [] for location in args.location: archive, lib = build.build_lib(location) lib_name = lib.long_name if archive.failed_documents: sys.stderr.write('library build failed\n') build.render_failed_documents(archive, console) write_output() return -1 test_libs.append(lib) for lib_name in test_libs: if not lib.load_tests(): sys.stderr.write('no tests for {}\n'.format(lib)) return -1 archive.finalize() if archive.failed_documents: sys.stderr.write('tests build failed\n') build.render_failed_documents(archive, console) write_output() return -1 if args.automated: archive.suppress_breakpoints = True all_results = [] for lib in test_libs: suites = list(lib.get_elements_by_type((namespaces.test, 'suite'))) for suite_no, suite in enumerate(suites, 1): try: suite_description = suite.description except: suite_description = suite.libid if args.quick and suite.slow: with self.console.progress("{} {} (skipped slow test)".format(lib, suite_description), 0): pass continue if args.exclude and suite.group is not None and suite.group in args.exclude: with self.console.progress("{} {} (excluded group)".format(lib, suite_description), 0): pass continue if args.group and suite.group not in args.group: with self.console.progress("{} {} (not in group)".format(lib, suite_description), 0): pass continue steps = 0 setup = suite.get_child((namespaces.test, 'setup')) teardown = suite.get_child((namespaces.test, 'teardown')) if setup is not None: setup_callable = archive.get_callable_from_element(setup) steps += 1 if teardown is not None: teardown_callable = archive.get_callable_from_element(teardown) steps += 1 tests = list(suite.children((namespaces.test, 'case'))) steps += len(tests) #test_runner = {"break": args._break} test_runner = {} context = Context({'_test_runner': test_runner}) results = TestResults(context, lib, suite.description) results.group = suite._definition all_results.append(results) context['._test_results'] = results test_info = "({} of {})".format(suite_no, len(suites)) progress_text = "{} {} {}".format(lib, suite_description, test_info) with self.console.progress(progress_text, steps) as progress: if steps == 0: progress.step() else: try: with TestRunner(context, results): if setup is not None: setup_callable(context) progress.step() except Exception as e: results.add_setup_error(setup, e) progress.step('setup failed') else: with TestRunner(context, results): for test in tests: results.case = test test_callable = archive.get_callable_from_element(test) try: test_return = test_callable(context) except Exception as e: results.add_error(test, e) else: if test_return != 'fail': results.add_pass(test) finally: progress.step() try: with TestRunner(context, results): if teardown is not None: teardown_callable(context) progress.step() except Exception as e: results.add_teardown_error(teardown, e) all_totals = {'pass': 0, 'fail': 0, 'error': 0} for result in all_results: _pass, fail, error = result.stats all_totals['pass'] += _pass all_totals['fail'] += fail all_totals['error'] += error summary = "{fail} fail(s), {error} error(s), {pass} pass(es)".format(**all_totals) #self.console.nl() if args.quick: console.div("Test results (quick) {}".format(datetime.now().ctime())) else: console.div("Test results {}".format(datetime.now().ctime())) test_count = sum(len(r.tests) for r in all_results) console.text('Ran {} test(s) in {} test suite(s) - {}'.format(test_count, len(all_results), summary)) for results in all_results: results.report(console, show_trace=not args.summary, show_passes=args.verbose or args.summary, verbose=args.verbose) header = ['lib', 'suite', 'passes', 'fails', 'errors'] table = [] passes = 0 fails = 0 errors = 0 for result in all_results: _pass, fail, error = result.stats passes += _pass fails += fail errors += error _pass = Cell(_pass, fg="green" if _pass else 'white', bold=True) fail = Cell(fail, fg="red" if fail else "green", bold=True) error = Cell(error, fg="red" if error else "green", bold=True) table.append([result.lib.long_name, result.suite, _pass, fail, error]) if not args.summary or args.verbose: console.nl() console.table(table, header_row=header) summary = "{fails} fail(s), {errors} error(s), {passes} pass(es)".format(fails=fails, errors=errors, passes=passes) if errors or fails: console.text(summary, fg="red", bold=True) else: console.text(summary, fg="green", bold=True) write_output() return fails
class MoyaSrv(object): """Moya Service""" def get_argparse(self): parser = argparse.ArgumentParser(prog="moya-srv", description=self.__doc__) parser.add_argument('-d', '--debug', dest="debug", action="store_true", help="enable debug information (show tracebacks)") parser.add_argument('--home', dest="home", metavar="PATH", default=None, help="moya service directory") subparsers = parser.add_subparsers(title="available sub-commands", dest="subcommand", help="sub-command help") list_parser = subparsers.add_parser('list', help="list projects", description="list enabled projects") list_parser where_parser = subparsers.add_parser('where', help='find project directory', description="output the location of the project") where_parser.add_argument(dest="name", metavar="PROJECT", help="name of a project") restart_parser = subparsers.add_parser('restart', help='restart a project server', description="restart a server") restart_parser.add_argument(dest="name", metavar="PROJECT", help="name of a project") return parser def error(self, msg, code=-1): sys.stderr.write(msg + '\n') sys.exit(code) def run(self): parser = self.get_argparse() self.args = args = parser.parse_args(sys.argv[1:]) self.console = Console() self.home_dir = args.home or os.environ.get('MOYA_SRV_HOME', None) or DEFAULT_HOME_DIR settings_path = os.path.join(self.home_dir, 'moya.conf') try: with io.open(settings_path, 'rt') as f: self.settings = SettingsContainer.read_from_file(f) except IOError: self.error('unable to read {}'.format(settings_path)) return -1 method_name = "run_" + args.subcommand.replace('-', '_') try: return getattr(self, method_name)() or 0 except CommandError as e: self.error(text_type(e)) except Exception as e: if args.debug: raise self.error(text_type(e)) def _get_projects(self): project_paths = self.settings.get_list('projects', 'read') paths = [] cwd = os.getcwd() try: os.chdir(self.home_dir) for path in project_paths: glob_paths = glob.glob(path) paths.extend([os.path.abspath(p) for p in glob_paths]) finally: os.chdir(cwd) return paths def project_exists(self, name): for path in self._get_projects(): settings = self.read_project(path) if settings.get('service', 'name', None) == name: return True return False def read_project(self, path): settings = SettingsContainer.read_os(path) return settings def run_list(self): table = [] for path in self._get_projects(): settings = self.read_project(path) location = settings.get('service', 'location', '?') name = settings.get('service', 'name', '?') domains = "\n".join(settings.get_list('service', 'domains', "")) table.append([name, domains, path, location]) self.console.table(table, header_row=['name', 'domain(s)', 'conf', 'location']) def run_where(self): name = self.args.name location = None for path in self._get_projects(): settings = self.read_project(path) if settings.get('service', 'name', None) == name: location = settings.get('service', 'location', None) if location is None: self.error("no project '{}'".format(name)) sys.stdout.write(location) def run_restart(self): name = self.args.name if not self.project_exists(name): self.error("no project '{}'".format(name)) temp_dir = os.path.join(self.settings.get('service', 'temp_dir', tempfile.gettempdir()), 'moyasrv') try: os.makedirs(temp_dir) except OSError: pass change_path = os.path.join(temp_dir, "{}.changes".format(name)) try: with open(change_path, 'a'): os.utime(change_path, None) except IOError as e: sys.stderr.write("{}\n".format(text_type(e))) return -1
class MoyaSrv(object): """Moya Service""" def get_argparse(self): parser = argparse.ArgumentParser(prog="moya-srv", description=self.__doc__) parser.add_argument('-d', '--debug', dest="debug", action="store_true", help="enable debug information (show tracebacks)") parser.add_argument('--home', dest="home", metavar="PATH", default=None, help="moya service directory") subparsers = parser.add_subparsers(title="available sub-commands", dest="subcommand", help="sub-command help") list_parser = subparsers.add_parser( 'list', help="list projects", description="list enabled projects") where_parser = subparsers.add_parser( 'where', help='find project directory', description="output the location of the project") where_parser.add_argument(dest="name", metavar="PROJECT", help="name of a project") restart_parser = subparsers.add_parser('restart', help='restart a project server', description="restart a server") restart_parser.add_argument(dest="name", metavar="PROJECT", help="name of a project") return parser def error(self, msg, code=-1): sys.stderr.write(msg + '\n') sys.exit(code) def run(self): parser = self.get_argparse() self.args = args = parser.parse_args(sys.argv[1:]) self.console = Console() self.home_dir = args.home or os.environ.get('MOYA_SRV_HOME', None) or DEFAULT_HOME_DIR settings_path = os.path.join(self.home_dir, 'moya.conf') try: with io.open(settings_path, 'rt') as f: self.settings = SettingsContainer.read_from_file(f) except IOError: self.error('unable to read {}'.format(settings_path)) return -1 method_name = "run_" + args.subcommand.replace('-', '_') try: return getattr(self, method_name)() or 0 except CommandError as e: self.error(text_type(e)) except Exception as e: if args.debug: raise self.error(text_type(e)) def _get_projects(self): project_paths = self.settings.get_list('projects', 'read') paths = [] cwd = os.getcwd() try: os.chdir(self.home_dir) for path in project_paths: glob_paths = glob.glob(path) paths.extend([os.path.abspath(p) for p in glob_paths]) finally: os.chdir(cwd) return paths def project_exists(self, name): for path in self._get_projects(): settings = self.read_project(path) if settings.get('service', 'name', None) == name: return True return False def read_project(self, path): settings = SettingsContainer.read_os(path) return settings def run_list(self): table = [] for path in self._get_projects(): settings = self.read_project(path) location = settings.get('service', 'location', '?') name = settings.get('service', 'name', '?') domains = "\n".join(settings.get_list('service', 'domains', "")) table.append([name, domains, path, location]) self.console.table( table, header_row=['name', 'domain(s)', 'conf', 'location']) def run_where(self): name = self.args.name location = None for path in self._get_projects(): settings = self.read_project(path) if settings.get('service', 'name', None) == name: location = settings.get('service', 'location', None) if location is None: self.error("no project '{}'".format(name)) sys.stdout.write(location) def run_restart(self): name = self.args.name if not self.project_exists(name): self.error("no project '{}'".format(name)) temp_dir = os.path.join( self.settings.get('service', 'temp_dir', tempfile.gettempdir()), 'moyasrv') try: os.makedirs(temp_dir) except OSError: pass change_path = os.path.join(temp_dir, "{}.changes".format(name)) try: with open(change_path, 'a'): os.utime(change_path, None) except IOError as e: sys.stderr.write("{}\n".format(text_type(e))) return -1