blob: 1c75fad3779e53402c3365c9b8ef5d0c4903049e [file] [log] [blame] [edit]
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2019, Data61, CSIRO (ABN 41 687 119 230)
#
# SPDX-License-Identifier: BSD-2-Clause
#
#
'''
This file contains unit test cases related to the template macros.
'''
from __future__ import absolute_import, division, print_function, \
unicode_literals
import ast, fnmatch, os, re, subprocess, sys, unittest
ME = os.path.abspath(__file__)
MY_DIR = os.path.dirname(ME)
# Make CapDL importable. Note that we just assume where it is in relation to
# our own directory.
sys.path.append(os.path.join(MY_DIR, '../../../../python-capdl'))
# Make CAmkES importable
sys.path.append(os.path.join(MY_DIR, '../../..'))
from camkes.internal.tests.utils import CAmkESTest, which
from camkes.templates.macros import NO_CHECK_UNUSED, get_perm
from camkes.templates import TemplateError
def uname():
'''
Determine the hardware architecture of this machine. Note that we're only
really interested in x86 or x86_64.
'''
try:
machine = subprocess.check_output(['uname', '-m']).strip()
except subprocess.CalledProcessError:
return None
if re.match(r'i\d86$', machine):
return 'x86'
return machine
class TestMacros(CAmkESTest):
def test_get_perm(self):
conf = {}
instance = "bah"
iface = "humbug"
field = '%s_access' % iface
conf[instance] = {}
self.assertEqual(get_perm(conf, instance, iface), "RWXP")
conf[instance][field] = "R"
self.assertEqual(get_perm(conf, instance, iface), "R")
conf[instance][field] = "FOO"
with self.assertRaises(TemplateError):
get_perm(conf, instance, iface)
def test_find_unused_macros(self):
'''
Find macros intended for the templates that are never actually used in
any template.
'''
# First obtain a set of available macros by parsing the source of
# macros.py.
macrospy = os.path.join(MY_DIR, '../macros.py')
with open(macrospy) as f:
source = f.read()
node = ast.parse(source, filename=macrospy)
macros = set()
for i in ast.iter_child_nodes(node):
if isinstance(i, ast.FunctionDef):
macros.add(i.name)
# Next get a set of gitignored globs.
# First up, ignore the tests.
ignored = set(('%s/**' % MY_DIR,))
# Now look at all .gitignores from three directories up.
for stem in ('../../..', '../..', '..'):
gitignore = os.path.join(MY_DIR, stem, '.gitignore')
if os.path.exists(gitignore):
with open(gitignore) as f:
for line in (x.strip() for x in f
if x.strip() != '' and not x.startswith('#')):
pattern = os.path.join(os.path.abspath(
os.path.dirname(gitignore)), line)
ignored.add(pattern)
# Now let's look at all the templates and note macro calls.
# A regex to match macro calls from the template context. Note that it is
# imprecise, so the resulting analysis could generate false negatives.
call = re.compile(r'/\*[-\?].*?\bmacros\.([a-zA-Z][a-zA-Z0-9_]*)\b')
used = set()
for root, _, files in os.walk(os.path.abspath(
os.path.join(MY_DIR, '..'))):
for f in (os.path.join(root, f) for f in files):
for pattern in ignored:
try:
if fnmatch.fnmatchcase(f, pattern):
break
except Exception:
# Suppress any errors resulting from invalid lines in
# .gitignore.
pass
else:
# This file did not match any of the ignore patterns; scan
# it for macro calls.
with open(f) as input:
source = input.read()
for m in call.finditer(source):
used.add(m.group(1))
unused = macros - used - NO_CHECK_UNUSED
if len(unused) > 0:
[print("Unused macro: %s" % u) for u in unused]
self.assertSetEqual(unused, set())
if __name__ == '__main__':
unittest.main()