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, shutil, stat, 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.internal.tests.utils import CAmkESTest, cpp_available
27from camkes.parser.stage0 import CPP, parse_makefile_rule
28
29class TestCPP(CAmkESTest):
30    def setUp(self):
31        super(TestCPP, self).setUp()
32        self.reader = CPP()
33
34    @unittest.skipIf(not cpp_available(), 'CPP not found')
35    def test_empty_string(self):
36        content, read = self.reader.parse_string('')
37
38    @unittest.skipIf(not cpp_available(), 'CPP not found')
39    def test_basic_string(self):
40        content, read = self.reader.parse_string('hello world')
41        self.assertIn('hello', content)
42        self.assertIn('world', content)
43
44    @unittest.skipIf(not cpp_available(), 'CPP not found')
45    def test_unicode_string(self):
46        content, read = self.reader.parse_string('���hello world')
47        self.assertIn('���', content)
48        self.assertIn('hello', content)
49        self.assertIn('world', content)
50
51    @unittest.skipIf(not cpp_available(), 'CPP not found')
52    def test_empty_file(self):
53        tmp = self.mkstemp()
54        with open(tmp, 'wt') as f:
55            f.write('hello world')
56
57        content, read = self.reader.parse_file(tmp)
58
59        self.assertIn('hello', content)
60        self.assertIn('world', content)
61        self.assertIn(tmp, read)
62
63    @unittest.skipIf(not cpp_available(), 'CPP not found')
64    def test_non_existent_file(self):
65        # Create a path that we know refers to a file that doesn't exist
66        _, tmp = tempfile.mkstemp()
67        os.remove(tmp)
68        assert not os.path.exists(tmp)
69
70        with self.assertRaises(Exception):
71            self.reader.parse_file(tmp)
72
73    @unittest.skipIf(not cpp_available(), 'CPP not found')
74    def test_include(self):
75        parent = self.mkstemp()
76        child = self.mkstemp()
77
78        with open(child, 'wt') as f:
79            f.write('hello world')
80
81        with open(parent, 'wt') as f:
82            f.write('hello world\n#include "%s"' % child)
83
84        _, read = self.reader.parse_file(parent)
85
86        self.assertIn(parent, read)
87        self.assertIn(child, read)
88
89    @unittest.skipIf(not cpp_available(), 'CPP not found')
90    def test_flags(self):
91        content, _ = self.reader.parse_string('HELLO')
92        self.assertNotIn('world', content)
93
94        parser = CPP(flags=['-DHELLO=world'])
95        content, _ = parser.parse_string('HELLO')
96
97        self.assertIn('world', content)
98
99    # The following tests probe the behaviour of the parse_makefile_rule
100    # function which has been buggy in the past.
101
102    def test_no_cpp_rules(self):
103        '''
104        Test that an empty list of Makefile rules yields no dependencies.
105        '''
106        filename = self.mkstemp()
107
108        # Create the file with no content.
109        with open(filename, 'wt') as f:
110            pass
111
112        # Confirm that we get no dependencies out of it.
113        with open(filename) as f:
114            deps = list(parse_makefile_rule(f))
115        self.assertLen(deps, 0)
116
117    def test_basic_cpp_rule(self):
118        '''
119        Test that we can correctly handle a basic Makefile rule.
120        '''
121        filename = self.mkstemp()
122
123        # Create a basic rule.
124        with open(filename, 'wt') as f:
125            f.write('target: a b\n')
126
127        # Confirm we get the correct dependencies.
128        with open(filename) as f:
129            deps = set(parse_makefile_rule(f))
130        self.assertSetEqual(deps, set(['a', 'b']))
131
132    def test_split_cpp_rule(self):
133        '''
134        Test that we can correctly handle rules split over multiple lines.
135        '''
136        filename = self.mkstemp()
137
138        # Create a split rule.
139        with open(filename, 'wt') as f:
140            f.write('target: a b \\\n'
141                    'c \\\n'
142                    'd\n')
143
144        # Confirm we get the correct dependencies.
145        with open(filename) as f:
146            deps = set(parse_makefile_rule(f))
147        self.assertSetEqual(deps, set(['a', 'b', 'c', 'd']))
148
149    def test_split_cpp_rule2(self):
150        '''
151        Test a more complicated split rule.
152        '''
153        filename = self.mkstemp()
154
155        # Create a complicated split rule.
156        with open(filename, 'wt') as f:
157            f.write('target: \\\n'
158                    'a b \\\n'
159                    '\\\n'
160                    'c\n')
161
162        # Confirm we get the correct dependencies.
163        with open(filename) as f:
164            deps = set(parse_makefile_rule(f))
165        self.assertSetEqual(deps, set(['a', 'b', 'c']))
166
167    def test_multiple_cpp_rules(self):
168        '''
169        We should ignore everything after the first Makefile dependency rule.
170        Test that this is actually true.
171        '''
172        filename = self.mkstemp()
173
174        # Create a file with multiple rules.
175        with open(filename, 'wt') as f:
176            f.write('target: a b c\n'
177                    'target2: d e f\n')
178
179        # Confirm we only get the dependencies of the first rule.
180        with open(filename) as f:
181            deps = set(parse_makefile_rule(f))
182        self.assertSetEqual(deps, set(['a', 'b', 'c']))
183
184if __name__ == '__main__':
185    unittest.main()
186