2
0
mirror of https://gitlab.com/apparmor/apparmor synced 2025-08-30 22:05:27 +00:00

parser testlib - use metaclass to mark all test functions keep_on_fail

This patch adds a python metaclass to wrap the test methods in the
subclasses of the template class AATestTemplate with the keep_on_fail
function, which sets the do_cleanup attribute to False when a testcase
failure occurs (i.e. an Exception is raised), and removes the manually
applied decorators to the caching tests that made use of this.

The downside to this approach is that the way metaclasses are declared
changed between python 2 and python 3 in an incompatible way. Since
python 3 is The Future™, I chose that approach and made the caching
and valgrind tests which use testlib be python3 (until this change,
they would have worked under either python 2 or python 3).

(An output message when a failure occurs is tweaked, to make the
output a little cleaner when verbose test output is requested and
failures occur.)

Signed-off-by: Steve Beattie <steve@nxnw.org>
Acked-by: Christian Boltz <apparmor@cboltz.de>
This commit is contained in:
Steve Beattie
2013-10-25 16:26:16 -07:00
parent 037924384f
commit 859774482f
4 changed files with 32 additions and 40 deletions

5
README
View File

@@ -211,8 +211,9 @@ AppArmor.pm (used by aa-audit, aa-autodep, aa-complain, aa-disable,
aa-enforce, aa-genprof, aa-logprof, aa-unconfined) requires minimum
Perl 5.10.1.
Python scripts require minimum Python 2.7. Some utilities may require
Python 3.3. Python 3.0, 3.1, 3.2 are largely untested.
Python scripts require a minimum of Python 2.7. Some utilities as well
as some of the parser test scripts may require Python 3.3. Python 3.0,
3.1, and 3.2 are largely untested.
Most shell scripts are written for POSIX-compatible sh. aa-decode expects
bash, probably version 3.2 and higher.

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# ------------------------------------------------------------------
#
# Copyright (C) 2013 Canonical Ltd.
@@ -71,7 +71,7 @@ class AAParserCachingCommon(testlib.AATestTemplate):
'''teardown for each test'''
if not self.do_cleanup:
print("===> Skipping cleanup, leaving testfiles behind in '%s'" % (self.tmp_dir))
print("\n===> Skipping cleanup, leaving testfiles behind in '%s'" % (self.tmp_dir))
else:
if os.path.exists(self.tmp_dir):
shutil.rmtree(self.tmp_dir)
@@ -102,7 +102,6 @@ class AAParserBasicCachingTests(AAParserCachingCommon):
def setUp(self):
super(AAParserBasicCachingTests, self).setUp()
@testlib.keep_on_fail
def test_no_cache_by_default(self):
'''test profiles are not cached by default'''
@@ -111,7 +110,6 @@ class AAParserBasicCachingTests(AAParserCachingCommon):
self.run_cmd_check(cmd)
self.assert_path_exists(os.path.join(self.cache_dir, PROFILE), expected=False)
@testlib.keep_on_fail
def test_no_cache_w_skip_cache(self):
'''test profiles are not cached with --skip-cache'''
@@ -120,7 +118,6 @@ class AAParserBasicCachingTests(AAParserCachingCommon):
self.run_cmd_check(cmd)
self.assert_path_exists(os.path.join(self.cache_dir, PROFILE), expected=False)
@testlib.keep_on_fail
def test_cache_when_requested(self):
'''test profiles are cached when requested'''
@@ -129,7 +126,6 @@ class AAParserBasicCachingTests(AAParserCachingCommon):
self.run_cmd_check(cmd)
self.assert_path_exists(os.path.join(self.cache_dir, PROFILE))
@testlib.keep_on_fail
def test_write_features_when_caching(self):
'''test features file is written when caching'''
@@ -139,7 +135,6 @@ class AAParserBasicCachingTests(AAParserCachingCommon):
self.assert_path_exists(os.path.join(self.cache_dir, PROFILE))
self.assert_path_exists(os.path.join(self.cache_dir, '.features'))
@testlib.keep_on_fail
def test_features_match_when_caching(self):
'''test features file is written when caching'''
@@ -219,7 +214,6 @@ class AAParserCachingTests(AAParserCachingCommon):
self.run_cmd_check(cmd)
self.assert_path_exists(os.path.join(self.cache_dir, PROFILE))
@testlib.keep_on_fail
def test_cache_loaded_when_exists(self):
'''test cache is loaded when it exists, is newer than profile, and features match'''
@@ -229,7 +223,6 @@ class AAParserCachingTests(AAParserCachingCommon):
cmd.extend(['-v', '-r', self.profile])
self.run_cmd_check(cmd, expected_string='Cached reload succeeded')
@testlib.keep_on_fail
def test_cache_not_loaded_when_skip_arg(self):
'''test cache is not loaded when --skip-cache is passed'''
@@ -239,7 +232,6 @@ class AAParserCachingTests(AAParserCachingCommon):
cmd.extend(['-v', '--skip-cache', '-r', self.profile])
self.run_cmd_check(cmd, expected_string='Replacement succeeded for')
@testlib.keep_on_fail
def test_cache_not_loaded_when_skip_read_arg(self):
'''test cache is not loaded when --skip-read-cache is passed'''
@@ -249,7 +241,6 @@ class AAParserCachingTests(AAParserCachingCommon):
cmd.extend(['-v', '--skip-read-cache', '-r', self.profile])
self.run_cmd_check(cmd, expected_string='Replacement succeeded for')
@testlib.keep_on_fail
def test_cache_not_loaded_when_features_differ(self):
'''test cache is not loaded when features file differs'''
@@ -261,7 +252,6 @@ class AAParserCachingTests(AAParserCachingCommon):
cmd.extend(['-v', '-r', self.profile])
self.run_cmd_check(cmd, expected_string='Replacement succeeded for')
@testlib.keep_on_fail
def test_cache_writing_does_not_overwrite_features_when_features_differ(self):
'''test cache writing does not overwrite the features files when it differs and --skip-bad-cache is given'''
@@ -274,7 +264,6 @@ class AAParserCachingTests(AAParserCachingCommon):
# ensure that the features does *not* match the current features set
self.compare_features_file(features_file, expected=False)
@testlib.keep_on_fail
def test_cache_writing_skipped_when_features_differ(self):
'''test cache writing is skipped when features file differs'''
@@ -285,7 +274,6 @@ class AAParserCachingTests(AAParserCachingCommon):
self.run_cmd_check(cmd, expected_string='Replacement succeeded for')
self.assert_path_exists(os.path.join(self.cache_dir, PROFILE), expected=False)
@testlib.keep_on_fail
def test_cache_writing_updates_features(self):
'''test cache writing updates features'''
@@ -297,7 +285,6 @@ class AAParserCachingTests(AAParserCachingCommon):
self.assert_path_exists(features_file)
self.compare_features_file(features_file)
@testlib.keep_on_fail
def test_cache_writing_updates_cache_file(self):
'''test cache writing updates cache file'''
@@ -315,7 +302,6 @@ class AAParserCachingTests(AAParserCachingCommon):
# file bytes into strings in python3
self.assertNotEquals(orig_size, new_size, 'Expected cache file to be updated, size is not changed.')
@testlib.keep_on_fail
def test_cache_writing_clears_all_files(self):
'''test cache writing clears all cache files'''
@@ -326,7 +312,6 @@ class AAParserCachingTests(AAParserCachingCommon):
self.run_cmd_check(cmd, expected_string='Replacement succeeded for')
self.assert_path_exists(check_file, expected=False)
@testlib.keep_on_fail
def test_profile_newer_skips_cache(self):
'''test cache is skipped if profile is newer'''
@@ -338,7 +323,6 @@ class AAParserCachingTests(AAParserCachingCommon):
cmd.extend(['-v', '-r', self.profile])
self.run_cmd_check(cmd, expected_string='Replacement succeeded for')
@testlib.keep_on_fail
def test_parser_newer_skips_cache(self):
'''test cache is skipped if parser is newer'''
@@ -365,17 +349,14 @@ class AAParserCachingTests(AAParserCachingCommon):
# no message is output
self.assert_path_exists(cache_file, expected=False)
@testlib.keep_on_fail
def test_cache_purge_removes_features_file(self):
'''test cache --purge-cache removes .features file'''
self._purge_cache_test('.features')
@testlib.keep_on_fail
def test_cache_purge_removes_cache_file(self):
'''test cache --purge-cache removes profile cache file'''
self._purge_cache_test(PROFILE)
@testlib.keep_on_fail
def test_cache_purge_removes_other_cache_files(self):
'''test cache --purge-cache removes other cache files'''
self._purge_cache_test('monkey')
@@ -400,7 +381,6 @@ class AAParserAltCacheTests(AAParserCachingTests):
self.fail('original cache dir \'%s\' not empty' % self.orig_cache_dir)
super(AAParserAltCacheTests, self).tearDown()
@testlib.keep_on_fail
def test_cache_purge_leaves_original_cache_alone(self):
'''test cache purging only touches alt cache'''

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# ------------------------------------------------------------------
#
# Copyright (C) 2013 Canonical Ltd.
@@ -31,7 +31,31 @@ def subprocess_setup():
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
class AATestTemplate(unittest.TestCase):
class AANoCleanupMetaClass(type):
def __new__(cls, name, bases, attrs):
for attr_name, attr_value in attrs.items():
if attr_name.startswith("test_"):
attrs[attr_name] = cls.keep_on_fail(attr_value)
return super(AANoCleanupMetaClass, cls).__new__(cls, name, bases, attrs)
@classmethod
def keep_on_fail(cls, unittest_func):
'''wrapping function for unittest testcases to detect failure
and leave behind test files in tearDown(); to be used as
a decorator'''
def new_unittest_func(self):
try:
return unittest_func(self)
except Exception:
self.do_cleanup = False
raise
return new_unittest_func
class AATestTemplate(unittest.TestCase, metaclass=AANoCleanupMetaClass):
'''Stub class for use by test scripts'''
debug = False
do_cleanup = True
@@ -173,16 +197,3 @@ def write_file(directory, file, contents):
return path
def keep_on_fail(unittest_func):
'''wrapping function for unittest testcases to detect failure
and leave behind test files in tearDown(); to be used as a
decorator'''
def new_unittest_func(self):
try:
unittest_func(self)
except Exception:
self.do_cleanup = False
raise
return new_unittest_func

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# ------------------------------------------------------------------
#
# Copyright (C) 2013 Canonical Ltd.