|
1 # *-* coding: utf8 *-* |
|
2 # |
|
3 # Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 # All rights reserved. |
|
5 # This component and the accompanying materials are made available |
|
6 # under the terms of "Eclipse Public License v1.0" |
|
7 # which accompanies this distribution, and is available |
|
8 # at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
9 # |
|
10 # Initial Contributors: |
|
11 # Nokia Corporation - initial contribution. |
|
12 # |
|
13 # Contributors: |
|
14 # |
|
15 # Description: |
|
16 # |
|
17 |
|
18 import unittest |
|
19 import sys, os |
|
20 import __init__ |
|
21 |
|
22 from cone.public.api import CompositeConfiguration, Feature |
|
23 from cone.public.rules import ASTInterpreter, RelationContainerImpl, RELATIONS, get_tokens |
|
24 from cone.public.rules import ParseException, DefaultContext, BaseRelation, RequireExpression, OPERATORS |
|
25 |
|
26 #### TEST RELATIONS #### |
|
27 |
|
28 AA_BA = 'a.a require b.b' |
|
29 AB_BB = 'a.b require b.b' |
|
30 BA_CA = 'b.a require c.a and c.b and a.b' |
|
31 |
|
32 CB_DA = 'c.b require d.a' |
|
33 DA_DB = 'd.a require d.b' |
|
34 |
|
35 AC_AB_BA = 'a.c and a.a require b.a' |
|
36 |
|
37 EA_FSTAR = 'e.a require f.*' |
|
38 |
|
39 TEST_RELATIONS = { |
|
40 'a.a' : [AA_BA], |
|
41 'a.b' : [AB_BB], |
|
42 'a.c' : [AC_AB_BA], |
|
43 'b.a' : [BA_CA], |
|
44 'c.b' : [CB_DA], |
|
45 'd.a' : [DA_DB], |
|
46 'e.a' : [EA_FSTAR] |
|
47 } |
|
48 |
|
49 class DummyRelationFactory(): |
|
50 def get_relations_for(self, configuration, ref): |
|
51 rels = TEST_RELATIONS.get(ref) |
|
52 |
|
53 if rels: |
|
54 relation_container = RelationContainerImpl() |
|
55 for rel in rels: |
|
56 rel_s = rel.split(' ') |
|
57 from_ref = rel_s[0] |
|
58 relation_name = 'require' |
|
59 to_ref = ' '.join(rel_s[2:]) |
|
60 relation = RELATIONS.get(relation_name)(configuration, from_ref, to_ref) |
|
61 relation_container.add_relation(relation) |
|
62 propagated_relations = self.get_relations_for(configuration, to_ref) |
|
63 if propagated_relations: |
|
64 for relation in propagated_relations: |
|
65 relation_container.add_relation(relation) |
|
66 |
|
67 return relation_container |
|
68 return None |
|
69 |
|
70 class DummyConfiguration(object): |
|
71 VALUES = { |
|
72 'a.a' : True, |
|
73 'a.b' : False, |
|
74 'a.c' : False, |
|
75 'b.a' : True, |
|
76 'b.b' : True, |
|
77 'c.b' : False, |
|
78 'd.a' : True, |
|
79 'e.a' : True, |
|
80 } |
|
81 |
|
82 def get_feature(self, ref): |
|
83 return DummyConfiguration.VALUES.get(ref, False) |
|
84 |
|
85 class DummyContext(DefaultContext): |
|
86 def handle_terminal(self, expression): |
|
87 return DummyConfiguration.VALUES.get(expression, False) |
|
88 |
|
89 class DummyBaseRelation(BaseRelation): |
|
90 def __init__(self, data, left, right): |
|
91 self.context = DummyContext(data) |
|
92 super(DummyBaseRelation, self).__init__(data, left, right) |
|
93 |
|
94 class DummyRequireRelation(DummyBaseRelation): |
|
95 KEY = 'require' |
|
96 |
|
97 def __init__(self, data, left, right): |
|
98 self.context = DummyContext(data) |
|
99 super(DummyRequireRelation, self).__init__(data, left, right) |
|
100 |
|
101 RELATIONS[DummyRequireRelation.KEY] = DummyRequireRelation |
|
102 OPERATORS['require'] = RequireExpression |
|
103 multilines = \ |
|
104 """ |
|
105 APs.AP configures KCRUidCommsDatCreator.KCommsDatCreatorInputFileName = 'VariantData_commsdat.xml' and |
|
106 KCRUidStartupSettings.KCRKeyAccessPointPlugin = '0' and |
|
107 KCRUidStartupSettings.KCRKeyStreamingPlugin = '0' and |
|
108 KCRUidStartupSettings.KCRKeyMusicShopPlugin = '0' and |
|
109 KCRUidStartupSettings.KCRKeyDeviceManagementPlugin = '0' and |
|
110 KCRUidStartupSettings.KCRKeyAGPSPlugin = '0' |
|
111 """ |
|
112 |
|
113 class TestRelations(unittest.TestCase): |
|
114 |
|
115 def setUp(self): |
|
116 self.configuration = DummyConfiguration() |
|
117 |
|
118 def test_has_ref(self): |
|
119 """ |
|
120 Tests the relation and relation container |
|
121 """ |
|
122 factory = DummyRelationFactory() |
|
123 rels = factory.get_relations_for(self.configuration, 'a.a') |
|
124 ret= rels.execute() |
|
125 self.assertTrue(ret) |
|
126 |
|
127 def test_not_has_ref(self): |
|
128 factory = DummyRelationFactory() |
|
129 # depends on c.a which has no value in conf |
|
130 rels = factory.get_relations_for(self.configuration, 'b.a') |
|
131 ret = rels.execute() |
|
132 self.assertFalse(ret) |
|
133 |
|
134 for rel in rels: |
|
135 ip = rel.interpreter |
|
136 self.assertTrue(ip.errors) |
|
137 errors = ip.errors |
|
138 self.assertTrue(errors.get('b.a')) |
|
139 |
|
140 def test_not_has_ref_in_container(self): |
|
141 factory = DummyRelationFactory() |
|
142 rels = factory.get_relations_for(self.configuration, 'c.b') |
|
143 ret = rels.execute() |
|
144 self.assertFalse(ret) |
|
145 |
|
146 def test_two_on_the_left(self): |
|
147 factory = DummyRelationFactory() |
|
148 rels = factory.get_relations_for(self.configuration, 'a.c') |
|
149 ret = rels.execute() |
|
150 self.assertTrue(ret) |
|
151 |
|
152 |
|
153 class TestASTInterpreter(unittest.TestCase): |
|
154 def test_require(self): |
|
155 ip = ASTInterpreter('a excludes b require 0') |
|
156 ret = ip.eval() |
|
157 |
|
158 def test_get_tokens(self): |
|
159 self.assertEquals(get_tokens("foo=(2+1) * 3"),['foo','=','(','2','+','1',')','*','3']) |
|
160 self.assertEquals(get_tokens("Arithmetic.MixedResult3 = (Arithmetic.Value2 / 2 + Arithmetic.Value1 * 9) - 7"),['Arithmetic.MixedResult3', '=', '(', 'Arithmetic.Value2', '/', '2', '+', 'Arithmetic.Value1', '*', '9', ')', '-', '7']) |
|
161 print get_tokens(multilines) |
|
162 self.assertEquals(len(get_tokens(multilines)),25) |
|
163 |
|
164 def test_get_unindented_multiline_tokens(self): |
|
165 self.assertEquals( |
|
166 get_tokens("foo = 2+bar\nand foobar = 3 and\nfubar=4"), |
|
167 ['foo', '=', '2', '+', 'bar', 'and', 'foobar', '=', '3', 'and', 'fubar', '=', '4']) |
|
168 |
|
169 def test_get_tab_separated_tokens(self): |
|
170 self.assertEquals( |
|
171 get_tokens("foo\tconfigures\t\tbar\t=\t5"), |
|
172 ['foo', 'configures', 'bar', '=', '5']) |
|
173 |
|
174 def test_get_unicode_tokens(self): |
|
175 self.assertEquals( |
|
176 get_tokens(u'xÿz configures xzÿ = ÿxá'), |
|
177 [u'xÿz', 'configures', u'xzÿ', '=', u'ÿxá']) |
|
178 |
|
179 def test_get_unicode_tokens_2(self): |
|
180 self.assertEquals( |
|
181 get_tokens(u'ελληνικά configures ünicode = u"test string" + ελληνικά'), |
|
182 [u'ελληνικά', 'configures', u'ünicode', '=', 'u"test string"', '+', u'ελληνικά']) |
|
183 |
|
184 def test_get_unicode_tokens_3(self): |
|
185 self.assertEquals( |
|
186 get_tokens(u'oöoä äöoö oöo öoö äaäa'), |
|
187 [u'oöoä', u'äöoö', u'oöo', u'öoö', u'äaäa']) |
|
188 |
|
189 def test_get_unicode_tokens_4(self): |
|
190 self.assertEquals( |
|
191 get_tokens(u'ünicode.rêf1 require rêf2 . ελληνικά'), |
|
192 [u'ünicode.rêf1', u'require', u'rêf2.ελληνικά']) |
|
193 |
|
194 def test_get_unicode_tokens_multiline(self): |
|
195 tokenstr = u""" |
|
196 foo=(2+1) * 3 |
|
197 xÿz configures xzÿ = ÿxá |
|
198 ελληνικά configures ünicode = u"test string" + ελληνικά""" |
|
199 expected = [ |
|
200 'foo', '=', '(', '2', '+', '1', ')', '*', '3', |
|
201 u'xÿz', 'configures', u'xzÿ', '=', u'ÿxá', |
|
202 u'ελληνικά', 'configures', u'ünicode', '=', 'u"test string"', '+', u'ελληνικά', |
|
203 ] |
|
204 actual = get_tokens(tokenstr) |
|
205 self.assertEquals(actual, expected, '\n%r \n!= \n%r' % (actual, expected)) |
|
206 |
|
207 def test_multiline_string(self): |
|
208 tokenstr = ''' |
|
209 """ |
|
210 tes- |
|
211 ti |
|
212 """ |
|
213 ''' |
|
214 expected = ['"""\ntes-\nti\n"""'] |
|
215 self.assertEquals(get_tokens(tokenstr), expected) |
|
216 |
|
217 def test_syntax_error(self): |
|
218 try: |
|
219 ip = ASTInterpreter('a and and') |
|
220 self.assertTrue(False) |
|
221 except ParseException: |
|
222 self.assertTrue(True) |
|
223 |
|
224 def test_empty_expression(self): |
|
225 expression = '' |
|
226 ip = ASTInterpreter(expression) |
|
227 result = ip.eval() |
|
228 self.assertFalse(result) |
|
229 |
|
230 def test_no_expression(self): |
|
231 ip = ASTInterpreter() |
|
232 result = ip.eval() |
|
233 self.assertFalse(result) |
|
234 |
|
235 try: |
|
236 ip.create_ast(None) |
|
237 self.assertFalse(True) |
|
238 except ParseException: |
|
239 self.assertTrue(True) |
|
240 |
|
241 ip.create_ast('1 and 1') |
|
242 result = ip.eval() |
|
243 self.assertTrue(result) |
|
244 |
|
245 def test_one_param_ops(self): |
|
246 ip = ASTInterpreter('1 and truth 1') |
|
247 result = ip.eval() |
|
248 self.assertTrue(result) |
|
249 |
|
250 ip.create_ast('1 and truth 0') |
|
251 result = ip.eval() |
|
252 self.assertFalse(result) |
|
253 |
|
254 ip.create_ast(u'1 and truth not 0') |
|
255 result = ip.eval() |
|
256 self.assertTrue(result) |
|
257 |
|
258 def test_infix_to_postfix(self): |
|
259 expression = '1 and not 1' |
|
260 ip = ASTInterpreter(expression) |
|
261 self.assertEqual(ip.postfix_array, ['1', '1', 'not', 'and']) |
|
262 self.assertFalse(ip.eval()) |
|
263 |
|
264 def test_infix_to_postfix_pars(self): |
|
265 expression = '1 and ( 0 or 1 and 1 )' |
|
266 ip = ASTInterpreter(expression) |
|
267 self.assertEqual(ip.postfix_array, ['1', '0', '1', 'or', '1', 'and', 'and']) |
|
268 self.assertTrue(ip.eval()) |
|
269 |
|
270 def test_not(self): |
|
271 ip = ASTInterpreter(u'not 1',) |
|
272 ret = ip.eval() |
|
273 self.assertFalse(ret) |
|
274 |
|
275 ip.create_ast(u'not 1') |
|
276 ret = ip.eval() |
|
277 self.assertFalse(ret) |
|
278 |
|
279 ip.create_ast('not STRING_VALUE') |
|
280 ret = ip.eval() |
|
281 self.assertFalse(ret) |
|
282 |
|
283 def test_not_with_multiple(self): |
|
284 ip = ASTInterpreter(u'1 and not 0') |
|
285 ret = ip.eval() |
|
286 self.assertTrue(ret) |
|
287 ip.create_ast(u'1 and not 1') |
|
288 ret = ip.eval() |
|
289 self.assertFalse(ret) |
|
290 |
|
291 def test_and(self): |
|
292 ip = ASTInterpreter(u'1 and 1 and 0') |
|
293 ret = ip.eval() |
|
294 self.assertFalse(ret) |
|
295 |
|
296 ip.create_ast(u'1 and 1 and 1') |
|
297 ret = ip.eval() |
|
298 self.assertTrue(ret) |
|
299 |
|
300 def test_nand(self): |
|
301 ip = ASTInterpreter(u'1 nand 1 nand 1') |
|
302 ret = ip.eval() |
|
303 self.assertTrue(ret) |
|
304 |
|
305 ip.create_ast(u'1 nand 1 nand 0') |
|
306 ret = ip.eval() |
|
307 self.assertTrue(ret) |
|
308 |
|
309 ip.create_ast(u'1 nand 0 nand 1') |
|
310 ret = ip.eval() |
|
311 self.assertFalse(ret) |
|
312 |
|
313 ip.create_ast(u'0 nand 0 nand 0') |
|
314 ret = ip.eval() |
|
315 self.assertTrue(ret) |
|
316 |
|
317 def test_or(self): |
|
318 ip = ASTInterpreter(u'1 or 1 or 0') |
|
319 ret = ip.eval() |
|
320 self.assertTrue(ret) |
|
321 |
|
322 def test_or_for_exclude(self): |
|
323 """ |
|
324 On exclude case if OR returns True -> some element is selected |
|
325 and the rule evaluation should fail, the exclude rule should |
|
326 evaluate if PostfixRuleEngine.eval(expression) -> return False |
|
327 """ |
|
328 ip = ASTInterpreter(u'1 or 1 or 1') |
|
329 ret = ip.eval() |
|
330 self.assertTrue(ret) |
|
331 |
|
332 ip.create_ast(u'1 or 1 or 0') |
|
333 ret = ip.eval() |
|
334 self.assertTrue(ret) |
|
335 |
|
336 ip.create_ast(u'1 or 0 or 1') |
|
337 ret = ip.eval() |
|
338 self.assertTrue(ret) |
|
339 |
|
340 ip.create_ast(u'0 or 1 or 1') |
|
341 ret = ip.eval() |
|
342 self.assertTrue(ret) |
|
343 |
|
344 ip.create_ast(u'1 or 0 or 0') |
|
345 ret = ip.eval() |
|
346 self.assertTrue(ret) |
|
347 |
|
348 ip.create_ast(u'0 or 0 or 1') |
|
349 ret = ip.eval() |
|
350 self.assertTrue(ret) |
|
351 |
|
352 ip.create_ast(u'0 or 0 or 0') |
|
353 ret = ip.eval() |
|
354 self.assertFalse(ret) |
|
355 |
|
356 def test_nor(self): |
|
357 ip = ASTInterpreter(u'1 nor 1 nor 1') |
|
358 ret = ip.eval() |
|
359 self.assertFalse(ret) |
|
360 |
|
361 ip.create_ast(u'1 nor 1 nor 0') |
|
362 ret = ip.eval() |
|
363 self.assertTrue(ret) |
|
364 |
|
365 ip.create_ast(u'0 nor 1 nor 0') |
|
366 ret = ip.eval() |
|
367 self.assertTrue(ret) |
|
368 |
|
369 ip.create_ast(u'0 nor 0 nor 0') |
|
370 ret = ip.eval() |
|
371 self.assertFalse(ret) |
|
372 |
|
373 |
|
374 def test_xor(self): |
|
375 ip = ASTInterpreter(u'1 xor 1') |
|
376 ret = ip.eval() |
|
377 self.assertFalse(ret) |
|
378 |
|
379 ip.create_ast(u'1 xor 0 xor 0') |
|
380 ret = ip.eval() |
|
381 self.assertTrue(ret) |
|
382 |
|
383 def test_eq_cmp(self): |
|
384 ip = ASTInterpreter(u'1 == 0') |
|
385 ret = ip.eval() |
|
386 self.assertFalse(ret) |
|
387 |
|
388 ip.create_ast(u'1 == 1') |
|
389 ret = ip.eval() |
|
390 self.assertTrue(ret) |
|
391 |
|
392 ip.create_ast(u'DEFINED == DEFINED') |
|
393 ret = ip.eval() |
|
394 self.assertTrue(ret) |
|
395 |
|
396 ip.create_ast(u'DEFINED == UNDEFINED') |
|
397 ret = ip.eval() |
|
398 self.assertFalse(ret) |
|
399 |
|
400 def test_neq_cmp(self): |
|
401 ip = ASTInterpreter(u'1 != 1') |
|
402 ret = ip.eval() |
|
403 self.assertFalse(ret) |
|
404 |
|
405 ip.create_ast(u'1 != 0') |
|
406 ret = ip.eval() |
|
407 self.assertTrue(ret) |
|
408 |
|
409 def test_lt_cmp(self): |
|
410 ip = ASTInterpreter(u'0 < 1') |
|
411 ret = ip.eval() |
|
412 self.assertTrue(ret) |
|
413 |
|
414 ip.create_ast(u'-1 < 1') |
|
415 ret = ip.eval() |
|
416 self.assertTrue(ret) |
|
417 |
|
418 ip.create_ast(u'-1 < -2') |
|
419 ret = ip.eval() |
|
420 self.assertFalse(ret) |
|
421 |
|
422 ip.create_ast(u'2 < 0') |
|
423 ret = ip.eval() |
|
424 self.assertFalse(ret) |
|
425 |
|
426 def test_gt_cmp(self): |
|
427 ip = ASTInterpreter(u'0 > -1') |
|
428 ret = ip.eval() |
|
429 self.assertTrue(ret) |
|
430 |
|
431 ip.create_ast(u'2 > 1') |
|
432 ret = ip.eval() |
|
433 self.assertTrue(ret) |
|
434 |
|
435 ip.create_ast(u'0 > 1') |
|
436 ret = ip.eval() |
|
437 self.assertFalse(ret) |
|
438 |
|
439 ip.create_ast(u'-1 > 1') |
|
440 ret = ip.eval() |
|
441 self.assertFalse(ret) |
|
442 |
|
443 def test_lte_cmp(self): |
|
444 ip = ASTInterpreter(u'0 <= 1') |
|
445 ret = ip.eval() |
|
446 self.assertTrue(ret) |
|
447 |
|
448 ip.create_ast(u'0 <= 0') |
|
449 ret = ip.eval() |
|
450 self.assertTrue(ret) |
|
451 |
|
452 ip.create_ast(u'1 <= 0') |
|
453 ret = ip.eval() |
|
454 self.assertFalse(ret) |
|
455 |
|
456 def test_gte_cmp(self): |
|
457 ip = ASTInterpreter(u'1 >= 0') |
|
458 ret = ip.eval() |
|
459 self.assertTrue(ret) |
|
460 |
|
461 ip.create_ast(u'0 >= 0') |
|
462 ret = ip.eval() |
|
463 self.assertTrue(ret) |
|
464 |
|
465 ip.create_ast(u'0 >= 1') |
|
466 ret = ip.eval() |
|
467 self.assertFalse(ret) |
|
468 |
|
469 def test_extract_refs(self): |
|
470 refs = ASTInterpreter.extract_refs('a.a and ( b.c and d.e )') |
|
471 self.assertTrue('a.a' in refs) |
|
472 self.assertTrue('b.c' in refs) |
|
473 self.assertTrue('d.e' in refs) |
|
474 self.assertTrue('and' not in refs) |
|
475 |
|
476 def test_one_of(self): |
|
477 """ Test for showing that relation one-of is basically "LEFT and R1 xor R2" |
|
478 """ |
|
479 ip = ASTInterpreter(u'1 and 1 and 1 xor 0') |
|
480 ret = ip.eval() |
|
481 self.assertTrue(ret) |
|
482 |
|
483 if __name__ == '__main__': |
|
484 unittest.main() |