0719853b3a402064ea2d06321c8ae6b3b5004ddc
[scons.git] / src / engine / SCons / EnvironmentTests.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 SCons.compat
27
28 import collections
29 import copy
30 import io
31 import os
32 import sys
33 import TestCmd
34 import unittest
35
36 from SCons.Environment import *
37 import SCons.Warnings
38
39 def diff_env(env1, env2):
40     s1 = "env1 = {\n"
41     s2 = "env2 = {\n"
42     d = {}
43     for k in env1._dict.keys() + env2._dict.keys():
44         d[k] = None
45     for k in sorted(d.keys()):
46         if k in env1:
47            if k in env2:
48                if env1[k] != env2[k]:
49                    s1 = s1 + "    " + repr(k) + " : " + repr(env1[k]) + "\n"
50                    s2 = s2 + "    " + repr(k) + " : " + repr(env2[k]) + "\n"
51            else:
52                s1 = s1 + "    " + repr(k) + " : " + repr(env1[k]) + "\n"
53         elif k in env2:
54            s2 = s2 + "    " + repr(k) + " : " + repr(env2[k]) + "\n"
55     s1 = s1 + "}\n"
56     s2 = s2 + "}\n"
57     return s1 + s2
58
59 def diff_dict(d1, d2):
60     s1 = "d1 = {\n"
61     s2 = "d2 = {\n"
62     d = {}
63     for k in d1.keys() + d2.keys():
64         d[k] = None
65     for k in sorted(d.keys()):
66         if k in d1:
67            if k in d2:
68                if d1[k] != d2[k]:
69                    s1 = s1 + "    " + repr(k) + " : " + repr(d1[k]) + "\n"
70                    s2 = s2 + "    " + repr(k) + " : " + repr(d2[k]) + "\n"
71            else:
72                s1 = s1 + "    " + repr(k) + " : " + repr(d1[k]) + "\n"
73         elif k in env2:
74            s2 = s2 + "    " + repr(k) + " : " + repr(d2[k]) + "\n"
75     s1 = s1 + "}\n"
76     s2 = s2 + "}\n"
77     return s1 + s2
78
79 called_it = {}
80 built_it = {}
81
82 class Builder(SCons.Builder.BuilderBase):
83     """A dummy Builder class for testing purposes.  "Building"
84     a target is simply setting a value in the dictionary.
85     """
86     def __init__(self, name = None):
87         self.name = name
88
89     def __call__(self, env, target=None, source=None, **kw):
90         global called_it
91         called_it['target'] = target
92         called_it['source'] = source
93         called_it.update(kw)
94
95     def execute(self, target = None, **kw):
96         global built_it
97         built_it[target] = 1
98
99
100
101 scanned_it = {}
102
103 class Scanner:
104     """A dummy Scanner class for testing purposes.  "Scanning"
105     a target is simply setting a value in the dictionary.
106     """
107     def __init__(self, name, skeys=[]):
108         self.name = name
109         self.skeys = skeys
110
111     def __call__(self, filename):
112         global scanned_it
113         scanned_it[filename] = 1
114
115     def __cmp__(self, other):
116         try:
117             return cmp(self.__dict__, other.__dict__)
118         except AttributeError:
119             return 1
120
121     def get_skeys(self, env):
122         return self.skeys
123
124     def __str__(self):
125         return self.name
126
127
128
129 class CLVar(collections.UserList):
130     def __init__(self, seq):
131         if isinstance(seq, str):
132             seq = seq.split()
133         collections.UserList.__init__(self, seq)
134     def __add__(self, other):
135         return collections.UserList.__add__(self, CLVar(other))
136     def __radd__(self, other):
137         return collections.UserList.__radd__(self, CLVar(other))
138     def __coerce__(self, other):
139         return (self, CLVar(other))
140
141
142
143 class DummyNode:
144     def __init__(self, name):
145         self.name = name
146     def __str__(self):
147         return self.name
148     def rfile(self):
149         return self
150     def get_subst_proxy(self):
151         return self
152
153 def test_tool( env ):
154     env['_F77INCFLAGS'] = '$( ${_concat(INCPREFIX, F77PATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)'
155
156 class TestEnvironmentFixture:
157     def TestEnvironment(self, *args, **kw):
158         if not kw or 'tools' not in kw:
159             kw['tools'] = [test_tool]
160         default_keys = { 'CC' : 'cc',
161                          'CCFLAGS' : '-DNDEBUG',
162                          'ENV' : { 'TMP' : '/tmp' } }
163         for key, value in default_keys.items():
164             if key not in kw:
165                 kw[key] = value
166         if 'BUILDERS' not in kw:
167             static_obj = SCons.Builder.Builder(action = {},
168                                                emitter = {},
169                                                suffix = '.o',
170                                                single_source = 1)
171             kw['BUILDERS'] = {'Object' : static_obj}
172             static_obj.add_action('.cpp', 'fake action')
173             
174         env = Environment(*args, **kw)
175         return env
176
177 class SubstitutionTestCase(unittest.TestCase):
178
179     def test___init__(self):
180         """Test initializing a SubstitutionEnvironment
181         """
182         env = SubstitutionEnvironment()
183         assert '__env__' not in env
184
185     def test___cmp__(self):
186         """Test comparing SubstitutionEnvironments
187         """
188
189         env1 = SubstitutionEnvironment(XXX = 'x')
190         env2 = SubstitutionEnvironment(XXX = 'x')
191         env3 = SubstitutionEnvironment(XXX = 'xxx')
192         env4 = SubstitutionEnvironment(XXX = 'x', YYY = 'x')
193
194         assert env1 == env2
195         assert env1 != env3
196         assert env1 != env4
197
198     def test___delitem__(self):
199         """Test deleting a variable from a SubstitutionEnvironment
200         """
201         env1 = SubstitutionEnvironment(XXX = 'x', YYY = 'y')
202         env2 = SubstitutionEnvironment(XXX = 'x')
203         del env1['YYY']
204         assert env1 == env2
205
206     def test___getitem__(self):
207         """Test fetching a variable from a SubstitutionEnvironment
208         """
209         env = SubstitutionEnvironment(XXX = 'x')
210         assert env['XXX'] == 'x', env['XXX']
211
212     def test___setitem__(self):
213         """Test setting a variable in a SubstitutionEnvironment
214         """
215         env1 = SubstitutionEnvironment(XXX = 'x')
216         env2 = SubstitutionEnvironment(XXX = 'x', YYY = 'y')
217         env1['YYY'] = 'y'
218         assert env1 == env2
219
220     def test_get(self):
221         """Test the SubstitutionEnvironment get() method
222         """
223         env = SubstitutionEnvironment(XXX = 'x')
224         assert env.get('XXX') == 'x', env.get('XXX')
225         assert env.get('YYY') is None, env.get('YYY')
226
227     def test_has_key(self):
228         """Test the SubstitutionEnvironment has_key() method
229         """
230         env = SubstitutionEnvironment(XXX = 'x')
231         assert 'XXX' in env
232         assert 'YYY' not in env
233
234     def test_contains(self):
235         """Test the SubstitutionEnvironment __contains__() method
236         """
237         try:
238             'x' in {'x':1}
239         except TypeError:
240             # TODO(1.5)
241             # An early version of Python that doesn't support "in"
242             # on dictionaries.  Just pass the test.
243             pass
244         else:
245             env = SubstitutionEnvironment(XXX = 'x')
246             assert 'XXX' in env
247             assert not 'YYY' in env
248
249     def test_items(self):
250         """Test the SubstitutionEnvironment items() method
251         """
252         env = SubstitutionEnvironment(XXX = 'x', YYY = 'y')
253         items = env.items()
254         assert items == [('XXX','x'), ('YYY','y')], items
255
256     def test_arg2nodes(self):
257         """Test the arg2nodes method
258         """
259         env = SubstitutionEnvironment()
260         dict = {}
261         class X(SCons.Node.Node):
262             pass
263         def Factory(name, directory = None, create = 1, dict=dict, X=X):
264             if name not in dict:
265                 dict[name] = X()
266                 dict[name].name = name
267             return dict[name]
268
269         nodes = env.arg2nodes("Util.py UtilTests.py", Factory)
270         assert len(nodes) == 1, nodes
271         assert isinstance(nodes[0], X)
272         assert nodes[0].name == "Util.py UtilTests.py"
273
274         try: unicode
275         except NameError: pass
276         else:
277             code = """if 1:
278                 nodes = env.arg2nodes(u"Util.py UtilTests.py", Factory)
279                 assert len(nodes) == 1, nodes
280                 assert isinstance(nodes[0], X)
281                 assert nodes[0].name == u"Util.py UtilTests.py"
282                 \n"""
283             exec code in globals(), locals()
284
285         nodes = env.arg2nodes(["Util.py", "UtilTests.py"], Factory)
286         assert len(nodes) == 2, nodes
287         assert isinstance(nodes[0], X)
288         assert isinstance(nodes[1], X)
289         assert nodes[0].name == "Util.py"
290         assert nodes[1].name == "UtilTests.py"
291
292         n1 = Factory("Util.py")
293         nodes = env.arg2nodes([n1, "UtilTests.py"], Factory)
294         assert len(nodes) == 2, nodes
295         assert isinstance(nodes[0], X)
296         assert isinstance(nodes[1], X)
297         assert nodes[0].name == "Util.py"
298         assert nodes[1].name == "UtilTests.py"
299
300         class SConsNode(SCons.Node.Node):
301             pass
302         nodes = env.arg2nodes(SConsNode())
303         assert len(nodes) == 1, nodes
304         assert isinstance(nodes[0], SConsNode), node
305
306         class OtherNode:
307             pass
308         nodes = env.arg2nodes(OtherNode())
309         assert len(nodes) == 1, nodes
310         assert isinstance(nodes[0], OtherNode), node
311
312         def lookup_a(str, F=Factory):
313             if str[0] == 'a':
314                 n = F(str)
315                 n.a = 1
316                 return n
317             else:
318                 return None
319
320         def lookup_b(str, F=Factory):
321             if str[0] == 'b':
322                 n = F(str)
323                 n.b = 1
324                 return n
325             else:
326                 return None
327
328         env_ll = SubstitutionEnvironment()
329         env_ll.lookup_list = [lookup_a, lookup_b]
330
331         nodes = env_ll.arg2nodes(['aaa', 'bbb', 'ccc'], Factory)
332         assert len(nodes) == 3, nodes
333
334         assert nodes[0].name == 'aaa', nodes[0]
335         assert nodes[0].a == 1, nodes[0]
336         assert not hasattr(nodes[0], 'b'), nodes[0]
337
338         assert nodes[1].name == 'bbb'
339         assert not hasattr(nodes[1], 'a'), nodes[1]
340         assert nodes[1].b == 1, nodes[1]
341
342         assert nodes[2].name == 'ccc'
343         assert not hasattr(nodes[2], 'a'), nodes[1]
344         assert not hasattr(nodes[2], 'b'), nodes[1]
345
346         def lookup_bbbb(str, F=Factory):
347             if str == 'bbbb':
348                 n = F(str)
349                 n.bbbb = 1
350                 return n
351             else:
352                 return None
353
354         def lookup_c(str, F=Factory):
355             if str[0] == 'c':
356                 n = F(str)
357                 n.c = 1
358                 return n
359             else:
360                 return None
361
362         nodes = env.arg2nodes(['bbbb', 'ccc'], Factory,
363                                      [lookup_c, lookup_bbbb, lookup_b])
364         assert len(nodes) == 2, nodes
365
366         assert nodes[0].name == 'bbbb'
367         assert not hasattr(nodes[0], 'a'), nodes[1]
368         assert not hasattr(nodes[0], 'b'), nodes[1]
369         assert nodes[0].bbbb == 1, nodes[1]
370         assert not hasattr(nodes[0], 'c'), nodes[0]
371
372         assert nodes[1].name == 'ccc'
373         assert not hasattr(nodes[1], 'a'), nodes[1]
374         assert not hasattr(nodes[1], 'b'), nodes[1]
375         assert not hasattr(nodes[1], 'bbbb'), nodes[0]
376         assert nodes[1].c == 1, nodes[1]
377
378     def test_arg2nodes_target_source(self):
379         """Test the arg2nodes method with target= and source= keywords
380         """
381         targets = [DummyNode('t1'), DummyNode('t2')]
382         sources = [DummyNode('s1'), DummyNode('s2')]
383         env = SubstitutionEnvironment()
384         nodes = env.arg2nodes(['${TARGET}-a',
385                                '${SOURCE}-b',
386                                '${TARGETS[1]}-c',
387                                '${SOURCES[1]}-d'],
388                               DummyNode,
389                               target=targets,
390                               source=sources)
391         names = [n.name for n in nodes]
392         assert names == ['t1-a', 's1-b', 't2-c', 's2-d'], names
393
394     def test_gvars(self):
395         """Test the base class gvars() method"""
396         env = SubstitutionEnvironment()
397         gvars = env.gvars()
398         assert gvars == {}, gvars
399
400     def test_lvars(self):
401         """Test the base class lvars() method"""
402         env = SubstitutionEnvironment()
403         lvars = env.lvars()
404         assert lvars == {}, lvars
405
406     def test_subst(self):
407         """Test substituting construction variables within strings
408
409         Check various combinations, including recursive expansion
410         of variables into other variables.
411         """
412         env = SubstitutionEnvironment(AAA = 'a', BBB = 'b')
413         mystr = env.subst("$AAA ${AAA}A $BBBB $BBB")
414         assert mystr == "a aA b", mystr
415
416         # Changed the tests below to reflect a bug fix in
417         # subst()
418         env = SubstitutionEnvironment(AAA = '$BBB', BBB = 'b', BBBA = 'foo')
419         mystr = env.subst("$AAA ${AAA}A ${AAA}B $BBB")
420         assert mystr == "b bA bB b", mystr
421
422         env = SubstitutionEnvironment(AAA = '$BBB', BBB = '$CCC', CCC = 'c')
423         mystr = env.subst("$AAA ${AAA}A ${AAA}B $BBB")
424         assert mystr == "c cA cB c", mystr
425
426         # Lists:
427         env = SubstitutionEnvironment(AAA = ['a', 'aa', 'aaa'])
428         mystr = env.subst("$AAA")
429         assert mystr == "a aa aaa", mystr
430
431         # Tuples:
432         env = SubstitutionEnvironment(AAA = ('a', 'aa', 'aaa'))
433         mystr = env.subst("$AAA")
434         assert mystr == "a aa aaa", mystr
435
436         t1 = DummyNode('t1')
437         t2 = DummyNode('t2')
438         s1 = DummyNode('s1')
439         s2 = DummyNode('s2')
440
441         env = SubstitutionEnvironment(AAA = 'aaa')
442         s = env.subst('$AAA $TARGET $SOURCES', target=[t1, t2], source=[s1, s2])
443         assert s == "aaa t1 s1 s2", s
444         s = env.subst('$AAA $TARGETS $SOURCE', target=[t1, t2], source=[s1, s2])
445         assert s == "aaa t1 t2 s1", s
446
447         # Test callables in the SubstitutionEnvironment
448         def foo(target, source, env, for_signature):
449             assert str(target) == 't', target
450             assert str(source) == 's', source
451             return env["FOO"]
452
453         env = SubstitutionEnvironment(BAR=foo, FOO='baz')
454         t = DummyNode('t')
455         s = DummyNode('s')
456
457         subst = env.subst('test $BAR', target=t, source=s)
458         assert subst == 'test baz', subst
459
460         # Test not calling callables in the SubstitutionEnvironment
461         if 0:
462             # This will take some serious surgery to subst() and
463             # subst_list(), so just leave these tests out until we can
464             # do that.
465             def bar(arg):
466                 pass
467
468             env = SubstitutionEnvironment(BAR=bar, FOO='$BAR')
469
470             subst = env.subst('$BAR', call=None)
471             assert subst is bar, subst
472
473             subst = env.subst('$FOO', call=None)
474             assert subst is bar, subst
475
476     def test_subst_kw(self):
477         """Test substituting construction variables within dictionaries"""
478         env = SubstitutionEnvironment(AAA = 'a', BBB = 'b')
479         kw = env.subst_kw({'$AAA' : 'aaa', 'bbb' : '$BBB'})
480         assert len(kw) == 2, kw
481         assert kw['a'] == 'aaa', kw['a']
482         assert kw['bbb'] == 'b', kw['bbb']
483
484     def test_subst_list(self):
485         """Test substituting construction variables in command lists
486         """
487         env = SubstitutionEnvironment(AAA = 'a', BBB = 'b')
488         l = env.subst_list("$AAA ${AAA}A $BBBB $BBB")
489         assert l == [["a", "aA", "b"]], l
490
491         # Changed the tests below to reflect a bug fix in
492         # subst()
493         env = SubstitutionEnvironment(AAA = '$BBB', BBB = 'b', BBBA = 'foo')
494         l = env.subst_list("$AAA ${AAA}A ${AAA}B $BBB")
495         assert l == [["b", "bA", "bB", "b"]], l
496
497         env = SubstitutionEnvironment(AAA = '$BBB', BBB = '$CCC', CCC = 'c')
498         l = env.subst_list("$AAA ${AAA}A ${AAA}B $BBB")
499         assert l == [["c", "cA", "cB", "c"]], mystr
500
501         env = SubstitutionEnvironment(AAA = '$BBB', BBB = '$CCC', CCC = [ 'a', 'b\nc' ])
502         lst = env.subst_list([ "$AAA", "B $CCC" ])
503         assert lst == [[ "a", "b"], ["c", "B a", "b"], ["c"]], lst
504
505         t1 = DummyNode('t1')
506         t2 = DummyNode('t2')
507         s1 = DummyNode('s1')
508         s2 = DummyNode('s2')
509
510         env = SubstitutionEnvironment(AAA = 'aaa')
511         s = env.subst_list('$AAA $TARGET $SOURCES', target=[t1, t2], source=[s1, s2])
512         assert s == [["aaa", "t1", "s1", "s2"]], s
513         s = env.subst_list('$AAA $TARGETS $SOURCE', target=[t1, t2], source=[s1, s2])
514         assert s == [["aaa", "t1", "t2", "s1"]], s
515
516         # Test callables in the SubstitutionEnvironment
517         def foo(target, source, env, for_signature):
518             assert str(target) == 't', target
519             assert str(source) == 's', source
520             return env["FOO"]
521
522         env = SubstitutionEnvironment(BAR=foo, FOO='baz')
523         t = DummyNode('t')
524         s = DummyNode('s')
525
526         lst = env.subst_list('test $BAR', target=t, source=s)
527         assert lst == [['test', 'baz']], lst
528
529         # Test not calling callables in the SubstitutionEnvironment
530         if 0:
531             # This will take some serious surgery to subst() and
532             # subst_list(), so just leave these tests out until we can
533             # do that.
534             def bar(arg):
535                 pass
536
537             env = SubstitutionEnvironment(BAR=bar, FOO='$BAR')
538
539             subst = env.subst_list('$BAR', call=None)
540             assert subst is bar, subst
541
542             subst = env.subst_list('$FOO', call=None)
543             assert subst is bar, subst
544
545     def test_subst_path(self):
546         """Test substituting a path list
547         """
548         class MyProxy:
549             def __init__(self, val):
550                 self.val = val
551             def get(self):
552                 return self.val + '-proxy'
553
554         class MyNode:
555             def __init__(self, val):
556                 self.val = val
557             def get_subst_proxy(self):
558                 return self
559             def __str__(self):
560                 return self.val
561
562         class MyObj:
563             def get(self):
564                 return self
565
566         env = SubstitutionEnvironment(FOO='foo',
567                                       BAR='bar',
568                                       LIST=['one', 'two'],
569                                       PROXY=MyProxy('my1'))
570
571         r = env.subst_path('$FOO')
572         assert r == ['foo'], r
573
574         r = env.subst_path(['$FOO', 'xxx', '$BAR'])
575         assert r == ['foo', 'xxx', 'bar'], r
576
577         r = env.subst_path(['$FOO', '$LIST', '$BAR'])
578         assert list(map(str, r)) == ['foo', 'one two', 'bar'], r
579
580         r = env.subst_path(['$FOO', '$TARGET', '$SOURCE', '$BAR'])
581         assert r == ['foo', '', '', 'bar'], r
582
583         r = env.subst_path(['$FOO', '$TARGET', '$BAR'], target=MyNode('ttt'))
584         assert list(map(str, r)) == ['foo', 'ttt', 'bar'], r
585
586         r = env.subst_path(['$FOO', '$SOURCE', '$BAR'], source=MyNode('sss'))
587         assert list(map(str, r)) == ['foo', 'sss', 'bar'], r
588
589         n = MyObj()
590
591         r = env.subst_path(['$PROXY', MyProxy('my2'), n])
592         assert r == ['my1-proxy', 'my2-proxy', n], r
593
594         class StringableObj:
595             def __init__(self, s):
596                 self.s = s
597             def __str__(self):
598                 return self.s
599
600         env = SubstitutionEnvironment(FOO=StringableObj("foo"),
601                           BAR=StringableObj("bar"))
602
603         r = env.subst_path([ "${FOO}/bar", "${BAR}/baz" ])
604         assert r == [ "foo/bar", "bar/baz" ], r
605
606         r = env.subst_path([ "bar/${FOO}", "baz/${BAR}" ])
607         assert r == [ "bar/foo", "baz/bar" ], r
608
609         r = env.subst_path([ "bar/${FOO}/bar", "baz/${BAR}/baz" ])
610         assert r == [ "bar/foo/bar", "baz/bar/baz" ], r
611
612     def test_subst_target_source(self):
613         """Test the base environment subst_target_source() method"""
614         env = SubstitutionEnvironment(AAA = 'a', BBB = 'b')
615         mystr = env.subst_target_source("$AAA ${AAA}A $BBBB $BBB")
616         assert mystr == "a aA b", mystr
617
618     def test_backtick(self):
619         """Test the backtick() method for capturing command output"""
620         env = SubstitutionEnvironment()
621
622         test = TestCmd.TestCmd(workdir = '')
623         test.write('stdout.py', """\
624 import sys
625 sys.stdout.write('this came from stdout.py\\n')
626 sys.exit(0)
627 """)
628         test.write('stderr.py', """\
629 import sys
630 sys.stderr.write('this came from stderr.py\\n')
631 sys.exit(0)
632 """)
633         test.write('fail.py', """\
634 import sys
635 sys.exit(1)
636 """)
637         test.write('echo.py', """\
638 import os, sys
639 sys.stdout.write(os.environ['ECHO'] + '\\n')
640 sys.exit(0)
641 """)
642
643         save_stderr = sys.stderr
644
645         python = '"' + sys.executable + '"'
646
647         try:
648             sys.stderr = io.StringIO()
649             cmd = '%s %s' % (python, test.workpath('stdout.py'))
650             output = env.backtick(cmd)
651             errout = sys.stderr.getvalue()
652             assert output == 'this came from stdout.py\n', output
653             assert errout == '', errout
654
655             sys.stderr = io.StringIO()
656             cmd = '%s %s' % (python, test.workpath('stderr.py'))
657             output = env.backtick(cmd)
658             errout = sys.stderr.getvalue()
659             assert output == '', output
660             assert errout == 'this came from stderr.py\n', errout
661
662             sys.stderr = io.StringIO()
663             cmd = '%s %s' % (python, test.workpath('fail.py'))
664             try:
665                 env.backtick(cmd)
666             except OSError, e:
667                 assert str(e) == "'%s' exited 1" % cmd, str(e)
668             else:
669                 self.fail("did not catch expected OSError")
670
671             sys.stderr = io.StringIO()
672             cmd = '%s %s' % (python, test.workpath('echo.py'))
673             env['ENV'] = os.environ.copy()
674             env['ENV']['ECHO'] = 'this came from ECHO'
675             output = env.backtick(cmd)
676             errout = sys.stderr.getvalue()
677             assert output == 'this came from ECHO\n', output
678             assert errout == '', errout
679
680         finally:
681             sys.stderr = save_stderr
682
683     def test_AddMethod(self):
684         """Test the AddMethod() method"""
685         env = SubstitutionEnvironment(FOO = 'foo')
686
687         def func(self):
688             return 'func-' + self['FOO']
689
690         assert not hasattr(env, 'func')
691         env.AddMethod(func)
692         r = env.func()
693         assert r == 'func-foo', r
694
695         assert not hasattr(env, 'bar')
696         env.AddMethod(func, 'bar')
697         r = env.bar()
698         assert r == 'func-foo', r
699
700         def func2(self, arg=''):
701             return 'func2-' + self['FOO'] + arg
702
703         env.AddMethod(func2)
704         r = env.func2()
705         assert r == 'func2-foo', r
706         r = env.func2('-xxx')
707         assert r == 'func2-foo-xxx', r
708
709         env.AddMethod(func2, 'func')
710         r = env.func()
711         assert r == 'func2-foo', r
712         r = env.func('-yyy')
713         assert r == 'func2-foo-yyy', r
714
715         # Test that clones of clones correctly re-bind added methods.
716         env1 = Environment(FOO = '1')
717         env1.AddMethod(func2)
718         env2 = env1.Clone(FOO = '2')
719         env3 = env2.Clone(FOO = '3')
720         env4 = env3.Clone(FOO = '4')
721         r = env1.func2()
722         assert r == 'func2-1', r
723         r = env2.func2()
724         assert r == 'func2-2', r
725         r = env3.func2()
726         assert r == 'func2-3', r
727         r = env4.func2()
728         assert r == 'func2-4', r
729
730         # Test that clones don't re-bind an attribute that the user
731         env1 = Environment(FOO = '1')
732         env1.AddMethod(func2)
733         def replace_func2():
734             return 'replace_func2'
735         env1.func2 = replace_func2
736         env2 = env1.Clone(FOO = '2')
737         r = env2.func2()
738         assert r == 'replace_func2', r
739
740     def test_Override(self):
741         "Test overriding construction variables"
742         env = SubstitutionEnvironment(ONE=1, TWO=2, THREE=3, FOUR=4)
743         assert env['ONE'] == 1, env['ONE']
744         assert env['TWO'] == 2, env['TWO']
745         assert env['THREE'] == 3, env['THREE']
746         assert env['FOUR'] == 4, env['FOUR']
747
748         env2 = env.Override({'TWO'   : '10',
749                              'THREE' :'x $THREE y',
750                              'FOUR'  : ['x', '$FOUR', 'y']})
751         assert env2['ONE'] == 1, env2['ONE']
752         assert env2['TWO'] == '10', env2['TWO']
753         assert env2['THREE'] == 'x 3 y', env2['THREE']
754         assert env2['FOUR'] == ['x', 4, 'y'], env2['FOUR']
755
756         assert env['ONE'] == 1, env['ONE']
757         assert env['TWO'] == 2, env['TWO']
758         assert env['THREE'] == 3, env['THREE']
759         assert env['FOUR'] == 4, env['FOUR']
760
761         env2.Replace(ONE = "won")
762         assert env2['ONE'] == "won", env2['ONE']
763         assert env['ONE'] == 1, env['ONE']
764
765     def test_ParseFlags(self):
766         """Test the ParseFlags() method
767         """
768         env = SubstitutionEnvironment()
769
770         empty = {
771             'ASFLAGS'       : [],
772             'CFLAGS'        : [],
773             'CCFLAGS'       : [],
774             'CPPDEFINES'    : [],
775             'CPPFLAGS'      : [],
776             'CPPPATH'       : [],
777             'FRAMEWORKPATH' : [],
778             'FRAMEWORKS'    : [],
779             'LIBPATH'       : [],
780             'LIBS'          : [],
781             'LINKFLAGS'     : [],
782             'RPATH'         : [],
783         }
784
785         d = env.ParseFlags(None)
786         assert d == empty, d
787
788         d = env.ParseFlags('')
789         assert d == empty, d
790
791         d = env.ParseFlags([])
792         assert d == empty, d
793
794         s = "-I/usr/include/fum -I bar -X\n" + \
795             '-I"C:\\Program Files\\ASCEND\\include" ' + \
796             "-L/usr/fax -L foo -lxxx -l yyy " + \
797             '-L"C:\\Program Files\\ASCEND" -lascend ' + \
798             "-Wa,-as -Wl,-link " + \
799             "-Wl,-rpath=rpath1 " + \
800             "-Wl,-R,rpath2 " + \
801             "-Wl,-Rrpath3 " + \
802             "-Wp,-cpp " + \
803             "-std=c99 " + \
804             "-framework Carbon " + \
805             "-frameworkdir=fwd1 " + \
806             "-Ffwd2 " + \
807             "-F fwd3 " + \
808             "-pthread " + \
809             "-mno-cygwin -mwindows " + \
810             "-arch i386 -isysroot /tmp +DD64 " + \
811             "-DFOO -DBAR=value -D BAZ "
812
813         d = env.ParseFlags(s)
814
815         assert d['ASFLAGS'] == ['-as'], d['ASFLAGS']
816         assert d['CFLAGS']  == ['-std=c99']
817         assert d['CCFLAGS'] == ['-X', '-Wa,-as',
818                                   '-pthread', '-mno-cygwin',
819                                   ('-arch', 'i386'), ('-isysroot', '/tmp'),
820                                   '+DD64'], d['CCFLAGS']
821         assert d['CPPDEFINES'] == ['FOO', ['BAR', 'value'], 'BAZ'], d['CPPDEFINES']
822         assert d['CPPFLAGS'] == ['-Wp,-cpp'], d['CPPFLAGS']
823         assert d['CPPPATH'] == ['/usr/include/fum',
824                                 'bar',
825                                 'C:\\Program Files\\ASCEND\\include'], d['CPPPATH']
826         assert d['FRAMEWORKPATH'] == ['fwd1', 'fwd2', 'fwd3'], d['FRAMEWORKPATH']
827         assert d['FRAMEWORKS'] == ['Carbon'], d['FRAMEWORKS']
828         assert d['LIBPATH'] == ['/usr/fax',
829                                 'foo',
830                                 'C:\\Program Files\\ASCEND'], d['LIBPATH']
831         LIBS = list(map(str, d['LIBS']))
832         assert LIBS == ['xxx', 'yyy', 'ascend'], (d['LIBS'], LIBS)
833         assert d['LINKFLAGS'] == ['-Wl,-link', '-pthread',
834                                   '-mno-cygwin', '-mwindows',
835                                   ('-arch', 'i386'),
836                                   ('-isysroot', '/tmp'),
837                                   '+DD64'], d['LINKFLAGS']
838         assert d['RPATH'] == ['rpath1', 'rpath2', 'rpath3'], d['RPATH']
839
840
841     def test_MergeFlags(self):
842         """Test the MergeFlags() method
843         """
844         env = SubstitutionEnvironment()
845         env.MergeFlags('')
846         assert 'CCFLAGS' not in env, env['CCFLAGS']
847         env.MergeFlags('-X')
848         assert env['CCFLAGS'] == ['-X'], env['CCFLAGS']
849         env.MergeFlags('-X')
850         assert env['CCFLAGS'] == ['-X'], env['CCFLAGS']
851
852         env = SubstitutionEnvironment(CCFLAGS=None)
853         env.MergeFlags('-Y')
854         assert env['CCFLAGS'] == ['-Y'], env['CCFLAGS']
855
856         env = SubstitutionEnvironment()
857         env.MergeFlags({'A':['aaa'], 'B':['bbb']})
858         assert env['A'] == ['aaa'], env['A']
859         assert env['B'] == ['bbb'], env['B']
860
861 #     def test_MergeShellPaths(self):
862 #         """Test the MergeShellPaths() method
863 #         """
864 #         env = Environment()
865 #         env.MergeShellPaths({})
866 #         assert not env['ENV'].has_key('INCLUDE'), env['INCLUDE']
867 #         env.MergeShellPaths({'INCLUDE': r'c:\Program Files\Stuff'})
868 #         assert env['ENV']['INCLUDE'] == r'c:\Program Files\Stuff', env['ENV']['INCLUDE']
869 #         env.MergeShellPaths({'INCLUDE': r'c:\Program Files\Stuff'})
870 #         assert env['ENV']['INCLUDE'] == r'c:\Program Files\Stuff', env['ENV']['INCLUDE']
871 #         env.MergeShellPaths({'INCLUDE': r'xyz'})
872 #         assert env['ENV']['INCLUDE'] == r'xyz%sc:\Program Files\Stuff'%os.pathsep, env['ENV']['INCLUDE']
873
874 #         env = Environment()
875 #         env['ENV']['INCLUDE'] = 'xyz'
876 #         env.MergeShellPaths({'INCLUDE':['c:/inc1', 'c:/inc2']} )
877 #         assert env['ENV']['INCLUDE'] == r'c:/inc1%sc:/inc2%sxyz'%(os.pathsep, os.pathsep), env['ENV']['INCLUDE']
878
879 #         # test prepend=0
880 #         env = Environment()
881 #         env.MergeShellPaths({'INCLUDE': r'c:\Program Files\Stuff'}, prepend=0)
882 #         assert env['ENV']['INCLUDE'] == r'c:\Program Files\Stuff', env['ENV']['INCLUDE']
883 #         env.MergeShellPaths({'INCLUDE': r'xyz'}, prepend=0)
884 #         assert env['ENV']['INCLUDE'] == r'c:\Program Files\Stuff%sxyz'%os.pathsep, env['ENV']['INCLUDE']
885
886
887 class BaseTestCase(unittest.TestCase,TestEnvironmentFixture):
888
889     reserved_variables = [
890         'CHANGED_SOURCES',
891         'CHANGED_TARGETS',
892         'SOURCE',
893         'SOURCES',
894         'TARGET',
895         'TARGETS',
896         'UNCHANGED_SOURCES',
897         'UNCHANGED_TARGETS',
898     ]
899
900     def test___init__(self):
901         """Test construction Environment creation
902
903         Create two with identical arguments and check that
904         they compare the same.
905         """
906         env1 = self.TestEnvironment(XXX = 'x', YYY = 'y')
907         env2 = self.TestEnvironment(XXX = 'x', YYY = 'y')
908         assert env1 == env2, diff_env(env1, env2)
909
910         assert '__env__' not in env1
911         assert '__env__' not in env2
912
913     def test_variables(self):
914         """Test that variables only get applied once."""
915         class FakeOptions:
916             def __init__(self, key, val):
917                 self.calls = 0
918                 self.key = key
919                 self.val = val
920             def keys(self):
921                 return [self.key]
922             def Update(self, env):
923                 env[self.key] = self.val
924                 self.calls = self.calls + 1
925
926         o = FakeOptions('AAA', 'fake_opt')
927         env = Environment(variables=o, AAA='keyword_arg')
928         assert o.calls == 1, o.calls
929         assert env['AAA'] == 'fake_opt', env['AAA']
930
931     def test_get(self):
932         """Test the get() method."""
933         env = self.TestEnvironment(aaa = 'AAA')
934
935         x = env.get('aaa')
936         assert x == 'AAA', x
937         x = env.get('aaa', 'XXX')
938         assert x == 'AAA', x
939         x = env.get('bbb')
940         assert x is None, x
941         x = env.get('bbb', 'XXX')
942         assert x == 'XXX', x
943
944     def test_Builder_calls(self):
945         """Test Builder calls through different environments
946         """
947         global called_it
948
949         b1 = Builder()
950         b2 = Builder()
951
952         env = Environment()
953         env.Replace(BUILDERS = { 'builder1' : b1,
954                                  'builder2' : b2 })
955         called_it = {}
956         env.builder1('in1')
957         assert called_it['target'] is None, called_it
958         assert called_it['source'] == ['in1'], called_it
959
960         called_it = {}
961         env.builder2(source = 'in2', xyzzy = 1)
962         assert called_it['target'] is None, called_it
963         assert called_it['source'] == ['in2'], called_it
964         assert called_it['xyzzy'] == 1, called_it
965
966         called_it = {}
967         env.builder1(foo = 'bar')
968         assert called_it['foo'] == 'bar', called_it
969         assert called_it['target'] is None, called_it
970         assert called_it['source'] is None, called_it
971
972     def test_BuilderWrapper_attributes(self):
973         """Test getting and setting of BuilderWrapper attributes
974         """
975         b1 = Builder()
976         b2 = Builder()
977         e1 = Environment()
978         e2 = Environment()
979
980         e1.Replace(BUILDERS = {'b' : b1})
981         bw = e1.b
982
983         assert bw.env is e1
984         bw.env = e2
985         assert bw.env is e2
986
987         assert bw.builder is b1
988         bw.builder = b2
989         assert bw.builder is b2
990
991         self.assertRaises(AttributeError, getattr, bw, 'foobar')
992         bw.foobar = 42
993         assert bw.foobar is 42
994
995     # This unit test is currently disabled because we don't think the
996     # underlying method it tests (Environment.BuilderWrapper.execute())
997     # is necessary, but we're leaving the code here for now in case
998     # that's mistaken.
999     def _DO_NOT_test_Builder_execs(self):
1000         """Test Builder execution through different environments
1001
1002         One environment is initialized with a single
1003         Builder object, one with a list of a single Builder
1004         object, and one with a list of two Builder objects.
1005         """
1006         global built_it
1007
1008         b1 = Builder()
1009         b2 = Builder()
1010
1011         built_it = {}
1012         env3 = Environment()
1013         env3.Replace(BUILDERS = { 'builder1' : b1,
1014                                   'builder2' : b2 })
1015         env3.builder1.execute(target = 'out1')
1016         env3.builder2.execute(target = 'out2')
1017         env3.builder1.execute(target = 'out3')
1018         assert built_it['out1']
1019         assert built_it['out2']
1020         assert built_it['out3']
1021
1022         env4 = env3.Clone()
1023         assert env4.builder1.env is env4, "builder1.env (%s) == env3 (%s)?" % (
1024 env4.builder1.env, env3)
1025         assert env4.builder2.env is env4, "builder2.env (%s) == env3 (%s)?" % (
1026 env4.builder1.env, env3)
1027
1028         # Now test BUILDERS as a dictionary.
1029         built_it = {}
1030         env5 = self.TestEnvironment(BUILDERS={ 'foo' : b1 })
1031         env5['BUILDERS']['bar'] = b2
1032         env5.foo.execute(target='out1')
1033         env5.bar.execute(target='out2')
1034         assert built_it['out1']
1035         assert built_it['out2']
1036
1037         built_it = {}
1038         env6 = Environment()
1039         env6['BUILDERS'] = { 'foo' : b1,
1040                              'bar' : b2 }
1041         env6.foo.execute(target='out1')
1042         env6.bar.execute(target='out2')
1043         assert built_it['out1']
1044         assert built_it['out2']
1045
1046
1047
1048     def test_Scanners(self):
1049         """Test setting SCANNERS in various ways
1050
1051         One environment is initialized with a single
1052         Scanner object, one with a list of a single Scanner
1053         object, and one with a list of two Scanner objects.
1054         """
1055         global scanned_it
1056
1057         s1 = Scanner(name = 'scanner1', skeys = [".c", ".cc"])
1058         s2 = Scanner(name = 'scanner2', skeys = [".m4"])
1059         s3 = Scanner(name = 'scanner3', skeys = [".m4", ".m5"])
1060         s4 = Scanner(name = 'scanner4', skeys = [None])
1061
1062 #        XXX Tests for scanner execution through different environments,
1063 #        XXX if we ever want to do that some day
1064 #        scanned_it = {}
1065 #        env1 = self.TestEnvironment(SCANNERS = s1)
1066 #        env1.scanner1(filename = 'out1')
1067 #        assert scanned_it['out1']
1068 #
1069 #        scanned_it = {}
1070 #        env2 = self.TestEnvironment(SCANNERS = [s1])
1071 #        env1.scanner1(filename = 'out1')
1072 #        assert scanned_it['out1']
1073 #
1074 #        scanned_it = {}
1075 #        env3 = Environment()
1076 #        env3.Replace(SCANNERS = [s1])
1077 #        env3.scanner1(filename = 'out1')
1078 #        env3.scanner2(filename = 'out2')
1079 #        env3.scanner1(filename = 'out3')
1080 #        assert scanned_it['out1']
1081 #        assert scanned_it['out2']
1082 #        assert scanned_it['out3']
1083
1084         suffixes = [".c", ".cc", ".cxx", ".m4", ".m5"]
1085
1086         env = Environment()
1087         try: del env['SCANNERS']
1088         except KeyError: pass
1089         s = list(map(env.get_scanner, suffixes))
1090         assert s == [None, None, None, None, None], s
1091
1092         env = self.TestEnvironment(SCANNERS = [])
1093         s = list(map(env.get_scanner, suffixes))
1094         assert s == [None, None, None, None, None], s
1095
1096         env.Replace(SCANNERS = [s1])
1097         s = list(map(env.get_scanner, suffixes))
1098         assert s == [s1, s1, None, None, None], s
1099
1100         env.Append(SCANNERS = [s2])
1101         s = list(map(env.get_scanner, suffixes))
1102         assert s == [s1, s1, None, s2, None], s
1103
1104         env.AppendUnique(SCANNERS = [s3])
1105         s = list(map(env.get_scanner, suffixes))
1106         assert s == [s1, s1, None, s2, s3], s
1107
1108         env = env.Clone(SCANNERS = [s2])
1109         s = list(map(env.get_scanner, suffixes))
1110         assert s == [None, None, None, s2, None], s
1111
1112         env['SCANNERS'] = [s1]
1113         s = list(map(env.get_scanner, suffixes))
1114         assert s == [s1, s1, None, None, None], s
1115
1116         env.PrependUnique(SCANNERS = [s2, s1])
1117         s = list(map(env.get_scanner, suffixes))
1118         assert s == [s1, s1, None, s2, None], s
1119
1120         env.Prepend(SCANNERS = [s3])
1121         s = list(map(env.get_scanner, suffixes))
1122         assert s == [s1, s1, None, s3, s3], s
1123
1124         # Verify behavior of case-insensitive suffix matches on Windows.
1125         uc_suffixes = [_.upper() for _ in suffixes]
1126
1127         env = Environment(SCANNERS = [s1, s2, s3],
1128                           PLATFORM = 'linux')
1129
1130         s = list(map(env.get_scanner, suffixes))
1131         assert s == [s1, s1, None, s2, s3], s
1132
1133         s = list(map(env.get_scanner, uc_suffixes))
1134         assert s == [None, None, None, None, None], s
1135
1136         env['PLATFORM'] = 'win32'
1137
1138         s = list(map(env.get_scanner, uc_suffixes))
1139         assert s == [s1, s1, None, s2, s3], s
1140
1141         # Verify behavior for a scanner returning None (on Windows
1142         # where we might try to perform case manipulation on None).
1143         env.Replace(SCANNERS = [s4])
1144         s = list(map(env.get_scanner, suffixes))
1145         assert s == [None, None, None, None, None], s
1146
1147     def test_ENV(self):
1148         """Test setting the external ENV in Environments
1149         """
1150         env = Environment()
1151         assert 'ENV' in env.Dictionary()
1152
1153         env = self.TestEnvironment(ENV = { 'PATH' : '/foo:/bar' })
1154         assert env.Dictionary('ENV')['PATH'] == '/foo:/bar'
1155
1156     def test_ReservedVariables(self):
1157         """Test warning generation when reserved variable names are set"""
1158
1159         reserved_variables = [
1160             'CHANGED_SOURCES',
1161             'CHANGED_TARGETS',
1162             'SOURCE',
1163             'SOURCES',
1164             'TARGET',
1165             'TARGETS',
1166             'UNCHANGED_SOURCES',
1167             'UNCHANGED_TARGETS',
1168         ]
1169
1170         warning = SCons.Warnings.ReservedVariableWarning
1171         SCons.Warnings.enableWarningClass(warning)
1172         old = SCons.Warnings.warningAsException(1)
1173
1174         try:
1175             env4 = Environment()
1176             for kw in self.reserved_variables:
1177                 exc_caught = None
1178                 try:
1179                     env4[kw] = 'xyzzy'
1180                 except warning:
1181                     exc_caught = 1
1182                 assert exc_caught, "Did not catch ReservedVariableWarning for `%s'" % kw
1183                 assert kw not in env4, "`%s' variable was incorrectly set" % kw
1184         finally:
1185             SCons.Warnings.warningAsException(old)
1186
1187     def test_FutureReservedVariables(self):
1188         """Test warning generation when future reserved variable names are set"""
1189
1190         future_reserved_variables = []
1191
1192         warning = SCons.Warnings.FutureReservedVariableWarning
1193         SCons.Warnings.enableWarningClass(warning)
1194         old = SCons.Warnings.warningAsException(1)
1195
1196         try:
1197             env4 = Environment()
1198             for kw in future_reserved_variables:
1199                 exc_caught = None
1200                 try:
1201                     env4[kw] = 'xyzzy'
1202                 except warning:
1203                     exc_caught = 1
1204                 assert exc_caught, "Did not catch FutureReservedVariableWarning for `%s'" % kw
1205                 assert kw in env4, "`%s' variable was not set" % kw
1206         finally:
1207             SCons.Warnings.warningAsException(old)
1208
1209     def test_IllegalVariables(self):
1210         """Test that use of illegal variables raises an exception"""
1211         env = Environment()
1212         def test_it(var, env=env):
1213             exc_caught = None
1214             try:
1215                 env[var] = 1
1216             except SCons.Errors.UserError:
1217                 exc_caught = 1
1218             assert exc_caught, "did not catch UserError for '%s'" % var
1219         env['aaa'] = 1
1220         assert env['aaa'] == 1, env['aaa']
1221         test_it('foo/bar')
1222         test_it('foo.bar')
1223         test_it('foo-bar')
1224
1225     def test_autogenerate(dict):
1226         """Test autogenerating variables in a dictionary."""
1227
1228         drive, p = os.path.splitdrive(os.getcwd())
1229         def normalize_path(path, drive=drive):
1230             if path[0] in '\\/':
1231                 path = drive + path
1232             path = os.path.normpath(path)
1233             drive, path = os.path.splitdrive(path)
1234             return drive.lower() + path
1235
1236         env = dict.TestEnvironment(LIBS = [ 'foo', 'bar', 'baz' ],
1237                           LIBLINKPREFIX = 'foo',
1238                           LIBLINKSUFFIX = 'bar')
1239
1240         def RDirs(pathlist, fs=env.fs):
1241             return fs.Dir('xx').Rfindalldirs(pathlist)
1242
1243         env['RDirs'] = RDirs
1244         flags = env.subst_list('$_LIBFLAGS', 1)[0]
1245         assert flags == ['foobar', 'foobar', 'foobazbar'], flags
1246
1247         blat = env.fs.Dir('blat')
1248
1249         env.Replace(CPPPATH = [ 'foo', '$FOO/bar', blat ],
1250                     INCPREFIX = 'foo ',
1251                     INCSUFFIX = 'bar',
1252                     FOO = 'baz')
1253         flags = env.subst_list('$_CPPINCFLAGS', 1)[0]
1254         expect = [ '$(',
1255                    normalize_path('foo'),
1256                    normalize_path('xx/foobar'),
1257                    normalize_path('foo'),
1258                    normalize_path('xx/baz/bar'),
1259                    normalize_path('foo'),
1260                    normalize_path('blatbar'),
1261                    '$)',
1262         ]
1263         assert flags == expect, flags
1264
1265         env.Replace(F77PATH = [ 'foo', '$FOO/bar', blat ],
1266                     INCPREFIX = 'foo ',
1267                     INCSUFFIX = 'bar',
1268                     FOO = 'baz')
1269         flags = env.subst_list('$_F77INCFLAGS', 1)[0]
1270         expect = [ '$(',
1271                    normalize_path('foo'),
1272                    normalize_path('xx/foobar'),
1273                    normalize_path('foo'),
1274                    normalize_path('xx/baz/bar'),
1275                    normalize_path('foo'),
1276                    normalize_path('blatbar'),
1277                    '$)',
1278         ]
1279         assert flags == expect, flags
1280
1281         env.Replace(CPPPATH = '', F77PATH = '', LIBPATH = '')
1282         l = env.subst_list('$_CPPINCFLAGS')
1283         assert l == [[]], l
1284         l = env.subst_list('$_F77INCFLAGS')
1285         assert l == [[]], l
1286         l = env.subst_list('$_LIBDIRFLAGS')
1287         assert l == [[]], l
1288
1289         env.fs.Repository('/rep1')
1290         env.fs.Repository('/rep2')
1291         env.Replace(CPPPATH = [ 'foo', '/a/b', '$FOO/bar', blat],
1292                     INCPREFIX = '-I ',
1293                     INCSUFFIX = 'XXX',
1294                     FOO = 'baz')
1295         flags = env.subst_list('$_CPPINCFLAGS', 1)[0]
1296         expect = [ '$(',
1297                    '-I', normalize_path('xx/fooXXX'),
1298                    '-I', normalize_path('/rep1/xx/fooXXX'),
1299                    '-I', normalize_path('/rep2/xx/fooXXX'),
1300                    '-I', normalize_path('/a/bXXX'),
1301                    '-I', normalize_path('xx/baz/barXXX'),
1302                    '-I', normalize_path('/rep1/xx/baz/barXXX'),
1303                    '-I', normalize_path('/rep2/xx/baz/barXXX'),
1304                    '-I', normalize_path('blatXXX'),
1305                    '$)'
1306         ]
1307         def normalize_if_path(arg, np=normalize_path):
1308             if arg not in ('$(','$)','-I'):
1309                 return np(str(arg))
1310             return arg
1311         flags = list(map(normalize_if_path, flags))
1312         assert flags == expect, flags
1313
1314     def test_platform(self):
1315         """Test specifying a platform callable when instantiating."""
1316         class platform:
1317             def __str__(self):        return "TestPlatform"
1318             def __call__(self, env):  env['XYZZY'] = 777
1319
1320         def tool(env):
1321             env['SET_TOOL'] = 'initialized'
1322             assert env['PLATFORM'] == "TestPlatform"
1323
1324         env = self.TestEnvironment(platform = platform(), tools = [tool])
1325         assert env['XYZZY'] == 777, env
1326         assert env['PLATFORM'] == "TestPlatform"
1327         assert env['SET_TOOL'] == "initialized"
1328
1329     def test_Default_PLATFORM(self):
1330         """Test overriding the default PLATFORM variable"""
1331         class platform:
1332             def __str__(self):        return "DefaultTestPlatform"
1333             def __call__(self, env):  env['XYZZY'] = 888
1334
1335         def tool(env):
1336             env['SET_TOOL'] = 'abcde'
1337             assert env['PLATFORM'] == "DefaultTestPlatform"
1338
1339         import SCons.Defaults
1340         save = SCons.Defaults.ConstructionEnvironment.copy()
1341         try:
1342             import SCons.Defaults
1343             SCons.Defaults.ConstructionEnvironment.update({
1344                 'PLATFORM' : platform(),
1345             })
1346             env = self.TestEnvironment(tools = [tool])
1347             assert env['XYZZY'] == 888, env
1348             assert env['PLATFORM'] == "DefaultTestPlatform"
1349             assert env['SET_TOOL'] == "abcde"
1350         finally:
1351             SCons.Defaults.ConstructionEnvironment = save
1352
1353     def test_tools(self):
1354         """Test specifying a tool callable when instantiating."""
1355         def t1(env):
1356             env['TOOL1'] = 111
1357         def t2(env):
1358             env['TOOL2'] = 222
1359         def t3(env):
1360             env['AAA'] = env['XYZ']
1361         def t4(env):
1362             env['TOOL4'] = 444
1363         env = self.TestEnvironment(tools = [t1, t2, t3], XYZ = 'aaa')
1364         assert env['TOOL1'] == 111, env['TOOL1']
1365         assert env['TOOL2'] == 222, env
1366         assert env['AAA'] == 'aaa', env
1367         t4(env)
1368         assert env['TOOL4'] == 444, env
1369
1370         test = TestCmd.TestCmd(workdir = '')
1371         test.write('faketool.py', """\
1372 def generate(env, **kw):
1373     for k, v in kw.items():
1374         env[k] = v
1375
1376 def exists(env):
1377     return 1
1378 """)
1379
1380         env = self.TestEnvironment(tools = [('faketool', {'a':1, 'b':2, 'c':3})],
1381                           toolpath = [test.workpath('')])
1382         assert env['a'] == 1, env['a']
1383         assert env['b'] == 2, env['b']
1384         assert env['c'] == 3, env['c']
1385
1386     def test_Default_TOOLS(self):
1387         """Test overriding the default TOOLS variable"""
1388         def t5(env):
1389             env['TOOL5'] = 555
1390         def t6(env):
1391             env['TOOL6'] = 666
1392         def t7(env):
1393             env['BBB'] = env['XYZ']
1394         def t8(env):
1395             env['TOOL8'] = 888
1396
1397         import SCons.Defaults
1398         save = SCons.Defaults.ConstructionEnvironment.copy()
1399         try:
1400             SCons.Defaults.ConstructionEnvironment.update({
1401                 'TOOLS' : [t5, t6, t7],
1402             })
1403             env = Environment(XYZ = 'bbb')
1404             assert env['TOOL5'] == 555, env['TOOL5']
1405             assert env['TOOL6'] == 666, env
1406             assert env['BBB'] == 'bbb', env
1407             t8(env)
1408             assert env['TOOL8'] == 888, env
1409         finally:
1410             SCons.Defaults.ConstructionEnvironment = save
1411
1412     def test_null_tools(self):
1413         """Test specifying a tool of None is OK."""
1414         def t1(env):
1415             env['TOOL1'] = 111
1416         def t2(env):
1417             env['TOOL2'] = 222
1418         env = self.TestEnvironment(tools = [t1, None, t2], XYZ = 'aaa')
1419         assert env['TOOL1'] == 111, env['TOOL1']
1420         assert env['TOOL2'] == 222, env
1421         assert env['XYZ'] == 'aaa', env
1422         env = self.TestEnvironment(tools = [None], XYZ = 'xyz')
1423         assert env['XYZ'] == 'xyz', env
1424         env = self.TestEnvironment(tools = [t1, '', t2], XYZ = 'ddd')
1425         assert env['TOOL1'] == 111, env['TOOL1']
1426         assert env['TOOL2'] == 222, env
1427         assert env['XYZ'] == 'ddd', env
1428
1429     def test_concat(self):
1430         "Test _concat()"
1431         e1 = self.TestEnvironment(PRE='pre', SUF='suf', STR='a b', LIST=['a', 'b'])
1432         s = e1.subst
1433         x = s("${_concat('', '', '', __env__)}")
1434         assert x == '', x
1435         x = s("${_concat('', [], '', __env__)}")
1436         assert x == '', x
1437         x = s("${_concat(PRE, '', SUF, __env__)}")
1438         assert x == '', x
1439         x = s("${_concat(PRE, STR, SUF, __env__)}")
1440         assert x == 'prea bsuf', x
1441         x = s("${_concat(PRE, LIST, SUF, __env__)}")
1442         assert x == 'preasuf prebsuf', x
1443
1444     def test_gvars(self):
1445         """Test the Environment gvars() method"""
1446         env = self.TestEnvironment(XXX = 'x', YYY = 'y', ZZZ = 'z')
1447         gvars = env.gvars()
1448         assert gvars['XXX'] == 'x', gvars['XXX']
1449         assert gvars['YYY'] == 'y', gvars['YYY']
1450         assert gvars['ZZZ'] == 'z', gvars['ZZZ']
1451
1452     def test__update(self):
1453         """Test the _update() method"""
1454         env = self.TestEnvironment(X = 'x', Y = 'y', Z = 'z')
1455         assert env['X'] == 'x', env['X']
1456         assert env['Y'] == 'y', env['Y']
1457         assert env['Z'] == 'z', env['Z']
1458         env._update({'X'       : 'xxx',
1459                      'TARGET'  : 't',
1460                      'TARGETS' : 'ttt',
1461                      'SOURCE'  : 's',
1462                      'SOURCES' : 'sss',
1463                      'Z'       : 'zzz'})
1464         assert env['X'] == 'xxx', env['X']
1465         assert env['Y'] == 'y', env['Y']
1466         assert env['Z'] == 'zzz', env['Z']
1467         assert env['TARGET'] == 't', env['TARGET']
1468         assert env['TARGETS'] == 'ttt', env['TARGETS']
1469         assert env['SOURCE'] == 's', env['SOURCE']
1470         assert env['SOURCES'] == 'sss', env['SOURCES']
1471
1472
1473
1474     def test_Append(self):
1475         """Test appending to construction variables in an Environment
1476         """
1477
1478         b1 = Environment()['BUILDERS']
1479         b2 = Environment()['BUILDERS']
1480         assert b1 == b2, diff_dict(b1, b2)
1481
1482         import UserDict
1483         UD = collections.UserDict
1484         import UserList
1485         UL = collections.UserList
1486
1487         cases = [
1488             'a1',       'A1',           'a1A1',
1489             'a2',       ['A2'],         ['a2', 'A2'],
1490             'a3',       UL(['A3']),     UL(['a', '3', 'A3']),
1491             'a4',       '',             'a4',
1492             'a5',       [],             ['a5'],
1493             'a6',       UL([]),         UL(['a', '6']),
1494             'a7',       [''],           ['a7', ''],
1495             'a8',       UL(['']),       UL(['a', '8', '']),
1496
1497             ['e1'],     'E1',           ['e1', 'E1'],
1498             ['e2'],     ['E2'],         ['e2', 'E2'],
1499             ['e3'],     UL(['E3']),     UL(['e3', 'E3']),
1500             ['e4'],     '',             ['e4'],
1501             ['e5'],     [],             ['e5'],
1502             ['e6'],     UL([]),         UL(['e6']),
1503             ['e7'],     [''],           ['e7', ''],
1504             ['e8'],     UL(['']),       UL(['e8', '']),
1505
1506             UL(['i1']), 'I1',           UL(['i1', 'I', '1']),
1507             UL(['i2']), ['I2'],         UL(['i2', 'I2']),
1508             UL(['i3']), UL(['I3']),     UL(['i3', 'I3']),
1509             UL(['i4']), '',             UL(['i4']),
1510             UL(['i5']), [],             UL(['i5']),
1511             UL(['i6']), UL([]),         UL(['i6']),
1512             UL(['i7']), [''],           UL(['i7', '']),
1513             UL(['i8']), UL(['']),       UL(['i8', '']),
1514
1515             {'d1':1},   'D1',           {'d1':1, 'D1':None},
1516             {'d2':1},   ['D2'],         {'d2':1, 'D2':None},
1517             {'d3':1},   UL(['D3']),     {'d3':1, 'D3':None},
1518             {'d4':1},   {'D4':1},       {'d4':1, 'D4':1},
1519             {'d5':1},   UD({'D5':1}),   UD({'d5':1, 'D5':1}),
1520
1521             UD({'u1':1}), 'U1',         UD({'u1':1, 'U1':None}),
1522             UD({'u2':1}), ['U2'],       UD({'u2':1, 'U2':None}),
1523             UD({'u3':1}), UL(['U3']),   UD({'u3':1, 'U3':None}),
1524             UD({'u4':1}), {'U4':1},     UD({'u4':1, 'U4':1}),
1525             UD({'u5':1}), UD({'U5':1}), UD({'u5':1, 'U5':1}),
1526
1527             '',         'M1',           'M1',
1528             '',         ['M2'],         ['M2'],
1529             '',         UL(['M3']),     UL(['M3']),
1530             '',         '',             '',
1531             '',         [],             [],
1532             '',         UL([]),         UL([]),
1533             '',         [''],           [''],
1534             '',         UL(['']),       UL(['']),
1535
1536             [],         'N1',           ['N1'],
1537             [],         ['N2'],         ['N2'],
1538             [],         UL(['N3']),     UL(['N3']),
1539             [],         '',             [],
1540             [],         [],             [],
1541             [],         UL([]),         UL([]),
1542             [],         [''],           [''],
1543             [],         UL(['']),       UL(['']),
1544
1545             UL([]),     'O1',           ['O', '1'],
1546             UL([]),     ['O2'],         ['O2'],
1547             UL([]),     UL(['O3']),     UL(['O3']),
1548             UL([]),     '',             UL([]),
1549             UL([]),     [],             UL([]),
1550             UL([]),     UL([]),         UL([]),
1551             UL([]),     [''],           UL(['']),
1552             UL([]),     UL(['']),       UL(['']),
1553
1554             [''],       'P1',           ['', 'P1'],
1555             [''],       ['P2'],         ['', 'P2'],
1556             [''],       UL(['P3']),     UL(['', 'P3']),
1557             [''],       '',             [''],
1558             [''],       [],             [''],
1559             [''],       UL([]),         UL(['']),
1560             [''],       [''],           ['', ''],
1561             [''],       UL(['']),       UL(['', '']),
1562
1563             UL(['']),   'Q1',           ['', 'Q', '1'],
1564             UL(['']),   ['Q2'],         ['', 'Q2'],
1565             UL(['']),   UL(['Q3']),     UL(['', 'Q3']),
1566             UL(['']),   '',             UL(['']),
1567             UL(['']),   [],             UL(['']),
1568             UL(['']),   UL([]),         UL(['']),
1569             UL(['']),   [''],           UL(['', '']),
1570             UL(['']),   UL(['']),       UL(['', '']),
1571         ]
1572
1573         env = Environment()
1574         failed = 0
1575         while cases:
1576             input, append, expect = cases[:3]
1577             env['XXX'] = copy.copy(input)
1578             try:
1579                 env.Append(XXX = append)
1580             except Exception, e:
1581                 if failed == 0: print
1582                 print "    %s Append %s exception: %s" % \
1583                       (repr(input), repr(append), e)
1584                 failed = failed + 1
1585             else:
1586                 result = env['XXX']
1587                 if result != expect:
1588                     if failed == 0: print
1589                     print "    %s Append %s => %s did not match %s" % \
1590                           (repr(input), repr(append), repr(result), repr(expect))
1591                     failed = failed + 1
1592             del cases[:3]
1593         assert failed == 0, "%d Append() cases failed" % failed
1594
1595         env['UL'] = UL(['foo'])
1596         env.Append(UL = 'bar')
1597         result = env['UL']
1598         assert isinstance(result, UL), repr(result)
1599         assert result == ['foo', 'b', 'a', 'r'], result
1600
1601         env['CLVar'] = CLVar(['foo'])
1602         env.Append(CLVar = 'bar')
1603         result = env['CLVar']
1604         assert isinstance(result, CLVar), repr(result)
1605         assert result == ['foo', 'bar'], result
1606
1607         class C:
1608             def __init__(self, name):
1609                 self.name = name
1610             def __str__(self):
1611                 return self.name
1612             def __cmp__(self, other):
1613                 raise "should not compare"
1614
1615         ccc = C('ccc')
1616
1617         env2 = self.TestEnvironment(CCC1 = ['c1'], CCC2 = ccc)
1618         env2.Append(CCC1 = ccc, CCC2 = ['c2'])
1619         assert env2['CCC1'][0] == 'c1', env2['CCC1']
1620         assert env2['CCC1'][1] is ccc, env2['CCC1']
1621         assert env2['CCC2'][0] is ccc, env2['CCC2']
1622         assert env2['CCC2'][1] == 'c2', env2['CCC2']
1623
1624         env3 = self.TestEnvironment(X = {'x1' : 7})
1625         env3.Append(X = {'x1' : 8, 'x2' : 9}, Y = {'y1' : 10})
1626         assert env3['X'] == {'x1': 8, 'x2': 9}, env3['X']
1627         assert env3['Y'] == {'y1': 10}, env3['Y']
1628
1629         z1 = Builder()
1630         z2 = Builder()
1631         env4 = self.TestEnvironment(BUILDERS = {'z1' : z1})
1632         env4.Append(BUILDERS = {'z2' : z2})
1633         assert env4['BUILDERS'] == {'z1' : z1, 'z2' : z2}, env4['BUILDERS']
1634         assert hasattr(env4, 'z1')
1635         assert hasattr(env4, 'z2')
1636
1637     def test_AppendENVPath(self):
1638         """Test appending to an ENV path."""
1639         env1 = self.TestEnvironment(ENV = {'PATH': r'C:\dir\num\one;C:\dir\num\two'},
1640                            MYENV = {'MYPATH': r'C:\mydir\num\one;C:\mydir\num\two'})
1641         # have to include the pathsep here so that the test will work on UNIX too.
1642         env1.AppendENVPath('PATH',r'C:\dir\num\two', sep = ';')
1643         env1.AppendENVPath('PATH',r'C:\dir\num\three', sep = ';')
1644         env1.AppendENVPath('MYPATH',r'C:\mydir\num\three','MYENV', sep = ';')
1645         env1.AppendENVPath('MYPATH',r'C:\mydir\num\one','MYENV', sep = ';')
1646         # this should do nothing since delete_existing is 0
1647         env1.AppendENVPath('MYPATH',r'C:\mydir\num\three','MYENV', sep = ';', delete_existing=0)
1648         assert(env1['ENV']['PATH'] == r'C:\dir\num\one;C:\dir\num\two;C:\dir\num\three')
1649         assert(env1['MYENV']['MYPATH'] == r'C:\mydir\num\two;C:\mydir\num\three;C:\mydir\num\one')
1650
1651         test = TestCmd.TestCmd(workdir = '')
1652         test.subdir('sub1', 'sub2')
1653         p=env1['ENV']['PATH']
1654         env1.AppendENVPath('PATH','#sub1', sep = ';')
1655         env1.AppendENVPath('PATH',env1.fs.Dir('sub2'), sep = ';')
1656         assert env1['ENV']['PATH'] == p + ';sub1;sub2', env1['ENV']['PATH']
1657
1658     def test_AppendUnique(self):
1659         """Test appending to unique values to construction variables
1660
1661         This strips values that are already present when lists are
1662         involved."""
1663         env = self.TestEnvironment(AAA1 = 'a1',
1664                           AAA2 = 'a2',
1665                           AAA3 = 'a3',
1666                           AAA4 = 'a4',
1667                           AAA5 = 'a5',
1668                           BBB1 = ['b1'],
1669                           BBB2 = ['b2'],
1670                           BBB3 = ['b3'],
1671                           BBB4 = ['b4'],
1672                           BBB5 = ['b5'],
1673                           CCC1 = '',
1674                           CCC2 = '',
1675                           DDD1 = ['a', 'b', 'c'])
1676         env.AppendUnique(AAA1 = 'a1',
1677                          AAA2 = ['a2'],
1678                          AAA3 = ['a3', 'b', 'c', 'c', 'b', 'a3'], # ignore dups
1679                          AAA4 = 'a4.new',
1680                          AAA5 = ['a5.new'],
1681                          BBB1 = 'b1',
1682                          BBB2 = ['b2'],
1683                          BBB3 = ['b3', 'c', 'd', 'c', 'b3'],
1684                          BBB4 = 'b4.new',
1685                          BBB5 = ['b5.new'],
1686                          CCC1 = 'c1',
1687                          CCC2 = ['c2'],
1688                          DDD1 = 'b')
1689
1690         assert env['AAA1'] == 'a1a1', env['AAA1']
1691         assert env['AAA2'] == ['a2'], env['AAA2']
1692         assert env['AAA3'] == ['a3', 'b', 'c'], env['AAA3']
1693         assert env['AAA4'] == 'a4a4.new', env['AAA4']
1694         assert env['AAA5'] == ['a5', 'a5.new'], env['AAA5']
1695         assert env['BBB1'] == ['b1'], env['BBB1']
1696         assert env['BBB2'] == ['b2'], env['BBB2']
1697         assert env['BBB3'] == ['b3', 'c', 'd'], env['BBB3']
1698         assert env['BBB4'] == ['b4', 'b4.new'], env['BBB4']
1699         assert env['BBB5'] == ['b5', 'b5.new'], env['BBB5']
1700         assert env['CCC1'] == 'c1', env['CCC1']
1701         assert env['CCC2'] == ['c2'], env['CCC2']
1702         assert env['DDD1'] == ['a', 'b', 'c'], env['DDD1']
1703
1704         env.AppendUnique(DDD1 = 'b', delete_existing=1)
1705         assert env['DDD1'] == ['a', 'c', 'b'], env['DDD1'] # b moves to end
1706         env.AppendUnique(DDD1 = ['a','b'], delete_existing=1)
1707         assert env['DDD1'] == ['c', 'a', 'b'], env['DDD1'] # a & b move to end
1708         env.AppendUnique(DDD1 = ['e','f', 'e'], delete_existing=1)
1709         assert env['DDD1'] == ['c', 'a', 'b', 'f', 'e'], env['DDD1'] # add last
1710         
1711         env['CLVar'] = CLVar([])
1712         env.AppendUnique(CLVar = 'bar')
1713         result = env['CLVar']
1714         if sys.version[0] == '1' or sys.version[:3] == '2.0':
1715             # Python 2.0 and before have a quirky behavior where CLVar([])
1716             # actually matches '' and [] due to different __coerce__()
1717             # semantics in the UserList implementation.  It isn't worth a
1718             # lot of effort to get this corner case to work identically
1719             # (support for Python 1.5 support will die soon anyway),
1720             # so just treat it separately for now.
1721             assert result == 'bar', result
1722         else:
1723             assert isinstance(result, CLVar), repr(result)
1724             assert result == ['bar'], result
1725
1726         env['CLVar'] = CLVar(['abc'])
1727         env.AppendUnique(CLVar = 'bar')
1728         result = env['CLVar']
1729         assert isinstance(result, CLVar), repr(result)
1730         assert result == ['abc', 'bar'], result
1731
1732         env['CLVar'] = CLVar(['bar'])
1733         env.AppendUnique(CLVar = 'bar')
1734         result = env['CLVar']
1735         assert isinstance(result, CLVar), repr(result)
1736         assert result == ['bar'], result
1737
1738     def test_Clone(self):
1739         """Test construction environment copying
1740
1741         Update the copy independently afterwards and check that
1742         the original remains intact (that is, no dangling
1743         references point to objects in the copied environment).
1744         Clone the original with some construction variable
1745         updates and check that the original remains intact
1746         and the copy has the updated values.
1747         """
1748         env1 = self.TestEnvironment(XXX = 'x', YYY = 'y')
1749         env2 = env1.Clone()
1750         env1copy = env1.Clone()
1751         assert env1copy == env1copy
1752         assert env2 == env2
1753         env2.Replace(YYY = 'yyy')
1754         assert env2 == env2
1755         assert env1 != env2
1756         assert env1 == env1copy
1757
1758         env3 = env1.Clone(XXX = 'x3', ZZZ = 'z3')
1759         assert env3 == env3
1760         assert env3.Dictionary('XXX') == 'x3'
1761         assert env3.Dictionary('YYY') == 'y'
1762         assert env3.Dictionary('ZZZ') == 'z3'
1763         assert env1 == env1copy
1764
1765         # Ensure that lists and dictionaries are
1766         # deep copied, but not instances.
1767         class TestA:
1768             pass
1769         env1 = self.TestEnvironment(XXX=TestA(), YYY = [ 1, 2, 3 ],
1770                            ZZZ = { 1:2, 3:4 })
1771         env2=env1.Clone()
1772         env2.Dictionary('YYY').append(4)
1773         env2.Dictionary('ZZZ')[5] = 6
1774         assert env1.Dictionary('XXX') is env2.Dictionary('XXX')
1775         assert 4 in env2.Dictionary('YYY')
1776         assert not 4 in env1.Dictionary('YYY')
1777         assert 5 in env2.Dictionary('ZZZ')
1778         assert 5 not in env1.Dictionary('ZZZ')
1779
1780         #
1781         env1 = self.TestEnvironment(BUILDERS = {'b1' : Builder()})
1782         assert hasattr(env1, 'b1'), "env1.b1 was not set"
1783         assert env1.b1.object == env1, "b1.object doesn't point to env1"
1784         env2 = env1.Clone(BUILDERS = {'b2' : Builder()})
1785         assert env2 is env2
1786         assert env2 == env2
1787         assert hasattr(env1, 'b1'), "b1 was mistakenly cleared from env1"
1788         assert env1.b1.object == env1, "b1.object was changed"
1789         assert not hasattr(env2, 'b1'), "b1 was not cleared from env2"
1790         assert hasattr(env2, 'b2'), "env2.b2 was not set"
1791         assert env2.b2.object == env2, "b2.object doesn't point to env2"
1792
1793         # Ensure that specifying new tools in a copied environment
1794         # works.
1795         def foo(env): env['FOO'] = 1
1796         def bar(env): env['BAR'] = 2
1797         def baz(env): env['BAZ'] = 3
1798         env1 = self.TestEnvironment(tools=[foo])
1799         env2 = env1.Clone()
1800         env3 = env1.Clone(tools=[bar, baz])
1801
1802         assert env1.get('FOO') is 1
1803         assert env1.get('BAR') is None
1804         assert env1.get('BAZ') is None
1805         assert env2.get('FOO') is 1
1806         assert env2.get('BAR') is None
1807         assert env2.get('BAZ') is None
1808         assert env3.get('FOO') is 1
1809         assert env3.get('BAR') is 2
1810         assert env3.get('BAZ') is 3
1811
1812         # Ensure that recursive variable substitution when copying
1813         # environments works properly.
1814         env1 = self.TestEnvironment(CCFLAGS = '-DFOO', XYZ = '-DXYZ')
1815         env2 = env1.Clone(CCFLAGS = '$CCFLAGS -DBAR',
1816                          XYZ = ['-DABC', 'x $XYZ y', '-DDEF'])
1817         x = env2.get('CCFLAGS')
1818         assert x == '-DFOO -DBAR', x
1819         x = env2.get('XYZ')
1820         assert x == ['-DABC', 'x -DXYZ y', '-DDEF'], x
1821
1822         # Ensure that special properties of a class don't get
1823         # lost on copying.
1824         env1 = self.TestEnvironment(FLAGS = CLVar('flag1 flag2'))
1825         x = env1.get('FLAGS')
1826         assert x == ['flag1', 'flag2'], x
1827         env2 = env1.Clone()
1828         env2.Append(FLAGS = 'flag3 flag4')
1829         x = env2.get('FLAGS')
1830         assert x == ['flag1', 'flag2', 'flag3', 'flag4'], x
1831
1832         # Test that the environment stores the toolpath and
1833         # re-uses it for copies.
1834         test = TestCmd.TestCmd(workdir = '')
1835
1836         test.write('xxx.py', """\
1837 def exists(env):
1838     1
1839 def generate(env):
1840     env['XXX'] = 'one'
1841 """)
1842
1843         test.write('yyy.py', """\
1844 def exists(env):
1845     1
1846 def generate(env):
1847     env['YYY'] = 'two'
1848 """)
1849
1850         env = self.TestEnvironment(tools=['xxx'], toolpath=[test.workpath('')])
1851         assert env['XXX'] == 'one', env['XXX']
1852         env = env.Clone(tools=['yyy'])
1853         assert env['YYY'] == 'two', env['YYY']
1854
1855
1856         # Test that
1857         real_value = [4]
1858
1859         def my_tool(env, rv=real_value):
1860             assert env['KEY_THAT_I_WANT'] == rv[0]
1861             env['KEY_THAT_I_WANT'] = rv[0] + 1
1862
1863         env = self.TestEnvironment()
1864
1865         real_value[0] = 5
1866         env = env.Clone(KEY_THAT_I_WANT=5, tools=[my_tool])
1867         assert env['KEY_THAT_I_WANT'] == real_value[0], env['KEY_THAT_I_WANT']
1868
1869         real_value[0] = 6
1870         env = env.Clone(KEY_THAT_I_WANT=6, tools=[my_tool])
1871         assert env['KEY_THAT_I_WANT'] == real_value[0], env['KEY_THAT_I_WANT']
1872
1873
1874     def test_Copy(self):
1875         """Test copying using the old env.Copy() method"""
1876         env1 = self.TestEnvironment(XXX = 'x', YYY = 'y')
1877         env2 = env1.Copy()
1878         env1copy = env1.Copy()
1879         assert env1copy == env1copy
1880         assert env2 == env2
1881         env2.Replace(YYY = 'yyy')
1882         assert env2 == env2
1883         assert env1 != env2
1884         assert env1 == env1copy
1885
1886     def test_Detect(self):
1887         """Test Detect()ing tools"""
1888         test = TestCmd.TestCmd(workdir = '')
1889         test.subdir('sub1', 'sub2')
1890         sub1 = test.workpath('sub1')
1891         sub2 = test.workpath('sub2')
1892
1893         if sys.platform == 'win32':
1894             test.write(['sub1', 'xxx'], "sub1/xxx\n")
1895             test.write(['sub2', 'xxx'], "sub2/xxx\n")
1896
1897             env = self.TestEnvironment(ENV = { 'PATH' : [sub1, sub2] })
1898
1899             x = env.Detect('xxx.exe')
1900             assert x is None, x
1901
1902             test.write(['sub2', 'xxx.exe'], "sub2/xxx.exe\n")
1903
1904             env = self.TestEnvironment(ENV = { 'PATH' : [sub1, sub2] })
1905
1906             x = env.Detect('xxx.exe')
1907             assert x == 'xxx.exe', x
1908
1909             test.write(['sub1', 'xxx.exe'], "sub1/xxx.exe\n")
1910
1911             x = env.Detect('xxx.exe')
1912             assert x == 'xxx.exe', x
1913
1914         else:
1915             test.write(['sub1', 'xxx.exe'], "sub1/xxx.exe\n")
1916             test.write(['sub2', 'xxx.exe'], "sub2/xxx.exe\n")
1917
1918             env = self.TestEnvironment(ENV = { 'PATH' : [sub1, sub2] })
1919
1920             x = env.Detect('xxx.exe')
1921             assert x is None, x
1922
1923             sub2_xxx_exe = test.workpath('sub2', 'xxx.exe')
1924             os.chmod(sub2_xxx_exe, 0755)
1925
1926             env = self.TestEnvironment(ENV = { 'PATH' : [sub1, sub2] })
1927
1928             x = env.Detect('xxx.exe')
1929             assert x == 'xxx.exe', x
1930
1931             sub1_xxx_exe = test.workpath('sub1', 'xxx.exe')
1932             os.chmod(sub1_xxx_exe, 0755)
1933
1934             x = env.Detect('xxx.exe')
1935             assert x == 'xxx.exe', x
1936
1937         env = self.TestEnvironment(ENV = { 'PATH' : [] })
1938         x = env.Detect('xxx.exe')
1939         assert x is None, x
1940
1941     def test_Dictionary(self):
1942         """Test retrieval of known construction variables
1943
1944         Fetch them from the Dictionary and check for well-known
1945         defaults that get inserted.
1946         """
1947         env = self.TestEnvironment(XXX = 'x', YYY = 'y', ZZZ = 'z')
1948         assert env.Dictionary('XXX') == 'x'
1949         assert env.Dictionary('YYY') == 'y'
1950         assert env.Dictionary('XXX', 'ZZZ') == ['x', 'z']
1951         xxx, zzz = env.Dictionary('XXX', 'ZZZ')
1952         assert xxx == 'x'
1953         assert zzz == 'z'
1954         assert 'BUILDERS' in env.Dictionary()
1955         assert 'CC' in env.Dictionary()
1956         assert 'CCFLAGS' in env.Dictionary()
1957         assert 'ENV' in env.Dictionary()
1958
1959         assert env['XXX'] == 'x'
1960         env['XXX'] = 'foo'
1961         assert env.Dictionary('XXX') == 'foo'
1962         del env['XXX']
1963         assert 'XXX' not in env.Dictionary()
1964
1965     def test_FindIxes(self):
1966         "Test FindIxes()"
1967         env = self.TestEnvironment(LIBPREFIX='lib',
1968                           LIBSUFFIX='.a',
1969                           SHLIBPREFIX='lib',
1970                           SHLIBSUFFIX='.so',
1971                           PREFIX='pre',
1972                           SUFFIX='post')
1973
1974         paths = [os.path.join('dir', 'libfoo.a'),
1975                  os.path.join('dir', 'libfoo.so')]
1976
1977         assert paths[0] == env.FindIxes(paths, 'LIBPREFIX', 'LIBSUFFIX')
1978         assert paths[1] == env.FindIxes(paths, 'SHLIBPREFIX', 'SHLIBSUFFIX')
1979         assert None is env.FindIxes(paths, 'PREFIX', 'POST')
1980
1981         paths = ['libfoo.a', 'prefoopost']
1982
1983         assert paths[0] == env.FindIxes(paths, 'LIBPREFIX', 'LIBSUFFIX')
1984         assert None is env.FindIxes(paths, 'SHLIBPREFIX', 'SHLIBSUFFIX')
1985         assert paths[1] == env.FindIxes(paths, 'PREFIX', 'SUFFIX')
1986
1987     def test_ParseConfig(self):
1988         """Test the ParseConfig() method"""
1989         env = self.TestEnvironment(COMMAND='command',
1990                           ASFLAGS='assembler',
1991                           CCFLAGS=[''],
1992                           CPPDEFINES=[],
1993                           CPPFLAGS=[''],
1994                           CPPPATH='string',
1995                           FRAMEWORKPATH=[],
1996                           FRAMEWORKS=[],
1997                           LIBPATH=['list'],
1998                           LIBS='',
1999                           LINKFLAGS=[''],
2000                           RPATH=[])
2001
2002         orig_backtick = env.backtick
2003         class my_backtick:
2004             def __init__(self, save_command, output):
2005                 self.save_command = save_command
2006                 self.output = output
2007             def __call__(self, command):
2008                 self.save_command.append(command)
2009                 return self.output
2010
2011         try:
2012             save_command = []
2013             env.backtick = my_backtick(save_command, 
2014                                  "-I/usr/include/fum -I bar -X\n" + \
2015                                  "-L/usr/fax -L foo -lxxx -l yyy " + \
2016                                  "-Wa,-as -Wl,-link " + \
2017                                  "-Wl,-rpath=rpath1 " + \
2018                                  "-Wl,-R,rpath2 " + \
2019                                  "-Wl,-Rrpath3 " + \
2020                                  "-Wp,-cpp abc " + \
2021                                  "-framework Carbon " + \
2022                                  "-frameworkdir=fwd1 " + \
2023                                  "-Ffwd2 " + \
2024                                  "-F fwd3 " + \
2025                                  "-pthread " + \
2026                                  "-mno-cygwin -mwindows " + \
2027                                  "-arch i386 -isysroot /tmp +DD64 " + \
2028                                  "-DFOO -DBAR=value")
2029             env.ParseConfig("fake $COMMAND")
2030             assert save_command == ['fake command'], save_command
2031             assert env['ASFLAGS'] == ['assembler', '-as'], env['ASFLAGS']
2032             assert env['CCFLAGS'] == ['', '-X', '-Wa,-as',
2033                                       '-pthread', '-mno-cygwin',
2034                                       ('-arch', 'i386'), ('-isysroot', '/tmp'),
2035                                       '+DD64'], env['CCFLAGS']
2036             assert env['CPPDEFINES'] == ['FOO', ['BAR', 'value']], env['CPPDEFINES']
2037             assert env['CPPFLAGS'] == ['', '-Wp,-cpp'], env['CPPFLAGS']
2038             assert env['CPPPATH'] == ['string', '/usr/include/fum', 'bar'], env['CPPPATH']
2039             assert env['FRAMEWORKPATH'] == ['fwd1', 'fwd2', 'fwd3'], env['FRAMEWORKPATH']
2040             assert env['FRAMEWORKS'] == ['Carbon'], env['FRAMEWORKS']
2041             assert env['LIBPATH'] == ['list', '/usr/fax', 'foo'], env['LIBPATH']
2042             assert env['LIBS'] == ['xxx', 'yyy', env.File('abc')], env['LIBS']
2043             assert env['LINKFLAGS'] == ['', '-Wl,-link', '-pthread',
2044                                         '-mno-cygwin', '-mwindows',
2045                                         ('-arch', 'i386'),
2046                                         ('-isysroot', '/tmp'),
2047                                         '+DD64'], env['LINKFLAGS']
2048             assert env['RPATH'] == ['rpath1', 'rpath2', 'rpath3'], env['RPATH']
2049
2050             env.backtick = my_backtick([], "-Ibar")
2051             env.ParseConfig("fake2")
2052             assert env['CPPPATH'] == ['string', '/usr/include/fum', 'bar'], env['CPPPATH']
2053             env.ParseConfig("fake2", unique=0)
2054             assert env['CPPPATH'] == ['string', '/usr/include/fum', 'bar', 'bar'], env['CPPPATH']
2055         finally:
2056             env.backtick = orig_backtick
2057
2058     def test_ParseDepends(self):
2059         """Test the ParseDepends() method"""
2060         test = TestCmd.TestCmd(workdir = '')
2061
2062         test.write('single', """
2063 #file: dependency
2064
2065 f0: \
2066    d1 \
2067    d2 \
2068    d3 \
2069
2070 """)
2071
2072         test.write('multiple', """
2073 f1: foo
2074 f2 f3: bar
2075 f4: abc def
2076 #file: dependency
2077 f5: \
2078    ghi \
2079    jkl \
2080    mno \
2081 """)
2082
2083         env = self.TestEnvironment(SINGLE = test.workpath('single'))
2084
2085         tlist = []
2086         dlist = []
2087         def my_depends(target, dependency, tlist=tlist, dlist=dlist):
2088             tlist.extend(target)
2089             dlist.extend(dependency)
2090
2091         env.Depends = my_depends
2092
2093         env.ParseDepends(test.workpath('does_not_exist'))
2094
2095         exc_caught = None
2096         try:
2097             env.ParseDepends(test.workpath('does_not_exist'), must_exist=1)
2098         except IOError:
2099             exc_caught = 1
2100         assert exc_caught, "did not catch expected IOError"
2101
2102         del tlist[:]
2103         del dlist[:]
2104
2105         env.ParseDepends('$SINGLE', only_one=1)
2106         t = list(map(str, tlist))
2107         d = list(map(str, dlist))
2108         assert t == ['f0'], t
2109         assert d == ['d1', 'd2', 'd3'], d
2110
2111         del tlist[:]
2112         del dlist[:]
2113
2114         env.ParseDepends(test.workpath('multiple'))
2115         t = list(map(str, tlist))
2116         d = list(map(str, dlist))
2117         assert t == ['f1', 'f2', 'f3', 'f4', 'f5'], t
2118         assert d == ['foo', 'bar', 'abc', 'def', 'ghi', 'jkl', 'mno'], d
2119
2120         exc_caught = None
2121         try:
2122             env.ParseDepends(test.workpath('multiple'), only_one=1)
2123         except SCons.Errors.UserError:
2124             exc_caught = 1
2125         assert exc_caught, "did not catch expected UserError"
2126
2127     def test_Platform(self):
2128         """Test the Platform() method"""
2129         env = self.TestEnvironment(WIN32='win32', NONE='no-such-platform')
2130
2131         exc_caught = None
2132         try:
2133             env.Platform('does_not_exist')
2134         except SCons.Errors.UserError:
2135             exc_caught = 1
2136         assert exc_caught, "did not catch expected UserError"
2137
2138         exc_caught = None
2139         try:
2140             env.Platform('$NONE')
2141         except SCons.Errors.UserError:
2142             exc_caught = 1
2143         assert exc_caught, "did not catch expected UserError"
2144
2145         env.Platform('posix')
2146         assert env['OBJSUFFIX'] == '.o', env['OBJSUFFIX']
2147
2148         env.Platform('$WIN32')
2149         assert env['OBJSUFFIX'] == '.obj', env['OBJSUFFIX']
2150
2151     def test_Prepend(self):
2152         """Test prepending to construction variables in an Environment
2153         """
2154         import UserDict
2155         UD = collections.UserDict
2156         import UserList
2157         UL = collections.UserList
2158
2159         cases = [
2160             'a1',       'A1',           'A1a1',
2161             'a2',       ['A2'],         ['A2', 'a2'],
2162             'a3',       UL(['A3']),     UL(['A3', 'a', '3']),
2163             'a4',       '',             'a4',
2164             'a5',       [],             ['a5'],
2165             'a6',       UL([]),         UL(['a', '6']),
2166             'a7',       [''],           ['', 'a7'],
2167             'a8',       UL(['']),       UL(['', 'a', '8']),
2168
2169             ['e1'],     'E1',           ['E1', 'e1'],
2170             ['e2'],     ['E2'],         ['E2', 'e2'],
2171             ['e3'],     UL(['E3']),     UL(['E3', 'e3']),
2172             ['e4'],     '',             ['e4'],
2173             ['e5'],     [],             ['e5'],
2174             ['e6'],     UL([]),         UL(['e6']),
2175             ['e7'],     [''],           ['', 'e7'],
2176             ['e8'],     UL(['']),       UL(['', 'e8']),
2177
2178             UL(['i1']), 'I1',           UL(['I', '1', 'i1']),
2179             UL(['i2']), ['I2'],         UL(['I2', 'i2']),
2180             UL(['i3']), UL(['I3']),     UL(['I3', 'i3']),
2181             UL(['i4']), '',             UL(['i4']),
2182             UL(['i5']), [],             UL(['i5']),
2183             UL(['i6']), UL([]),         UL(['i6']),
2184             UL(['i7']), [''],           UL(['', 'i7']),
2185             UL(['i8']), UL(['']),       UL(['', 'i8']),
2186
2187             {'d1':1},   'D1',           {'d1':1, 'D1':None},
2188             {'d2':1},   ['D2'],         {'d2':1, 'D2':None},
2189             {'d3':1},   UL(['D3']),     {'d3':1, 'D3':None},
2190             {'d4':1},   {'D4':1},       {'d4':1, 'D4':1},
2191             {'d5':1},   UD({'D5':1}),   UD({'d5':1, 'D5':1}),
2192
2193             UD({'u1':1}), 'U1',         UD({'u1':1, 'U1':None}),
2194             UD({'u2':1}), ['U2'],       UD({'u2':1, 'U2':None}),
2195             UD({'u3':1}), UL(['U3']),   UD({'u3':1, 'U3':None}),
2196             UD({'u4':1}), {'U4':1},     UD({'u4':1, 'U4':1}),
2197             UD({'u5':1}), UD({'U5':1}), UD({'u5':1, 'U5':1}),
2198
2199             '',         'M1',           'M1',
2200             '',         ['M2'],         ['M2'],
2201             '',         UL(['M3']),     UL(['M3']),
2202             '',         '',             '',
2203             '',         [],             [],
2204             '',         UL([]),         UL([]),
2205             '',         [''],           [''],
2206             '',         UL(['']),       UL(['']),
2207
2208             [],         'N1',           ['N1'],
2209             [],         ['N2'],         ['N2'],
2210             [],         UL(['N3']),     UL(['N3']),
2211             [],         '',             [],
2212             [],         [],             [],
2213             [],         UL([]),         UL([]),
2214             [],         [''],           [''],
2215             [],         UL(['']),       UL(['']),
2216
2217             UL([]),     'O1',           UL(['O', '1']),
2218             UL([]),     ['O2'],         UL(['O2']),
2219             UL([]),     UL(['O3']),     UL(['O3']),
2220             UL([]),     '',             UL([]),
2221             UL([]),     [],             UL([]),
2222             UL([]),     UL([]),         UL([]),
2223             UL([]),     [''],           UL(['']),
2224             UL([]),     UL(['']),       UL(['']),
2225
2226             [''],       'P1',           ['P1', ''],
2227             [''],       ['P2'],         ['P2', ''],
2228             [''],       UL(['P3']),     UL(['P3', '']),
2229             [''],       '',             [''],
2230             [''],       [],             [''],
2231             [''],       UL([]),         UL(['']),
2232             [''],       [''],           ['', ''],
2233             [''],       UL(['']),       UL(['', '']),
2234
2235             UL(['']),   'Q1',           UL(['Q', '1', '']),
2236             UL(['']),   ['Q2'],         UL(['Q2', '']),
2237             UL(['']),   UL(['Q3']),     UL(['Q3', '']),
2238             UL(['']),   '',             UL(['']),
2239             UL(['']),   [],             UL(['']),
2240             UL(['']),   UL([]),         UL(['']),
2241             UL(['']),   [''],           UL(['', '']),
2242             UL(['']),   UL(['']),       UL(['', '']),
2243         ]
2244
2245         env = Environment()
2246         failed = 0
2247         while cases:
2248             input, prepend, expect = cases[:3]
2249             env['XXX'] = copy.copy(input)
2250             try:
2251                 env.Prepend(XXX = prepend)
2252             except Exception, e:
2253                 if failed == 0: print
2254                 print "    %s Prepend %s exception: %s" % \
2255                       (repr(input), repr(prepend), e)
2256                 failed = failed + 1
2257             else:
2258                 result = env['XXX']
2259                 if result != expect:
2260                     if failed == 0: print
2261                     print "    %s Prepend %s => %s did not match %s" % \
2262                           (repr(input), repr(prepend), repr(result), repr(expect))
2263                     failed = failed + 1
2264             del cases[:3]
2265         assert failed == 0, "%d Prepend() cases failed" % failed
2266
2267         env['UL'] = UL(['foo'])
2268         env.Prepend(UL = 'bar')
2269         result = env['UL']
2270         assert isinstance(result, UL), repr(result)
2271         assert result == ['b', 'a', 'r', 'foo'], result
2272
2273         env['CLVar'] = CLVar(['foo'])
2274         env.Prepend(CLVar = 'bar')
2275         result = env['CLVar']
2276         assert isinstance(result, CLVar), repr(result)
2277         assert result == ['bar', 'foo'], result
2278
2279         env3 = self.TestEnvironment(X = {'x1' : 7})
2280         env3.Prepend(X = {'x1' : 8, 'x2' : 9}, Y = {'y1' : 10})
2281         assert env3['X'] == {'x1': 8, 'x2' : 9}, env3['X']
2282         assert env3['Y'] == {'y1': 10}, env3['Y']
2283
2284         z1 = Builder()
2285         z2 = Builder()
2286         env4 = self.TestEnvironment(BUILDERS = {'z1' : z1})
2287         env4.Prepend(BUILDERS = {'z2' : z2})
2288         assert env4['BUILDERS'] == {'z1' : z1, 'z2' : z2}, env4['BUILDERS']
2289         assert hasattr(env4, 'z1')
2290         assert hasattr(env4, 'z2')
2291
2292     def test_PrependENVPath(self):
2293         """Test prepending to an ENV path."""
2294         env1 = self.TestEnvironment(ENV = {'PATH': r'C:\dir\num\one;C:\dir\num\two'},
2295                            MYENV = {'MYPATH': r'C:\mydir\num\one;C:\mydir\num\two'})
2296         # have to include the pathsep here so that the test will work on UNIX too.
2297         env1.PrependENVPath('PATH',r'C:\dir\num\two',sep = ';')
2298         env1.PrependENVPath('PATH',r'C:\dir\num\three',sep = ';')
2299         env1.PrependENVPath('MYPATH',r'C:\mydir\num\three','MYENV',sep = ';')
2300         env1.PrependENVPath('MYPATH',r'C:\mydir\num\one','MYENV',sep = ';')
2301         # this should do nothing since delete_existing is 0
2302         env1.PrependENVPath('MYPATH',r'C:\mydir\num\three','MYENV', sep = ';', delete_existing=0)
2303         assert(env1['ENV']['PATH'] == r'C:\dir\num\three;C:\dir\num\two;C:\dir\num\one')
2304         assert(env1['MYENV']['MYPATH'] == r'C:\mydir\num\one;C:\mydir\num\three;C:\mydir\num\two')
2305
2306         test = TestCmd.TestCmd(workdir = '')
2307         test.subdir('sub1', 'sub2')
2308         p=env1['ENV']['PATH']
2309         env1.PrependENVPath('PATH','#sub1', sep = ';')
2310         env1.PrependENVPath('PATH',env1.fs.Dir('sub2'), sep = ';')
2311         assert env1['ENV']['PATH'] == 'sub2;sub1;' + p, env1['ENV']['PATH']
2312
2313     def test_PrependUnique(self):
2314         """Test prepending unique values to construction variables
2315
2316         This strips values that are already present when lists are
2317         involved."""
2318         env = self.TestEnvironment(AAA1 = 'a1',
2319                           AAA2 = 'a2',
2320                           AAA3 = 'a3',
2321                           AAA4 = 'a4',
2322                           AAA5 = 'a5',
2323                           BBB1 = ['b1'],
2324                           BBB2 = ['b2'],
2325                           BBB3 = ['b3'],
2326                           BBB4 = ['b4'],
2327                           BBB5 = ['b5'],
2328                           CCC1 = '',
2329                           CCC2 = '',
2330                           DDD1 = ['a', 'b', 'c'])
2331         env.PrependUnique(AAA1 = 'a1',
2332                           AAA2 = ['a2'],
2333                           AAA3 = ['a3', 'b', 'c', 'b', 'a3'], # ignore dups
2334                           AAA4 = 'a4.new',
2335                           AAA5 = ['a5.new'],
2336                           BBB1 = 'b1',
2337                           BBB2 = ['b2'],
2338                           BBB3 = ['b3', 'b', 'c', 'b3'],
2339                           BBB4 = 'b4.new',
2340                           BBB5 = ['b5.new'],
2341                           CCC1 = 'c1',
2342                           CCC2 = ['c2'],
2343                           DDD1 = 'b')
2344         assert env['AAA1'] == 'a1a1', env['AAA1']
2345         assert env['AAA2'] == ['a2'], env['AAA2']
2346         assert env['AAA3'] == ['c', 'b', 'a3'], env['AAA3']
2347         assert env['AAA4'] == 'a4.newa4', env['AAA4']
2348         assert env['AAA5'] == ['a5.new', 'a5'], env['AAA5']
2349         assert env['BBB1'] == ['b1'], env['BBB1']
2350         assert env['BBB2'] == ['b2'], env['BBB2']
2351         assert env['BBB3'] == ['b', 'c', 'b3'], env['BBB3']
2352         assert env['BBB4'] == ['b4.new', 'b4'], env['BBB4']
2353         assert env['BBB5'] == ['b5.new', 'b5'], env['BBB5']
2354         assert env['CCC1'] == 'c1', env['CCC1']
2355         assert env['CCC2'] == ['c2'], env['CCC2']
2356         assert env['DDD1'] == ['a', 'b', 'c'], env['DDD1']
2357
2358         env.PrependUnique(DDD1 = 'b', delete_existing=1)
2359         assert env['DDD1'] == ['b', 'a', 'c'], env['DDD1'] # b moves to front
2360         env.PrependUnique(DDD1 = ['a','c'], delete_existing=1)
2361         assert env['DDD1'] == ['a', 'c', 'b'], env['DDD1'] # a & c move to front
2362         env.PrependUnique(DDD1 = ['d','e','d'], delete_existing=1)
2363         assert env['DDD1'] == ['d', 'e', 'a', 'c', 'b'], env['DDD1'] 
2364
2365
2366         env['CLVar'] = CLVar([])
2367         env.PrependUnique(CLVar = 'bar')
2368         result = env['CLVar']
2369         if sys.version[0] == '1' or sys.version[:3] == '2.0':
2370             # Python 2.0 and before have a quirky behavior where CLVar([])
2371             # actually matches '' and [] due to different __coerce__()
2372             # semantics in the UserList implementation.  It isn't worth a
2373             # lot of effort to get this corner case to work identically
2374             # (support for Python 1.5 support will die soon anyway),
2375             # so just treat it separately for now.
2376             assert result == 'bar', result
2377         else:
2378             assert isinstance(result, CLVar), repr(result)
2379             assert result == ['bar'], result
2380
2381         env['CLVar'] = CLVar(['abc'])
2382         env.PrependUnique(CLVar = 'bar')
2383         result = env['CLVar']
2384         assert isinstance(result, CLVar), repr(result)
2385         assert result == ['bar', 'abc'], result
2386
2387         env['CLVar'] = CLVar(['bar'])
2388         env.PrependUnique(CLVar = 'bar')
2389         result = env['CLVar']
2390         assert isinstance(result, CLVar), repr(result)
2391         assert result == ['bar'], result
2392
2393     def test_Replace(self):
2394         """Test replacing construction variables in an Environment
2395
2396         After creation of the Environment, of course.
2397         """
2398         env1 = self.TestEnvironment(AAA = 'a', BBB = 'b')
2399         env1.Replace(BBB = 'bbb', CCC = 'ccc')
2400
2401         env2 = self.TestEnvironment(AAA = 'a', BBB = 'bbb', CCC = 'ccc')
2402         assert env1 == env2, diff_env(env1, env2)
2403
2404         b1 = Builder()
2405         b2 = Builder()
2406         env3 = self.TestEnvironment(BUILDERS = {'b1' : b1})
2407         assert hasattr(env3, 'b1'), "b1 was not set"
2408         env3.Replace(BUILDERS = {'b2' : b2})
2409         assert not hasattr(env3, 'b1'), "b1 was not cleared"
2410         assert hasattr(env3, 'b2'), "b2 was not set"
2411
2412     def test_ReplaceIxes(self):
2413         "Test ReplaceIxes()"
2414         env = self.TestEnvironment(LIBPREFIX='lib',
2415                           LIBSUFFIX='.a',
2416                           SHLIBPREFIX='lib',
2417                           SHLIBSUFFIX='.so',
2418                           PREFIX='pre',
2419                           SUFFIX='post')
2420
2421         assert 'libfoo.a' == env.ReplaceIxes('libfoo.so',
2422                                              'SHLIBPREFIX', 'SHLIBSUFFIX',
2423                                              'LIBPREFIX', 'LIBSUFFIX')
2424
2425         assert os.path.join('dir', 'libfoo.a') == env.ReplaceIxes(os.path.join('dir', 'libfoo.so'),
2426                                                                    'SHLIBPREFIX', 'SHLIBSUFFIX',
2427                                                                    'LIBPREFIX', 'LIBSUFFIX')
2428
2429         assert 'libfoo.a' == env.ReplaceIxes('prefoopost',
2430                                              'PREFIX', 'SUFFIX',
2431                                              'LIBPREFIX', 'LIBSUFFIX')
2432
2433     def test_SetDefault(self):
2434         """Test the SetDefault method"""
2435         env = self.TestEnvironment(tools = [])
2436         env.SetDefault(V1 = 1)
2437         env.SetDefault(V1 = 2)
2438         assert env['V1'] == 1
2439         env['V2'] = 2
2440         env.SetDefault(V2 = 1)
2441         assert env['V2'] == 2
2442
2443     def test_Tool(self):
2444         """Test the Tool() method"""
2445         env = self.TestEnvironment(LINK='link', NONE='no-such-tool')
2446
2447         exc_caught = None
2448         try:
2449             env.Tool('does_not_exist')
2450         except SCons.Errors.EnvironmentError:
2451             exc_caught = 1
2452         assert exc_caught, "did not catch expected EnvironmentError"
2453
2454         exc_caught = None
2455         try:
2456             env.Tool('$NONE')
2457         except SCons.Errors.EnvironmentError:
2458             exc_caught = 1
2459         assert exc_caught, "did not catch expected EnvironmentError"
2460
2461         # Use a non-existent toolpath directory just to make sure we
2462         # can call Tool() with the keyword argument.
2463         env.Tool('cc', toolpath=['/no/such/directory'])
2464         assert env['CC'] == 'cc', env['CC']
2465
2466         env.Tool('$LINK')
2467         assert env['LINK'] == '$SMARTLINK', env['LINK']
2468
2469         # Test that the environment stores the toolpath and
2470         # re-uses it for later calls.
2471         test = TestCmd.TestCmd(workdir = '')
2472
2473         test.write('xxx.py', """\
2474 def exists(env):
2475     1
2476 def generate(env):
2477     env['XXX'] = 'one'
2478 """)
2479
2480         test.write('yyy.py', """\
2481 def exists(env):
2482     1
2483 def generate(env):
2484     env['YYY'] = 'two'
2485 """)
2486
2487         env = self.TestEnvironment(tools=['xxx'], toolpath=[test.workpath('')])
2488         assert env['XXX'] == 'one', env['XXX']
2489         env.Tool('yyy')
2490         assert env['YYY'] == 'two', env['YYY']
2491
2492     def test_WhereIs(self):
2493         """Test the WhereIs() method"""
2494         test = TestCmd.TestCmd(workdir = '')
2495
2496         sub1_xxx_exe = test.workpath('sub1', 'xxx.exe')
2497         sub2_xxx_exe = test.workpath('sub2', 'xxx.exe')
2498         sub3_xxx_exe = test.workpath('sub3', 'xxx.exe')
2499         sub4_xxx_exe = test.workpath('sub4', 'xxx.exe')
2500
2501         test.subdir('subdir', 'sub1', 'sub2', 'sub3', 'sub4')
2502
2503         if sys.platform != 'win32':
2504             test.write(sub1_xxx_exe, "\n")
2505
2506         os.mkdir(sub2_xxx_exe)
2507
2508         test.write(sub3_xxx_exe, "\n")
2509         os.chmod(sub3_xxx_exe, 0777)
2510
2511         test.write(sub4_xxx_exe, "\n")
2512         os.chmod(sub4_xxx_exe, 0777)
2513
2514         env_path = os.environ['PATH']
2515
2516         pathdirs_1234 = [ test.workpath('sub1'),
2517                           test.workpath('sub2'),
2518                           test.workpath('sub3'),
2519                           test.workpath('sub4'),
2520                         ] + env_path.split(os.pathsep)
2521
2522         pathdirs_1243 = [ test.workpath('sub1'),
2523                           test.workpath('sub2'),
2524                           test.workpath('sub4'),
2525                           test.workpath('sub3'),
2526                         ] + env_path.split(os.pathsep)
2527
2528         path = os.pathsep.join(pathdirs_1234)
2529         env = self.TestEnvironment(ENV = {'PATH' : path})
2530         wi = env.WhereIs('xxx.exe')
2531         assert wi == test.workpath(sub3_xxx_exe), wi
2532         wi = env.WhereIs('xxx.exe', pathdirs_1243)
2533         assert wi == test.workpath(sub4_xxx_exe), wi
2534         wi = env.WhereIs('xxx.exe', os.pathsep.join(pathdirs_1243))
2535         assert wi == test.workpath(sub4_xxx_exe), wi
2536
2537         wi = env.WhereIs('xxx.exe', reject = sub3_xxx_exe)
2538         assert wi == test.workpath(sub4_xxx_exe), wi
2539         wi = env.WhereIs('xxx.exe', pathdirs_1243, reject = sub3_xxx_exe)
2540         assert wi == test.workpath(sub4_xxx_exe), wi
2541
2542         path = os.pathsep.join(pathdirs_1243)
2543         env = self.TestEnvironment(ENV = {'PATH' : path})
2544         wi = env.WhereIs('xxx.exe')
2545         assert wi == test.workpath(sub4_xxx_exe), wi
2546         wi = env.WhereIs('xxx.exe', pathdirs_1234)
2547         assert wi == test.workpath(sub3_xxx_exe), wi
2548         wi = env.WhereIs('xxx.exe', os.pathsep.join(pathdirs_1234))
2549         assert wi == test.workpath(sub3_xxx_exe), wi
2550
2551         if sys.platform == 'win32':
2552             wi = env.WhereIs('xxx', pathext = '')
2553             assert wi is None, wi
2554
2555             wi = env.WhereIs('xxx', pathext = '.exe')
2556             assert wi == test.workpath(sub4_xxx_exe), wi
2557
2558             wi = env.WhereIs('xxx', path = pathdirs_1234, pathext = '.BAT;.EXE')
2559             assert wi.lower() == test.workpath(sub3_xxx_exe).lower(), wi
2560
2561             # Test that we return a normalized path even when
2562             # the path contains forward slashes.
2563             forward_slash = test.workpath('') + '/sub3'
2564             wi = env.WhereIs('xxx', path = forward_slash, pathext = '.EXE')
2565             assert wi.lower() == test.workpath(sub3_xxx_exe).lower(), wi
2566
2567
2568
2569     def test_Action(self):
2570         """Test the Action() method"""
2571         import SCons.Action
2572
2573         env = self.TestEnvironment(FOO = 'xyzzy')
2574
2575         a = env.Action('foo')
2576         assert a, a
2577         assert a.__class__ is SCons.Action.CommandAction, a.__class__
2578
2579         a = env.Action('$FOO')
2580         assert a, a
2581         assert a.__class__ is SCons.Action.CommandAction, a.__class__
2582
2583         a = env.Action('$$FOO')
2584         assert a, a
2585         assert a.__class__ is SCons.Action.LazyAction, a.__class__
2586
2587         a = env.Action(['$FOO', 'foo'])
2588         assert a, a
2589         assert a.__class__ is SCons.Action.ListAction, a.__class__
2590
2591         def func(arg):
2592             pass
2593         a = env.Action(func)
2594         assert a, a
2595         assert a.__class__ is SCons.Action.FunctionAction, a.__class__
2596
2597     def test_AddPostAction(self):
2598         """Test the AddPostAction() method"""
2599         env = self.TestEnvironment(FOO='fff', BAR='bbb')
2600
2601         n = env.AddPostAction('$FOO', lambda x: x)
2602         assert str(n[0]) == 'fff', n[0]
2603
2604         n = env.AddPostAction(['ggg', '$BAR'], lambda x: x)
2605         assert str(n[0]) == 'ggg', n[0]
2606         assert str(n[1]) == 'bbb', n[1]
2607
2608     def test_AddPreAction(self):
2609         """Test the AddPreAction() method"""
2610         env = self.TestEnvironment(FOO='fff', BAR='bbb')
2611
2612         n = env.AddPreAction('$FOO', lambda x: x)
2613         assert str(n[0]) == 'fff', n[0]
2614
2615         n = env.AddPreAction(['ggg', '$BAR'], lambda x: x)
2616         assert str(n[0]) == 'ggg', n[0]
2617         assert str(n[1]) == 'bbb', n[1]
2618
2619     def test_Alias(self):
2620         """Test the Alias() method"""
2621         env = self.TestEnvironment(FOO='kkk', BAR='lll', EA='export_alias')
2622
2623         tgt = env.Alias('new_alias')[0]
2624         assert str(tgt) == 'new_alias', tgt
2625         assert tgt.sources == [], tgt.sources
2626         assert not hasattr(tgt, 'builder'), tgt.builder
2627
2628         tgt = env.Alias('None_alias', None)[0]
2629         assert str(tgt) == 'None_alias', tgt
2630         assert tgt.sources == [], tgt.sources
2631
2632         tgt = env.Alias('empty_list', [])[0]
2633         assert str(tgt) == 'empty_list', tgt
2634         assert tgt.sources == [], tgt.sources
2635
2636         tgt = env.Alias('export_alias', [ 'asrc1', '$FOO' ])[0]
2637         assert str(tgt) == 'export_alias', tgt
2638         assert len(tgt.sources) == 2, list(map(str, tgt.sources))
2639         assert str(tgt.sources[0]) == 'asrc1', list(map(str, tgt.sources))
2640         assert str(tgt.sources[1]) == 'kkk', list(map(str, tgt.sources))
2641
2642         n = env.Alias(tgt, source = ['$BAR', 'asrc4'])[0]
2643         assert n is tgt, n
2644         assert len(tgt.sources) == 4, list(map(str, tgt.sources))
2645         assert str(tgt.sources[2]) == 'lll', list(map(str, tgt.sources))
2646         assert str(tgt.sources[3]) == 'asrc4', list(map(str, tgt.sources))
2647
2648         n = env.Alias('$EA', 'asrc5')[0]
2649         assert n is tgt, n
2650         assert len(tgt.sources) == 5, list(map(str, tgt.sources))
2651         assert str(tgt.sources[4]) == 'asrc5', list(map(str, tgt.sources))
2652
2653         t1, t2 = env.Alias(['t1', 't2'], ['asrc6', 'asrc7'])
2654         assert str(t1) == 't1', t1
2655         assert str(t2) == 't2', t2
2656         assert len(t1.sources) == 2, list(map(str, t1.sources))
2657         assert str(t1.sources[0]) == 'asrc6', list(map(str, t1.sources))
2658         assert str(t1.sources[1]) == 'asrc7', list(map(str, t1.sources))
2659         assert len(t2.sources) == 2, list(map(str, t2.sources))
2660         assert str(t2.sources[0]) == 'asrc6', list(map(str, t2.sources))
2661         assert str(t2.sources[1]) == 'asrc7', list(map(str, t2.sources))
2662
2663         tgt = env.Alias('add', 's1')
2664         tgt = env.Alias('add', 's2')[0]
2665         s = list(map(str, tgt.sources))
2666         assert s == ['s1', 's2'], s
2667         tgt = env.Alias(tgt, 's3')[0]
2668         s = list(map(str, tgt.sources))
2669         assert s == ['s1', 's2', 's3'], s
2670
2671         tgt = env.Alias('act', None, "action1")[0]
2672         s = str(tgt.builder.action)
2673         assert s == "action1", s
2674         tgt = env.Alias('act', None, "action2")[0]
2675         s = str(tgt.builder.action)
2676         assert s == "action1\naction2", s
2677         tgt = env.Alias(tgt, None, "action3")[0]
2678         s = str(tgt.builder.action)
2679         assert s == "action1\naction2\naction3", s
2680
2681     def test_AlwaysBuild(self):
2682         """Test the AlwaysBuild() method"""
2683         env = self.TestEnvironment(FOO='fff', BAR='bbb')
2684         t = env.AlwaysBuild('a', 'b$FOO', ['c', 'd'], '$BAR',
2685                             env.fs.Dir('dir'), env.fs.File('file'))
2686         assert t[0].__class__.__name__ == 'Entry'
2687         assert t[0].path == 'a'
2688         assert t[0].always_build
2689         assert t[1].__class__.__name__ == 'Entry'
2690         assert t[1].path == 'bfff'
2691         assert t[1].always_build
2692         assert t[2].__class__.__name__ == 'Entry'
2693         assert t[2].path == 'c'
2694         assert t[2].always_build
2695         assert t[3].__class__.__name__ == 'Entry'
2696         assert t[3].path == 'd'
2697         assert t[3].always_build
2698         assert t[4].__class__.__name__ == 'Entry'
2699         assert t[4].path == 'bbb'
2700         assert t[4].always_build
2701         assert t[5].__class__.__name__ == 'Dir'
2702         assert t[5].path == 'dir'
2703         assert t[5].always_build
2704         assert t[6].__class__.__name__ == 'File'
2705         assert t[6].path == 'file'
2706         assert t[6].always_build
2707
2708     def test_VariantDir(self):
2709         """Test the VariantDir() method"""
2710         class MyFS:
2711              def Dir(self, name):
2712                  return name
2713              def VariantDir(self, variant_dir, src_dir, duplicate):
2714                  self.variant_dir = variant_dir
2715                  self.src_dir = src_dir
2716                  self.duplicate = duplicate
2717
2718         env = self.TestEnvironment(FOO = 'fff', BAR = 'bbb')
2719         env.fs = MyFS()
2720
2721         env.VariantDir('build', 'src')
2722         assert env.fs.variant_dir == 'build', env.fs.variant_dir
2723         assert env.fs.src_dir == 'src', env.fs.src_dir
2724         assert env.fs.duplicate == 1, env.fs.duplicate
2725
2726         env.VariantDir('build${FOO}', '${BAR}src', 0)
2727         assert env.fs.variant_dir == 'buildfff', env.fs.variant_dir
2728         assert env.fs.src_dir == 'bbbsrc', env.fs.src_dir
2729         assert env.fs.duplicate == 0, env.fs.duplicate
2730
2731     def test_Builder(self):
2732         """Test the Builder() method"""
2733         env = self.TestEnvironment(FOO = 'xyzzy')
2734
2735         b = env.Builder(action = 'foo')
2736         assert b is not None, b
2737
2738         b = env.Builder(action = '$FOO')
2739         assert b is not None, b
2740
2741         b = env.Builder(action = ['$FOO', 'foo'])
2742         assert b is not None, b
2743
2744         def func(arg):
2745             pass
2746         b = env.Builder(action = func)
2747         assert b is not None, b
2748         b = env.Builder(generator = func)
2749         assert b is not None, b
2750
2751     def test_CacheDir(self):
2752         """Test the CacheDir() method"""
2753         env = self.TestEnvironment(CD = 'CacheDir')
2754
2755         env.CacheDir('foo')
2756         assert env._CacheDir_path == 'foo', env._CacheDir_path
2757
2758         env.CacheDir('$CD')
2759         assert env._CacheDir_path == 'CacheDir', env._CacheDir_path
2760
2761     def test_Clean(self):
2762         """Test the Clean() method"""
2763         env = self.TestEnvironment(FOO = 'fff', BAR = 'bbb')
2764
2765         CT = SCons.Environment.CleanTargets
2766
2767         foo = env.arg2nodes('foo')[0]
2768         fff = env.arg2nodes('fff')[0]
2769
2770         t = env.Clean('foo', 'aaa')
2771         l = list(map(str, CT[foo]))
2772         assert l == ['aaa'], l
2773
2774         t = env.Clean(foo, ['$BAR', 'ccc'])
2775         l = list(map(str, CT[foo]))
2776         assert l == ['aaa', 'bbb', 'ccc'], l
2777
2778         eee = env.arg2nodes('eee')[0]
2779
2780         t = env.Clean('$FOO', 'ddd')
2781         l = list(map(str, CT[fff]))
2782         assert l == ['ddd'], l
2783         t = env.Clean(fff, [eee, 'fff'])
2784         l = list(map(str, CT[fff]))
2785         assert l == ['ddd', 'eee', 'fff'], l
2786
2787     def test_Command(self):
2788         """Test the Command() method."""
2789         env = Environment()
2790         t = env.Command(target='foo.out', source=['foo1.in', 'foo2.in'],
2791                         action='buildfoo $target $source')[0]
2792         assert t.builder is not None
2793         assert t.builder.action.__class__.__name__ == 'CommandAction'
2794         assert t.builder.action.cmd_list == 'buildfoo $target $source'
2795         assert 'foo1.in' in [x.path for x in t.sources]
2796         assert 'foo2.in' in [x.path for x in t.sources]
2797
2798         sub = env.fs.Dir('sub')
2799         t = env.Command(target='bar.out', source='sub',
2800                         action='buildbar $target $source')[0]
2801         assert 'sub' in [x.path for x in t.sources]
2802
2803         def testFunc(env, target, source):
2804             assert str(target[0]) == 'foo.out'
2805             assert 'foo1.in' in list(map(str, source)) and 'foo2.in' in list(map(str, source)), list(map(str, source))
2806             return 0
2807         t = env.Command(target='foo.out', source=['foo1.in','foo2.in'],
2808                         action=testFunc)[0]
2809         assert t.builder is not None
2810         assert t.builder.action.__class__.__name__ == 'FunctionAction'
2811         t.build()
2812         assert 'foo1.in' in [x.path for x in t.sources]
2813         assert 'foo2.in' in [x.path for x in t.sources]
2814
2815         x = []
2816         def test2(baz, x=x):
2817             x.append(baz)
2818         env = self.TestEnvironment(TEST2 = test2)
2819         t = env.Command(target='baz.out', source='baz.in',
2820                         action='${TEST2(XYZ)}',
2821                         XYZ='magic word')[0]
2822         assert t.builder is not None
2823         t.build()
2824         assert x[0] == 'magic word', x
2825
2826         t = env.Command(target='${X}.out', source='${X}.in',
2827                         action = 'foo',
2828                         X = 'xxx')[0]
2829         assert str(t) == 'xxx.out', str(t)
2830         assert 'xxx.in' in [x.path for x in t.sources]
2831
2832         env = self.TestEnvironment(source_scanner = 'should_not_find_this')
2833         t = env.Command(target='file.out', source='file.in',
2834                         action = 'foo',
2835                         source_scanner = 'fake')[0]
2836         assert t.builder.source_scanner == 'fake', t.builder.source_scanner
2837
2838     def test_Configure(self):
2839         """Test the Configure() method"""
2840         # Configure() will write to a local temporary file.
2841         test = TestCmd.TestCmd(workdir = '')
2842         save = os.getcwd()
2843
2844         try:
2845             os.chdir(test.workpath())
2846
2847             env = self.TestEnvironment(FOO = 'xyzzy')
2848
2849             def func(arg):
2850                 pass
2851
2852             c = env.Configure()
2853             assert c is not None, c
2854             c.Finish()
2855
2856             c = env.Configure(custom_tests = {'foo' : func, '$FOO' : func})
2857             assert c is not None, c
2858             assert hasattr(c, 'foo')
2859             assert hasattr(c, 'xyzzy')
2860             c.Finish()
2861         finally:
2862             os.chdir(save)
2863
2864     def test_Depends(self):
2865         """Test the explicit Depends method."""
2866         env = self.TestEnvironment(FOO = 'xxx', BAR='yyy')
2867         env.Dir('dir1')
2868         env.Dir('dir2')
2869         env.File('xxx.py')
2870         env.File('yyy.py')
2871         t = env.Depends(target='EnvironmentTest.py',
2872                         dependency='Environment.py')[0]
2873         assert t.__class__.__name__ == 'Entry', t.__class__.__name__
2874         assert t.path == 'EnvironmentTest.py'
2875         assert len(t.depends) == 1
2876         d = t.depends[0]
2877         assert d.__class__.__name__ == 'Entry', d.__class__.__name__
2878         assert d.path == 'Environment.py'
2879
2880         t = env.Depends(target='${FOO}.py', dependency='${BAR}.py')[0]
2881         assert t.__class__.__name__ == 'File', t.__class__.__name__
2882         assert t.path == 'xxx.py'
2883         assert len(t.depends) == 1
2884         d = t.depends[0]
2885         assert d.__class__.__name__ == 'File', d.__class__.__name__
2886         assert d.path == 'yyy.py'
2887
2888         t = env.Depends(target='dir1', dependency='dir2')[0]
2889         assert t.__class__.__name__ == 'Dir', t.__class__.__name__
2890         assert t.path == 'dir1'
2891         assert len(t.depends) == 1
2892         d = t.depends[0]
2893         assert d.__class__.__name__ == 'Dir', d.__class__.__name__
2894         assert d.path == 'dir2'
2895
2896     def test_Dir(self):
2897         """Test the Dir() method"""
2898         class MyFS:
2899             def Dir(self, name):
2900                 return 'Dir(%s)' % name
2901
2902         env = self.TestEnvironment(FOO = 'foodir', BAR = 'bardir')
2903         env.fs = MyFS()
2904
2905         d = env.Dir('d')
2906         assert d == 'Dir(d)', d
2907
2908         d = env.Dir('$FOO')
2909         assert d == 'Dir(foodir)', d
2910
2911         d = env.Dir('${BAR}_$BAR')
2912         assert d == 'Dir(bardir_bardir)', d
2913
2914         d = env.Dir(['dir1'])
2915         assert d == ['Dir(dir1)'], d
2916
2917         d = env.Dir(['dir1', 'dir2'])
2918         assert d == ['Dir(dir1)', 'Dir(dir2)'], d
2919
2920     def test_NoClean(self):
2921         """Test the NoClean() method"""
2922         env = self.TestEnvironment(FOO='ggg', BAR='hhh')
2923         env.Dir('p_hhhb')
2924         env.File('p_d')
2925         t = env.NoClean('p_a', 'p_${BAR}b', ['p_c', 'p_d'], 'p_$FOO')
2926
2927         assert t[0].__class__.__name__ == 'Entry', t[0].__class__.__name__
2928         assert t[0].path == 'p_a'
2929         assert t[0].noclean
2930         assert t[1].__class__.__name__ == 'Dir', t[1].__class__.__name__
2931         assert t[1].path == 'p_hhhb'
2932         assert t[1].noclean
2933         assert t[2].__class__.__name__ == 'Entry', t[2].__class__.__name__
2934         assert t[2].path == 'p_c'
2935         assert t[2].noclean
2936         assert t[3].__class__.__name__ == 'File', t[3].__class__.__name__
2937         assert t[3].path == 'p_d'
2938         assert t[3].noclean
2939         assert t[4].__class__.__name__ == 'Entry', t[4].__class__.__name__
2940         assert t[4].path == 'p_ggg'
2941         assert t[4].noclean
2942
2943     def test_Dump(self):
2944         """Test the Dump() method"""
2945
2946         env = self.TestEnvironment(FOO = 'foo')
2947         assert env.Dump('FOO') == "'foo'", env.Dump('FOO')
2948         assert len(env.Dump()) > 200, env.Dump()    # no args version
2949
2950     def test_Environment(self):
2951         """Test the Environment() method"""
2952         env = self.TestEnvironment(FOO = 'xxx', BAR = 'yyy')
2953
2954         e2 = env.Environment(X = '$FOO', Y = '$BAR')
2955         assert e2['X'] == 'xxx', e2['X']
2956         assert e2['Y'] == 'yyy', e2['Y']
2957
2958     def test_Execute(self):
2959         """Test the Execute() method"""
2960
2961         class MyAction:
2962             def __init__(self, *args, **kw):
2963                 self.args = args
2964             def __call__(self, target, source, env):
2965                 return "%s executed" % self.args
2966
2967         env = Environment()
2968         env.Action = MyAction
2969
2970         result = env.Execute("foo")
2971         assert result == "foo executed", result
2972
2973     def test_Entry(self):
2974         """Test the Entry() method"""
2975         class MyFS:
2976             def Entry(self, name):
2977                 return 'Entry(%s)' % name
2978
2979         env = self.TestEnvironment(FOO = 'fooentry', BAR = 'barentry')
2980         env.fs = MyFS()
2981
2982         e = env.Entry('e')
2983         assert e == 'Entry(e)', e
2984
2985         e = env.Entry('$FOO')
2986         assert e == 'Entry(fooentry)', e
2987
2988         e = env.Entry('${BAR}_$BAR')
2989         assert e == 'Entry(barentry_barentry)', e
2990
2991         e = env.Entry(['entry1'])
2992         assert e == ['Entry(entry1)'], e
2993
2994         e = env.Entry(['entry1', 'entry2'])
2995         assert e == ['Entry(entry1)', 'Entry(entry2)'], e
2996
2997     def test_File(self):
2998         """Test the File() method"""
2999         class MyFS:
3000             def File(self, name):
3001                 return 'File(%s)' % name
3002
3003         env = self.TestEnvironment(FOO = 'foofile', BAR = 'barfile')
3004         env.fs = MyFS()
3005
3006         f = env.File('f')
3007         assert f == 'File(f)', f
3008
3009         f = env.File('$FOO')
3010         assert f == 'File(foofile)', f
3011
3012         f = env.File('${BAR}_$BAR')
3013         assert f == 'File(barfile_barfile)', f
3014
3015         f = env.File(['file1'])
3016         assert f == ['File(file1)'], f
3017
3018         f = env.File(['file1', 'file2'])
3019         assert f == ['File(file1)', 'File(file2)'], f
3020
3021     def test_FindFile(self):
3022         """Test the FindFile() method"""
3023         env = self.TestEnvironment(FOO = 'fff', BAR = 'bbb')
3024
3025         r = env.FindFile('foo', ['no_such_directory'])
3026         assert r is None, r
3027
3028         # XXX
3029
3030     def test_Flatten(self):
3031         """Test the Flatten() method"""
3032         env = Environment()
3033         l = env.Flatten([1])
3034         assert l == [1]
3035         l = env.Flatten([1, [2, [3, [4]]]])
3036         assert l == [1, 2, 3, 4], l
3037
3038     def test_GetBuildPath(self):
3039         """Test the GetBuildPath() method."""
3040         env = self.TestEnvironment(MAGIC = 'xyzzy')
3041
3042         p = env.GetBuildPath('foo')
3043         assert p == 'foo', p
3044
3045         p = env.GetBuildPath('$MAGIC')
3046         assert p == 'xyzzy', p
3047
3048     def test_Ignore(self):
3049         """Test the explicit Ignore method."""
3050         env = self.TestEnvironment(FOO='yyy', BAR='zzz')
3051         env.Dir('dir1')
3052         env.Dir('dir2')
3053         env.File('yyyzzz')
3054         env.File('zzzyyy')
3055
3056         t = env.Ignore(target='targ.py', dependency='dep.py')[0]
3057         assert t.__class__.__name__ == 'Entry', t.__class__.__name__
3058         assert t.path == 'targ.py'
3059         assert len(t.ignore) == 1
3060         i = t.ignore[0]
3061         assert i.__class__.__name__ == 'Entry', i.__class__.__name__
3062         assert i.path == 'dep.py'
3063
3064         t = env.Ignore(target='$FOO$BAR', dependency='$BAR$FOO')[0]
3065         assert t.__class__.__name__ == 'File', t.__class__.__name__
3066         assert t.path == 'yyyzzz'
3067         assert len(t.ignore) == 1
3068         i = t.ignore[0]
3069         assert i.__class__.__name__ == 'File', i.__class__.__name__
3070         assert i.path == 'zzzyyy'
3071
3072         t = env.Ignore(target='dir1', dependency='dir2')[0]
3073         assert t.__class__.__name__ == 'Dir', t.__class__.__name__
3074         assert t.path == 'dir1'
3075         assert len(t.ignore) == 1
3076         i = t.ignore[0]
3077         assert i.__class__.__name__ == 'Dir', i.__class__.__name__
3078         assert i.path == 'dir2'
3079
3080     def test_Literal(self):
3081         """Test the Literal() method"""
3082         env = self.TestEnvironment(FOO='fff', BAR='bbb')
3083         list = env.subst_list([env.Literal('$FOO'), '$BAR'])[0]
3084         assert list == ['$FOO', 'bbb'], list
3085         list = env.subst_list(['$FOO', env.Literal('$BAR')])[0]
3086         assert list == ['fff', '$BAR'], list
3087
3088     def test_Local(self):
3089         """Test the Local() method."""
3090         env = self.TestEnvironment(FOO='lll')
3091
3092         l = env.Local(env.fs.File('fff'))
3093         assert str(l[0]) == 'fff', l[0]
3094
3095         l = env.Local('ggg', '$FOO')
3096         assert str(l[0]) == 'ggg', l[0]
3097         assert str(l[1]) == 'lll', l[1]
3098
3099     def test_Precious(self):
3100         """Test the Precious() method"""
3101         env = self.TestEnvironment(FOO='ggg', BAR='hhh')
3102         env.Dir('p_hhhb')
3103         env.File('p_d')
3104         t = env.Precious('p_a', 'p_${BAR}b', ['p_c', 'p_d'], 'p_$FOO')
3105
3106         assert t[0].__class__.__name__ == 'Entry', t[0].__class__.__name__
3107         assert t[0].path == 'p_a'
3108         assert t[0].precious
3109         assert t[1].__class__.__name__ == 'Dir', t[1].__class__.__name__
3110         assert t[1].path == 'p_hhhb'
3111         assert t[1].precious
3112         assert t[2].__class__.__name__ == 'Entry', t[2].__class__.__name__
3113         assert t[2].path == 'p_c'
3114         assert t[2].precious
3115         assert t[3].__class__.__name__ == 'File', t[3].__class__.__name__
3116         assert t[3].path == 'p_d'
3117         assert t[3].precious
3118         assert t[4].__class__.__name__ == 'Entry', t[4].__class__.__name__
3119         assert t[4].path == 'p_ggg'
3120         assert t[4].precious
3121
3122     def test_Repository(self):
3123         """Test the Repository() method."""
3124         class MyFS:
3125             def __init__(self):
3126                 self.list = []
3127             def Repository(self, *dirs):
3128                 self.list.extend(list(dirs))
3129             def Dir(self, name):
3130                 return name
3131         env = self.TestEnvironment(FOO='rrr', BAR='sss')
3132         env.fs = MyFS()
3133         env.Repository('/tmp/foo')
3134         env.Repository('/tmp/$FOO', '/tmp/$BAR/foo')
3135         expect = ['/tmp/foo', '/tmp/rrr', '/tmp/sss/foo']
3136         assert env.fs.list == expect, env.fs.list
3137
3138     def test_Scanner(self):
3139         """Test the Scanner() method"""
3140         def scan(node, env, target, arg):
3141             pass
3142
3143         env = self.TestEnvironment(FOO = scan)
3144
3145         s = env.Scanner('foo')
3146         assert s is not None, s
3147
3148         s = env.Scanner(function = 'foo')
3149         assert s is not None, s
3150
3151         if 0:
3152             s = env.Scanner('$FOO')
3153             assert s is not None, s
3154
3155             s = env.Scanner(function = '$FOO')
3156             assert s is not None, s
3157
3158     def test_SConsignFile(self):
3159         """Test the SConsignFile() method"""
3160         import SCons.SConsign
3161
3162         class MyFS:
3163             SConstruct_dir = os.sep + 'dir'
3164
3165         env = self.TestEnvironment(FOO = 'SConsign',
3166                           BAR = os.path.join(os.sep, 'File'))
3167         env.fs = MyFS()
3168         env.Execute = lambda action: None
3169
3170         try:
3171             fnames = []
3172             dbms = []
3173             def capture(name, dbm_module, fnames=fnames, dbms=dbms):
3174                 fnames.append(name)
3175                 dbms.append(dbm_module)
3176
3177             save_SConsign_File = SCons.SConsign.File
3178             SCons.SConsign.File = capture
3179
3180             env.SConsignFile('foo')
3181             assert fnames[-1] == os.path.join(os.sep, 'dir', 'foo'), fnames
3182             assert dbms[-1] is None, dbms
3183
3184             env.SConsignFile('$FOO')
3185             assert fnames[-1] == os.path.join(os.sep, 'dir', 'SConsign'), fnames
3186             assert dbms[-1] is None, dbms
3187
3188             env.SConsignFile('/$FOO')
3189             assert fnames[-1] == os.sep + 'SConsign', fnames
3190             assert dbms[-1] is None, dbms
3191
3192             env.SConsignFile(os.sep + '$FOO')
3193             assert fnames[-1] == os.sep + 'SConsign', fnames
3194             assert dbms[-1] is None, dbms
3195
3196             env.SConsignFile('$BAR', 'x')
3197             assert fnames[-1] == os.path.join(os.sep, 'File'), fnames
3198             assert dbms[-1] == 'x', dbms
3199
3200             env.SConsignFile('__$BAR', 7)
3201             assert fnames[-1] == os.path.join(os.sep, 'dir', '__', 'File'), fnames
3202             assert dbms[-1] == 7, dbms
3203
3204             env.SConsignFile()
3205             assert fnames[-1] == os.path.join(os.sep, 'dir', '.sconsign'), fnames
3206             assert dbms[-1] is None, dbms
3207
3208             env.SConsignFile(None)
3209             assert fnames[-1] is None, fnames
3210             assert dbms[-1] is None, dbms
3211         finally:
3212             SCons.SConsign.File = save_SConsign_File
3213
3214     def test_SideEffect(self):
3215         """Test the SideEffect() method"""
3216         env = self.TestEnvironment(LIB='lll', FOO='fff', BAR='bbb')
3217         env.File('mylll.pdb')
3218         env.Dir('mymmm.pdb')
3219
3220         foo = env.Object('foo.obj', 'foo.cpp')[0]
3221         bar = env.Object('bar.obj', 'bar.cpp')[0]
3222         s = env.SideEffect('mylib.pdb', ['foo.obj', 'bar.obj'])[0]
3223         assert s.__class__.__name__ == 'Entry', s.__class__.__name__
3224         assert s.path == 'mylib.pdb'
3225         assert s.side_effect
3226         assert foo.side_effects == [s]
3227         assert bar.side_effects == [s]
3228
3229         fff = env.Object('fff.obj', 'fff.cpp')[0]
3230         bbb = env.Object('bbb.obj', 'bbb.cpp')[0]
3231         s = env.SideEffect('my${LIB}.pdb', ['${FOO}.obj', '${BAR}.obj'])[0]
3232         assert s.__class__.__name__ == 'File', s.__class__.__name__
3233         assert s.path == 'mylll.pdb'
3234         assert s.side_effect
3235         assert fff.side_effects == [s], fff.side_effects
3236         assert bbb.side_effects == [s], bbb.side_effects
3237
3238         ggg = env.Object('ggg.obj', 'ggg.cpp')[0]
3239         ccc = env.Object('ccc.obj', 'ccc.cpp')[0]
3240         s = env.SideEffect('mymmm.pdb', ['ggg.obj', 'ccc.obj'])[0]
3241         assert s.__class__.__name__ == 'Dir', s.__class__.__name__
3242         assert s.path == 'mymmm.pdb'
3243         assert s.side_effect
3244         assert ggg.side_effects == [s], ggg.side_effects
3245         assert ccc.side_effects == [s], ccc.side_effects
3246
3247     def test_SourceCode(self):
3248         """Test the SourceCode() method."""
3249         env = self.TestEnvironment(FOO='mmm', BAR='nnn')
3250         e = env.SourceCode('foo', None)[0]
3251         assert e.path == 'foo'
3252         s = e.src_builder()
3253         assert s is None, s
3254
3255         b = Builder()
3256         e = env.SourceCode(e, b)[0]
3257         assert e.path == 'foo'
3258         s = e.src_builder()
3259         assert s is b, s
3260
3261         e = env.SourceCode('$BAR$FOO', None)[0]
3262         assert e.path == 'nnnmmm'
3263         s = e.src_builder()
3264         assert s is None, s
3265
3266     def test_SourceSignatures(type):
3267         """Test the SourceSignatures() method"""
3268         import SCons.Errors
3269
3270         env = type.TestEnvironment(M = 'MD5', T = 'timestamp')
3271
3272         exc_caught = None
3273         try:
3274             env.SourceSignatures('invalid_type')
3275         except SCons.Errors.UserError:
3276             exc_caught = 1
3277         assert exc_caught, "did not catch expected UserError"
3278
3279         env.SourceSignatures('MD5')
3280         assert env.src_sig_type == 'MD5', env.src_sig_type
3281
3282         env.SourceSignatures('$M')
3283         assert env.src_sig_type == 'MD5', env.src_sig_type
3284
3285         env.SourceSignatures('timestamp')
3286         assert env.src_sig_type == 'timestamp', env.src_sig_type
3287
3288         env.SourceSignatures('$T')
3289         assert env.src_sig_type == 'timestamp', env.src_sig_type
3290
3291         try:
3292             import SCons.Util
3293             save_md5 = SCons.Util.md5
3294             SCons.Util.md5 = None
3295             try:
3296                 env.SourceSignatures('MD5')
3297             except SCons.Errors.UserError:
3298                 pass
3299             else:
3300                 self.fail('Did not catch expected UserError')
3301         finally:
3302             SCons.Util.md5 = save_md5
3303
3304     def test_Split(self):
3305         """Test the Split() method"""
3306         env = self.TestEnvironment(FOO='fff', BAR='bbb')
3307         s = env.Split("foo bar")
3308         assert s == ["foo", "bar"], s
3309         s = env.Split("$FOO bar")
3310         assert s == ["fff", "bar"], s
3311         s = env.Split(["foo", "bar"])
3312         assert s == ["foo", "bar"], s
3313         s = env.Split(["foo", "${BAR}-bbb"])
3314         assert s == ["foo", "bbb-bbb"], s
3315         s = env.Split("foo")
3316         assert s == ["foo"], s
3317         s = env.Split("$FOO$BAR")
3318         assert s == ["fffbbb"], s
3319
3320     def test_TargetSignatures(type):
3321         """Test the TargetSignatures() method"""
3322         import SCons.Errors
3323
3324         env = type.TestEnvironment(B = 'build', C = 'content')
3325
3326         exc_caught = None
3327         try:
3328             env.TargetSignatures('invalid_type')
3329         except SCons.Errors.UserError:
3330             exc_caught = 1
3331         assert exc_caught, "did not catch expected UserError"
3332         assert not hasattr(env, '_build_signature')
3333
3334         env.TargetSignatures('build')
3335         assert env.tgt_sig_type == 'build', env.tgt_sig_type
3336
3337         env.TargetSignatures('$B')
3338         assert env.tgt_sig_type == 'build', env.tgt_sig_type
3339
3340         env.TargetSignatures('content')
3341         assert env.tgt_sig_type == 'content', env.tgt_sig_type
3342
3343         env.TargetSignatures('$C')
3344         assert env.tgt_sig_type == 'content', env.tgt_sig_type
3345
3346         env.TargetSignatures('MD5')
3347         assert env.tgt_sig_type == 'MD5', env.tgt_sig_type
3348
3349         env.TargetSignatures('timestamp')
3350         assert env.tgt_sig_type == 'timestamp', env.tgt_sig_type
3351
3352         try:
3353             import SCons.Util
3354             save_md5 = SCons.Util.md5
3355             SCons.Util.md5 = None
3356             try:
3357                 env.TargetSignatures('MD5')
3358             except SCons.Errors.UserError:
3359                 pass
3360             else:
3361                 self.fail('Did not catch expected UserError')
3362             try:
3363                 env.TargetSignatures('content')
3364             except SCons.Errors.UserError:
3365                 pass
3366             else:
3367                 self.fail('Did not catch expected UserError')
3368         finally:
3369             SCons.Util.md5 = save_md5
3370
3371     def test_Value(self):
3372         """Test creating a Value() object
3373         """
3374         env = Environment()
3375         v1 = env.Value('a')
3376         assert v1.value == 'a', v1.value
3377
3378         value2 = 'a'
3379         v2 = env.Value(value2)
3380         assert v2.value == value2, v2.value
3381         assert v2.value is value2, v2.value
3382
3383         assert not v1 is v2
3384         assert v1.value == v2.value
3385
3386         v3 = env.Value('c', 'build-c')
3387         assert v3.value == 'c', v3.value
3388
3389
3390
3391     def test_Environment_global_variable(type):
3392         """Test setting Environment variable to an Environment.Base subclass"""
3393         class MyEnv(SCons.Environment.Base):
3394             def xxx(self, string):
3395                 return self.subst(string)
3396
3397         SCons.Environment.Environment = MyEnv
3398
3399         env = SCons.Environment.Environment(FOO = 'foo')
3400
3401         f = env.subst('$FOO')
3402         assert f == 'foo', f
3403
3404         f = env.xxx('$FOO')
3405         assert f == 'foo', f
3406
3407     def test_bad_keywords(self):
3408         """Test trying to use reserved keywords in an Environment"""
3409         added = []
3410
3411         env = self.TestEnvironment(TARGETS = 'targets',
3412                                    SOURCES = 'sources',
3413                                    SOURCE = 'source',
3414                                    TARGET = 'target',
3415                                    CHANGED_SOURCES = 'changed_sources',
3416                                    CHANGED_TARGETS = 'changed_targets',
3417                                    UNCHANGED_SOURCES = 'unchanged_sources',
3418                                    UNCHANGED_TARGETS = 'unchanged_targets',
3419                                    INIT = 'init')
3420         bad_msg = '%s is not reserved, but got omitted; see Environment.construction_var_name_ok'
3421         added.append('INIT')
3422         for x in self.reserved_variables:
3423             assert x not in env, env[x]
3424         for x in added:
3425             assert x in env, bad_msg % x
3426
3427         env.Append(TARGETS = 'targets',
3428                    SOURCES = 'sources',
3429                    SOURCE = 'source',
3430                    TARGET = 'target',
3431                    CHANGED_SOURCES = 'changed_sources',
3432                    CHANGED_TARGETS = 'changed_targets',
3433                    UNCHANGED_SOURCES = 'unchanged_sources',
3434                    UNCHANGED_TARGETS = 'unchanged_targets',
3435                    APPEND = 'append')
3436         added.append('APPEND')
3437         for x in self.reserved_variables:
3438             assert x not in env, env[x]
3439         for x in added:
3440             assert x in env, bad_msg % x
3441
3442         env.AppendUnique(TARGETS = 'targets',
3443                          SOURCES = 'sources',
3444                          SOURCE = 'source',
3445                          TARGET = 'target',
3446                          CHANGED_SOURCES = 'changed_sources',
3447                          CHANGED_TARGETS = 'changed_targets',
3448                          UNCHANGED_SOURCES = 'unchanged_sources',
3449                          UNCHANGED_TARGETS = 'unchanged_targets',
3450                          APPENDUNIQUE = 'appendunique')
3451         added.append('APPENDUNIQUE')
3452         for x in self.reserved_variables:
3453             assert x not in env, env[x]
3454         for x in added:
3455             assert x in env, bad_msg % x
3456
3457         env.Prepend(TARGETS = 'targets',
3458                     SOURCES = 'sources',
3459                     SOURCE = 'source',
3460                     TARGET = 'target',
3461                     CHANGED_SOURCES = 'changed_sources',
3462                     CHANGED_TARGETS = 'changed_targets',
3463                     UNCHANGED_SOURCES = 'unchanged_sources',
3464                     UNCHANGED_TARGETS = 'unchanged_targets',
3465                     PREPEND = 'prepend')
3466         added.append('PREPEND')
3467         for x in self.reserved_variables:
3468             assert x not in env, env[x]
3469         for x in added:
3470             assert x in env, bad_msg % x
3471
3472         env.Prepend(TARGETS = 'targets',
3473                     SOURCES = 'sources',
3474                     SOURCE = 'source',
3475                     TARGET = 'target',
3476                     CHANGED_SOURCES = 'changed_sources',
3477                     CHANGED_TARGETS = 'changed_targets',
3478                     UNCHANGED_SOURCES = 'unchanged_sources',
3479                     UNCHANGED_TARGETS = 'unchanged_targets',
3480                     PREPENDUNIQUE = 'prependunique')
3481         added.append('PREPENDUNIQUE')
3482         for x in self.reserved_variables:
3483             assert x not in env, env[x]
3484         for x in added:
3485             assert x in env, bad_msg % x
3486
3487         env.Replace(TARGETS = 'targets',
3488                     SOURCES = 'sources',
3489                     SOURCE = 'source',
3490                     TARGET = 'target',
3491                     CHANGED_SOURCES = 'changed_sources',
3492                     CHANGED_TARGETS = 'changed_targets',
3493                     UNCHANGED_SOURCES = 'unchanged_sources',
3494                     UNCHANGED_TARGETS = 'unchanged_targets',
3495                     REPLACE = 'replace')
3496         added.append('REPLACE')
3497         for x in self.reserved_variables:
3498             assert x not in env, env[x]
3499         for x in added:
3500             assert x in env, bad_msg % x
3501
3502         copy = env.Clone(TARGETS = 'targets',
3503                          SOURCES = 'sources',
3504                          SOURCE = 'source',
3505                          TARGET = 'target',
3506                          CHANGED_SOURCES = 'changed_sources',
3507                          CHANGED_TARGETS = 'changed_targets',
3508                          UNCHANGED_SOURCES = 'unchanged_sources',
3509                          UNCHANGED_TARGETS = 'unchanged_targets',
3510                          COPY = 'copy')
3511         for x in self.reserved_variables:
3512             assert x not in copy, env[x]
3513         for x in added + ['COPY']:
3514             assert x in copy, bad_msg % x
3515
3516         over = env.Override({'TARGETS' : 'targets',
3517                              'SOURCES' : 'sources',
3518                              'SOURCE' : 'source',
3519                              'TARGET' : 'target',
3520                              'CHANGED_SOURCES' : 'changed_sources',
3521                              'CHANGED_TARGETS' : 'changed_targets',
3522                              'UNCHANGED_SOURCES' : 'unchanged_sources',
3523                              'UNCHANGED_TARGETS' : 'unchanged_targets',
3524                              'OVERRIDE' : 'override'})
3525         for x in self.reserved_variables:
3526             assert x not in over, over[x]
3527         for x in added + ['OVERRIDE']:
3528             assert x in over, bad_msg % x
3529
3530     def test_parse_flags(self):
3531         '''Test the Base class parse_flags argument'''
3532         # all we have to show is that it gets to MergeFlags internally
3533         env = Environment(tools=[], parse_flags = '-X')
3534         assert env['CCFLAGS'] == ['-X'], env['CCFLAGS']
3535
3536         env = Environment(tools=[], CCFLAGS=None, parse_flags = '-Y')
3537         assert env['CCFLAGS'] == ['-Y'], env['CCFLAGS']
3538
3539         env = Environment(tools=[], CPPDEFINES = 'FOO', parse_flags = '-std=c99 -X -DBAR')
3540         assert env['CFLAGS']  == ['-std=c99'], env['CFLAGS']
3541         assert env['CCFLAGS'] == ['-X'], env['CCFLAGS']
3542         assert env['CPPDEFINES'] == ['FOO', 'BAR'], env['CPPDEFINES']
3543
3544     def test_clone_parse_flags(self):
3545         '''Test the env.Clone() parse_flags argument'''
3546         # all we have to show is that it gets to MergeFlags internally
3547         env = Environment(tools = [])
3548         env2 = env.Clone(parse_flags = '-X')
3549         assert 'CCFLAGS' not in env
3550         assert env2['CCFLAGS'] == ['-X'], env2['CCFLAGS']
3551
3552         env = Environment(tools = [], CCFLAGS=None)
3553         env2 = env.Clone(parse_flags = '-Y')
3554         assert env['CCFLAGS'] is None, env['CCFLAGS']
3555         assert env2['CCFLAGS'] == ['-Y'], env2['CCFLAGS']
3556
3557         env = Environment(tools = [], CPPDEFINES = 'FOO')
3558         env2 = env.Clone(parse_flags = '-std=c99 -X -DBAR')
3559         assert 'CFLAGS' not in env
3560         assert env2['CFLAGS']  == ['-std=c99'], env2['CFLAGS']
3561         assert 'CCFLAGS' not in env
3562         assert env2['CCFLAGS'] == ['-X'], env2['CCFLAGS']
3563         assert env['CPPDEFINES'] == 'FOO', env['CPPDEFINES']
3564         assert env2['CPPDEFINES'] == ['FOO','BAR'], env2['CPPDEFINES']
3565
3566
3567
3568 class OverrideEnvironmentTestCase(unittest.TestCase,TestEnvironmentFixture):
3569
3570     def setUp(self):
3571         env = Environment()
3572         env._dict = {'XXX' : 'x', 'YYY' : 'y'}
3573         env2 = OverrideEnvironment(env, {'XXX' : 'x2'})
3574         env3 = OverrideEnvironment(env2, {'XXX' : 'x3', 'YYY' : 'y3', 'ZZZ' : 'z3'})
3575         self.envs = [ env, env2, env3 ]
3576
3577     def checkpath(self, node, expect):
3578         return str(node) == os.path.normpath(expect)
3579
3580     def test___init__(self):
3581         """Test OverrideEnvironment initialization"""
3582         env, env2, env3 = self.envs
3583         assert env['XXX'] == 'x', env['XXX']
3584         assert env2['XXX'] == 'x2', env2['XXX']
3585         assert env3['XXX'] == 'x3', env3['XXX']
3586         assert env['YYY'] == 'y', env['YYY']
3587         assert env2['YYY'] == 'y', env2['YYY']
3588         assert env3['YYY'] == 'y3', env3['YYY']
3589
3590     def test___delitem__(self):
3591         """Test deleting variables from an OverrideEnvironment"""
3592         env, env2, env3 = self.envs
3593
3594         del env3['XXX']
3595         assert 'XXX' not in env, "env has XXX?"
3596         assert 'XXX' not in env2, "env2 has XXX?"
3597         assert 'XXX' not in env3, "env3 has XXX?"
3598
3599         del env3['YYY']
3600         assert 'YYY' not in env, "env has YYY?"
3601         assert 'YYY' not in env2, "env2 has YYY?"
3602         assert 'YYY' not in env3, "env3 has YYY?"
3603
3604         del env3['ZZZ']
3605         assert 'ZZZ' not in env, "env has ZZZ?"
3606         assert 'ZZZ' not in env2, "env2 has ZZZ?"
3607         assert 'ZZZ' not in env3, "env3 has ZZZ?"
3608
3609     def test_get(self):
3610         """Test the OverrideEnvironment get() method"""
3611         env, env2, env3 = self.envs
3612         assert env.get('XXX') == 'x', env.get('XXX')
3613         assert env2.get('XXX') == 'x2', env2.get('XXX')
3614         assert env3.get('XXX') == 'x3', env3.get('XXX')
3615         assert env.get('YYY') == 'y', env.get('YYY')
3616         assert env2.get('YYY') == 'y', env2.get('YYY')
3617         assert env3.get('YYY') == 'y3', env3.get('YYY')
3618         assert env.get('ZZZ') is None, env.get('ZZZ')
3619         assert env2.get('ZZZ') is None, env2.get('ZZZ')
3620         assert env3.get('ZZZ') == 'z3', env3.get('ZZZ')
3621
3622     def test_has_key(self):
3623         """Test the OverrideEnvironment has_key() method"""
3624         env, env2, env3 = self.envs
3625         assert 'XXX' in env, 'XXX' in env
3626         assert 'XXX' in env2, 'XXX' in env2
3627         assert 'XXX' in env3, 'XXX' in env3
3628         assert 'YYY' in env, 'YYY' in env
3629         assert 'YYY' in env2, 'YYY' in env2
3630         assert 'YYY' in env3, 'YYY' in env3
3631         assert 'ZZZ' not in env, 'ZZZ' in env
3632         assert 'ZZZ' not in env2, 'ZZZ' in env2
3633         assert 'ZZZ' in env3, 'ZZZ' in env3
3634
3635     def test_contains(self):
3636         """Test the OverrideEnvironment __contains__() method"""
3637         try:
3638             'x' in {'x':1}
3639         except TypeError:
3640             # TODO(1.5)
3641             # An early version of Python that doesn't support "in"
3642             # on dictionaries.  Just pass the test.
3643             pass
3644         else:
3645             env, env2, env3 = self.envs
3646             assert 'XXX' in env
3647             assert 'XXX' in env2
3648             assert 'XXX' in env3
3649             assert 'YYY' in env
3650             assert 'YYY' in env2
3651             assert 'YYY' in env3
3652             assert not 'ZZZ' in env
3653             assert not 'ZZZ' in env2
3654             assert 'ZZZ' in env3
3655
3656     def test_items(self):
3657         """Test the OverrideEnvironment Dictionary() method"""
3658         env, env2, env3 = self.envs
3659         items = env.Dictionary()
3660         assert items == {'XXX' : 'x', 'YYY' : 'y'}, items
3661         items = env2.Dictionary()
3662         assert items == {'XXX' : 'x2', 'YYY' : 'y'}, items
3663         items = env3.Dictionary()
3664         assert items == {'XXX' : 'x3', 'YYY' : 'y3', 'ZZZ' : 'z3'}, items
3665
3666     def test_items(self):
3667         """Test the OverrideEnvironment items() method"""
3668         env, env2, env3 = self.envs
3669         items = sorted(env.items())
3670         assert items == [('XXX', 'x'), ('YYY', 'y')], items
3671         items = sorted(env2.items())
3672         assert items == [('XXX', 'x2'), ('YYY', 'y')], items
3673         items = sorted(env3.items())
3674         assert items == [('XXX', 'x3'), ('YYY', 'y3'), ('ZZZ', 'z3')], items
3675
3676     def test_gvars(self):
3677         """Test the OverrideEnvironment gvars() method"""
3678         env, env2, env3 = self.envs
3679         gvars = env.gvars()
3680         assert gvars == {'XXX' : 'x', 'YYY' : 'y'}, gvars
3681         gvars = env2.gvars()
3682         assert gvars == {'XXX' : 'x', 'YYY' : 'y'}, gvars
3683         gvars = env3.gvars()
3684         assert gvars == {'XXX' : 'x', 'YYY' : 'y'}, gvars
3685
3686     def test_lvars(self):
3687         """Test the OverrideEnvironment lvars() method"""
3688         env, env2, env3 = self.envs
3689         lvars = env.lvars()
3690         assert lvars == {}, lvars
3691         lvars = env2.lvars()
3692         assert lvars == {'XXX' : 'x2'}, lvars
3693         lvars = env3.lvars()
3694         assert lvars == {'XXX' : 'x3', 'YYY' : 'y3', 'ZZZ' : 'z3'}, lvars
3695
3696     def test_Replace(self):
3697         """Test the OverrideEnvironment Replace() method"""
3698         env, env2, env3 = self.envs
3699         assert env['XXX'] == 'x', env['XXX']
3700         assert env2['XXX'] == 'x2', env2['XXX']
3701         assert env3['XXX'] == 'x3', env3['XXX']
3702         assert env['YYY'] == 'y', env['YYY']
3703         assert env2['YYY'] == 'y', env2['YYY']
3704         assert env3['YYY'] == 'y3', env3['YYY']
3705
3706         env.Replace(YYY = 'y4')
3707
3708         assert env['XXX'] == 'x', env['XXX']
3709         assert env2['XXX'] == 'x2', env2['XXX']
3710         assert env3['XXX'] == 'x3', env3['XXX']
3711         assert env['YYY'] == 'y4', env['YYY']
3712         assert env2['YYY'] == 'y4', env2['YYY']
3713         assert env3['YYY'] == 'y3', env3['YYY']
3714
3715     # Tests a number of Base methods through an OverrideEnvironment to
3716     # make sure they handle overridden constructionv variables properly.
3717     #
3718     # The following Base methods also call self.subst(), and so could
3719     # theoretically be subject to problems with evaluating overridden
3720     # variables, but they're never really called that way in the rest
3721     # of our code, so we won't worry about them (at least for now):
3722     #
3723     # ParseConfig()
3724     # ParseDepends()
3725     # Platform()
3726     # Tool()
3727     #
3728     # Action()
3729     # Alias()
3730     # Builder()
3731     # CacheDir()
3732     # Configure()
3733     # Environment()
3734     # FindFile()
3735     # Scanner()
3736     # SourceSignatures()
3737     # TargetSignatures()
3738
3739     # It's unlikely Clone() will ever be called this way, so let the
3740     # other methods test that handling overridden values works.
3741     #def test_Clone(self):
3742     #    """Test the OverrideEnvironment Clone() method"""
3743     #    pass
3744
3745     def test_FindIxes(self):
3746         """Test the OverrideEnvironment FindIxes() method"""
3747         env, env2, env3 = self.envs
3748         x = env.FindIxes(['xaaay'], 'XXX', 'YYY')
3749         assert x == 'xaaay', x
3750         x = env2.FindIxes(['x2aaay'], 'XXX', 'YYY')
3751         assert x == 'x2aaay', x
3752         x = env3.FindIxes(['x3aaay3'], 'XXX', 'YYY')
3753         assert x == 'x3aaay3', x
3754
3755     def test_ReplaceIxes(self):
3756         """Test the OverrideEnvironment ReplaceIxes() method"""
3757         env, env2, env3 = self.envs
3758         x = env.ReplaceIxes('xaaay', 'XXX', 'YYY', 'YYY', 'XXX')
3759         assert x == 'yaaax', x
3760         x = env2.ReplaceIxes('x2aaay', 'XXX', 'YYY', 'YYY', 'XXX')
3761         assert x == 'yaaax2', x
3762         x = env3.ReplaceIxes('x3aaay3', 'XXX', 'YYY', 'YYY', 'XXX')
3763         assert x == 'y3aaax3', x
3764
3765     # It's unlikely WhereIs() will ever be called this way, so let the
3766     # other methods test that handling overridden values works.
3767     #def test_WhereIs(self):
3768     #    """Test the OverrideEnvironment WhereIs() method"""
3769     #    pass
3770
3771     def test_Dir(self):
3772         """Test the OverrideEnvironment Dir() method"""
3773         env, env2, env3 = self.envs
3774         x = env.Dir('ddir/$XXX')
3775         assert self.checkpath(x, 'ddir/x'), str(x)
3776         x = env2.Dir('ddir/$XXX')
3777         assert self.checkpath(x, 'ddir/x2'), str(x)
3778         x = env3.Dir('ddir/$XXX')
3779         assert self.checkpath(x, 'ddir/x3'), str(x)
3780
3781     def test_Entry(self):
3782         """Test the OverrideEnvironment Entry() method"""
3783         env, env2, env3 = self.envs
3784         x = env.Entry('edir/$XXX')
3785         assert self.checkpath(x, 'edir/x'), str(x)
3786         x = env2.Entry('edir/$XXX')
3787         assert self.checkpath(x, 'edir/x2'), str(x)
3788         x = env3.Entry('edir/$XXX')
3789         assert self.checkpath(x, 'edir/x3'), str(x)
3790
3791     def test_File(self):
3792         """Test the OverrideEnvironment File() method"""
3793         env, env2, env3 = self.envs
3794         x = env.File('fdir/$XXX')
3795         assert self.checkpath(x, 'fdir/x'), str(x)
3796         x = env2.File('fdir/$XXX')
3797         assert self.checkpath(x, 'fdir/x2'), str(x)
3798         x = env3.File('fdir/$XXX')
3799         assert self.checkpath(x, 'fdir/x3'), str(x)
3800
3801     def test_Split(self):
3802         """Test the OverrideEnvironment Split() method"""
3803         env, env2, env3 = self.envs
3804         env['AAA'] = '$XXX $YYY $ZZZ'
3805         x = env.Split('$AAA')
3806         assert x == ['x', 'y'], x
3807         x = env2.Split('$AAA')
3808         assert x == ['x2', 'y'], x
3809         x = env3.Split('$AAA')
3810         assert x == ['x3', 'y3', 'z3'], x
3811
3812     def test_parse_flags(self):
3813         '''Test the OverrideEnvironment parse_flags argument'''
3814         # all we have to show is that it gets to MergeFlags internally
3815         env = SubstitutionEnvironment()
3816         env2 = env.Override({'parse_flags' : '-X'})
3817         assert 'CCFLAGS' not in env
3818         assert env2['CCFLAGS'] == ['-X'], env2['CCFLAGS']
3819
3820         env = SubstitutionEnvironment(CCFLAGS=None)
3821         env2 = env.Override({'parse_flags' : '-Y'})
3822         assert env['CCFLAGS'] is None, env['CCFLAGS']
3823         assert env2['CCFLAGS'] == ['-Y'], env2['CCFLAGS']
3824
3825         env = SubstitutionEnvironment(CPPDEFINES = 'FOO')
3826         env2 = env.Override({'parse_flags' : '-std=c99 -X -DBAR'})
3827         assert 'CFLAGS' not in env
3828         assert env2['CFLAGS']  == ['-std=c99'], env2['CFLAGS']
3829         assert 'CCFLAGS' not in env
3830         assert env2['CCFLAGS'] == ['-X'], env2['CCFLAGS']
3831         assert env['CPPDEFINES'] == 'FOO', env['CPPDEFINES']
3832         assert env2['CPPDEFINES'] == ['FOO','BAR'], env2['CPPDEFINES']
3833
3834
3835
3836 class NoSubstitutionProxyTestCase(unittest.TestCase,TestEnvironmentFixture):
3837
3838     def test___init__(self):
3839         """Test NoSubstitutionProxy initialization"""
3840         env = self.TestEnvironment(XXX = 'x', YYY = 'y')
3841         assert env['XXX'] == 'x', env['XXX']
3842         assert env['YYY'] == 'y', env['YYY']
3843
3844         proxy = NoSubstitutionProxy(env)
3845         assert proxy['XXX'] == 'x', proxy['XXX']
3846         assert proxy['YYY'] == 'y', proxy['YYY']
3847
3848     def test_attributes(self):
3849         """Test getting and setting NoSubstitutionProxy attributes"""
3850         env = Environment()
3851         setattr(env, 'env_attr', 'value1')
3852
3853         proxy = NoSubstitutionProxy(env)
3854         setattr(proxy, 'proxy_attr', 'value2')
3855
3856         x = getattr(env, 'env_attr')
3857         assert x == 'value1', x
3858         x = getattr(proxy, 'env_attr')
3859         assert x == 'value1', x
3860
3861         x = getattr(env, 'proxy_attr')
3862         assert x == 'value2', x
3863         x = getattr(proxy, 'proxy_attr')
3864         assert x == 'value2', x
3865
3866     def test_subst(self):
3867         """Test the NoSubstitutionProxy.subst() method"""
3868         env = self.TestEnvironment(XXX = 'x', YYY = 'y')
3869         assert env['XXX'] == 'x', env['XXX']
3870         assert env['YYY'] == 'y', env['YYY']
3871
3872         proxy = NoSubstitutionProxy(env)
3873         assert proxy['XXX'] == 'x', proxy['XXX']
3874         assert proxy['YYY'] == 'y', proxy['YYY']
3875
3876         x = env.subst('$XXX')
3877         assert x == 'x', x
3878         x = proxy.subst('$XXX')
3879         assert x == '$XXX', x
3880
3881         x = proxy.subst('$YYY', raw=7, target=None, source=None,
3882                         conv=None,
3883                         extra_meaningless_keyword_argument=None)
3884         assert x == '$YYY', x
3885
3886     def test_subst_kw(self):
3887         """Test the NoSubstitutionProxy.subst_kw() method"""
3888         env = self.TestEnvironment(XXX = 'x', YYY = 'y')
3889         assert env['XXX'] == 'x', env['XXX']
3890         assert env['YYY'] == 'y', env['YYY']
3891
3892         proxy = NoSubstitutionProxy(env)
3893         assert proxy['XXX'] == 'x', proxy['XXX']
3894         assert proxy['YYY'] == 'y', proxy['YYY']
3895
3896         x = env.subst_kw({'$XXX':'$YYY'})
3897         assert x == {'x':'y'}, x
3898         x = proxy.subst_kw({'$XXX':'$YYY'})
3899         assert x == {'$XXX':'$YYY'}, x
3900
3901     def test_subst_list(self):
3902         """Test the NoSubstitutionProxy.subst_list() method"""
3903         env = self.TestEnvironment(XXX = 'x', YYY = 'y')
3904         assert env['XXX'] == 'x', env['XXX']
3905         assert env['YYY'] == 'y', env['YYY']
3906
3907         proxy = NoSubstitutionProxy(env)
3908         assert proxy['XXX'] == 'x', proxy['XXX']
3909         assert proxy['YYY'] == 'y', proxy['YYY']
3910
3911         x = env.subst_list('$XXX')
3912         assert x == [['x']], x
3913         x = proxy.subst_list('$XXX')
3914         assert x == [[]], x
3915
3916         x = proxy.subst_list('$YYY', raw=0, target=None, source=None, conv=None)
3917         assert x == [[]], x
3918
3919     def test_subst_target_source(self):
3920         """Test the NoSubstitutionProxy.subst_target_source() method"""
3921         env = self.TestEnvironment(XXX = 'x', YYY = 'y')
3922         assert env['XXX'] == 'x', env['XXX']
3923         assert env['YYY'] == 'y', env['YYY']
3924
3925         proxy = NoSubstitutionProxy(env)
3926         assert proxy['XXX'] == 'x', proxy['XXX']
3927         assert proxy['YYY'] == 'y', proxy['YYY']
3928
3929         args = ('$XXX $TARGET $SOURCE $YYY',)
3930         kw = {'target' : DummyNode('ttt'), 'source' : DummyNode('sss')}
3931         x = env.subst_target_source(*args, **kw)
3932         assert x == 'x ttt sss y', x
3933         x = proxy.subst_target_source(*args, **kw)
3934         assert x == ' ttt sss ', x
3935
3936 class EnvironmentVariableTestCase(unittest.TestCase):
3937
3938     def test_is_valid_construction_var(self):
3939         """Testing is_valid_construction_var()"""
3940         r = is_valid_construction_var("_a")
3941         assert r is not None, r
3942         r = is_valid_construction_var("z_")
3943         assert r is not None, r
3944         r = is_valid_construction_var("X_")
3945         assert r is not None, r
3946         r = is_valid_construction_var("2a")
3947         assert r is None, r
3948         r = is_valid_construction_var("a2_")
3949         assert r is not None, r
3950         r = is_valid_construction_var("/")
3951         assert r is None, r
3952         r = is_valid_construction_var("_/")
3953         assert r is None, r
3954         r = is_valid_construction_var("a/")
3955         assert r is None, r
3956         r = is_valid_construction_var(".b")
3957         assert r is None, r
3958         r = is_valid_construction_var("_.b")
3959         assert r is None, r
3960         r = is_valid_construction_var("b1._")
3961         assert r is None, r
3962         r = is_valid_construction_var("-b")
3963         assert r is None, r
3964         r = is_valid_construction_var("_-b")
3965         assert r is None, r
3966         r = is_valid_construction_var("b1-_")
3967         assert r is None, r
3968
3969
3970
3971 if __name__ == "__main__":
3972     suite = unittest.TestSuite()
3973     tclasses = [ SubstitutionTestCase,
3974                  BaseTestCase,
3975                  OverrideEnvironmentTestCase,
3976                  NoSubstitutionProxyTestCase,
3977                  EnvironmentVariableTestCase ]
3978     for tclass in tclasses:
3979         names = unittest.getTestCaseNames(tclass, 'test_')
3980         suite.addTests(list(map(tclass, names)))
3981     if not unittest.TextTestRunner().run(suite).wasSuccessful():
3982         sys.exit(1)
3983
3984 # Local Variables:
3985 # tab-width:4
3986 # indent-tabs-mode:nil
3987 # End:
3988 # vim: set expandtab tabstop=4 shiftwidth=4: