[apparmor] [patch 10/10] libapparmor - add python bindings tests based on C tests

Tyler Hicks tyhicks at canonical.com
Fri Sep 6 18:45:50 UTC 2013


On 2013-09-05 01:37:05, Steve Beattie wrote:
> 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 at nxnw.org>

I only gave this patch a quick look, but testing more stuff can't be a
bad thing, right? :)

Acked-by: Tyler Hicks <tyhicks at canonical.com>

> ---
>  libraries/libapparmor/configure.ac                       |    1 
>  libraries/libapparmor/swig/python/Makefile.am            |    2 
>  libraries/libapparmor/swig/python/setup.py.in            |    1 
>  libraries/libapparmor/swig/python/test/Makefile.am       |   21 ++
>  libraries/libapparmor/swig/python/test/test_python.py.in |  147 +++++++++++++++
>  5 files changed, 172 insertions(+)
> 
> Index: b/libraries/libapparmor/swig/python/Makefile.am
> ===================================================================
> --- a/libraries/libapparmor/swig/python/Makefile.am
> +++ b/libraries/libapparmor/swig/python/Makefile.am
> @@ -2,6 +2,8 @@ if HAVE_PYTHON
>  
>  EXTRA_DIST      = libapparmor_wrap.c
>  
> +SUBDIRS = test
> +
>  libapparmor_wrap.c: $(srcdir)/../SWIG/libapparmor.i
>  	$(SWIG) -python -I$(srcdir)/../../src -module LibAppArmor -o $@ $(srcdir)/../SWIG/libapparmor.i
>  	mv LibAppArmor.py __init__.py
> Index: b/libraries/libapparmor/swig/python/setup.py.in
> ===================================================================
> --- a/libraries/libapparmor/swig/python/setup.py.in
> +++ b/libraries/libapparmor/swig/python/setup.py.in
> @@ -15,4 +15,5 @@ setup(name          = 'LibAppArmor',
>                                   include_dirs=['@top_srcdir@/src'],
>                                   extra_link_args = '-L at top_builddir@/src/.libs -lapparmor'.split(),
>                      )],
> +      scripts       = [],
>        )
> Index: b/libraries/libapparmor/configure.ac
> ===================================================================
> --- a/libraries/libapparmor/configure.ac
> +++ b/libraries/libapparmor/configure.ac
> @@ -76,6 +76,7 @@ swig/perl/Makefile
>  swig/perl/Makefile.PL
>  swig/python/Makefile
>  swig/python/setup.py
> +swig/python/test/Makefile
>  swig/ruby/Makefile
>  testsuite/Makefile
>  testsuite/config/Makefile
> Index: b/libraries/libapparmor/swig/python/test/Makefile.am
> ===================================================================
> --- /dev/null
> +++ b/libraries/libapparmor/swig/python/test/Makefile.am
> @@ -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
> Index: b/libraries/libapparmor/swig/python/test/test_python.py.in
> ===================================================================
> --- /dev/null
> +++ b/libraries/libapparmor/swig/python/test/test_python.py.in
> @@ -0,0 +1,147 @@
> +#! @PYTHON@
> +# ------------------------------------------------------------------
> +#
> +#   Copyright (C) 2013 Canonical Ltd.
> +#   Author: Steve Beattie <steve at 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()
> 
> 
> -- 
> AppArmor mailing list
> AppArmor at lists.ubuntu.com
> Modify settings or unsubscribe at: https://lists.ubuntu.com/mailman/listinfo/apparmor
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <https://lists.ubuntu.com/archives/apparmor/attachments/20130906/6f9c7948/attachment.pgp>


More information about the AppArmor mailing list