Make the GenerateHelpText format easily configurable.
[scons.git] / src / engine / SCons / Options / OptionsTests.py
1 #
2 # __COPYRIGHT__
3 #
4 # Permission is hereby granted, free of charge, to any person obtaining
5 # a copy of this software and associated documentation files (the
6 # "Software"), to deal in the Software without restriction, including
7 # without limitation the rights to use, copy, modify, merge, publish,
8 # distribute, sublicense, and/or sell copies of the Software, and to
9 # permit persons to whom the Software is furnished to do so, subject to
10 # the following conditions:
11 #
12 # The above copyright notice and this permission notice shall be included
13 # in all copies or substantial portions of the Software.
14 #
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
16 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
17 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 #
23
24 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
25
26 import string
27 import sys
28 import unittest
29 import TestSCons
30
31 import SCons.Options
32 import SCons.Util
33 import SCons.Warnings
34
35
36 class Environment:
37     def __init__(self):
38         self.dict = {}
39     def subst(self, x):
40         return SCons.Util.scons_subst(x, self, gvars=self.dict)
41     def __setitem__(self, key, value):
42         self.dict[key] = value
43     def __getitem__(self, key):
44         return self.dict[key]
45     def has_key(self, key):
46         return self.dict.has_key(key)
47
48
49 def check(key, value, env):
50     assert int(value) == 6 * 9, "key %s = %s" % (key, repr(value))
51     
52 # Check saved option file by executing and comparing against
53 # the expected dictionary
54 def checkSave(file, expected):
55     gdict = {}
56     ldict = {}
57     execfile(file, gdict, ldict)
58     assert expected == ldict, "%s\n...not equal to...\n%s" % (expected, ldict)
59
60 class OptionsTestCase(unittest.TestCase):
61
62     def test_Add(self):
63         """Test adding to an Options object"""
64         opts = SCons.Options.Options()
65
66         opts.Add('VAR')
67         opts.Add('ANSWER',
68                  'THE answer to THE question',
69                  "42",
70                  check,
71                  lambda x: int(x) + 12)
72
73         o = opts.options[0]
74         assert o.key == 'VAR'
75         assert o.help == ''
76         assert o.default == None
77         assert o.validator == None
78         assert o.converter == None
79
80         o = opts.options[1]
81         assert o.key == 'ANSWER'
82         assert o.help == 'THE answer to THE question'
83         assert o.default == "42"
84         o.validator(o.key, o.converter(o.default), {})
85
86         def test_it(var, opts=opts):
87             exc_caught = None
88             try:
89                 opts.Add(var)
90             except SCons.Errors.UserError:
91                 exc_caught = 1
92             assert exc_caught, "did not catch UserError for '%s'" % var
93         test_it('foo/bar')
94         test_it('foo-bar')
95         test_it('foo.bar')
96
97     def test_AddOptions(self):
98         """Test adding a list of options to an Options object"""
99         opts = SCons.Options.Options()
100
101         opts.AddOptions(('VAR2',),
102                         ('ANSWER2',
103                          'THE answer to THE question',
104                          "42",
105                          check,
106                          lambda x: int(x) + 12))
107
108         o = opts.options[0]
109         assert o.key == 'VAR2', o.key
110         assert o.help == '', o.help
111         assert o.default == None, o.default
112         assert o.validator == None, o.validator
113         assert o.converter == None, o.converter
114
115         o = opts.options[1]
116         assert o.key == 'ANSWER2', o.key
117         assert o.help == 'THE answer to THE question', o.help
118         assert o.default == "42", o.default
119         o.validator(o.key, o.converter(o.default), {})
120
121     def test_Update(self):
122         """Test updating an Environment"""
123
124         # Test that a default value is validated correctly.
125         test = TestSCons.TestSCons()
126         file = test.workpath('custom.py')
127         opts = SCons.Options.Options(file)
128         
129         opts.Add('ANSWER',
130                  'THE answer to THE question',
131                  "42",
132                  check,
133                  lambda x: int(x) + 12)
134
135         env = Environment()
136         opts.Update(env)
137         assert env['ANSWER'] == 54
138
139         env = Environment()
140         opts.Update(env, {})
141         assert env['ANSWER'] == 54
142
143         # Test that a bad value from the file is used and
144         # validation fails correctly.
145         test = TestSCons.TestSCons()
146         file = test.workpath('custom.py')
147         test.write('custom.py', 'ANSWER=54')
148         opts = SCons.Options.Options(file)
149         
150         opts.Add('ANSWER',
151                  'THE answer to THE question',
152                  "42",
153                  check,
154                  lambda x: int(x) + 12)
155
156         env = Environment()
157         exc_caught = None
158         try:
159             opts.Update(env)
160         except AssertionError:
161             exc_caught = 1
162         assert exc_caught, "did not catch expected assertion"
163
164         env = Environment()
165         exc_caught = None
166         try:
167             opts.Update(env, {})
168         except AssertionError:
169             exc_caught = 1
170         assert exc_caught, "did not catch expected assertion"
171
172         # Test that a good value from the file is used and validated.
173         test = TestSCons.TestSCons()
174         file = test.workpath('custom.py')
175         test.write('custom.py', 'ANSWER=42')
176         opts = SCons.Options.Options(file)
177         
178         opts.Add('ANSWER',
179                  'THE answer to THE question',
180                  "10",
181                  check,
182                  lambda x: int(x) + 12)
183
184         env = Environment()
185         opts.Update(env)
186         assert env['ANSWER'] == 54
187
188         env = Environment()
189         opts.Update(env, {})
190         assert env['ANSWER'] == 54
191
192         # Test that a bad value from an args dictionary passed to
193         # Update() is used and validation fails correctly.
194         test = TestSCons.TestSCons()
195         file = test.workpath('custom.py')
196         test.write('custom.py', 'ANSWER=10')
197         opts = SCons.Options.Options(file)
198         
199         opts.Add('ANSWER',
200                  'THE answer to THE question',
201                  "12",
202                  check,
203                  lambda x: int(x) + 12)
204
205         env = Environment()
206         exc_caught = None
207         try:
208             opts.Update(env, {'ANSWER':'54'})
209         except AssertionError:
210             exc_caught = 1
211         assert exc_caught, "did not catch expected assertion"
212
213         # Test that a good value from an args dictionary
214         # passed to Update() is used and validated.
215         test = TestSCons.TestSCons()
216         file = test.workpath('custom.py')
217         test.write('custom.py', 'ANSWER=10')
218         opts = SCons.Options.Options(file)
219         
220         opts.Add('ANSWER',
221                  'THE answer to THE question',
222                  "12",
223                  check,
224                  lambda x: int(x) + 12)
225
226         env = Environment()
227         opts.Update(env, {'ANSWER':'42'})
228         assert env['ANSWER'] == 54
229
230         # Test against a former bug.  If we supply a converter,
231         # but no default, the value should *not* appear in the
232         # Environment if no value is specified in the options file
233         # or args.
234         test = TestSCons.TestSCons()
235         file = test.workpath('custom.py')
236         opts = SCons.Options.Options(file)
237         
238         opts.Add('ANSWER',
239                  help='THE answer to THE question',
240                  converter=str)
241
242         env = Environment()
243         opts.Update(env, {})
244         assert not env.has_key('ANSWER')
245
246         # Test that a default value of None is all right.
247         test = TestSCons.TestSCons()
248         file = test.workpath('custom.py')
249         opts = SCons.Options.Options(file)
250
251         opts.Add('ANSWER',
252                  "This is the answer",
253                  None,
254                  check)
255
256         env = Environment()
257         opts.Update(env, {})
258         assert not env.has_key('ANSWER')
259
260     def test_args(self):
261         """Test updating an Environment with arguments overridden"""
262
263         # Test that a bad (command-line) argument is used
264         # and the validation fails correctly.
265         test = TestSCons.TestSCons()
266         file = test.workpath('custom.py')
267         test.write('custom.py', 'ANSWER=42')
268         opts = SCons.Options.Options(file, {'ANSWER':54})
269         
270         opts.Add('ANSWER',
271                  'THE answer to THE question',
272                  "42",
273                  check,
274                  lambda x: int(x) + 12)
275
276         env = Environment()
277         exc_caught = None
278         try:
279             opts.Update(env)
280         except AssertionError:
281             exc_caught = 1
282         assert exc_caught, "did not catch expected assertion"
283
284         # Test that a good (command-line) argument is used and validated.
285         test = TestSCons.TestSCons()
286         file = test.workpath('custom.py')
287         test.write('custom.py', 'ANSWER=54')
288         opts = SCons.Options.Options(file, {'ANSWER':42})
289         
290         opts.Add('ANSWER',
291                  'THE answer to THE question',
292                  "54",
293                  check,
294                  lambda x: int(x) + 12)
295
296         env = Environment()
297         opts.Update(env)
298         assert env['ANSWER'] == 54
299
300         # Test that a (command-line) argument is overridden by a dictionary
301         # supplied to Update() and the dictionary value is validated correctly.
302         test = TestSCons.TestSCons()
303         file = test.workpath('custom.py')
304         test.write('custom.py', 'ANSWER=54')
305         opts = SCons.Options.Options(file, {'ANSWER':54})
306         
307         opts.Add('ANSWER',
308                  'THE answer to THE question',
309                  "54",
310                  check,
311                  lambda x: int(x) + 12)
312
313         env = Environment()
314         opts.Update(env, {'ANSWER':42})
315         assert env['ANSWER'] == 54
316
317     def test_Save(self):
318         """Testing saving Options"""
319
320         test = TestSCons.TestSCons()
321         cache_file = test.workpath('cached.options')
322         opts = SCons.Options.Options()
323         
324         # test saving out empty file
325         opts.Add('OPT_VAL',
326                  'An option to test',
327                  21,
328                  None,
329                  None)
330         opts.Add('OPT_VAL_2',
331                  default='foo')
332         opts.Add('OPT_VAL_3',
333                  default=1)
334
335         env = Environment()
336         opts.Update(env, {'OPT_VAL_3' : 2})
337         assert env['OPT_VAL'] == 21
338         assert env['OPT_VAL_2'] == 'foo'
339         assert env['OPT_VAL_3'] == 2
340         env['OPT_VAL_2'] = 'bar'
341         opts.Save(cache_file, env)
342         checkSave(cache_file, { 'OPT_VAL_2' : 'bar',
343                                 'OPT_VAL_3' : 2 })
344
345         # Test against some old bugs
346         class Foo:
347             def __init__(self, x):
348                 self.x = x
349             def __str__(self):
350                 return self.x
351             
352         test = TestSCons.TestSCons()
353         cache_file = test.workpath('cached.options')
354         opts = SCons.Options.Options()
355         
356         opts.Add('THIS_USED_TO_BREAK',
357                  'An option to test',
358                  "Default")
359
360         opts.Add('THIS_ALSO_BROKE',
361                  'An option to test',
362                  "Default2")
363         
364         opts.Add('THIS_SHOULD_WORK',
365                  'An option to test',
366                  Foo('bar'))
367         
368         env = Environment()
369         opts.Update(env, { 'THIS_USED_TO_BREAK' : "Single'Quotes'In'String",
370                            'THIS_ALSO_BROKE' : "\\Escape\nSequences\t",
371                            'THIS_SHOULD_WORK' : Foo('baz') })
372         opts.Save(cache_file, env)
373         checkSave(cache_file, { 'THIS_USED_TO_BREAK' : "Single'Quotes'In'String",
374                                 'THIS_ALSO_BROKE' : "\\Escape\nSequences\t",
375                                 'THIS_SHOULD_WORK' : 'baz' })
376
377     def test_GenerateHelpText(self):
378         """Test generating the default format help text"""
379         opts = SCons.Options.Options()
380
381         opts.Add('ANSWER',
382                  'THE answer to THE question',
383                  "42",
384                  check,
385                  lambda x: int(x) + 12)
386
387         opts.Add('B',
388                  'b - alpha test',
389                  "42",
390                  check,
391                  lambda x: int(x) + 12)
392
393         opts.Add('A',
394                  'a - alpha test',
395                  "42",
396                  check,
397                  lambda x: int(x) + 12)
398
399         env = Environment()
400         opts.Update(env, {})
401
402         expect = """
403 ANSWER: THE answer to THE question
404     default: 42
405     actual: 54
406
407 B: b - alpha test
408     default: 42
409     actual: 54
410
411 A: a - alpha test
412     default: 42
413     actual: 54
414 """
415
416         text = opts.GenerateHelpText(env)
417         assert text == expect, text
418
419         expectAlpha = """
420 A: a - alpha test
421     default: 42
422     actual: 54
423
424 ANSWER: THE answer to THE question
425     default: 42
426     actual: 54
427
428 B: b - alpha test
429     default: 42
430     actual: 54
431 """
432         text = opts.GenerateHelpText(env, sort=cmp)
433         assert text == expectAlpha, text
434
435     def test_FormatOptionHelpText(self):
436         """Test generating custom format help text"""
437         opts = SCons.Options.Options()
438
439         def my_format(env, opt, help, default, actual):
440             return '%s %s %s %s\n' % (opt, default, actual, help)
441
442         opts.FormatOptionHelpText = my_format
443
444         opts.Add('ANSWER',
445                  'THE answer to THE question',
446                  "42",
447                  check,
448                  lambda x: int(x) + 12)
449
450         opts.Add('B',
451                  'b - alpha test',
452                  "42",
453                  check,
454                  lambda x: int(x) + 12)
455
456         opts.Add('A',
457                  'a - alpha test',
458                  "42",
459                  check,
460                  lambda x: int(x) + 12)
461
462         env = Environment()
463         opts.Update(env, {})
464
465         expect = """\
466 ANSWER 42 54 THE answer to THE question
467 B 42 54 b - alpha test
468 A 42 54 a - alpha test
469 """
470
471         text = opts.GenerateHelpText(env)
472         assert text == expect, text
473
474         expectAlpha = """\
475 A 42 54 a - alpha test
476 ANSWER 42 54 THE answer to THE question
477 B 42 54 b - alpha test
478 """
479         text = opts.GenerateHelpText(env, sort=cmp)
480         assert text == expectAlpha, text
481
482
483     
484         
485 if __name__ == "__main__":
486     suite = unittest.makeSuite(OptionsTestCase, 'test_')
487     if not unittest.TextTestRunner().run(suite).wasSuccessful():
488         sys.exit(1)