mirror of
https://gitlab.com/apparmor/apparmor
synced 2025-08-22 10:07:12 +00:00
Subject: libapparmor - add python bindings tests based on C tests
This patch adds tests for the swig generated python library bindings that reuse the C language tests. Fitting it into autotools was a bit of a trick, and is likely pretty brittle, as before the test script runs, it needs to know the location of the built libapparmor.so library, the built _LibAppArmor.so library and the python wrapper bits (thankfully, the latter two are the same directory). It's also unclear how to get autotools to emit the output of the test_python.py script when building, rather than just summarizing it as one test run. Also note that test_python.py is doing a bit of magic to automatically generate test case methods based on the contents of the test_multi/ directory. This has the disadvantage of breaking tools like nosetests and other external tools that try to automatically detect testcases. Signed-off-by: Steve Beattie <steve@nxnw.org> Acked-by: Tyler Hicks <tyhicks@canonical.com>
This commit is contained in:
parent
fce987ffc8
commit
a1bf63dbe9
@ -76,6 +76,7 @@ swig/perl/Makefile
|
|||||||
swig/perl/Makefile.PL
|
swig/perl/Makefile.PL
|
||||||
swig/python/Makefile
|
swig/python/Makefile
|
||||||
swig/python/setup.py
|
swig/python/setup.py
|
||||||
|
swig/python/test/Makefile
|
||||||
swig/ruby/Makefile
|
swig/ruby/Makefile
|
||||||
testsuite/Makefile
|
testsuite/Makefile
|
||||||
testsuite/config/Makefile
|
testsuite/config/Makefile
|
||||||
|
@ -2,6 +2,8 @@ if HAVE_PYTHON
|
|||||||
|
|
||||||
EXTRA_DIST = libapparmor_wrap.c
|
EXTRA_DIST = libapparmor_wrap.c
|
||||||
|
|
||||||
|
SUBDIRS = test
|
||||||
|
|
||||||
libapparmor_wrap.c: $(srcdir)/../SWIG/libapparmor.i
|
libapparmor_wrap.c: $(srcdir)/../SWIG/libapparmor.i
|
||||||
$(SWIG) -python -I$(srcdir)/../../src -module LibAppArmor -o $@ $(srcdir)/../SWIG/libapparmor.i
|
$(SWIG) -python -I$(srcdir)/../../src -module LibAppArmor -o $@ $(srcdir)/../SWIG/libapparmor.i
|
||||||
mv LibAppArmor.py __init__.py
|
mv LibAppArmor.py __init__.py
|
||||||
|
@ -15,4 +15,5 @@ setup(name = 'LibAppArmor',
|
|||||||
include_dirs=['@top_srcdir@/src'],
|
include_dirs=['@top_srcdir@/src'],
|
||||||
extra_link_args = '-L@top_builddir@/src/.libs -lapparmor'.split(),
|
extra_link_args = '-L@top_builddir@/src/.libs -lapparmor'.split(),
|
||||||
)],
|
)],
|
||||||
|
scripts = [],
|
||||||
)
|
)
|
||||||
|
21
libraries/libapparmor/swig/python/test/Makefile.am
Normal file
21
libraries/libapparmor/swig/python/test/Makefile.am
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
if HAVE_PYTHON
|
||||||
|
|
||||||
|
# NOTE: tests needs to exist in test/test*.py for python's setuptools
|
||||||
|
# not to treat it as a script to install.
|
||||||
|
|
||||||
|
test_python.py: test_python.py.in $(top_builddir)/config.status
|
||||||
|
$(AM_V_GEN)cd "$(top_builddir)" && \
|
||||||
|
$(SHELL) ./config.status --file="swig/python/test/$@"
|
||||||
|
chmod +x test_python.py
|
||||||
|
|
||||||
|
CLEANFILES = test_python.py
|
||||||
|
|
||||||
|
# bah, how brittle is this?
|
||||||
|
PYTHON_DIST_BUILD_PATH = '$(builddir)/../build/$$($(PYTHON) -c "import distutils.util; import platform; print(\"lib.%s-%s\" %(distutils.util.get_platform(), platform.python_version()[:3]))")'
|
||||||
|
|
||||||
|
TESTS = test_python.py
|
||||||
|
TESTS_ENVIRONMENT = \
|
||||||
|
LD_LIBRARY_PATH='$(top_builddir)/src/.libs:$(PYTHON_DIST_BUILD_PATH)' \
|
||||||
|
PYTHONPATH='$(PYTHON_DIST_BUILD_PATH)'
|
||||||
|
|
||||||
|
endif
|
147
libraries/libapparmor/swig/python/test/test_python.py.in
Normal file
147
libraries/libapparmor/swig/python/test/test_python.py.in
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
#! @PYTHON@
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Copyright (C) 2013 Canonical Ltd.
|
||||||
|
# Author: Steve Beattie <steve@nxnw.org>
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of version 2 of the GNU General Public
|
||||||
|
# License published by the Free Software Foundation.
|
||||||
|
#
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
import ctypes
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
import LibAppArmor as libapparmor
|
||||||
|
|
||||||
|
TESTDIR = "../../../testsuite/test_multi"
|
||||||
|
|
||||||
|
# map of testsuite .out entries that aren't a simple to_lower() and
|
||||||
|
# s/ /_/ of the field name
|
||||||
|
|
||||||
|
OUTPUT_MAP = {
|
||||||
|
'Event type': 'event',
|
||||||
|
'Mask': 'requested_mask',
|
||||||
|
'Command': 'comm',
|
||||||
|
'Token': 'magic_token',
|
||||||
|
'ErrorCode': 'error_code',
|
||||||
|
'Network family': 'net_family',
|
||||||
|
'Socket type': 'net_sock_type',
|
||||||
|
'Protocol': 'net_protocol',
|
||||||
|
'Local addr': 'net_local_addr',
|
||||||
|
'Foreign addr': 'net_foreign_addr',
|
||||||
|
'Local port': 'net_local_port',
|
||||||
|
'Foreign port': 'net_foreign_port',
|
||||||
|
'Audit subid': 'audit_sub_id',
|
||||||
|
}
|
||||||
|
|
||||||
|
# FIXME: pull this automatically out of LibAppArmor, but swig
|
||||||
|
# classes aren't real enough. (Perhaps we're not using swig correctly)
|
||||||
|
EVENT_MAP = {
|
||||||
|
libapparmor.AA_RECORD_ALLOWED: 'AA_RECORD_ALLOWED',
|
||||||
|
libapparmor.AA_RECORD_AUDIT: 'AA_RECORD_AUDIT',
|
||||||
|
libapparmor.AA_RECORD_DENIED: 'AA_RECORD_DENIED',
|
||||||
|
libapparmor.AA_RECORD_ERROR: 'AA_RECORD_ERROR',
|
||||||
|
libapparmor.AA_RECORD_HINT: 'AA_RECORD_HINT',
|
||||||
|
libapparmor.AA_RECORD_INVALID: 'AA_RECORD_INVALID',
|
||||||
|
libapparmor.AA_RECORD_STATUS: 'AA_RECORD_STATUS',
|
||||||
|
}
|
||||||
|
|
||||||
|
# default is None if not in this table
|
||||||
|
NO_VALUE_MAP = {
|
||||||
|
'fsuid': int(ctypes.c_ulonglong(-1).value),
|
||||||
|
'ouid': int(ctypes.c_ulonglong(-1).value),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class AAPythonBindingsTests(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
# REPORT ALL THE OUTPUT
|
||||||
|
self.maxDiff = None
|
||||||
|
|
||||||
|
def _runtest(self, testname):
|
||||||
|
infile = "%s.in" % (testname)
|
||||||
|
outfile = "%s.out" % (testname)
|
||||||
|
# infile *should* only contain one line
|
||||||
|
with open(os.path.join(TESTDIR, infile), 'r') as f:
|
||||||
|
line = f.read()
|
||||||
|
swig_record = libapparmor.parse_record(line)
|
||||||
|
|
||||||
|
record = self.create_record_dict(swig_record)
|
||||||
|
record['file'] = infile
|
||||||
|
libapparmor.free_record(swig_record)
|
||||||
|
|
||||||
|
expected = self.parse_output_file(outfile)
|
||||||
|
self.assertEquals(expected, record,
|
||||||
|
"expected records did not match\n" +
|
||||||
|
"expected = %s\nactual = %s" % (expected, record))
|
||||||
|
|
||||||
|
def parse_output_file(self, outfile):
|
||||||
|
'''parse testcase .out file and return dict'''
|
||||||
|
|
||||||
|
output = dict()
|
||||||
|
with open(os.path.join(TESTDIR, outfile), 'r') as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
for l in lines:
|
||||||
|
line = l.rstrip('\n')
|
||||||
|
count += 1
|
||||||
|
if line == "START":
|
||||||
|
self.assertEquals(count, 1,
|
||||||
|
"Unexpected output format in %s" % (outfile))
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
key, value = line.split(": ", 1)
|
||||||
|
if key in OUTPUT_MAP:
|
||||||
|
newkey = OUTPUT_MAP[key]
|
||||||
|
else:
|
||||||
|
newkey = key.lower().replace(' ', '_')
|
||||||
|
# check if entry already exists?
|
||||||
|
output[newkey] = value
|
||||||
|
|
||||||
|
return output
|
||||||
|
|
||||||
|
def create_record_dict(self, record):
|
||||||
|
'''parse the swig created record and construct a dict from it'''
|
||||||
|
|
||||||
|
new_record = dict()
|
||||||
|
for key in [x for x in dir(record) if not (x.startswith('_') or x == 'this')]:
|
||||||
|
value = record.__getattr__(key)
|
||||||
|
if key == "event" and value in EVENT_MAP:
|
||||||
|
new_record[key] = EVENT_MAP[value]
|
||||||
|
elif key == "version":
|
||||||
|
# FIXME: out files should report log version?
|
||||||
|
# FIXME: or can we just deprecate v1 logs?
|
||||||
|
continue
|
||||||
|
elif key in NO_VALUE_MAP:
|
||||||
|
if NO_VALUE_MAP[key] == value:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
new_record[key] = str(value)
|
||||||
|
elif record.__getattr__(key):
|
||||||
|
new_record[key] = str(value)
|
||||||
|
|
||||||
|
return new_record
|
||||||
|
|
||||||
|
|
||||||
|
def find_testcases(testdir):
|
||||||
|
'''dig testcases out of passed directory'''
|
||||||
|
|
||||||
|
for f in os.listdir(testdir):
|
||||||
|
if f.endswith(".in"):
|
||||||
|
yield f[:-3]
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
for f in find_testcases(TESTDIR):
|
||||||
|
def stub_test(self, testname=f):
|
||||||
|
self._runtest(testname)
|
||||||
|
stub_test.__doc__ = "test %s" % (f)
|
||||||
|
setattr(AAPythonBindingsTests, 'test_%s' % (f), stub_test)
|
||||||
|
return unittest.main(verbosity=2)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
Loading…
x
Reference in New Issue
Block a user