1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4#
5# Copyright 2017, Data61
6# Commonwealth Scientific and Industrial Research Organisation (CSIRO)
7# ABN 41 687 119 230.
8#
9# This software may be distributed and modified according to the terms of
10# the BSD 2-Clause license. Note that NO WARRANTY is provided.
11# See "LICENSE_BSD2.txt" for details.
12#
13# @TAG(DATA61_BSD)
14#
15
16from __future__ import absolute_import, division, print_function, \
17    unicode_literals
18
19import os, subprocess, sys, tempfile, unittest
20
21ME = os.path.abspath(__file__)
22
23# Make CAmkES importable
24sys.path.append(os.path.join(os.path.dirname(ME), '../../..'))
25
26from camkes.ast.ckeywords import C_KEYWORDS
27from camkes.internal.tests.utils import CAmkESTest, c_compiler
28
29class TestCKeywords(CAmkESTest):
30    '''
31    Test generation of the file ckeywords.py.
32    '''
33
34    @unittest.skipIf(c_compiler() is None, 'C compiler not available')
35    def test_missing(self):
36        '''
37        Check if there are C keywords our local Clang is aware of that are not
38        noted in ckeywords.py.
39        '''
40
41        cc = c_compiler()
42        assert cc is not None
43
44        # Check we can #include TokenKinds.def, otherwise there's no point
45        # continuing.
46        with open(os.devnull, 'wt') as f:
47            p = subprocess.Popen([cc, '-x', 'c', '-E', '-'], stdout=f, stderr=f,
48                stdin=subprocess.PIPE, universal_newlines=True)
49            p.communicate('#include <TokenKinds.def>\n')
50            if p.returncode != 0:
51                self.skipTest('TokenKinds.def unavailble for including (try '
52                    'setting C_INCLUDE_PATH)')
53
54        tmp = self.mkdtemp()
55
56        src = os.path.join(os.path.dirname(ME), '../../../tools/ckeywords.c')
57        assert os.path.exists(src)
58
59        # Compile the C keyword generator.
60        aout = os.path.join(tmp, 'a.out')
61        subprocess.check_call([cc, src, '-std=c11', '-W', '-Wall', '-Wextra',
62            '-Werror', '-o', aout])
63
64        generated = subprocess.check_output([aout])
65
66        # Unsafe, but we trust the generated code.
67        namespace = {}
68        exec generated in namespace
69
70        self.assertIn('C_KEYWORDS', namespace, 'C keywords apparently not '
71            'generated by keyword generator')
72        new_keywords = namespace['C_KEYWORDS']
73
74        # Check for keywords picked up locally that are not recognised by the
75        # AST sources. We don't bother checking the other way around because the
76        # AST sources may recognise extended keywords that our current compiler
77        # does not.
78        diff = new_keywords - C_KEYWORDS
79        self.assertEqual(diff, set([]), 'extra C keywords (%s) found that are '
80            'not currently recognised' % ', '.join(diff))
81
82if __name__ == '__main__':
83    unittest.main()
84