def _do_discovery(self, argv, Loader=None): """The discovery process is complicated by the fact that: * different test suites live under different directories * some test suites may not be available (CUDA) * some tests may have to be run serially, even in the presence of the '-m' flag.""" from numba import cuda join = os.path.join loader = unittest.TestLoader() if Loader is None else Loader() topdir = os.path.abspath(join(os.path.dirname(__file__), '../..')) base_tests = loader.discover(join(topdir, 'numba/tests'), 'test*.py', topdir) cuda_tests = [ loader.discover(join(topdir, 'numba/cuda/tests/nocuda'), 'test*.py', topdir) ] if cuda.is_available(): gpus = cuda.list_devices() if gpus and gpus[0].compute_capability >= (2, 0): cuda_tests.append( loader.discover(join(topdir, 'numba/cuda/tests/cudadrv'), 'test*.py', topdir)) cuda_tests.append( loader.discover(join(topdir, 'numba/cuda/tests/cudapy'), 'test*.py', topdir)) else: print("skipped CUDA tests because GPU CC < 2.0") else: print("skipped CUDA tests") self.test = suite.TestSuite(tests=(base_tests, SerialSuite(cuda_tests)))
def build_tests(path, loader, host=None, port=8001, intercept=None, test_loader_name=None, fixture_module=None, response_handlers=None, prefix=''): """Read YAML files from a directory to create tests. Each YAML file represents an ordered sequence of HTTP requests. :param path: The directory where yaml files are located. :param loader: The TestLoader. :param host: The host to test against. Do not use with ``intercept``. :param port: The port to test against. Used with ``host``. :param intercept: WSGI app factory for wsgi-intercept. :param test_loader_name: Base name for test classes. Rarely used. :param fixture_module: Python module containing fixture classes. :param response_handers: ResponseHandler classes. :type response_handlers: List of ResponseHandler classes. :param prefix: A URL prefix for all URLs that are not fully qualified. :rtype: TestSuite containing multiple TestSuites (one for each YAML file). """ # Exit immediately if we have no host to access, either via a real host # or an intercept. if not (bool(host) ^ bool(intercept)): raise AssertionError('must specify exactly one of host or intercept') if test_loader_name is None: test_loader_name = inspect.stack()[1] test_loader_name = os.path.splitext( os.path.basename(test_loader_name[1]))[0] # Initialize response handlers. response_handlers = response_handlers or [] for handler in RESPONSE_HANDLERS + response_handlers: handler(case.HTTPTestCase) top_suite = suite.TestSuite() for test_file in glob.iglob('%s/*.yaml' % path): if intercept: host = str(uuid.uuid4()) suite_dict = load_yaml(test_file) test_base_name = '%s_%s' % ( test_loader_name, os.path.splitext(os.path.basename(test_file))[0]) file_suite = test_suite_from_dict(loader, test_base_name, suite_dict, path, host, port, fixture_module, intercept, prefix) top_suite.addTest(file_suite) return top_suite
def load_tests(modules, loader): if modules: log.info("Executing modules: {0}".format(modules)) module_suites = [] for mod_name in modules: # Test names like cephfs.test_auto_repair module_suites.append(loader.loadTestsFromName(mod_name)) log.info("Loaded: {0}".format(list(module_suites))) return suite.TestSuite(module_suites) else: log.info("Executing all cephfs tests") return loader.discover( os.path.join(os.path.dirname(os.path.abspath(__file__)), "cephfs"))
def build_tests(path, loader, host=None, port=8001, intercept=None, test_loader_name=None, fixture_module=None, response_handlers=None): """Read YAML files from a directory to create tests. Each YAML file represents an ordered sequence of HTTP requests. """ if not (bool(host) ^ bool(intercept)): raise AssertionError('must specify exactly one of host or intercept') response_handlers = response_handlers or [] top_suite = suite.TestSuite() if test_loader_name is None: test_loader_name = inspect.stack()[1] test_loader_name = os.path.splitext( os.path.basename(test_loader_name[1]))[0] yaml_file_glob = '%s/*.yaml' % path # Initialize the extensions for response handling. for handler in RESPONSE_HANDLERS + response_handlers: handler(case.HTTPTestCase) # Return an empty suite if we have no host to access, either via # a real host or an intercept for test_file in glob.iglob(yaml_file_glob): if intercept: host = str(uuid.uuid4()) test_yaml = load_yaml(test_file) test_name = '%s_%s' % ( test_loader_name, os.path.splitext(os.path.basename(test_file))[0]) file_suite = test_suite_from_yaml(loader, test_name, test_yaml, path, host, port, fixture_module, intercept) top_suite.addTest(file_suite) return top_suite
def task(ctx, config): """ Run the CephFS test cases. Run everything in tasks/cephfs/test_*.py: :: tasks: - install: - ceph: - ceph-fuse: - cephfs_test_runner: `modules` argument allows running only some specific modules: :: tasks: ... - cephfs_test_runner: modules: - tasks.cephfs.test_sessionmap - tasks.cephfs.test_auto_repair By default, any cases that can't be run on the current cluster configuration will generate a failure. When the optional `fail_on_skip` argument is set to false, any tests that can't be run on the current configuration will simply be skipped: :: tasks: ... - cephfs_test_runner: fail_on_skip: false """ ceph_cluster = CephCluster(ctx) if len(list(misc.all_roles_of_type(ctx.cluster, 'mds'))): mds_cluster = MDSCluster(ctx) fs = Filesystem(ctx) else: mds_cluster = None fs = None if len(list(misc.all_roles_of_type(ctx.cluster, 'mgr'))): mgr_cluster = MgrCluster(ctx) else: mgr_cluster = None # Mount objects, sorted by ID if hasattr(ctx, 'mounts'): mounts = [ v for k, v in sorted(ctx.mounts.items(), lambda a, b: cmp(a[0], b[0])) ] else: # The test configuration has a filesystem but no fuse/kclient mounts mounts = [] decorating_loader = DecoratingLoader({ "ctx": ctx, "mounts": mounts, "fs": fs, "ceph_cluster": ceph_cluster, "mds_cluster": mds_cluster, "mgr_cluster": mgr_cluster, }) fail_on_skip = config.get('fail_on_skip', True) # Put useful things onto ctx for interactive debugging ctx.fs = fs ctx.mds_cluster = mds_cluster ctx.mgr_cluster = mgr_cluster # Depending on config, either load specific modules, or scan for moduless if config and 'modules' in config and config['modules']: module_suites = [] for mod_name in config['modules']: # Test names like cephfs.test_auto_repair module_suites.append(decorating_loader.loadTestsFromName(mod_name)) overall_suite = suite.TestSuite(module_suites) else: # Default, run all tests overall_suite = decorating_loader.discover( os.path.join(os.path.dirname(os.path.abspath(__file__)), "cephfs/")) if ctx.config.get("interactive-on-error", False): InteractiveFailureResult.ctx = ctx result_class = InteractiveFailureResult else: result_class = unittest.TextTestResult class LoggingResult(result_class): def startTest(self, test): log.info("Starting test: {0}".format(self.getDescription(test))) return super(LoggingResult, self).startTest(test) def addSkip(self, test, reason): if fail_on_skip: # Don't just call addFailure because that requires a traceback self.failures.append((test, reason)) else: super(LoggingResult, self).addSkip(test, reason) # Execute! result = unittest.TextTestRunner(stream=LogStream(), resultclass=LoggingResult, verbosity=2, failfast=True).run(overall_suite) if not result.wasSuccessful(): result.printErrors() # duplicate output at end for convenience bad_tests = [] for test, error in result.errors: bad_tests.append(str(test)) for test, failure in result.failures: bad_tests.append(str(test)) raise RuntimeError("Test failure: {0}".format(", ".join(bad_tests))) yield
def __runner_run(self, item): # 开始run test test_suites = suite.TestSuite(item) runner = TextTestRunner(verbosity=1) result = runner.run(test_suites) self.all_result.append(result)
def exec_test(): # Help developers by stopping up-front if their tree isn't built enough for all the # tools that the tests might want to use (add more here if needed) require_binaries = [ "ceph-dencoder", "cephfs-journal-tool", "cephfs-data-scan", "cephfs-table-tool", "ceph-fuse", "rados" ] missing_binaries = [ b for b in require_binaries if not os.path.exists(os.path.join(BIN_PREFIX, b)) ] if missing_binaries: log.error("Some ceph binaries missing, please build them: {0}".format( " ".join(missing_binaries))) sys.exit(-1) test_dir = tempfile.mkdtemp() # Create as many of these as the biggest test requires clients = ["0", "1", "2", "3"] remote = LocalRemote() # Tolerate no MDSs or clients running at start ps_txt = remote.run(args=["ps", "-u" + str(os.getuid())]).stdout.getvalue().strip() lines = ps_txt.split("\n")[1:] for line in lines: if 'ceph-fuse' in line or 'ceph-mds' in line: pid = int(line.split()[0]) log.warn("Killing stray process {0}".format(line)) os.kill(pid, signal.SIGKILL) class LocalCluster(object): def __init__(self, rolename="placeholder"): self.remotes = {remote: [rolename]} def only(self, requested): return self.__class__(rolename=requested) teuth_config['test_path'] = test_dir class LocalContext(object): def __init__(self): self.config = {} self.teuthology_config = teuth_config self.cluster = LocalCluster() self.daemons = DaemonGroup() # Shove some LocalDaemons into the ctx.daemons DaemonGroup instance so that any # tests that want to look these up via ctx can do so. # Inspect ceph.conf to see what roles exist for conf_line in open("ceph.conf").readlines(): for svc_type in ["mon", "osd", "mds", "mgr"]: if svc_type not in self.daemons.daemons: self.daemons.daemons[svc_type] = {} match = re.match("^\[{0}\.(.+)\]$".format(svc_type), conf_line) if match: svc_id = match.group(1) self.daemons.daemons[svc_type][svc_id] = LocalDaemon( svc_type, svc_id) def __del__(self): shutil.rmtree(self.teuthology_config['test_path']) ctx = LocalContext() mounts = [] for client_id in clients: # Populate client keyring (it sucks to use client.admin for test clients # because it's awkward to find the logs later) client_name = "client.{0}".format(client_id) if client_name not in open("./keyring").read(): p = remote.run(args=[ os.path.join(BIN_PREFIX, "ceph"), "auth", "get-or-create", client_name, "osd", "allow rw", "mds", "allow", "mon", "allow r" ]) open("./keyring", "a").write(p.stdout.getvalue()) mount = LocalFuseMount(test_dir, client_id) mounts.append(mount) if mount.is_mounted(): log.warn("unmounting {0}".format(mount.mountpoint)) mount.umount_wait() else: if os.path.exists(mount.mountpoint): os.rmdir(mount.mountpoint) filesystem = LocalFilesystem(ctx) ceph_cluster = LocalCephCluster(ctx) mds_cluster = LocalMDSCluster(ctx) mgr_cluster = LocalMgrCluster(ctx) from tasks.cephfs_test_runner import DecoratingLoader class LogStream(object): def __init__(self): self.buffer = "" def write(self, data): self.buffer += data if "\n" in self.buffer: lines = self.buffer.split("\n") for line in lines[:-1]: pass # sys.stderr.write(line + "\n") log.info(line) self.buffer = lines[-1] def flush(self): pass decorating_loader = DecoratingLoader({ "ctx": ctx, "mounts": mounts, "ceph_cluster": ceph_cluster, "fs": filesystem, "mds_cluster": mds_cluster, "mgr_cluster": mgr_cluster, }) # For the benefit of polling tests like test_full -- in teuthology land we set this # in a .yaml, here it's just a hardcoded thing for the developer's pleasure. remote.run(args=[ os.path.join(BIN_PREFIX, "ceph"), "tell", "osd.*", "injectargs", "--osd-mon-report-interval-max", "5" ]) filesystem.set_ceph_conf("osd", "osd_mon_report_interval_max", "5") # Vstart defaults to two segments, which very easily gets a "behind on trimming" health warning # from normal IO latency. Increase it for running teests. filesystem.set_ceph_conf("mds", "mds log max segments", "10") # Make sure the filesystem created in tests has uid/gid that will let us talk to # it after mounting it (without having to go root). Set in 'global' not just 'mds' # so that cephfs-data-scan will pick it up too. filesystem.set_ceph_conf("global", "mds root ino uid", "%s" % os.getuid()) filesystem.set_ceph_conf("global", "mds root ino gid", "%s" % os.getgid()) # Monkeypatch get_package_version to avoid having to work out what kind of distro we're on def _get_package_version(remote, pkg_name): # Used in cephfs tests to find fuse version. Your development workstation *does* have >=2.9, right? return "2.9" import teuthology.packaging teuthology.packaging.get_package_version = _get_package_version def enumerate_methods(s): for t in s._tests: if isinstance(t, suite.BaseTestSuite): for sub in enumerate_methods(t): yield sub else: yield s, t interactive_on_error = False args = sys.argv[1:] flags = [a for a in args if a.startswith("-")] modules = [a for a in args if not a.startswith("-")] for f in flags: if f == "--interactive": interactive_on_error = True else: log.error("Unknown option '{0}'".format(f)) sys.exit(-1) if modules: log.info("Executing modules: {0}".format(modules)) module_suites = [] for mod_name in modules: # Test names like cephfs.test_auto_repair module_suites.append(decorating_loader.loadTestsFromName(mod_name)) log.info("Loaded: {0}".format(list(module_suites))) overall_suite = suite.TestSuite(module_suites) else: log.info("Excuting all tests") overall_suite = decorating_loader.discover( os.path.dirname(os.path.abspath(__file__))) # Filter out tests that don't lend themselves to interactive running, victims = [] for case, method in enumerate_methods(overall_suite): fn = getattr(method, method._testMethodName) drop_test = False if hasattr(fn, 'is_for_teuthology') and getattr( fn, 'is_for_teuthology') is True: drop_test = True log.warn("Dropping test because long running: ".format( method.id())) if getattr(fn, "needs_trimming", False) is True: drop_test = (os.getuid() != 0) log.warn("Dropping test because client trim unavailable: ".format( method.id())) if drop_test: # Don't drop the test if it was explicitly requested in arguments is_named = False for named in modules: if named.endswith(method.id()): is_named = True break if not is_named: victims.append((case, method)) log.info( "Disabling {0} tests because of is_for_teuthology or needs_trimming". format(len(victims))) for s, method in victims: s._tests.remove(method) if interactive_on_error: result_class = InteractiveFailureResult else: result_class = unittest.TextTestResult fail_on_skip = False class LoggingResult(result_class): def startTest(self, test): log.info("Starting test: {0}".format(self.getDescription(test))) test.started_at = datetime.datetime.utcnow() return super(LoggingResult, self).startTest(test) def stopTest(self, test): log.info("Stopped test: {0} in {1}s".format( self.getDescription(test), (datetime.datetime.utcnow() - test.started_at).total_seconds())) def addSkip(self, test, reason): if fail_on_skip: # Don't just call addFailure because that requires a traceback self.failures.append((test, reason)) else: super(LoggingResult, self).addSkip(test, reason) # Execute! result = unittest.TextTestRunner(stream=LogStream(), resultclass=LoggingResult, verbosity=2, failfast=True).run(overall_suite) if not result.wasSuccessful(): result.printErrors() # duplicate output at end for convenience bad_tests = [] for test, error in result.errors: bad_tests.append(str(test)) for test, failure in result.failures: bad_tests.append(str(test)) sys.exit(-1) else: sys.exit(0)
def build_tests(path, loader, host=None, port=8001, intercept=None, test_loader_name=None, fixture_module=None, response_handlers=None, content_handlers=None, prefix='', require_ssl=False, cert_validate=True, url=None, inner_fixtures=None, verbose=False, use_prior_test=True, safe_yaml=True): """Read YAML files from a directory to create tests. Each YAML file represents a list of HTTP requests. :param path: The directory where yaml files are located. :param loader: The TestLoader. :param host: The host to test against. Do not use with ``intercept``. :param port: The port to test against. Used with ``host``. :param intercept: WSGI app factory for wsgi-intercept. :param test_loader_name: Base name for test classes. Use this to align the naming of the tests with other tests in a system. :param fixture_module: Python module containing fixture classes. :param response_handers: :class:`~gabbi.handlers.ResponseHandler` classes. :type response_handlers: List of ResponseHandler classes. :param content_handlers: ContentHandler classes. :type content_handlers: List of ContentHandler classes. :param prefix: A URL prefix for all URLs that are not fully qualified. :param url: A full URL to test against. Replaces host, port and prefix. :param require_ssl: If ``True``, make all tests default to using SSL. :param inner_fixtures: A list of ``Fixtures`` to use with each individual test request. :type inner_fixtures: List of classes with setUp and cleanUp methods to be used as fixtures. :param verbose: If ``True`` or ``'all'``, make tests verbose by default ``'headers'`` and ``'body'`` are also accepted. :param use_prior_test: If ``True``, uses prior test to create ordered sequence of tests :param safe_yaml: If ``True``, recognizes only standard YAML tags and not Python object :param cert_validate: If ``False`` ssl server certificate will be ignored, further it will not be validated if provided (set cert_reqs=CERT_NONE to the Http object) :rtype: TestSuite containing multiple TestSuites (one for each YAML file). """ # If url is being used, reset host, port and prefix. if url: host, port, prefix, force_ssl = utils.host_info_from_target(url) if force_ssl and not require_ssl: require_ssl = force_ssl # Exit immediately if we have no host to access, either via a real host # or an intercept. if not ((host is not None) ^ bool(intercept)): raise AssertionError( 'must specify exactly one of host or url, or intercept') # If the client has not provided a name to use as our base, # create one so that tests are effectively namespaced. if test_loader_name is None: all_test_base_name = inspect.stack()[1] all_test_base_name = os.path.splitext( os.path.basename(all_test_base_name[1]))[0] else: all_test_base_name = None # Initialize response and content handlers. This is effectively # duplication of effort but not results. This allows for # backwards compatibility for existing callers. response_handlers = response_handlers or [] content_handlers = content_handlers or [] handler_objects = [] for handler in (content_handlers + response_handlers + handlers.RESPONSE_HANDLERS): handler_objects.append(handler()) top_suite = suite.TestSuite() for test_file in glob.iglob('%s/*.yaml' % path): if '_' in os.path.basename(test_file): warnings.warn( exception.GabbiSyntaxWarning( "'_' in test filename %s. This can break suite grouping." % test_file)) if intercept: host = str(uuid.uuid4()) suite_dict = utils.load_yaml(yaml_file=test_file, safe=safe_yaml) test_base_name = os.path.splitext(os.path.basename(test_file))[0] if all_test_base_name: test_base_name = '%s_%s' % (all_test_base_name, test_base_name) if require_ssl: if 'defaults' in suite_dict: suite_dict['defaults']['ssl'] = True else: suite_dict['defaults'] = {'ssl': True} if any((verbose == opt for opt in [True, 'all', 'headers', 'body'])): if 'defaults' in suite_dict: suite_dict['defaults']['verbose'] = verbose else: suite_dict['defaults'] = {'verbose': verbose} if not cert_validate: if 'defaults' in suite_dict: suite_dict['defaults']['cert_validate'] = False else: suite_dict['defaults'] = {'cert_validate': False} if not use_prior_test: if 'defaults' in suite_dict: suite_dict['defaults']['use_prior_test'] = use_prior_test else: suite_dict['defaults'] = {'use_prior_test': use_prior_test} file_suite = suitemaker.test_suite_from_dict( loader, test_base_name, suite_dict, path, host, port, fixture_module, intercept, prefix=prefix, test_loader_name=test_loader_name, handlers=handler_objects, inner_fixtures=inner_fixtures) top_suite.addTest(file_suite) return top_suite