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, sys, 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 import Component, Composition, Connection, ConnectionEnd, \
27    Export, Instance, Provides, Uses
28from camkes.internal.tests.utils import CAmkESTest
29from camkes.parser.exception import ParseError
30from camkes.parser.stage0 import CPP, Reader
31from camkes.parser.stage1 import Parse1
32from camkes.parser.stage2 import Parse2
33from camkes.parser.stage3 import Parse3
34from camkes.parser.stage4 import Parse4
35from camkes.parser.stage5 import Parse5
36
37class TestStage5(CAmkESTest):
38    def setUp(self):
39        super(TestStage5, self).setUp()
40        r = Reader()
41        s1 = Parse1(r)
42        s2 = Parse2(s1)
43        s3 = Parse3(s2, debug=True)
44        s4 = Parse4(s3)
45        self.parser = Parse5(s4)
46
47    def test_group_basic(self):
48        ast, _ = self.parser.parse_string('''
49            component A {}
50            component B {}
51            composition {
52                group foo {
53                    component A a;
54                    component B b;
55                }
56            }
57            ''')
58
59        self.assertLen(ast.items, 3)
60        comp = ast.items[2]
61
62        self.assertIsInstance(comp, Composition)
63        self.assertLen(comp.instances, 2)
64        self.assertLen(comp.groups, 0)
65        a, b = comp.instances
66
67        self.assertIsInstance(a, Instance)
68        self.assertEqual(a.address_space, 'foo')
69
70        self.assertIsInstance(b, Instance)
71        self.assertEqual(b.address_space, 'foo')
72
73    def test_group_mixed(self):
74        ast, _ = self.parser.parse_string('''
75            component A {}
76            composition {
77                component A a1;
78                group b {
79                    component A a2;
80                }
81                component A a3;
82                group c {
83                    component A a4;
84                }
85            }
86            ''')
87
88        self.assertLen(ast.items, 2)
89        comp = ast.items[1]
90
91        self.assertIsInstance(comp, Composition)
92        self.assertLen(comp.instances, 4)
93
94        seen = set()
95        for a in comp.instances:
96            if a.name == 'a1':
97                self.assertEqual(a.address_space, 'a1')
98            elif a.name == 'a2':
99                self.assertEqual(a.address_space, 'b')
100            elif a.name == 'a3':
101                self.assertEqual(a.address_space, 'a3')
102            elif a.name == 'a4':
103                self.assertEqual(a.address_space, 'c')
104            else:
105                self.fail('unexpected instance %s found' % a.name)
106            seen.add(a.name)
107
108        self.assertLen(seen, 4, 'some expected instances not found')
109
110    def test_group_with_connection(self):
111        ast, _ = self.parser.parse_string('''
112            connector Conn {
113                from Procedure;
114                to Procedure;
115            }
116            procedure P {}
117            component A {
118                provides P a;
119                uses P b;
120            }
121            composition {
122                component A a1;
123                group g {
124                    component A a2;
125                }
126                connection Conn c(from a1.b, to g.a2.a);
127                connection Conn c2(from g.a2.b, to a1.a);
128            }
129            ''')
130
131        self.assertLen(ast.items, 4)
132        _, _, A, comp = ast.items
133
134        self.assertIsInstance(A, Component)
135        self.assertLen(A.provides, 1)
136        a = A.provides[0]
137        self.assertIsInstance(a, Provides)
138        self.assertLen(A.uses, 1)
139        b = A.uses[0]
140        self.assertIsInstance(b, Uses)
141
142        self.assertIsInstance(comp, Composition)
143
144        # The group will have been collapsed, so we'll now have two instances.
145
146        self.assertLen(comp.instances, 2)
147        a1, a2 = comp.instances
148        self.assertIsInstance(a1, Instance)
149        self.assertIsInstance(a2, Instance)
150
151        self.assertLen(comp.connections, 2)
152        c, c2 = comp.connections
153        self.assertIsInstance(c, Connection)
154        self.assertIsInstance(c2, Connection)
155
156        self.assertLen(c.from_ends, 1)
157        a1_b = c.from_ends[0]
158        self.assertIsInstance(a1_b, ConnectionEnd)
159
160        self.assertIs(a1_b.instance, a1)
161        self.assertIs(a1_b.interface, b)
162
163        self.assertLen(c.to_ends, 1)
164        g_a2_a = c.to_ends[0]
165        self.assertIsInstance(g_a2_a, ConnectionEnd)
166
167        self.assertIs(g_a2_a.instance, a2)
168        self.assertIs(g_a2_a.interface, a)
169
170    def test_group_with_connection2(self):
171        '''
172        For the purpose of this, see the identically named test case in the
173        stage 4 tests.
174        '''
175        ast, _ = self.parser.parse_string('''
176            connector Conn {
177                from Procedure;
178                to Procedure;
179            }
180            procedure P {}
181            component A {
182                provides P a;
183                uses P b;
184            }
185            composition {
186                group g {
187                    component A a2;
188                }
189                component A a1;
190                connection Conn c(from a1.b, to g.a2.a);
191                connection Conn c2(from g.a2.b, to a1.a);
192            }
193            ''')
194
195        self.assertLen(ast.items, 4)
196        _, _, A, comp = ast.items
197
198        self.assertIsInstance(A, Component)
199        self.assertLen(A.provides, 1)
200        a = A.provides[0]
201        self.assertIsInstance(a, Provides)
202        self.assertLen(A.uses, 1)
203        b = A.uses[0]
204        self.assertIsInstance(b, Uses)
205
206        self.assertIsInstance(comp, Composition)
207
208        # The group will have been collapsed, so we'll now have two instances.
209
210        self.assertLen(comp.instances, 2)
211        a1, a2 = comp.instances
212        self.assertIsInstance(a1, Instance)
213        self.assertIsInstance(a2, Instance)
214
215        self.assertLen(comp.connections, 2)
216        c, c2 = comp.connections
217        self.assertIsInstance(c, Connection)
218        self.assertIsInstance(c2, Connection)
219
220        self.assertLen(c.from_ends, 1)
221        a1_b = c.from_ends[0]
222        self.assertIsInstance(a1_b, ConnectionEnd)
223
224        self.assertIs(a1_b.instance, a1)
225        self.assertIs(a1_b.interface, b)
226
227        self.assertLen(c.to_ends, 1)
228        g_a2_a = c.to_ends[0]
229        self.assertIsInstance(g_a2_a, ConnectionEnd)
230
231        self.assertIs(g_a2_a.instance, a2)
232        self.assertIs(g_a2_a.interface, a)
233
234    def test_export_reference(self):
235        ast, _ = self.parser.parse_string('''
236            procedure P {}
237            component Foo {
238                provides P a;
239            }
240            component Bar {
241                provides P b;
242                composition {
243                    component Foo c;
244                    export c.a -> b;
245                }
246            }
247            ''')
248
249        self.assertLen(ast.items, 3)
250        P, Foo, Bar = ast.items
251
252        self.assertIsInstance(Bar, Component)
253        comp = Bar.composition
254
255        self.assertIsInstance(comp, Composition)
256        self.assertLen(comp.instances, 1)
257        c = comp.instances[0]
258
259        self.assertLen(comp.exports, 1)
260        e = comp.exports[0]
261
262        self.assertIsInstance(e.source_instance, Instance)
263        self.assertIs(e.source_instance, c)
264        self.assertIsInstance(e.source_interface, Provides)
265        self.assertIs(e.source_interface, Foo.provides[0])
266        self.assertIsInstance(e.destination, Provides)
267        self.assertIs(e.destination, Bar.provides[0])
268
269    def test_invalid_export(self):
270        with self.assertRaises(ParseError):
271            self.parser.parse_string('''
272                procedure P {}
273                component Foo {
274                    provides P a;
275                }
276                composition {
277                    component Foo b;
278                }
279                component Bar {
280                    composition {
281                        component Foo c;
282                        export c.a -> b;
283                    }
284                }
285                ''')
286
287if __name__ == '__main__':
288    unittest.main()
289