def test_shouldStop(self): """ When result.shouldStop == True, the suite should exit early. """ mock_test = MagicMock() gts = GreenTestSuite(args=default_args) gts._tests = (mock_test,) mock_result = MagicMock() mock_result.shouldStop = True gts.run(mock_result)
def test_failedModuleSetup(self): """ When module setup fails, we skip to the next test. """ mock_test = MagicMock() mock_test.__iter__.side_effect = TypeError gts = GreenTestSuite(args=default_args) gts._tests = (mock_test,) mock_result = MagicMock() mock_result._moduleSetUpFailed = True mock_result.shouldStop = False gts.run(mock_result)
def test_addTest_testPattern(self): """ Setting test_pattern will cause a test to be filtered. """ mock_test = MagicMock() mock_test._testMethodName = 'test_hello' mock_test2 = MagicMock() mock_test2._testMethodName = 'test_goodbye' args = copy.deepcopy(default_args) args.test_pattern = '_good*' gts = GreenTestSuite(args=args) gts.addTest(mock_test) self.assertEqual(gts._tests, []) gts.addTest(mock_test2) self.assertEqual(gts._tests, [mock_test2])
def test_GreenStream(self): """ run() can use a GreenStream for output. """ gs = GreenStream(self.stream) run(GreenTestSuite(), gs, args=self.args) self.assertIn("No Tests Found", self.stream.getvalue())
def loadTargets(targets, file_pattern='test*.py'): # If a string was passed in, put it into a list. if type(targets) != list: targets = [targets] # Make sure there are no duplicate entries, preserving order target_dict = OrderedDict() for target in targets: target_dict[target] = True targets = target_dict.keys() suites = [] for target in targets: suite = loadTarget(target, file_pattern) if not suite: debug("Found 0 tests for target '{}'".format(target)) continue suites.append(suite) num_tests = suite.countTestCases() debug("Found {} test{} for target '{}'".format( num_tests, '' if (num_tests == 1) else 's', target)) if suites: return GreenTestSuite(suites) else: return None
def loadFromModuleFilename(filename): dotted_module, parent_dir = findDottedModuleAndParentDir(filename) # Adding the parent path of the module to the start of sys.path is # the closest we can get to an absolute import in Python that I can # find. sys.path.insert(0, parent_dir) try: __import__(dotted_module) loaded_module = sys.modules[dotted_module] debug("Imported {}".format(dotted_module), 2) except unittest.case.SkipTest as e: # TODO: #25 - Right now this mimics the behavior in unittest. Lets # refactor it and simplify it after we make sure it works. # This is a cause of the traceback mangling I observed. reason = str(e) @unittest.case.skip(reason) def testSkipped(self): pass # pragma: no cover TestClass = type( str("ModuleSkipped"), (unittest.case.TestCase,), {filename: testSkipped}) return GreenTestSuite((TestClass(filename),)) except: # TODO: #25 - Right now this mimics the behavior in unittest. Lets # refactor it and simplify it after we make sure it works. # This is a cause of the traceback mangling I observed. message = ('Failed to import {} computed from filename {}\n{}').format( dotted_module, filename, traceback.format_exc()) def testFailure(self): raise ImportError(message) TestClass = type( str("ModuleImportFailure"), (unittest.case.TestCase,), {filename: testFailure}) return GreenTestSuite((TestClass(filename),)) finally: # This gets called before return statements in except clauses # actually return. Yay! sys.path.pop(0) # --- Find the tests inside the loaded module --- return loadFromModule(loaded_module)
def test_empty(self): """ run() does not crash with empty suite and processes """ suite = GreenTestSuite() self.args.processes = 2 self.args.termcolor = False run(suite, self.stream, self.args)
def test_noTestsFound(self): """ When we don't find any tests, we say so. """ result = run(GreenTestSuite(), self.stream, self.args) self.assertIn('No Tests Found', self.stream.getvalue()) self.assertEqual(result.testsRun, 0) self.assertEqual(result.wasSuccessful(), False)
def test_failedModuleTeardown(self): """ When module teardown fails, we report an error. """ mock_module = MagicMock() mock_test = MagicMock() mock_err = MagicMock() args = copy.deepcopy(default_args) gts = GreenTestSuite(args=args) gts._get_previous_module = mock_module mock_result = MagicMock() mock_result.errors.__len__.side_effect = [0, 1, 1] mock_result.errors.__getitem__.side_effect = [[], [(mock_test, mock_err)]] mock_result._previousTestClass.__name__ = "mockClass" gts.run(mock_result) self.assertTrue(mock_test.is_class_or_module_teardown_error)
def test_stdout(self): """ run() can use sys.stdout as the stream. """ saved_stdout = sys.stdout sys.stdout = self.stream self.addCleanup(setattr, sys, 'stdout', saved_stdout) run(GreenTestSuite(), sys.stdout, args=self.args) self.assertIn('No Tests Found', self.stream.getvalue())
def test_skip_in_setUpClass(self): """ If SkipTest is raised in setUpClass, then the test gets skipped """ gts = GreenTestSuite(args=default_args) mock_test = MagicMock() mock_result = MagicMock() mock_class = MagicMock() mock_result._previousTestClass = None mock_result._moduleSetUpFailed = None mock_result.__unittest_skip__ = None mock_test.__class__ = mock_class mock_class.setUpClass.side_effect = unittest.SkipTest("kaboom") gts._handleClassSetUp(mock_test, mock_result) self.assertTrue(mock_class.__unittest_skip__) self.assertEqual(mock_class.__unittest_skip_why__, "kaboom")
def test_allow_stdout(self): """ The allow_stdout setting should not get ignored. """ class Object(object): pass args = Object() args.allow_stdout = True gts = GreenTestSuite(args=args) self.assertEqual(gts.allow_stdout, True)
def discover(self, current_path, file_pattern="test*.py", top_level_dir=None): """ I take a path to a directory and discover all the tests inside files matching file_pattern. If path is not a readable directory, I raise an ImportError. If I don't find anything, I return None. Otherwise I return a GreenTestSuite """ current_abspath = os.path.abspath(current_path) if not os.path.isdir(current_abspath): raise ImportError("'{}' is not a directory".format( str(current_path))) suite = GreenTestSuite() try: for file_or_dir_name in sorted(os.listdir(current_abspath)): path = os.path.join(current_abspath, file_or_dir_name) # Recurse into directories, attempting to skip virtual environments bin_activate = os.path.join(path, "bin", "activate") if os.path.isdir(path) and not os.path.isfile(bin_activate): # Don't follow symlinks if os.path.islink(path): continue # Don't recurse into directories that couldn't be a package name if not python_dir_pattern.match(file_or_dir_name): continue subdir_suite = self.discover( path, file_pattern=file_pattern, top_level_dir=top_level_dir or current_path, ) if subdir_suite: suite.addTest(subdir_suite) elif os.path.isfile(path): # Skip irrelevant files if not python_file_pattern.match(file_or_dir_name): continue if not fnmatch(file_or_dir_name, file_pattern): continue # Try loading the file as a module module_suite = self.loadFromModuleFilename(path) if module_suite: suite.addTest(module_suite) except OSError: debug("WARNING: Test discovery failed at path {}".format( current_path)) return flattenTestSuite(suite) if suite.countTestCases() else None
def loadFromTestCase(test_case_class): debug("Examining test case {}".format(test_case_class.__name__), 3) test_case_names = list( filter( lambda attrname: (attrname.startswith('test') and callable( getattr(test_case_class, attrname)) and not isTestCaseDisabled( test_case_class, attrname)), dir(test_case_class))) debug("Test case names: {}".format(test_case_names)) test_case_names.sort( key=functools.cmp_to_key(lambda x, y: (x > y) - (x < y))) if not test_case_names and hasattr(test_case_class, 'runTest'): test_case_names = ['runTest'] return GreenTestSuite(map(test_case_class, test_case_names))
def discover(self, current_path, file_pattern='test*.py', top_level_dir=None): """ I take a path to a directory and discover all the tests inside files matching file_pattern. If path is not a readable directory, I raise an ImportError. If I don't find anything, I return None. Otherwise I return a GreenTestSuite """ current_abspath = os.path.abspath(current_path) if not os.path.isdir(current_abspath): raise ImportError( "'{}' is not a directory".format(str(current_path))) suite = GreenTestSuite() try: for file_or_dir_name in sorted(os.listdir(current_abspath)): path = os.path.join(current_abspath, file_or_dir_name) # Recurse into directories, attempting to skip virtual environments bin_activate = os.path.join(path, 'bin', 'activate') if os.path.isdir(path) and not os.path.isfile(bin_activate): # Don't follow symlinks if os.path.islink(path): continue # Don't recurse into directories that couldn't be a package name if not python_dir_pattern.match(file_or_dir_name): continue subdir_suite = self.discover( path, file_pattern=file_pattern, top_level_dir=top_level_dir or current_path ) if subdir_suite: suite.addTest(subdir_suite) elif os.path.isfile(path): # Skip irrelevant files if not python_file_pattern.match(file_or_dir_name): continue if not fnmatch(file_or_dir_name, file_pattern): continue # Try loading the file as a module module_suite = self.loadFromModuleFilename(path) if module_suite: suite.addTest(module_suite) except OSError: debug("WARNING: Test discovery failed at path {}".format(current_path)) return flattenTestSuite(suite) if suite.countTestCases() else None
def test_addTest_testPattern(self): """ Setting test_pattern will cause a test to be filtered. """ mock_test = MagicMock() mock_test._testMethodName = "test_hello" mock_test2 = MagicMock() mock_test2._testMethodName = "test_goodbye" args = copy.deepcopy(default_args) args.test_pattern = "_good*" gts = GreenTestSuite(args=args) gts.addTest(mock_test) self.assertEqual(gts._tests, []) gts.addTest(mock_test2) self.assertEqual(gts._tests, [mock_test2])
def run_tests(self): import sys if sys.version_info[:2] == (2, 6): import unittest2 as unittest # Python 2.6 else: import unittest setup_file = sys.modules['__main__'].__file__ setup_dir = os.path.abspath(os.path.dirname(setup_file)) tests = unittest.TestLoader().discover(os.path.join( setup_dir, 'tests'), pattern='*.py') try: # https://github.com/CleanCut/green/issues/50 from green.runner import run from green.suite import GreenTestSuite from green.config import default_args default_args.verbose = 3 run(GreenTestSuite(tests), sys.stdout, default_args) except ImportError: unittest.TextTestRunner(verbosity=2).run(tests)
def discover(current_path, file_pattern='test*.py'): """ I take a path to a directory and discover all the tests inside files matching file_pattern. If path is not a readable directory, I raise an ImportError. If I don't find anything, I return None. Otherwise I return a GreenTestSuite """ current_abspath = os.path.abspath(current_path) if not os.path.isdir(current_abspath): raise ImportError("'{}' is not a directory".format(str(current_path))) suite = GreenTestSuite() for file_or_dir_name in sorted(os.listdir(current_abspath)): path = os.path.join(current_abspath, file_or_dir_name) # Recurse into directories, attempting to skip virtual environments if os.path.isdir(path) and not os.path.isfile( os.path.join(path, 'bin', 'activate')): # Don't follow symlinks if os.path.islink(path): continue # Don't recurse into directories that couldn't be a package name if not python_dir_pattern.match(file_or_dir_name): continue subdir_suite = discover(path, file_pattern=file_pattern) if subdir_suite: suite.addTest(subdir_suite) elif os.path.isfile(path): # Skip irrelevant files if not python_file_pattern.match(file_or_dir_name): continue if not fnmatch(file_or_dir_name, file_pattern): continue # Try loading the file as a module module_suite = loadFromModuleFilename(path) if module_suite: suite.addTest(module_suite) return ((suite.countTestCases() and suite) or None)
def run_tests(self, test_labels, extra_tests=None, **kwargs): """ Run the unit tests for all the test labels in the provided list. Test labels should be dotted Python paths to test modules, test classes, or test methods. A list of 'extra' tests may also be provided; these tests will be added to the test suite. Returns the number of tests that failed. """ # Django setup self.setup_test_environment() django_db = self.setup_databases() # Green if type(test_labels) == tuple: test_labels = list(test_labels) else: raise ValueError("test_labels should be a tuple of strings") if not test_labels: test_labels = ["."] args = mergeConfig(Namespace()) if self.verbose != -1: args.verbose = self.verbose args.targets = test_labels stream = GreenStream(sys.stdout) suite = self.loader.loadTargets(args.targets) if not suite: suite = GreenTestSuite() result = run(suite, stream, args) # Django teardown self.teardown_databases(django_db) self.teardown_test_environment() return self.suite_result(suite, result)
def discover(current_path, file_pattern='test*.py'): """ I take a path to a directory and discover all the tests inside files matching file_pattern. If path is not a readable directory, I raise an ImportError. If I don't find anything, I return None. Otherwise I return a GreenTestSuite """ current_abspath = os.path.abspath(current_path) if not os.path.isdir(current_abspath): raise ImportError( "'%s' is not a directory".format(str(current_path))) suite = GreenTestSuite() for file_or_dir_name in sorted(os.listdir(current_abspath)): path = os.path.join(current_abspath, file_or_dir_name) # Recurse into directories, attempting to skip virtual environments if os.path.isdir(path) and not os.path.isfile(os.path.join(path, 'bin', 'activate')): subdir_suite = discover(path, file_pattern=file_pattern) if subdir_suite: suite.addTest(subdir_suite) elif os.path.isfile(path): # Skip irrelevant files if not python_file_pattern.match(file_or_dir_name): continue if not fnmatch(file_or_dir_name, file_pattern): continue # Try loading the file as a module module_suite = loadFromModuleFilename(path) if module_suite: suite.addTest(module_suite) return ((suite.countTestCases() and suite) or None)
debug("Test case names: {}".format(test_case_names)) test_case_names.sort( key=functools.cmp_to_key(lambda x, y: (x > y) - (x < y))) if not test_case_names and hasattr(test_case_class, 'runTest'): test_case_names = ['runTest'] return GreenTestSuite(map(test_case_class, test_case_names)) def loadFromModule(module): debug("Examining module {} for test cases".format(module.__name__), 2) test_cases = [] for item in dir(module): obj = getattr(module, item) if isinstance(obj, type) and issubclass(obj, unittest.case.TestCase): test_cases.append(loadFromTestCase(obj)) return GreenTestSuite(test_cases) def loadFromModuleFilename(filename): dotted_module, parent_dir = findDottedModuleAndParentDir(filename) # Adding the parent path of the module to the start of sys.path is # the closest we can get to an absolute import in Python that I can # find. sys.path.insert(0, parent_dir) try: __import__(dotted_module) loaded_module = sys.modules[dotted_module] debug("Imported {}".format(dotted_module), 2) except unittest.case.SkipTest as e: # TODO: #25 - Right now this mimics the behavior in unittest. Lets # refactor it and simplify it after we make sure it works.
def suite(): """ return our TestSuite """ suite = GreenTestSuite() suite = loadFromTestCase(TestConfig) return suite
def test_empty(self): """ An empty suite can be instantiated. """ GreenTestSuite()
def test_noTestsFound(self): """ When we don't find any tests, we say so. """ run(GreenTestSuite(), self.stream, self.args) self.assertIn('No Tests Found', self.stream.getvalue())
def _main(argv, testing): args = config.parseArguments(argv) args = config.mergeConfig(args, testing) if args.shouldExit: return args.exitCode # Clear out all the passed-in-options just in case someone tries to run a # test that assumes sys.argv is clean. I can't guess at the script name # that they want, though, so we'll just leave ours. sys.argv = sys.argv[:1] # Set up our various main objects from green.loader import GreenTestLoader, getCompletions from green.runner import run from green.output import GreenStream, debug import green.output from green.suite import GreenTestSuite GreenTestSuite.args = args if args.debug: green.output.debug_level = args.debug stream = GreenStream(sys.stdout, disable_windows=args.disable_windows) # Location of shell completion file if args.completion_file: print(os.path.join(os.path.dirname(__file__), "shell_completion.sh")) return 0 # Argument-completion for bash and zsh (for test-target completion) if args.completions: print(getCompletions(args.targets)) return 0 # Option-completion for bash and zsh if args.options: print("\n".join(sorted(args.store_opt.options))) return 0 # Add debug logging for stuff that happened before this point here if config.files_loaded: debug("Loaded config file(s): {}".format(", ".join( config.files_loaded))) # Discover/Load the test suite if testing: test_suite = None else: # pragma: no cover loader = GreenTestLoader() test_suite = loader.loadTargets(args.targets, file_pattern=args.file_pattern) # We didn't even load 0 tests... if not test_suite: debug( "No test loading attempts succeeded. Created an empty test suite." ) test_suite = GreenTestSuite() # Actually run the test_suite result = run(test_suite, stream, args, testing) # Generate a test report if required if args.junit_report: from green.junit import JUnitXML adapter = JUnitXML() with open(args.junit_report, "w") as report_file: adapter.save_as(result, report_file) return int(not result.wasSuccessful())
def loadTarget(target, file_pattern='test*.py'): """ """ debug("Attempting to load target '{}' with file_pattern '{}'".format( target, file_pattern)) loader = unittest.TestLoader() loader.suiteClass = GreenTestSuite # For a test loader, we want to always the current working directory to be # the first item in sys.path, just like when a python interpreter is loaded # interactively. See also # https://docs.python.org/3.4/library/sys.html#sys.path if sys.path[0] != '': sys.path.insert(0, '') # DIRECTORY VARIATIONS - These will discover all tests in a directory # structure, whether or not they are accessible by the root package. # some/real/dir bare_dir = target # some.real.dir if ('.' in target) and (len(target) > 1): dot_dir = target[0] + target[1:].replace('.', os.sep) else: dot_dir = None # pyzmq.tests (Package (=dir) in PYTHONPATH, including installed ones) pkg_in_path_dir = None if target and (target[0] != '.'): try: filename = importlib.import_module(target).__file__ if '__init__.py' in filename: pkg_in_path_dir = os.path.dirname(filename) except: pkg_in_path_dir = None # => DISCOVER DIRS tests = None for candidate in [bare_dir, dot_dir, pkg_in_path_dir]: if (candidate is None) or (not os.path.isdir(candidate)): continue tests = discover(candidate, file_pattern=file_pattern) if tests and tests.countTestCases(): debug("Load method: DISCOVER - {}".format(candidate)) return tests # DOTTED OBJECT - These will discover a specific object if it is # globally importable or importable from the current working directory. # Examples: pkg, pkg.module, pkg.module.class, pkg.module.class.func tests = None if target and (target[0] != '.'): # We don't handle relative dot objects try: tests = loader.loadTestsFromName(target) for index, test in enumerate(tests): if test.__class__.__name__ == '_FailedTest': # pragma: no cover del (tests._tests[index]) except Exception as e: debug("IGNORED exception: {}".format(e)) if tests and tests.countTestCases(): debug("Load method: DOTTED OBJECT - {}".format(target)) return tests # FILE VARIATIONS - These will import a specific file and any tests # accessible from its scope. # some/file.py bare_file = target # some/file pyless_file = target + '.py' for candidate in [bare_file, pyless_file]: if (candidate is None) or (not os.path.isfile(candidate)): continue need_cleanup = False cwd = os.getcwd() if cwd != sys.path[0]: need_cleanup = True sys.path.insert(0, cwd) try: dotted_path = target.replace('.py', '').replace(os.sep, '.') tests = loader.loadTestsFromName(dotted_path) except: # Any exception could occur here # TODO: #25 - Right now this mimics the behavior in unittest. Lets # refactor it and simplify it after we make sure it works. # This is a cause of the traceback mangling I observed. try: message = ('Failed to import "{}":\n{}').format( dotted_path, traceback.format_exc()) # If the line that caused the exception has unicode literals in it # anywhere, then python 2.7 will crash on traceback.format_exc(). # Python 3 is ok. except UnicodeDecodeError: # pragma: no cover message = ('Failed to import "{}", and the import traceback ' 'has a unicode decode error, so I can\'t display ' 'it.'.format(dotted_path)) def testFailure(self): raise ImportError(message) TestClass = type(str("ModuleImportFailure"), (unittest.case.TestCase, ), {dotted_path: testFailure}) return GreenTestSuite((TestClass(dotted_path), )) if need_cleanup: sys.path.remove(cwd) if tests and tests.countTestCases(): debug("Load method: FILE - {}".format(candidate)) return tests return None
def test_defaultArgs(self): """ Passing in default arguments causes attributes to be set. """ gts = GreenTestSuite(args=default_args) self.assertEqual(gts.allow_stdout, default_args.allow_stdout)