Add BuildDir(), Export(), and Install() functionality (courtesy Charles Crain).
[scons.git] / src / engine / SCons / EnvironmentTests.py
1 #
2 # Copyright (c) 2001 Steven Knight
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 sys
27 import unittest
28
29 from SCons.Environment import *
30
31
32
33 built_it = {}
34
35 class Builder:
36     """A dummy Builder class for testing purposes.  "Building"
37     a target is simply setting a value in the dictionary.
38     """
39     def __init__(self, name = None):
40         self.name = name
41
42     def execute(self, target = None, **kw):
43         built_it[target] = 1
44
45
46
47 scanned_it = {}
48
49 class Scanner:
50     """A dummy Scanner class for testing purposes.  "Scanning"
51     a target is simply setting a value in the dictionary.
52     """
53     def __init__(self, name, skeys=[]):
54         self.name = name
55         self.skeys = skeys
56
57     def scan(self, filename):
58         scanned_it[filename] = 1
59
60     def __cmp__(self, other):
61         return cmp(self.__dict__, other.__dict__)
62
63
64
65 class EnvironmentTestCase(unittest.TestCase):
66
67     def test_Builders(self):
68         """Test Builder execution through different environments
69
70         One environment is initialized with a single
71         Builder object, one with a list of a single Builder
72         object, and one with a list of two Builder objects.
73         """
74         global built_it
75
76         b1 = Builder(name = 'builder1')
77         b2 = Builder(name = 'builder2')
78
79         built_it = {}
80         env1 = Environment(BUILDERS = b1)
81         env1.builder1.execute(target = 'out1')
82         assert built_it['out1']
83
84         built_it = {}
85         env2 = Environment(BUILDERS = [b1])
86         env1.builder1.execute(target = 'out1')
87         assert built_it['out1']
88
89         built_it = {}
90         env3 = Environment()
91         env3.Update(BUILDERS = [b1, b2])
92         env3.builder1.execute(target = 'out1')
93         env3.builder2.execute(target = 'out2')
94         env3.builder1.execute(target = 'out3')
95         assert built_it['out1']
96         assert built_it['out2']
97         assert built_it['out3']
98
99         env4 = env3.Copy()
100         assert env4.builder1.env is env4
101         assert env4.builder2.env is env4
102
103     def test_Scanners(self):
104         """Test Scanner execution through different environments
105
106         One environment is initialized with a single
107         Scanner object, one with a list of a single Scanner
108         object, and one with a list of two Scanner objects.
109         """
110         global scanned_it
111
112         s1 = Scanner(name = 'scanner1', skeys = [".c", ".cc"])
113         s2 = Scanner(name = 'scanner2', skeys = [".m4"])
114
115         scanned_it = {}
116         env1 = Environment(SCANNERS = s1)
117         env1.scanner1.scan(filename = 'out1')
118         assert scanned_it['out1']
119
120         scanned_it = {}
121         env2 = Environment(SCANNERS = [s1])
122         env1.scanner1.scan(filename = 'out1')
123         assert scanned_it['out1']
124
125         scanned_it = {}
126         env3 = Environment()
127         env3.Update(SCANNERS = [s1, s2])
128         env3.scanner1.scan(filename = 'out1')
129         env3.scanner2.scan(filename = 'out2')
130         env3.scanner1.scan(filename = 'out3')
131         assert scanned_it['out1']
132         assert scanned_it['out2']
133         assert scanned_it['out3']
134
135         s = env3.get_scanner(".c")
136         assert s == s1, s
137         s = env3.get_scanner(skey=".m4")
138         assert s == s2, s
139         s = env3.get_scanner(".cxx")
140         assert s == None, s
141
142     def test_Copy(self):
143         """Test construction Environment copying
144
145         Update the copy independently afterwards and check that
146         the original remains intact (that is, no dangling
147         references point to objects in the copied environment).
148         Copy the original with some construction variable
149         updates and check that the original remains intact
150         and the copy has the updated values.
151         """
152         env1 = Environment(XXX = 'x', YYY = 'y')
153         env2 = env1.Copy()
154         env1copy = env1.Copy()
155         env2.Update(YYY = 'yyy')
156         assert env1 != env2
157         assert env1 == env1copy
158
159         env3 = env1.Copy(XXX = 'x3', ZZZ = 'z3')
160         assert env3.Dictionary('XXX') == 'x3'
161         assert env3.Dictionary('YYY') == 'y'
162         assert env3.Dictionary('ZZZ') == 'z3'
163         assert env1 == env1copy
164
165         # Ensure that lists and dictionaries are
166         # deep copied, but not instances.
167         class TestA:
168             pass
169         env1 = Environment(XXX=TestA(), YYY = [ 1, 2, 3 ],
170                            ZZZ = { 1:2, 3:4 })
171         env2=env1.Copy()
172         env2.Dictionary('YYY').append(4)
173         env2.Dictionary('ZZZ')[5] = 6
174         assert env1.Dictionary('XXX') is env2.Dictionary('XXX')
175         assert 4 in env2.Dictionary('YYY')
176         assert not 4 in env1.Dictionary('YYY')
177         assert env2.Dictionary('ZZZ').has_key(5)
178         assert not env1.Dictionary('ZZZ').has_key(5)
179
180     def test_Dictionary(self):
181         """Test retrieval of known construction variables
182
183         Fetch them from the Dictionary and check for well-known
184         defaults that get inserted.
185         """
186         env = Environment(XXX = 'x', YYY = 'y', ZZZ = 'z')
187         assert env.Dictionary('XXX') == 'x'
188         assert env.Dictionary('YYY') == 'y'
189         assert env.Dictionary('XXX', 'ZZZ') == ['x', 'z']
190         xxx, zzz = env.Dictionary('XXX', 'ZZZ')
191         assert xxx == 'x'
192         assert zzz == 'z'
193         assert env.Dictionary().has_key('BUILDERS')
194         assert env.Dictionary().has_key('CC')
195         assert env.Dictionary().has_key('CCFLAGS')
196         assert env.Dictionary().has_key('ENV')
197
198     def test_ENV(self):
199         """Test setting the external ENV in Environments
200         """
201         env = Environment()
202         assert env.Dictionary().has_key('ENV')
203
204         env = Environment(ENV = { 'PATH' : '/foo:/bar' })
205         assert env.Dictionary('ENV')['PATH'] == '/foo:/bar'
206
207     def test_Environment(self):
208         """Test construction Environments creation
209         
210         Create two with identical arguments and check that
211         they compare the same.
212         """
213         env1 = Environment(XXX = 'x', YYY = 'y')
214         env2 = Environment(XXX = 'x', YYY = 'y')
215         assert env1 == env2
216
217     def test_Install(self):
218         """Test Install method"""
219         env=Environment()
220         tgt = env.Install('export', [ 'build/foo1', 'build/foo2' ])
221         paths = map(str, tgt)
222         paths.sort()
223         assert paths == [ 'export/foo1', 'export/foo2' ], paths
224         for tnode in tgt:
225             assert tnode.builder == InstallBuilder
226
227     def test_InstallAs(self):
228         pass    # XXX
229
230     def test_Update(self):
231         """Test updating an Environment with new construction variables
232
233         After creation of the Environment, of course.
234         """
235         env1 = Environment(AAA = 'a', BBB = 'b')
236         env1.Update(BBB = 'bbb', CCC = 'ccc')
237         env2 = Environment(AAA = 'a', BBB = 'bbb', CCC = 'c')
238         assert env1 != env2
239
240     def test_Depends(self):
241         """Test the explicit Depends method."""
242         env = Environment()
243         t = env.Depends(target='EnvironmentTest.py', dependency='Environment.py')
244         assert t.__class__.__name__ == 'File'
245         assert t.path == 'EnvironmentTest.py'
246         assert len(t.depends) == 1
247         d = t.depends[0]
248         assert d.__class__.__name__ == 'File'
249         assert d.path == 'Environment.py'
250
251     def test_Command(self):
252         """Test the Command() method."""
253         env = Environment()
254         t = env.Command(target='foo.out', source=['foo1.in', 'foo2.in'],
255                         action='buildfoo $target $source')
256         assert t.builder
257         assert t.builder.action.__class__.__name__ == 'CommandAction'
258         assert t.builder.action.command == 'buildfoo $target $source'
259         assert 'foo1.in' in map(lambda x: x.path, t.sources)
260         assert 'foo2.in' in map(lambda x: x.path, t.sources)
261
262         def testFunc(env, target, source):
263             assert target == 'foo.out'
264             assert 'foo1.in' in source and 'foo2.in' in source, source
265             return 0
266         t = env.Command(target='foo.out', source=['foo1.in','foo2.in'],
267                         action=testFunc)
268         assert t.builder
269         assert t.builder.action.__class__.__name__ == 'FunctionAction'
270         t.build()
271         assert 'foo1.in' in map(lambda x: x.path, t.sources)
272         assert 'foo2.in' in map(lambda x: x.path, t.sources)
273
274     def test_subst(self):
275         """Test substituting construction variables within strings
276         
277         Check various combinations, including recursive expansion
278         of variables into other variables.
279         """
280         env = Environment(AAA = 'a', BBB = 'b')
281         str = env.subst("$AAA ${AAA}A $BBBB $BBB")
282         assert str == "a aA b", str
283         env = Environment(AAA = '$BBB', BBB = 'b', BBBA = 'foo')
284         str = env.subst("$AAA ${AAA}A ${AAA}B $BBB")
285         assert str == "b foo b", str
286         env = Environment(AAA = '$BBB', BBB = '$CCC', CCC = 'c')
287         str = env.subst("$AAA ${AAA}A ${AAA}B $BBB")
288         assert str == "c c", str
289
290
291
292 if __name__ == "__main__":
293     suite = unittest.makeSuite(EnvironmentTestCase, 'test_')
294     if not unittest.TextTestRunner().run(suite).wasSuccessful():
295         sys.exit(1)