]> mj.ucw.cz Git - eval.git/blob - t/moe/conftest.py
Implemented varname as a shorthand for "{varname}"
[eval.git] / t / moe / conftest.py
1 # -*- coding: utf-8 -*-
2
3 import moe.conf as conf
4 from moe.confparser import *
5 import logging as log
6 import unittest
7 import tempfile
8
9 class TestConfig(unittest.TestCase):
10
11   def setUp(s):
12     s.t = conf.ConfigTree()    
13
14   def parse(s, string, level=0, fname='test'):
15     c=ConfigParser(string, s.t, fname, level)
16     ops = c.parse()
17     c.p_WS()
18     assert c.eof()
19     return ops
20
21   def var(s, varname, create=True):
22     return s.t.lookup(varname, create=create)
23
24   def val(s, varname):
25     return s.var(varname, create=False).value()
26
27   def eqparse(s, string, *args, **kwargs):
28     "Parse expression `s`, return parts that should be equal to anoter run of `eqparse(s)`."
29     return [(i[0], i[1].operation) for i in s.parse(string, *args, **kwargs)]
30
31
32 class TestParser(TestConfig):
33   s1 = r"""a="1";z{b='2';w{S.Tr_an-g.e='"'}};c.d='\n';e="{a}{b}";e+='{c.d}';a+="\"\n\{\}";f+='Z{a.b}';e+=c.d;e+=c;e+=a"""
34   s2 = '\t\n \n ' + s1.replace('=', '= \n ').replace(';', '\t \n\t \n ').replace('+=',' \n += ') + '\n\n '
35
36   def test_noWS(s):
37     assert len(s.parse(s.s1)) == 11
38
39   def test_noWS_COMMENT(s):
40     assert s.eqparse(s.s1+'#COMMENT') == s.eqparse(s.s1+'#') == s.eqparse(s.s1+'#\n') == s.eqparse(s.s1+'\n#')
41
42   def test_manyWS(s):
43     assert s.eqparse(s.s2) == s.eqparse(s.s1)
44
45   def test_manyWS_COMMENT(s):
46     assert s.eqparse(s.s2.replace('\n',' #COMMENT \n')) == s.eqparse(s.s2.replace('\n','#\n')) == s.eqparse(s.s1)
47
48   def test_empty(s):
49     assert s.eqparse('') == s.eqparse('\n') == s.eqparse('') == s.eqparse('a{}') == \
50       s.eqparse('a.b.c{if ""==\'\' {d.e{\n\n#Nothing\n}} }') == []
51
52   def test_syntax_errors(s):
53     s.assertRaises(ConfigSyntaxError, s.parse, "a=#")
54     s.assertRaises(ConfigSyntaxError, s.parse, "a='\"")
55     s.assertRaises(ConfigSyntaxError, s.parse, 'a="{a@b}"')
56     s.assertRaises(ConfigSyntaxError, s.parse, 'a="A{A"')
57     s.assertRaises(ConfigSyntaxError, s.parse, 'a=b"42"')
58     s.assertRaises(ConfigSyntaxError, s.parse, 'a=b.c.d.')
59
60   def test_error_location(s):
61     try: s.parse('\t \n  \n  { \n \n ')
62     except ConfigSyntaxError, e:
63       assert e.line == 3 and e.column in range(2,4)
64
65   def test_quoting(s):
66     s.parse(' a="\\"\\{a$b\\}\'\n\n\'{z}" ')
67     assert s.var('z', create=False)
68     # No escaping in '-string 
69     s.assertRaises(ConfigSyntaxError, s.parse, " a='\"\\'\n\n' ")
70     # Variable should not be created
71     s.parse(" a='{z2}' ")
72     s.assertRaises(conf.ConfigError, s.var, 'z2', create=False)
73
74   def test_conditions(s):
75     s.assertRaises(ConfigSyntaxError, s.parse, "if '{a}'=='{b}' and ''!='' {}")
76     s.parse('if ((#C\n (\n (not not not""!="")\n#C\n)\t ) ) {}')
77     s.parse('if (""=="" and not (not not ""!="" or ""=="")){}')
78     s.parse('if ((a=="{b}" and a.b.c!=c.b.a)or x!=y){}')
79     s.parse('if(""==""){a{if(""==""){if(""==""){b{if(""==""){if(""==""){}}}}}}}')
80     s.assertRaises(ConfigSyntaxError, s.parse, "if notnot'{a}'=='{b}' {}")
81     s.assertRaises(ConfigSyntaxError, s.parse, "if ('{a}'=='{b}' not and ''!='') {}")
82     s.assertRaises(ConfigSyntaxError, s.parse, "if ('{a}'=='{b}' ornot ''!='') {}")
83
84
85 class TestConfigEval(TestConfig):
86
87   def test_ops(s):
88     s.parse('c+="-C_APP"', level=20)
89     s.parse('a="A"; b="{a}-B"; c="C1-{b}-C2"; a+="FOO"; a="AA"')
90     assert s.val('c') == 'C1-AA-B-C2-C_APP'
91     s.parse('b+="-A:\{{a}\}";a+="A"', level=10)
92     assert s.val('c') == 'C1-AAA-B-A:{AAA}-C2-C_APP'
93
94   def test_nested(s):
95     s.parse('a="0"; b{a="1"; b{a="2"; b{a="3"; b{a="4"; b{a="5"}}}}}')
96     assert s.val('b.b.b.a') == '3'
97     s.parse('b.b{b.b{b.a="5MOD"}}')
98     assert s.val('b.b.b.b.b.a') == '5MOD'
99
100   def test_escape_chars(s):
101     s.parse(r"""a='{a}\\\\#\n'; b="{a}'\"\{\}"; c='\'; c+="\{{b}\}";""")
102     assert s.val('c') == r"""\{{a}\\\\#\n'"{}}"""
103
104   def test_expressions(s):
105     s.parse('A="4"; B.B=\'2\'; B.A=A; B.C="{B.A}{B.B}"; if B.C=="42" {D=C}; C="OK"')
106     assert s.val('D') == 'OK'
107   
108   ts = 'a="A:"; if "{c1}"=="1" {a+="C1"; b="B"; if ("{c2a}"=="1" or not "{c2b}"=="1") { a+="C2"; '\
109     'if ("{c3a}"=="1" and "{c3b}"=="1") { a+="C3" }}}'
110
111   def test_cond_chain(s):
112     s.parse(s.ts)
113     s.parse('c1="1"; c2="0"')
114     # b should have determined value, a should not (since c3a is undefined)
115     s.assertRaises(conf.UndefinedError, s.val, 'a')
116     assert s.val('b') == 'B'
117     s.parse('c1="0"')
118     # now b should be undefined
119     s.assertRaises(conf.UndefinedError, s.val, 'b')
120     # Normal evaluation
121     s.parse('c1="1"; c2a="1"; c2b="0"; c3a="0"')
122     assert s.val('a') == 'A:C1C2'
123     s.parse('c3a="1"; c3b="1"; c2b="1"')
124     assert s.val('a') == 'A:C1C2C3'
125     # tests condition invalidating 
126     s.parse('c2a+="0"')
127     assert s.val('a') == 'A:C1'
128
129   def test_cond_eager(s):
130     s.parse(s.ts)
131     # undefined c2b and c3a should not be evaluated 
132     s.parse('c1="1"; c2a="1"; c3a="0"')
133     assert s.val('a') == 'A:C1C2'
134     # but now c3b should be evaluated 
135     s.parse('c1="1"; c2a="1"; c3a="1"')
136     s.assertRaises(conf.UndefinedError, s.val, 'a')
137     s.parse('c1="1"; c2a="1"; c3b="1"')
138     assert s.val('a') == 'A:C1C2C3'
139
140   def test_undef(s):
141     s.assertRaises(conf.UndefinedError, s.val, 'a')
142     s.parse('a="{b}"')
143     s.assertRaises(conf.UndefinedError, s.val, 'a')
144     s.parse('b+="1"')
145     s.assertRaises(conf.UndefinedError, s.val, 'b')
146
147   def test_loopy_def(s):
148     s.parse('a="A"; a+="{a}"')
149     s.assertRaises(conf.CyclicConfigError, s.val, 'a')
150     s.parse('b="{c}"; c="{b}"')
151     s.assertRaises(conf.CyclicConfigError, s.val, 'b')
152
153   def test_varname(s):
154     s.assertRaises(conf.VariableNameError, s.val, 'b/c')
155     s.assertRaises(conf.VariableNameError, s.val, '.b.c')
156     s.assertRaises(conf.VariableNameError, s.val, 'b.c.')
157     s.assertRaises(conf.VariableNameError, s.val, 'b..c')
158
159   def test_remove(s):
160     l = s.parse('a="A1"; b="B1"; if "{cond}"=="1" {a+="A2"; b+="B2"}; a+="A3"; b+="B3"; cond="1"')
161     assert s.val('a') == 'A1A2A3'
162     assert s.val('b') == 'B1B2B3'
163     # remove b+="B2"
164     s.var('b').remove_operation(l[3][1])
165     assert s.val('a') == 'A1A2A3'
166     assert s.val('b') == 'B1B3'
167     # are the dependencies still handled properly? 
168     s.parse('cond+="-invalidated"')
169     assert s.val('a') == 'A1A3'
170     s.parse('cond="1"')
171     assert s.val('a') == 'A1A2A3'
172
173   def test_fix(s):
174     s.parse(""" A='4'; B="{A}"; C="2"; B+="{C}"; D="{B}"; E="{D}" """)
175     s.var('D').fix()
176     # Break by C 
177     l = s.parse('C="3"')
178     s.assertRaises(conf.VariableFixedError, s.val, "E")
179     s.assertRaises(conf.VariableFixedError, s.val, "D")
180     s.var('C').remove_operation(l[0][1])
181     # Break directly by D 
182     l = s.parse('D="41"')
183     s.assertRaises(conf.VariableFixedError, s.val, "D")
184     s.assertRaises(conf.VariableFixedError, s.val, "E")
185     # Unfix
186     s.var('D').unfix()
187     assert s.val('E') == '41'
188     s.var('D').remove_operation(l[0][1])
189     assert s.val('D') == '42'
190
191   def test_unicode(s):
192     # Ascii (1b) and unicode (2b)
193     s.parse(u'A="Ú"; C="ě"')
194     # String
195     s.parse('B=\'chyln\'')
196     # By escapes  
197     s.parse(u'D=\'\u0159e\u017eav\xe1\'')
198     s.parse(u'E="ŽluŤ"')
199     # Via utf8 file
200     f = tempfile.TemporaryFile(mode='w+t')
201     f.write(u'S1="\xdachyln\u011b \u0159e\u017eav\xe1 \u017dlu\u0164" ; S2="{A}{B}{C} {D} {E}"'.encode('utf8'))
202     f.seek(0)
203     s.parse(f)
204     # Test
205     s.parse(u'if "{S1}"=="{S2}" { ANS="jó!" } ')
206     assert s.val('ANS') == u"jó!"
207     assert s.val('S1') == s.val('S2') == u'\xdachyln\u011b \u0159e\u017eav\xe1 \u017dlu\u0164'
208
209
210
211
212 # TODO: Test priority
213 # TODO: Fail on 1st April
214 # TODO: Somehow add log.debug('Maximum encountered depth: %d', conf.debug_maxdepth)
215
216 # Coverage via command "nosetests conftest --with-coverage --cover-html-dir=cover --cover-html"
217