# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of logilab-common. # # logilab-common is free software: you can redistribute it and/or modify it under # the terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 2.1 of the License, or (at your option) any # later version. # # logilab-common is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License along # with logilab-common. If not, see . """unittest module for logilab.comon.testlib""" from __future__ import print_function import os import sys from os.path import join, dirname, isdir, isfile, abspath, exists import tempfile import shutil try: __file__ except NameError: __file__ = sys.argv[0] from logilab.common.compat import StringIO from logilab.common.testlib import ( unittest, TestSuite, unittest_main, Tags, TestCase, mock_object, create_files, InnerTest, with_tempdir, tag, require_version, require_module, ) from logilab.common.pytest import SkipAwareTextTestRunner, NonStrictTestLoader class MockTestCase(TestCase): def __init__(self): # Do not call unittest.TestCase's __init__ pass def fail(self, msg): raise AssertionError(msg) class UtilTC(TestCase): def test_mockobject(self): obj = mock_object(foo="bar", baz="bam") self.assertEqual(obj.foo, "bar") self.assertEqual(obj.baz, "bam") def test_create_files(self): chroot = tempfile.mkdtemp() path_to = lambda path: join(chroot, path) dircontent = lambda path: sorted(os.listdir(join(chroot, path))) try: self.assertFalse(isdir(path_to("a/"))) create_files(["a/b/foo.py", "a/b/c/", "a/b/c/d/e.py"], chroot) # make sure directories exist self.assertTrue(isdir(path_to("a"))) self.assertTrue(isdir(path_to("a/b"))) self.assertTrue(isdir(path_to("a/b/c"))) self.assertTrue(isdir(path_to("a/b/c/d"))) # make sure files exist self.assertTrue(isfile(path_to("a/b/foo.py"))) self.assertTrue(isfile(path_to("a/b/c/d/e.py"))) # make sure only asked files were created self.assertEqual(dircontent("a"), ["b"]) self.assertEqual(dircontent("a/b"), ["c", "foo.py"]) self.assertEqual(dircontent("a/b/c"), ["d"]) self.assertEqual(dircontent("a/b/c/d"), ["e.py"]) finally: shutil.rmtree(chroot) class TestlibTC(TestCase): def mkdir(self, path): if not exists(path): self._dirs.add(path) os.mkdir(path) def setUp(self): self.tc = MockTestCase() self._dirs = set() def tearDown(self): while self._dirs: shutil.rmtree(self._dirs.pop(), ignore_errors=True) def test_dict_equals(self): """tests TestCase.assertDictEqual""" d1 = {"a": 1, "b": 2} d2 = {"a": 1, "b": 3} d3 = dict(d1) self.assertRaises(AssertionError, self.tc.assertDictEqual, d1, d2) self.tc.assertDictEqual(d1, d3) self.tc.assertDictEqual(d3, d1) self.tc.assertDictEqual(d1, d1) def test_list_equals(self): """tests TestCase.assertListEqual""" l1 = list(range(10)) l2 = list(range(5)) l3 = list(range(10)) self.assertRaises(AssertionError, self.tc.assertListEqual, l1, l2) self.tc.assertListEqual(l1, l1) self.tc.assertListEqual(l1, l3) self.tc.assertListEqual(l3, l1) def test_equality_for_sets(self): s1 = set("ab") s2 = set("a") self.assertRaises(AssertionError, self.tc.assertSetEqual, s1, s2) self.tc.assertSetEqual(s1, s1) self.tc.assertSetEqual(set(), set()) def test_text_equality(self): self.assertRaises(AssertionError, self.tc.assertMultiLineEqual, "toto", 12) self.assertRaises(AssertionError, self.tc.assertMultiLineEqual, "toto", 12) self.assertRaises(AssertionError, self.tc.assertMultiLineEqual, "toto", None) self.assertRaises(AssertionError, self.tc.assertMultiLineEqual, "toto", None) self.assertRaises(AssertionError, self.tc.assertMultiLineEqual, 3.12, "toto") self.assertRaises(AssertionError, self.tc.assertMultiLineEqual, 3.12, "toto") self.assertRaises(AssertionError, self.tc.assertMultiLineEqual, None, "toto") self.assertRaises(AssertionError, self.tc.assertMultiLineEqual, None, "toto") self.tc.assertMultiLineEqual("toto\ntiti", "toto\ntiti") self.tc.assertMultiLineEqual("toto\ntiti", "toto\ntiti") self.assertRaises( AssertionError, self.tc.assertMultiLineEqual, "toto\ntiti", "toto\n titi\n" ) self.assertRaises( AssertionError, self.tc.assertMultiLineEqual, "toto\ntiti", "toto\n titi\n" ) foo = join(dirname(__file__), "data", "foo.txt") spam = join(dirname(__file__), "data", "spam.txt") with open(foo) as fobj: text1 = fobj.read() self.tc.assertMultiLineEqual(text1, text1) self.tc.assertMultiLineEqual(text1, text1) with open(spam) as fobj: text2 = fobj.read() self.assertRaises(AssertionError, self.tc.assertMultiLineEqual, text1, text2) self.assertRaises(AssertionError, self.tc.assertMultiLineEqual, text1, text2) def test_default_datadir(self): expected_datadir = join(dirname(abspath(__file__)), "data") self.assertEqual(self.datadir, expected_datadir) self.assertEqual(self.datapath("foo"), join(expected_datadir, "foo")) def test_multiple_args_datadir(self): expected_datadir = join(dirname(abspath(__file__)), "data") self.assertEqual(self.datadir, expected_datadir) self.assertEqual(self.datapath("foo", "bar"), join(expected_datadir, "foo", "bar")) def test_custom_datadir(self): class MyTC(TestCase): datadir = "foo" def test_1(self): pass # class' custom datadir tc = MyTC("test_1") self.assertEqual(tc.datapath("bar"), join("foo", "bar")) def test_cached_datadir(self): """test datadir is cached on the class""" class MyTC(TestCase): def test_1(self): pass expected_datadir = join(dirname(abspath(__file__)), "data") tc = MyTC("test_1") self.assertEqual(tc.datadir, expected_datadir) # changing module should not change the datadir MyTC.__module__ = "os" self.assertEqual(tc.datadir, expected_datadir) # even on new instances tc2 = MyTC("test_1") self.assertEqual(tc2.datadir, expected_datadir) def test_is(self): obj_1 = [] obj_2 = [] self.assertIs(obj_1, obj_1) self.assertRaises(AssertionError, self.assertIs, obj_1, obj_2) def test_isnot(self): obj_1 = [] obj_2 = [] self.assertIsNot(obj_1, obj_2) self.assertRaises(AssertionError, self.assertIsNot, obj_1, obj_1) def test_none(self): self.assertIsNone(None) self.assertRaises(AssertionError, self.assertIsNone, object()) def test_not_none(self): self.assertIsNotNone(object()) self.assertRaises(AssertionError, self.assertIsNotNone, None) def test_in(self): self.assertIn("a", "dsqgaqg") obj, seq = "a", ("toto", "azf", "coin") self.assertRaises(AssertionError, self.assertIn, obj, seq) def test_not_in(self): self.assertNotIn("a", ("toto", "azf", "coin")) self.assertRaises(AssertionError, self.assertNotIn, "a", "dsqgaqg") class GenerativeTestsTC(TestCase): def setUp(self): output = StringIO() self.runner = SkipAwareTextTestRunner(stream=output) def test_generative_ok(self): class FooTC(TestCase): def test_generative(self): for i in range(10): yield self.assertEqual, i, i result = self.runner.run(FooTC("test_generative")) self.assertEqual(result.testsRun, 10) self.assertEqual(len(result.failures), 0) self.assertEqual(len(result.errors), 0) def test_generative_half_bad(self): class FooTC(TestCase): def test_generative(self): for i in range(10): yield self.assertEqual, i % 2, 0 result = self.runner.run(FooTC("test_generative")) self.assertEqual(result.testsRun, 10) self.assertEqual(len(result.failures), 5) self.assertEqual(len(result.errors), 0) def test_generative_error(self): class FooTC(TestCase): def test_generative(self): for i in range(10): if i == 5: raise ValueError("STOP !") yield self.assertEqual, i, i result = self.runner.run(FooTC("test_generative")) self.assertEqual(result.testsRun, 5) self.assertEqual(len(result.failures), 0) self.assertEqual(len(result.errors), 1) def test_generative_error2(self): class FooTC(TestCase): def test_generative(self): for i in range(10): if i == 5: yield self.ouch yield self.assertEqual, i, i def ouch(self): raise ValueError("stop !") result = self.runner.run(FooTC("test_generative")) self.assertEqual(result.testsRun, 11) self.assertEqual(len(result.failures), 0) self.assertEqual(len(result.errors), 1) def test_generative_setup(self): class FooTC(TestCase): def setUp(self): raise ValueError("STOP !") def test_generative(self): for i in range(10): yield self.assertEqual, i, i result = self.runner.run(FooTC("test_generative")) self.assertEqual(result.testsRun, 1) self.assertEqual(len(result.failures), 0) self.assertEqual(len(result.errors), 1) def test_generative_inner_skip(self): class FooTC(TestCase): def check(self, val): if val == 5: self.innerSkip("no 5") else: self.assertEqual(val, val) def test_generative(self): for i in range(10): yield InnerTest("check_%s" % i, self.check, i) result = self.runner.run(FooTC("test_generative")) self.assertEqual(result.testsRun, 10) self.assertEqual(len(result.failures), 0) self.assertEqual(len(result.errors), 0) self.assertEqual(len(result.skipped), 1) def test_generative_skip(self): class FooTC(TestCase): def check(self, val): if val == 5: self.skipTest("no 5") else: self.assertEqual(val, val) def test_generative(self): for i in range(10): yield InnerTest("check_%s" % i, self.check, i) result = self.runner.run(FooTC("test_generative")) self.assertEqual(result.testsRun, 10) self.assertEqual(len(result.failures), 0) self.assertEqual(len(result.errors), 0) self.assertEqual(len(result.skipped), 1) def test_generative_inner_error(self): class FooTC(TestCase): def check(self, val): if val == 5: raise ValueError("no 5") else: self.assertEqual(val, val) def test_generative(self): for i in range(10): yield InnerTest("check_%s" % i, self.check, i) result = self.runner.run(FooTC("test_generative")) self.assertEqual(result.testsRun, 10) self.assertEqual(len(result.failures), 0) self.assertEqual(len(result.errors), 1) self.assertEqual(len(result.skipped), 0) def test_generative_inner_failure(self): class FooTC(TestCase): def check(self, val): if val == 5: self.assertEqual(val, val + 1) else: self.assertEqual(val, val) def test_generative(self): for i in range(10): yield InnerTest("check_%s" % i, self.check, i) result = self.runner.run(FooTC("test_generative")) self.assertEqual(result.testsRun, 10) self.assertEqual(len(result.failures), 1) self.assertEqual(len(result.errors), 0) self.assertEqual(len(result.skipped), 0) def test_generative_outer_failure(self): class FooTC(TestCase): def test_generative(self): self.fail() yield result = self.runner.run(FooTC("test_generative")) self.assertEqual(result.testsRun, 0) self.assertEqual(len(result.failures), 1) self.assertEqual(len(result.errors), 0) self.assertEqual(len(result.skipped), 0) def test_generative_outer_skip(self): class FooTC(TestCase): def test_generative(self): self.skipTest("blah") yield result = self.runner.run(FooTC("test_generative")) self.assertEqual(result.testsRun, 0) self.assertEqual(len(result.failures), 0) self.assertEqual(len(result.errors), 0) self.assertEqual(len(result.skipped), 1) class ExitFirstTC(TestCase): def setUp(self): output = StringIO() self.runner = SkipAwareTextTestRunner(stream=output, exitfirst=True) def test_failure_exit_first(self): class FooTC(TestCase): def test_1(self): pass def test_2(self): assert False def test_3(self): pass tests = [FooTC("test_1"), FooTC("test_2")] result = self.runner.run(TestSuite(tests)) self.assertEqual(result.testsRun, 2) self.assertEqual(len(result.failures), 1) self.assertEqual(len(result.errors), 0) def test_error_exit_first(self): class FooTC(TestCase): def test_1(self): pass def test_2(self): raise ValueError() def test_3(self): pass tests = [FooTC("test_1"), FooTC("test_2"), FooTC("test_3")] result = self.runner.run(TestSuite(tests)) self.assertEqual(result.testsRun, 2) self.assertEqual(len(result.failures), 0) self.assertEqual(len(result.errors), 1) def test_generative_exit_first(self): class FooTC(TestCase): def test_generative(self): for i in range(10): yield self.assertTrue, False result = self.runner.run(FooTC("test_generative")) self.assertEqual(result.testsRun, 1) self.assertEqual(len(result.failures), 1) self.assertEqual(len(result.errors), 0) class TestLoaderTC(TestCase): ## internal classes for test purposes ######## class FooTC(TestCase): def test_foo1(self): pass def test_foo2(self): pass def test_bar1(self): pass class BarTC(TestCase): def test_bar2(self): pass ############################################## def setUp(self): self.loader = NonStrictTestLoader() self.module = ( TestLoaderTC # mock_object(FooTC=TestLoaderTC.FooTC, BarTC=TestLoaderTC.BarTC) ) self.output = StringIO() self.runner = SkipAwareTextTestRunner(stream=self.output) def assertRunCount(self, pattern, module, expected_count, skipped=()): self.loader.test_pattern = pattern self.loader.skipped_patterns = skipped if pattern: suite = self.loader.loadTestsFromNames([pattern], module) else: suite = self.loader.loadTestsFromModule(module) result = self.runner.run(suite) self.loader.test_pattern = None self.loader.skipped_patterns = () self.assertEqual(result.testsRun, expected_count) def test_collect_everything(self): """make sure we don't change the default behaviour for loadTestsFromModule() and loadTestsFromTestCase """ testsuite = self.loader.loadTestsFromModule(self.module) self.assertEqual(len(testsuite._tests), 2) suite1, suite2 = testsuite._tests self.assertEqual(len(suite1._tests) + len(suite2._tests), 4) def test_collect_with_classname(self): self.assertRunCount("FooTC", self.module, 3) self.assertRunCount("BarTC", self.module, 1) def test_collect_with_classname_and_pattern(self): data = [ ("FooTC.test_foo1", 1), ("FooTC.test_foo", 2), ("FooTC.test_fo", 2), ("FooTC.foo1", 1), ("FooTC.foo", 2), ("FooTC.whatever", 0), ] for pattern, expected_count in data: yield self.assertRunCount, pattern, self.module, expected_count def test_collect_with_pattern(self): data = [ ("test_foo1", 1), ("test_foo", 2), ("test_bar", 2), ("foo1", 1), ("foo", 2), ("bar", 2), ("ba", 2), ("test", 4), ("ab", 0), ] for pattern, expected_count in data: yield self.assertRunCount, pattern, self.module, expected_count def test_testcase_with_custom_metaclass(self): class mymetaclass(type): pass class MyMod: class MyTestCase(TestCase): __metaclass__ = mymetaclass def test_foo1(self): pass def test_foo2(self): pass def test_bar(self): pass data = [ ("test_foo1", 1), ("test_foo", 2), ("test_bar", 1), ("foo1", 1), ("foo", 2), ("bar", 1), ("ba", 1), ("test", 3), ("ab", 0), ("MyTestCase.test_foo1", 1), ("MyTestCase.test_foo", 2), ("MyTestCase.test_fo", 2), ("MyTestCase.foo1", 1), ("MyTestCase.foo", 2), ("MyTestCase.whatever", 0), ] for pattern, expected_count in data: yield self.assertRunCount, pattern, MyMod, expected_count def test_collect_everything_and_skipped_patterns(self): testdata = [ (["foo1"], 3), (["foo"], 2), (["foo", "bar"], 0), ] for skipped, expected_count in testdata: yield self.assertRunCount, None, self.module, expected_count, skipped def test_collect_specific_pattern_and_skip_some(self): testdata = [ ("bar", ["foo1"], 2), ("bar", [], 2), ("bar", ["bar"], 0), ] for runpattern, skipped, expected_count in testdata: yield self.assertRunCount, runpattern, self.module, expected_count, skipped def test_skip_classname(self): testdata = [ (["BarTC"], 3), (["FooTC"], 1), ] for skipped, expected_count in testdata: yield self.assertRunCount, None, self.module, expected_count, skipped def test_skip_classname_and_specific_collect(self): testdata = [ ("bar", ["BarTC"], 1), ("foo", ["FooTC"], 0), ] for runpattern, skipped, expected_count in testdata: yield self.assertRunCount, runpattern, self.module, expected_count, skipped def test_nonregr_dotted_path(self): self.assertRunCount("FooTC.test_foo", self.module, 2) def test_inner_tests_selection(self): class MyMod: class MyTestCase(TestCase): def test_foo(self): pass def test_foobar(self): for i in range(5): if i % 2 == 0: yield InnerTest("even", lambda: None) else: yield InnerTest("odd", lambda: None) yield lambda: None # FIXME InnerTest masked by pattern usage # data = [('foo', 7), ('test_foobar', 6), ('even', 3), ('odd', 2), ] data = [ ("foo", 7), ("test_foobar", 6), ("even", 0), ("odd", 0), ] for pattern, expected_count in data: yield self.assertRunCount, pattern, MyMod, expected_count def test_nonregr_class_skipped_option(self): class MyMod: class MyTestCase(TestCase): def test_foo(self): pass def test_bar(self): pass class FooTC(TestCase): def test_foo(self): pass self.assertRunCount("foo", MyMod, 2) self.assertRunCount(None, MyMod, 3) self.assertRunCount("foo", MyMod, 1, ["FooTC"]) self.assertRunCount(None, MyMod, 2, ["FooTC"]) def test__classes_are_ignored(self): class MyMod: class _Base(TestCase): def test_1(self): pass class MyTestCase(_Base): def test_2(self): pass self.assertRunCount(None, MyMod, 2) class DecoratorTC(TestCase): @with_tempdir def test_tmp_dir_normal_1(self): tempdir = tempfile.gettempdir() # assert temp directory is empty self.assertListEqual(list(os.walk(tempdir)), [(tempdir, [], [])]) witness = [] @with_tempdir def createfile(list): fd1, fn1 = tempfile.mkstemp() fd2, fn2 = tempfile.mkstemp() dir = tempfile.mkdtemp() fd3, fn3 = tempfile.mkstemp(dir=dir) tempfile.mkdtemp() list.append(True) for fd in (fd1, fd2, fd3): os.close(fd) self.assertFalse(witness) createfile(witness) self.assertTrue(witness) self.assertEqual(tempfile.gettempdir(), tempdir) # assert temp directory is empty self.assertListEqual(list(os.walk(tempdir)), [(tempdir, [], [])]) @with_tempdir def test_tmp_dir_normal_2(self): tempdir = tempfile.gettempdir() # assert temp directory is empty self.assertListEqual(list(os.walk(tempfile.tempdir)), [(tempfile.tempdir, [], [])]) class WitnessException(Exception): pass @with_tempdir def createfile(): fd1, fn1 = tempfile.mkstemp() fd2, fn2 = tempfile.mkstemp() dir = tempfile.mkdtemp() fd3, fn3 = tempfile.mkstemp(dir=dir) tempfile.mkdtemp() for fd in (fd1, fd2, fd3): os.close(fd) raise WitnessException() self.assertRaises(WitnessException, createfile) # assert tempdir didn't change self.assertEqual(tempfile.gettempdir(), tempdir) # assert temp directory is empty self.assertListEqual(list(os.walk(tempdir)), [(tempdir, [], [])]) def test_tmpdir_generator(self): orig_tempdir = tempfile.gettempdir() @with_tempdir def gen(): yield tempfile.gettempdir() for tempdir in gen(): self.assertNotEqual(orig_tempdir, tempdir) self.assertEqual(orig_tempdir, tempfile.gettempdir()) def setUp(self): self.pyversion = sys.version_info def tearDown(self): sys.version_info = self.pyversion def test_require_version_good(self): """ should return the same function """ def func(): pass sys.version_info = (2, 5, 5, "final", 4) current = sys.version_info[:3] compare = ("2.4", "2.5", "2.5.4", "2.5.5") for version in compare: decorator = require_version(version) self.assertEqual( func, decorator(func), "%s =< %s : function \ return by the decorator should be the same." % (version, ".".join([str(element) for element in current])), ) def test_require_version_bad(self): """ should return a different function : skipping test """ def func(): pass sys.version_info = (2, 5, 5, "final", 4) current = sys.version_info[:3] compare = ("2.5.6", "2.6", "2.6.5") for version in compare: decorator = require_version(version) self.assertNotEqual( func, decorator(func), "%s >= %s : function \ return by the decorator should NOT be the same." % (".".join([str(element) for element in current]), version), ) def test_require_version_exception(self): """ should throw a ValueError exception """ def func(): pass compare = ("2.5.a", "2.a", "azerty") for version in compare: decorator = require_version(version) self.assertRaises(ValueError, decorator, func) def test_require_module_good(self): """ should return the same function """ def func(): pass module = "sys" decorator = require_module(module) self.assertEqual( func, decorator(func), "module %s exists : function \ return by the decorator should be the same." % module, ) def test_require_module_bad(self): """ should return a different function : skipping test """ def func(): pass modules = ("bla", "blo", "bli") for module in modules: try: __import__(module) pass except ImportError: decorator = require_module(module) self.assertNotEqual( func, decorator(func), "module %s does \ not exist : function return by the decorator should \ NOT be the same." % module, ) return print( "all modules in %s exist. Could not test %s" % (", ".join(modules), sys._getframe().f_code.co_name) ) class TagTC(TestCase): def setUp(self): @tag("testing", "bob") def bob(a, b, c): return (a + b) * c self.func = bob class TagTestTC(TestCase): tags = Tags("one", "two") def test_one(self): self.assertTrue(True) @tag("two", "three") def test_two(self): self.assertTrue(True) @tag("three", inherit=False) def test_three(self): self.assertTrue(True) self.cls = TagTestTC def test_tag_decorator(self): bob = self.func self.assertEqual(bob(2, 3, 7), 35) self.assertTrue(hasattr(bob, "tags")) self.assertSetEqual(bob.tags, set(["testing", "bob"])) def test_tags_class(self): tags = self.func.tags self.assertTrue(tags["testing"]) self.assertFalse(tags["Not inside"]) def test_tags_match(self): tags = self.func.tags self.assertTrue(tags.match("testing")) self.assertFalse(tags.match("other")) self.assertFalse(tags.match("testing and coin")) self.assertTrue(tags.match("testing or other")) self.assertTrue(tags.match("not other")) self.assertTrue(tags.match("not other or (testing and bibi)")) self.assertTrue(tags.match("other or (testing and bob)")) def test_tagged_class(self): def options(tags): class Options(object): tags_pattern = tags return Options() tc = self.cls("test_one") runner = SkipAwareTextTestRunner() self.assertTrue(runner.does_match_tags(tc.test_one)) self.assertTrue(runner.does_match_tags(tc.test_two)) self.assertTrue(runner.does_match_tags(tc.test_three)) runner = SkipAwareTextTestRunner(options=options("one")) self.assertTrue(runner.does_match_tags(tc.test_one)) self.assertTrue(runner.does_match_tags(tc.test_two)) self.assertFalse(runner.does_match_tags(tc.test_three)) runner = SkipAwareTextTestRunner(options=options("two")) self.assertTrue(runner.does_match_tags(tc.test_one)) self.assertTrue(runner.does_match_tags(tc.test_two)) self.assertFalse(runner.does_match_tags(tc.test_three)) runner = SkipAwareTextTestRunner(options=options("three")) self.assertFalse(runner.does_match_tags(tc.test_one)) self.assertTrue(runner.does_match_tags(tc.test_two)) self.assertTrue(runner.does_match_tags(tc.test_three)) runner = SkipAwareTextTestRunner(options=options("two or three")) self.assertTrue(runner.does_match_tags(tc.test_one)) self.assertTrue(runner.does_match_tags(tc.test_two)) self.assertTrue(runner.does_match_tags(tc.test_three)) runner = SkipAwareTextTestRunner(options=options("two and three")) self.assertFalse(runner.does_match_tags(tc.test_one)) self.assertTrue(runner.does_match_tags(tc.test_two)) self.assertFalse(runner.does_match_tags(tc.test_three)) if __name__ == "__main__": unittest_main()