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.internal.tests.utils import CAmkESTest, cpp_available 27from camkes.parser.stage0 import CPP, Reader 28from camkes.parser.stage1 import Parse1 29from camkes.parser.stage2 import Parse2 30 31class TestStage2(CAmkESTest): 32 def setUp(self): 33 super(TestStage2, self).setUp() 34 r = Reader() 35 s1 = Parse1(r) 36 self.parser = Parse2(s1) 37 38 r = CPP() 39 s1 = Parse1(r) 40 self.cpp_parser = Parse2(s1) 41 42 def test_empty_string(self): 43 content, read = self.parser.parse_string('') 44 45 self.assertEqual(content, []) 46 self.assertLen(read, 0) 47 48 def test_basic_entity(self): 49 content, read = self.parser.parse_string('component foo {}') 50 51 self.assertLen(content, 1) 52 self.assertEqual(content[0][0], 'component foo {}') 53 self.assertIsNone(content[0][1]) 54 comp = content[0][2] 55 56 self.assertEqual(comp.head, 'component_decl') 57 self.assertLen(comp.tail, 2) 58 self.assertEqual(comp.tail[0].head, 'id') 59 self.assertLen(comp.tail[0].tail, 1) 60 self.assertEqual(comp.tail[0].tail[0], 'foo') 61 self.assertEqual(comp.tail[1].head, 'component_defn') 62 self.assertLen(comp.tail[1].tail, 0) 63 self.assertLen(read, 0) 64 65 def test_malformed(self): 66 with self.assertRaises(Exception): 67 self.parser.parse_string('hello world') 68 69 def test_unicode(self): 70 content, read = self.parser.parse_string('component fo�� {}') 71 72 self.assertLen(content, 1) 73 self.assertEqual(content[0][0], 'component fo�� {}') 74 self.assertIsNone(content[0][1]) 75 comp = content[0][2] 76 77 self.assertEqual(comp.head, 'component_decl') 78 self.assertLen(comp.tail, 2) 79 self.assertEqual(comp.tail[0].head, 'id') 80 self.assertLen(comp.tail[0].tail, 1) 81 self.assertEqual(comp.tail[0].tail[0], 'fo��') 82 self.assertEqual(comp.tail[1].head, 'component_defn') 83 self.assertLen(comp.tail[1].tail, 0) 84 self.assertLen(read, 0) 85 86 def test_from_file(self): 87 tmp = self.mkstemp() 88 with open(tmp, 'wt') as f: 89 f.write('component foo {}') 90 91 content, read = self.parser.parse_file(tmp) 92 93 self.assertLen(content, 1) 94 self.assertEqual(content[0][0], 'component foo {}') 95 self.assertEqual(content[0][1], tmp) 96 comp = content[0][2] 97 98 self.assertEqual(comp.head, 'component_decl') 99 self.assertLen(comp.tail, 2) 100 self.assertEqual(comp.tail[0].head, 'id') 101 self.assertLen(comp.tail[0].tail, 1) 102 self.assertEqual(comp.tail[0].tail[0], 'foo') 103 self.assertEqual(comp.tail[1].head, 'component_defn') 104 self.assertLen(comp.tail[1].tail, 0) 105 self.assertEqual(read, set([tmp])) 106 107 @unittest.skipIf(not cpp_available(), 'CPP not found') 108 def test_with_cpp(self): 109 parent = self.mkstemp() 110 child = self.mkstemp() 111 112 with open(parent, 'wt') as f: 113 f.write('component foo\n#include "%s"' % child) 114 with open(child, 'wt') as f: 115 f.write('{}') 116 117 content, read = self.cpp_parser.parse_file(parent) 118 119 self.assertLen(content, 1) 120 self.assertEqual(content[0][1], parent) 121 comp = content[0][2] 122 123 self.assertEqual(comp.head, 'component_decl') 124 self.assertLen(comp.tail, 2) 125 self.assertEqual(comp.tail[0].head, 'id') 126 self.assertLen(comp.tail[0].tail, 1) 127 self.assertEqual(comp.tail[0].tail[0], 'foo') 128 self.assertEqual(comp.tail[1].head, 'component_defn') 129 self.assertLen(comp.tail[1].tail, 0) 130 self.assertIn(parent, read) 131 self.assertIn(child, read) 132 133 def test_simple_spec_complete(self): 134 content, _ = self.parser.parse_string(''' 135 procedure Hello { 136 void hello(void); 137 } 138 139 component Foo { 140 provides Hello h; 141 } 142 143 component Bar { 144 control; 145 uses Hello w; 146 } 147 148 assembly { 149 composition { 150 component Foo f; 151 component Bar b; 152 connection Conn conn(from Foo.h, to Bar.w); 153 } 154 } 155 ''') 156 157 def test_self_import(self): 158 ''' 159 The stage 2 parser should notice cycles in the import graph and 160 automatically terminate. This case validates a trivial cycle. 161 ''' 162 163 input = self.mkstemp() 164 165 with open(input, 'wt') as f: 166 f.write(''' 167 component Foo {} 168 import "%s"; 169 ''' % input) 170 171 content, read = self.parser.parse_file(input) 172 173 self.assertLen(content, 1) 174 Foo = content[0][2] 175 self.assertEqual(Foo.head, 'component_decl') 176 177 self.assertEqual(read, set([input])) 178 179 content, read = self.cpp_parser.parse_file(input) 180 181 self.assertLen(content, 1) 182 Foo = content[0][2] 183 self.assertEqual(Foo.head, 'component_decl') 184 185 self.assertIn(input, read) 186 187 def test_cycle_import(self): 188 ''' 189 Similar to the previous test, but a cycle involving multiple files. 190 ''' 191 192 a = self.mkstemp() 193 b = self.mkstemp() 194 c = self.mkstemp() 195 196 with open(a, 'wt') as f: 197 f.write(''' 198 component Foo {} 199 import "%s"; 200 ''' % b) 201 with open(b, 'wt') as f: 202 f.write('import "%s";' % c) 203 with open(c, 'wt') as f: 204 f.write('import "%s";' % a) 205 206 content, read = self.parser.parse_file(a) 207 208 self.assertLen(content, 1) 209 Foo = content[0][2] 210 self.assertEqual(Foo.head, 'component_decl') 211 212 self.assertEqual(read, set([a, b, c])) 213 214 content, read = self.cpp_parser.parse_file(a) 215 216 self.assertLen(content, 1) 217 Foo = content[0][2] 218 self.assertEqual(Foo.head, 'component_decl') 219 220 self.assertIn(a, read) 221 self.assertIn(b, read) 222 self.assertIn(c, read) 223 224if __name__ == '__main__': 225 unittest.main() 226