0b60fb9617944e29fcc6586e5ea9622fb79a9c91
[scons.git] / src / engine / SCons / Node / FSTests.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 os
27 import os.path
28 import string
29 import sys
30 import time
31 import unittest
32 from TestCmd import TestCmd
33 import shutil
34 import stat
35
36 import SCons.Errors
37 import SCons.Node.FS
38 import SCons.Warnings
39
40 built_it = None
41
42 # This will be built-in in 2.3.  For now fake it.
43 try :
44     True , False
45 except NameError :
46     True = 1 ; False = 0
47
48
49 scanner_count = 0
50
51 class Scanner:
52     def __init__(self, node=None):
53         global scanner_count
54         scanner_count = scanner_count + 1
55         self.hash = scanner_count
56         self.node = node
57     def path(self, env, dir, target=None, source=None):
58         return ()
59     def __call__(self, node, env, path):
60         return [self.node]
61     def __hash__(self):
62         return self.hash
63     def select(self, node):
64         return self
65     def recurse_nodes(self, nodes):
66         return nodes
67
68 class Environment:
69     def __init__(self):
70         self.scanner = Scanner()
71     def Dictionary(self, *args):
72         return {}
73     def autogenerate(self, **kw):
74         return {}
75     def get_scanner(self, skey):
76         return self.scanner
77     def Override(self, overrides):
78         return self
79     def _update(self, dict):
80         pass
81
82 class Action:
83     def __call__(self, targets, sources, env, errfunc, **kw):
84         global built_it
85         if kw.get('execute', 1):
86             built_it = 1
87         return 0
88     def show(self, string):
89         pass
90     def strfunction(self, targets, sources, env):
91         return ""
92 class Builder:
93     def __init__(self, factory, action=Action()):
94         self.factory = factory
95         self.env = Environment()
96         self.overrides = {}
97         self.action = action
98         self.target_scanner = None
99         self.source_scanner = None
100
101     def targets(self, t):
102         return [t]
103
104     def source_factory(self, name):
105         return self.factory(name)
106
107 class _tempdirTestCase(unittest.TestCase):
108     def setUp(self):
109         self.save_cwd = os.getcwd()
110         self.test = TestCmd(workdir='')
111         # FS doesn't like the cwd to be something other than its root.
112         os.chdir(self.test.workpath(""))
113         self.fs = SCons.Node.FS.FS()
114
115     def tearDown(self):
116         os.chdir(self.save_cwd)
117
118 class BuildDirTestCase(unittest.TestCase):
119     def runTest(self):
120         """Test build dir functionality"""
121         test=TestCmd(workdir='')
122
123         fs = SCons.Node.FS.FS()
124         f1 = fs.File('build/test1')
125         fs.BuildDir('build', 'src')
126         f2 = fs.File('build/test2')
127         d1 = fs.Dir('build')
128         assert f1.srcnode().path == os.path.normpath('src/test1'), f1.srcnode().path
129         assert f2.srcnode().path == os.path.normpath('src/test2'), f2.srcnode().path
130         assert d1.srcnode().path == 'src', d1.srcnode().path
131
132         fs = SCons.Node.FS.FS()
133         f1 = fs.File('build/test1')
134         fs.BuildDir('build', '.')
135         f2 = fs.File('build/test2')
136         d1 = fs.Dir('build')
137         assert f1.srcnode().path == 'test1', f1.srcnode().path
138         assert f2.srcnode().path == 'test2', f2.srcnode().path
139         assert d1.srcnode().path == '.', d1.srcnode().path
140
141         fs = SCons.Node.FS.FS()
142         fs.BuildDir('build/var1', 'src')
143         fs.BuildDir('build/var2', 'src')
144         f1 = fs.File('build/var1/test1')
145         f2 = fs.File('build/var2/test1')
146         assert f1.srcnode().path == os.path.normpath('src/test1'), f1.srcnode().path
147         assert f2.srcnode().path == os.path.normpath('src/test1'), f2.srcnode().path
148
149         fs = SCons.Node.FS.FS()
150         fs.BuildDir('../var1', 'src')
151         fs.BuildDir('../var2', 'src')
152         f1 = fs.File('../var1/test1')
153         f2 = fs.File('../var2/test1')
154         assert f1.srcnode().path == os.path.normpath('src/test1'), f1.srcnode().path
155         assert f2.srcnode().path == os.path.normpath('src/test1'), f2.srcnode().path
156
157         # Set up some files
158         test.subdir('work', ['work', 'src'])
159         test.subdir(['work', 'build'], ['work', 'build', 'var1'])
160         test.subdir(['work', 'build', 'var2'])
161         test.subdir('rep1', ['rep1', 'src'])
162         test.subdir(['rep1', 'build'], ['rep1', 'build', 'var1'])
163         test.subdir(['rep1', 'build', 'var2'])
164
165         # A source file in the source directory
166         test.write([ 'work', 'src', 'test.in' ], 'test.in')
167
168         # A source file in a subdir of the source directory
169         test.subdir([ 'work', 'src', 'new_dir' ])
170         test.write([ 'work', 'src', 'new_dir', 'test9.out' ], 'test9.out\n')
171
172         # A source file in the repository
173         test.write([ 'rep1', 'src', 'test2.in' ], 'test2.in')
174
175         # Some source files in the build directory
176         test.write([ 'work', 'build', 'var2', 'test.in' ], 'test.old')
177         test.write([ 'work', 'build', 'var2', 'test2.in' ], 'test2.old')
178
179         # An old derived file in the build directories
180         test.write([ 'work', 'build', 'var1', 'test.out' ], 'test.old')
181         test.write([ 'work', 'build', 'var2', 'test.out' ], 'test.old')
182
183         # And just in case we are weird, a derived file in the source
184         # dir.
185         test.write([ 'work', 'src', 'test.out' ], 'test.out.src')
186
187         # A derived file in the repository
188         test.write([ 'rep1', 'build', 'var1', 'test2.out' ], 'test2.out_rep')
189         test.write([ 'rep1', 'build', 'var2', 'test2.out' ], 'test2.out_rep')
190
191         os.chdir(test.workpath('work'))
192
193         fs = SCons.Node.FS.FS(test.workpath('work'))
194         fs.BuildDir('build/var1', 'src', duplicate=0)
195         fs.BuildDir('build/var2', 'src')
196         f1 = fs.File('build/var1/test.in')
197         f1out = fs.File('build/var1/test.out')
198         f1out.builder = 1
199         f1out_2 = fs.File('build/var1/test2.out')
200         f1out_2.builder = 1
201         f2 = fs.File('build/var2/test.in')
202         f2out = fs.File('build/var2/test.out')
203         f2out.builder = 1
204         f2out_2 = fs.File('build/var2/test2.out')
205         f2out_2.builder = 1
206         fs.Repository(test.workpath('rep1'))
207
208         assert f1.srcnode().path == os.path.normpath('src/test.in'),\
209                f1.srcnode().path
210         # str(node) returns source path for duplicate = 0
211         assert str(f1) == os.path.normpath('src/test.in'), str(f1)
212         # Build path does not exist
213         assert not f1.exists()
214         # ...but the actual file is not there...
215         assert not os.path.exists(f1.get_abspath())
216         # And duplicate=0 should also work just like a Repository
217         assert f1.rexists()
218         # rfile() should point to the source path
219         assert f1.rfile().path == os.path.normpath('src/test.in'),\
220                f1.rfile().path
221
222         assert f2.srcnode().path == os.path.normpath('src/test.in'),\
223                f2.srcnode().path
224         # str(node) returns build path for duplicate = 1
225         assert str(f2) == os.path.normpath('build/var2/test.in'), str(f2)
226         # Build path exists
227         assert f2.exists()
228         # ...and exists() should copy the file from src to build path
229         assert test.read(['work', 'build', 'var2', 'test.in']) == 'test.in',\
230                test.read(['work', 'build', 'var2', 'test.in'])
231         # Since exists() is true, so should rexists() be
232         assert f2.rexists()
233
234         f3 = fs.File('build/var1/test2.in')
235         f4 = fs.File('build/var2/test2.in')
236
237         assert f3.srcnode().path == os.path.normpath('src/test2.in'),\
238                f3.srcnode().path
239         # str(node) returns source path for duplicate = 0
240         assert str(f3) == os.path.normpath('src/test2.in'), str(f3)
241         # Build path does not exist
242         assert not f3.exists()
243         # Source path does not either
244         assert not f3.srcnode().exists()
245         # But we do have a file in the Repository
246         assert f3.rexists()
247         # rfile() should point to the source path
248         assert f3.rfile().path == os.path.normpath(test.workpath('rep1/src/test2.in')),\
249                f3.rfile().path
250
251         assert f4.srcnode().path == os.path.normpath('src/test2.in'),\
252                f4.srcnode().path
253         # str(node) returns build path for duplicate = 1
254         assert str(f4) == os.path.normpath('build/var2/test2.in'), str(f4)
255         # Build path should exist
256         assert f4.exists()
257         # ...and copy over the file into the local build path
258         assert test.read(['work', 'build', 'var2', 'test2.in']) == 'test2.in'
259         # should exist in repository, since exists() is true
260         assert f4.rexists()
261         # rfile() should point to ourselves
262         assert f4.rfile().path == os.path.normpath('build/var2/test2.in'),\
263                f4.rfile().path
264
265         f5 = fs.File('build/var1/test.out')
266         f6 = fs.File('build/var2/test.out')
267
268         assert f5.exists()
269         # We should not copy the file from the source dir, since this is
270         # a derived file.
271         assert test.read(['work', 'build', 'var1', 'test.out']) == 'test.old'
272
273         assert f6.exists()
274         # We should not copy the file from the source dir, since this is
275         # a derived file.
276         assert test.read(['work', 'build', 'var2', 'test.out']) == 'test.old'
277
278         f7 = fs.File('build/var1/test2.out')
279         f8 = fs.File('build/var2/test2.out')
280
281         assert not f7.exists()
282         assert f7.rexists()
283         assert f7.rfile().path == os.path.normpath(test.workpath('rep1/build/var1/test2.out')),\
284                f7.rfile().path
285
286         assert not f8.exists()
287         assert f8.rexists()
288         assert f8.rfile().path == os.path.normpath(test.workpath('rep1/build/var2/test2.out')),\
289                f8.rfile().path
290
291         # Verify the Mkdir and Link actions are called
292         d9 = fs.Dir('build/var2/new_dir')
293         f9 = fs.File('build/var2/new_dir/test9.out')
294
295         class MkdirAction(Action):
296             def __init__(self, dir_made):
297                 self.dir_made = dir_made
298             def __call__(self, target, source, env, errfunc):
299                 self.dir_made.extend(target)
300
301         save_Link = SCons.Node.FS.Link
302         link_made = []
303         def link_func(target, source, env, link_made=link_made):
304             link_made.append(target)
305         SCons.Node.FS.Link = link_func
306
307         try:
308             dir_made = []
309             d9.builder = Builder(fs.Dir, action=MkdirAction(dir_made))
310             d9.reset_executor()
311             f9.exists()
312             expect = os.path.join('build', 'var2', 'new_dir')
313             assert dir_made[0].path == expect, dir_made[0].path
314             expect = os.path.join('build', 'var2', 'new_dir', 'test9.out')
315             assert link_made[0].path == expect, link_made[0].path
316             assert f9.linked
317         finally:
318             SCons.Node.FS.Link = save_Link
319
320         # Test for an interesting pathological case...we have a source
321         # file in a build path, but not in a source path.  This can
322         # happen if you switch from duplicate=1 to duplicate=0, then
323         # delete a source file.  At one time, this would cause exists()
324         # to return a 1 but get_contents() to throw.
325         test.write([ 'work', 'build', 'var1', 'asourcefile' ], 'stuff')
326         f10 = fs.File('build/var1/asourcefile')
327         assert f10.exists()
328         assert f10.get_contents() == 'stuff', f10.get_contents()
329
330         f11 = fs.File('src/file11')
331         t, m = f11.alter_targets()
332         bdt = map(lambda n: n.path, t)
333         var1_file11 = os.path.normpath('build/var1/file11')
334         var2_file11 = os.path.normpath('build/var2/file11')
335         assert bdt == [var1_file11, var2_file11], bdt
336
337         f12 = fs.File('src/file12')
338         f12.builder = 1
339         bdt, m = f12.alter_targets()
340         assert bdt == [], map(lambda n: n.path, bdt)
341
342         d13 = fs.Dir('src/new_dir')
343         t, m = d13.alter_targets()
344         bdt = map(lambda n: n.path, t)
345         var1_new_dir = os.path.normpath('build/var1/new_dir')
346         var2_new_dir = os.path.normpath('build/var2/new_dir')
347         assert bdt == [var1_new_dir, var2_new_dir], bdt
348
349         # Test that an IOError trying to Link a src file
350         # into a BuildDir ends up throwing a StopError.
351         fIO = fs.File("build/var2/IOError")
352
353         save_Link = SCons.Node.FS.Link
354         def Link_IOError(target, source, env):
355             raise IOError, "Link_IOError"
356         SCons.Node.FS.Link = SCons.Action.Action(Link_IOError, None)
357
358         test.write(['work', 'src', 'IOError'], "work/src/IOError\n")
359
360         try:
361             exc_caught = 0
362             try:
363                 fIO.exists()
364             except SCons.Errors.StopError:
365                 exc_caught = 1
366             assert exc_caught, "Should have caught a StopError"
367
368         finally:
369             SCons.Node.FS.Link = save_Link
370
371         # Test to see if Link() works...
372         test.subdir('src','build')
373         test.write('src/foo', 'src/foo\n')
374         os.chmod(test.workpath('src/foo'), stat.S_IRUSR)
375         SCons.Node.FS.Link(fs.File(test.workpath('build/foo')),
376                            fs.File(test.workpath('src/foo')),
377                            None)
378         os.chmod(test.workpath('src/foo'), stat.S_IRUSR | stat.S_IWRITE)
379         st=os.stat(test.workpath('build/foo'))
380         assert (stat.S_IMODE(st[stat.ST_MODE]) & stat.S_IWRITE), \
381                stat.S_IMODE(st[stat.ST_MODE])
382
383         # This used to generate a UserError when we forbid the source
384         # directory from being outside the top-level SConstruct dir.
385         fs = SCons.Node.FS.FS()
386         fs.BuildDir('build', '/test/foo')
387
388         exc_caught = 0
389         try:
390             try:
391                 fs = SCons.Node.FS.FS()
392                 fs.BuildDir('build', 'build/src')
393             except SCons.Errors.UserError:
394                 exc_caught = 1
395             assert exc_caught, "Should have caught a UserError."
396         finally:
397             test.unlink( "src/foo" )
398             test.unlink( "build/foo" )
399
400         fs = SCons.Node.FS.FS()
401         fs.BuildDir('build', 'src1')
402
403         # Calling the same BuildDir twice should work fine.
404         fs.BuildDir('build', 'src1')
405
406         # Trying to move a build dir to a second source dir
407         # should blow up
408         try:
409             fs.BuildDir('build', 'src2')
410         except SCons.Errors.UserError:
411             pass
412         else:
413             assert 0, "Should have caught a UserError."
414
415         # Test against a former bug.  Make sure we can get a repository
416         # path for the build directory itself!
417         fs=SCons.Node.FS.FS(test.workpath('work'))
418         test.subdir('work')
419         fs.BuildDir('build/var3', 'src', duplicate=0)
420         d1 = fs.Dir('build/var3')
421         r = d1.rdir()
422         assert r == d1, "%s != %s" % (r, d1)
423
424         # verify the link creation attempts in file_link()
425         class LinkSimulator :
426             """A class to intercept os.[sym]link() calls and track them."""
427
428             def __init__( self, duplicate, link, symlink, copy ) :
429                 self.duplicate = duplicate
430                 self.have = {}
431                 self.have['hard'] = link
432                 self.have['soft'] = symlink
433                 self.have['copy'] = copy
434
435                 self.links_to_be_called = []
436                 for link in string.split(self.duplicate, '-'):
437                     if self.have[link]:
438                         self.links_to_be_called.append(link)
439
440             def link_fail( self , src , dest ) :
441                 next_link = self.links_to_be_called.pop(0)
442                 assert next_link == "hard", \
443                        "Wrong link order: expected %s to be called "\
444                        "instead of hard" % next_link
445                 raise OSError( "Simulating hard link creation error." )
446
447             def symlink_fail( self , src , dest ) :
448                 next_link = self.links_to_be_called.pop(0)
449                 assert next_link == "soft", \
450                        "Wrong link order: expected %s to be called "\
451                        "instead of soft" % next_link
452                 raise OSError( "Simulating symlink creation error." )
453
454             def copy( self , src , dest ) :
455                 next_link = self.links_to_be_called.pop(0)
456                 assert next_link == "copy", \
457                        "Wrong link order: expected %s to be called "\
458                        "instead of copy" % next_link
459                 # copy succeeds, but use the real copy
460                 self.have['copy'](src, dest)
461         # end class LinkSimulator
462
463         try:
464             SCons.Node.FS.set_duplicate("no-link-order")
465             assert 0, "Expected exception when passing an invalid duplicate to set_duplicate"
466         except SCons.Errors.InternalError:
467             pass
468
469         for duplicate in SCons.Node.FS.Valid_Duplicates:
470             # save the real functions for later restoration
471             try:
472                 real_link = os.link
473             except AttributeError:
474                 real_link = None
475             try:
476                 real_symlink = os.symlink
477             except AttributeError:
478                 real_symlink = None
479             real_copy = shutil.copy2
480
481             simulator = LinkSimulator(duplicate, real_link, real_symlink, real_copy)
482
483             # override the real functions with our simulation
484             os.link = simulator.link_fail
485             os.symlink = simulator.symlink_fail
486             shutil.copy2 = simulator.copy
487
488             try:
489
490                 SCons.Node.FS.set_duplicate(duplicate)
491
492                 src_foo = test.workpath('src', 'foo')
493                 build_foo = test.workpath('build', 'foo')
494
495                 test.write(src_foo, 'src/foo\n')
496                 os.chmod(src_foo, stat.S_IRUSR)
497                 try:
498                     SCons.Node.FS.Link(fs.File(build_foo),
499                                        fs.File(src_foo),
500                                        None)
501                 finally:
502                     os.chmod(src_foo, stat.S_IRUSR | stat.S_IWRITE)
503                     test.unlink(src_foo)
504                     test.unlink(build_foo)
505
506             finally:
507                 # restore the real functions
508                 if real_link:
509                     os.link = real_link
510                 else:
511                     delattr(os, 'link')
512                 if real_symlink:
513                     os.symlink = real_symlink
514                 else:
515                     delattr(os, 'symlink')
516                 shutil.copy2 = real_copy
517
518         # Test BuildDir "reflection," where a same-named subdirectory
519         # exists underneath a build_dir.
520         fs = SCons.Node.FS.FS()
521         fs.BuildDir('work/src/b1/b2', 'work/src')
522
523         dir_list = [
524                 'work/src',
525                 'work/src/b1',
526                 'work/src/b1/b2',
527                 'work/src/b1/b2/b1',
528                 'work/src/b1/b2/b1/b2',
529         ]
530
531         srcnode_map = {
532                 'work/src/b1/b2' : 'work/src',
533                 'work/src/b1/b2/f' : 'work/src/f',
534                 'work/src/b1/b2/b1' : 'work/src/b1/',
535                 'work/src/b1/b2/b1/f' : 'work/src/b1/f',
536                 'work/src/b1/b2/b1/b2' : 'work/src/b1/b2',
537                 'work/src/b1/b2/b1/b2/f' : 'work/src/b1/b2/f',
538         }
539
540         alter_map = {
541                 'work/src' : 'work/src/b1/b2',
542                 'work/src/f' : 'work/src/b1/b2/f',
543                 'work/src/b1' : 'work/src/b1/b2/b1',
544                 'work/src/b1/f' : 'work/src/b1/b2/b1/f',
545         }
546
547         errors = 0
548
549         for dir in dir_list:
550             dnode = fs.Dir(dir)
551             f = dir + '/f'
552             fnode = fs.File(dir + '/f')
553
554             dp = dnode.srcnode().path
555             expect = os.path.normpath(srcnode_map.get(dir, dir))
556             if dp != expect:
557                 print "Dir `%s' srcnode() `%s' != expected `%s'" % (dir, dp, expect)
558                 errors = errors + 1
559
560             fp = fnode.srcnode().path
561             expect = os.path.normpath(srcnode_map.get(f, f))
562             if fp != expect:
563                 print "File `%s' srcnode() `%s' != expected `%s'" % (f, fp, expect)
564                 errors = errors + 1
565
566         for dir in dir_list:
567             dnode = fs.Dir(dir)
568             f = dir + '/f'
569             fnode = fs.File(dir + '/f')
570
571             t, m = dnode.alter_targets()
572             tp = t[0].path
573             expect = os.path.normpath(alter_map.get(dir, dir))
574             if tp != expect:
575                 print "Dir `%s' alter_targets() `%s' != expected `%s'" % (dir, tp, expect)
576                 errors = errors + 1
577
578             t, m = fnode.alter_targets()
579             tp = t[0].path
580             expect = os.path.normpath(alter_map.get(f, f))
581             if tp != expect:
582                 print "File `%s' alter_targets() `%s' != expected `%s'" % (f, tp, expect)
583                 errors = errors + 1
584
585         self.failIf(errors)
586
587 class BaseTestCase(_tempdirTestCase):
588     def test_stat(self):
589         """Test the Base.stat() method"""
590         test = self.test
591         test.write("e1", "e1\n")
592         fs = SCons.Node.FS.FS()
593
594         e1 = fs.Entry('e1')
595         s = e1.stat()
596         assert not s is None, s
597
598         e2 = fs.Entry('e2')
599         s = e2.stat()
600         assert s is None, s
601
602     def test_getmtime(self):
603         """Test the Base.getmtime() method"""
604         test = self.test
605         test.write("file", "file\n")
606         fs = SCons.Node.FS.FS()
607
608         file = fs.Entry('file')
609         assert file.getmtime()
610
611         file = fs.Entry('nonexistent')
612         mtime = file.getmtime()
613         assert mtime is None, mtime
614
615     def test_getsize(self):
616         """Test the Base.getsize() method"""
617         test = self.test
618         test.write("file", "file\n")
619         fs = SCons.Node.FS.FS()
620
621         file = fs.Entry('file')
622         size = file.getsize()
623         assert size == 5, size
624
625         file = fs.Entry('nonexistent')
626         size = file.getsize()
627         assert size is None, size
628
629     def test_isdir(self):
630         """Test the Base.isdir() method"""
631         test = self.test
632         test.subdir('dir')
633         test.write("file", "file\n")
634         fs = SCons.Node.FS.FS()
635
636         dir = fs.Entry('dir')
637         assert dir.isdir()
638
639         file = fs.Entry('file')
640         assert not file.isdir()
641
642         nonexistent = fs.Entry('nonexistent')
643         assert not nonexistent.isdir()
644
645     def test_isfile(self):
646         """Test the Base.isfile() method"""
647         test = self.test
648         test.subdir('dir')
649         test.write("file", "file\n")
650         fs = SCons.Node.FS.FS()
651
652         dir = fs.Entry('dir')
653         assert not dir.isfile()
654
655         file = fs.Entry('file')
656         assert file.isfile()
657
658         nonexistent = fs.Entry('nonexistent')
659         assert not nonexistent.isfile()
660
661     if hasattr(os, 'symlink'):
662         def test_islink(self):
663             """Test the Base.islink() method"""
664             test = self.test
665             test.subdir('dir')
666             test.write("file", "file\n")
667             test.symlink("symlink", "symlink")
668             fs = SCons.Node.FS.FS()
669
670             dir = fs.Entry('dir')
671             assert not dir.islink()
672
673             file = fs.Entry('file')
674             assert not file.islink()
675
676             symlink = fs.Entry('symlink')
677             assert symlink.islink()
678
679             nonexistent = fs.Entry('nonexistent')
680             assert not nonexistent.islink()
681
682 class DirNodeInfoTestCase(_tempdirTestCase):
683     def test___init__(self):
684         """Test DirNodeInfo initialization"""
685         ddd = self.fs.Dir('ddd')
686         ni = SCons.Node.FS.DirNodeInfo(ddd)
687
688 class DirBuildInfoTestCase(_tempdirTestCase):
689     def test___init__(self):
690         """Test DirBuildInfo initialization"""
691         ddd = self.fs.Dir('ddd')
692         bi = SCons.Node.FS.DirBuildInfo(ddd)
693
694 class FileNodeInfoTestCase(_tempdirTestCase):
695     def test___init__(self):
696         """Test FileNodeInfo initialization"""
697         fff = self.fs.File('fff')
698         ni = SCons.Node.FS.FileNodeInfo(fff)
699         assert hasattr(ni, 'timestamp')
700         assert hasattr(ni, 'size')
701
702     def test___cmp__(self):
703         """Test comparing File.NodeInfo objects"""
704         f1 = self.fs.File('f1')
705         f2 = self.fs.File('f2')
706
707         ni1 = SCons.Node.FS.FileNodeInfo(f1)
708         ni2 = SCons.Node.FS.FileNodeInfo(f2)
709
710         msg = "cmp(%s, %s) returned %s, not %s"
711
712         c = cmp(ni1, ni2)
713         assert c == 1, msg % (ni1, ni2, c, 1)
714
715         ni1.bsig = 777
716         c = cmp(ni1, ni2)
717         assert c == 1, msg % (ni1.bsig, ni2, c, 1)
718
719         ni2.bsig = 666
720         c = cmp(ni1, ni2)
721         assert c == 1, msg % (ni1.bsig, ni2.bsig, c, 1)
722
723         ni2.bsig = 777
724         c = cmp(ni1, ni2)
725         assert c == 0, msg % (ni1.bsig, ni2.bsig, c, 0)
726
727         ni2.bsig = 888
728         c = cmp(ni1, ni2)
729         assert c == -1, msg % (ni1.bsig, ni2.bsig, c, -1)
730
731     def test_update(self):
732         """Test updating a File.NodeInfo with on-disk information"""
733         test = self.test
734         fff = self.fs.File('fff')
735
736         ni = SCons.Node.FS.FileNodeInfo(fff)
737
738         import time
739         time.sleep(2)
740
741         test.write('fff', "fff\n")
742
743         st = os.stat('fff')
744
745         mtime = st[stat.ST_MTIME]
746         assert ni.timestamp != mtime, (ni.timestamp, mtime)
747         size = st[stat.ST_SIZE]
748         assert ni.size != size, (ni.size, size)
749
750         fff.clear()
751         ni.update(fff)
752
753         st = os.stat('fff')
754
755         mtime = st[stat.ST_MTIME]
756         assert ni.timestamp == mtime, (ni.timestamp, mtime)
757         size = st[stat.ST_SIZE]
758         assert ni.size == size, (ni.size, size)
759
760 class FileBuildInfoTestCase(_tempdirTestCase):
761     def test___init__(self):
762         """Test File.BuildInfo initialization"""
763         fff = self.fs.File('fff')
764         bi = SCons.Node.FS.BuildInfo(fff)
765         assert bi.node is fff, bi.node
766
767     def test_convert_to_sconsign(self):
768         """Test converting to .sconsign file format"""
769         fff = self.fs.File('fff')
770         bi = SCons.Node.FS.FileBuildInfo(fff)
771         assert hasattr(bi, 'convert_to_sconsign')
772
773     def test_convert_from_sconsign(self):
774         """Test converting from .sconsign file format"""
775         fff = self.fs.File('fff')
776         bi = SCons.Node.FS.FileBuildInfo(fff)
777         assert hasattr(bi, 'convert_from_sconsign')
778
779     def test_prepare_dependencies(self):
780         """Test that we have a prepare_dependencies() method"""
781         fff = self.fs.File('fff')
782         bi = SCons.Node.FS.FileBuildInfo(fff)
783         bi.prepare_dependencies()
784
785     def test_format(self):
786         """Test the format() method"""
787         f1 = self.fs.File('f1')
788         bi1 = SCons.Node.FS.BuildInfo(f1)
789
790         s1sig = SCons.Node.FS.FileNodeInfo(self.fs.File('n1'))
791         s1sig.a = 1
792         d1sig = SCons.Node.FS.FileNodeInfo(self.fs.File('n2'))
793         d1sig.a = 2
794         i1sig = SCons.Node.FS.FileNodeInfo(self.fs.File('n3'))
795         i1sig.a = 3
796
797         bi1.bsources = [self.fs.File('s1')]
798         bi1.bdepends = [self.fs.File('d1')]
799         bi1.bimplicit = [self.fs.File('i1')]
800         bi1.bsourcesigs = [s1sig]
801         bi1.bdependsigs = [d1sig]
802         bi1.bimplicitsigs = [i1sig]
803
804         expect_lines = [
805             'None 0',
806             's1: 1 None 0',
807             'd1: 2 None 0',
808             'i1: 3 None 0',
809         ]
810
811         expect = string.join(expect_lines, '\n')
812         format = bi1.format()
813         assert format == expect, (repr(format), repr(expect))
814
815 class FSTestCase(_tempdirTestCase):
816     def test_runTest(self):
817         """Test FS (file system) Node operations
818
819         This test case handles all of the file system node
820         tests in one environment, so we don't have to set up a
821         complicated directory structure for each test individually.
822         """
823         test = self.test
824
825         test.subdir('sub', ['sub', 'dir'])
826
827         wp = test.workpath('')
828         sub = test.workpath('sub', '')
829         sub_dir = test.workpath('sub', 'dir', '')
830         sub_dir_foo = test.workpath('sub', 'dir', 'foo', '')
831         sub_dir_foo_bar = test.workpath('sub', 'dir', 'foo', 'bar', '')
832         sub_foo = test.workpath('sub', 'foo', '')
833
834         os.chdir(sub_dir)
835
836         fs = SCons.Node.FS.FS()
837
838         e1 = fs.Entry('e1')
839         assert isinstance(e1, SCons.Node.FS.Entry)
840
841         d1 = fs.Dir('d1')
842         assert isinstance(d1, SCons.Node.FS.Dir)
843         assert d1.cwd is d1, d1
844
845         f1 = fs.File('f1', directory = d1)
846         assert isinstance(f1, SCons.Node.FS.File)
847
848         d1_f1 = os.path.join('d1', 'f1')
849         assert f1.path == d1_f1, "f1.path %s != %s" % (f1.path, d1_f1)
850         assert str(f1) == d1_f1, "str(f1) %s != %s" % (str(f1), d1_f1)
851
852         x1 = d1.File('x1')
853         assert isinstance(x1, SCons.Node.FS.File)
854         assert str(x1) == os.path.join('d1', 'x1')
855
856         x2 = d1.Dir('x2')
857         assert isinstance(x2, SCons.Node.FS.Dir)
858         assert str(x2) == os.path.join('d1', 'x2')
859
860         x3 = d1.Entry('x3')
861         assert isinstance(x3, SCons.Node.FS.Entry)
862         assert str(x3) == os.path.join('d1', 'x3')
863
864         assert d1.File(x1) == x1
865         assert d1.Dir(x2) == x2
866         assert d1.Entry(x3) == x3
867
868         x1.cwd = d1
869
870         x4 = x1.File('x4')
871         assert str(x4) == os.path.join('d1', 'x4')
872
873         x5 = x1.Dir('x5')
874         assert str(x5) == os.path.join('d1', 'x5')
875
876         x6 = x1.Entry('x6')
877         assert str(x6) == os.path.join('d1', 'x6')
878         x7 = x1.Entry('x7')
879         assert str(x7) == os.path.join('d1', 'x7')
880
881         assert x1.File(x4) == x4
882         assert x1.Dir(x5) == x5
883         assert x1.Entry(x6) == x6
884         assert x1.Entry(x7) == x7
885
886         assert x1.Entry(x5) == x5
887         try:
888             x1.File(x5)
889         except TypeError:
890             pass
891         else:
892             assert 0
893
894         assert x1.Entry(x4) == x4
895         try:
896             x1.Dir(x4)
897         except TypeError:
898             pass
899         else:
900             assert 0
901
902         x6 = x1.File(x6)
903         assert isinstance(x6, SCons.Node.FS.File)
904
905         x7 = x1.Dir(x7)
906         assert isinstance(x7, SCons.Node.FS.Dir)
907
908         seps = [os.sep]
909         if os.sep != '/':
910             seps = seps + ['/']
911
912         drive, path = os.path.splitdrive(os.getcwd())
913
914         for sep in seps:
915
916             def Dir_test(lpath, path_, abspath_, up_path_, fileSys=fs, s=sep, drive=drive):
917                 dir = fileSys.Dir(string.replace(lpath, '/', s))
918
919                 if os.sep != '/':
920                     path_ = string.replace(path_, '/', os.sep)
921                     abspath_ = string.replace(abspath_, '/', os.sep)
922                     up_path_ = string.replace(up_path_, '/', os.sep)
923
924                 def strip_slash(p, drive=drive):
925                     if p[-1] == os.sep and len(p) > 1:
926                         p = p[:-1]
927                     if p[0] == os.sep:
928                         p = drive + p
929                     return p
930                 path = strip_slash(path_)
931                 abspath = strip_slash(abspath_)
932                 up_path = strip_slash(up_path_)
933                 name = string.split(abspath, os.sep)[-1]
934
935                 assert dir.name == name, \
936                        "dir.name %s != expected name %s" % \
937                        (dir.name, name)
938                 assert dir.path == path, \
939                        "dir.path %s != expected path %s" % \
940                        (dir.path, path)
941                 assert str(dir) == path, \
942                        "str(dir) %s != expected path %s" % \
943                        (str(dir), path)
944                 assert dir.get_abspath() == abspath, \
945                        "dir.abspath %s != expected absolute path %s" % \
946                        (dir.get_abspath(), abspath)
947                 assert dir.up().path == up_path, \
948                        "dir.up().path %s != expected parent path %s" % \
949                        (dir.up().path, up_path)
950
951             Dir_test('foo',         'foo/',        sub_dir_foo,       './')
952             Dir_test('foo/bar',     'foo/bar/',    sub_dir_foo_bar,   'foo/')
953             Dir_test('/foo',        '/foo/',       '/foo/',           '/')
954             Dir_test('/foo/bar',    '/foo/bar/',   '/foo/bar/',       '/foo/')
955             Dir_test('..',          sub,           sub,               wp)
956             Dir_test('foo/..',      './',          sub_dir,           sub)
957             Dir_test('../foo',      sub_foo,       sub_foo,           sub)
958             Dir_test('.',           './',          sub_dir,           sub)
959             Dir_test('./.',         './',          sub_dir,           sub)
960             Dir_test('foo/./bar',   'foo/bar/',    sub_dir_foo_bar,   'foo/')
961             Dir_test('#../foo',     sub_foo,       sub_foo,           sub)
962             Dir_test('#/../foo',    sub_foo,       sub_foo,           sub)
963             Dir_test('#foo/bar',    'foo/bar/',    sub_dir_foo_bar,   'foo/')
964             Dir_test('#/foo/bar',   'foo/bar/',    sub_dir_foo_bar,   'foo/')
965             Dir_test('#',           './',          sub_dir,           sub)
966
967             try:
968                 f2 = fs.File(string.join(['f1', 'f2'], sep), directory = d1)
969             except TypeError, x:
970                 assert str(x) == ("Tried to lookup File '%s' as a Dir." %
971                                   d1_f1), x
972             except:
973                 raise
974
975             try:
976                 dir = fs.Dir(string.join(['d1', 'f1'], sep))
977             except TypeError, x:
978                 assert str(x) == ("Tried to lookup File '%s' as a Dir." %
979                                   d1_f1), x
980             except:
981                 raise
982
983             try:
984                 f2 = fs.File('d1')
985             except TypeError, x:
986                 assert str(x) == ("Tried to lookup Dir '%s' as a File." %
987                                   'd1'), x
988             except:
989                 raise
990
991             # Test that just specifying the drive works to identify
992             # its root directory.
993             p = os.path.abspath(test.workpath('root_file'))
994             drive, path = os.path.splitdrive(p)
995             if drive:
996                 # The assert below probably isn't correct for the general
997                 # case, but it works for Windows, which covers a lot
998                 # of ground...
999                 dir = fs.Dir(drive)
1000                 assert str(dir) == drive + os.sep, str(dir)
1001
1002         # Test for a bug in 0.04 that did not like looking up
1003         # dirs with a trailing slash on Windows.
1004         d=fs.Dir('./')
1005         assert d.path == '.', d.abspath
1006         d=fs.Dir('foo/')
1007         assert d.path == 'foo', d.abspath
1008
1009         # Test for sub-classing of node building.
1010         global built_it
1011
1012         built_it = None
1013         assert not built_it
1014         d1.add_source([SCons.Node.Node()])    # XXX FAKE SUBCLASS ATTRIBUTE
1015         d1.builder_set(Builder(fs.File))
1016         d1.reset_executor()
1017         d1.env_set(Environment())
1018         d1.build()
1019         assert built_it
1020
1021         built_it = None
1022         assert not built_it
1023         f1.add_source([SCons.Node.Node()])    # XXX FAKE SUBCLASS ATTRIBUTE
1024         f1.builder_set(Builder(fs.File))
1025         f1.reset_executor()
1026         f1.env_set(Environment())
1027         f1.build()
1028         assert built_it
1029
1030         def match(path, expect):
1031             expect = string.replace(expect, '/', os.sep)
1032             assert path == expect, "path %s != expected %s" % (path, expect)
1033
1034         e1 = fs.Entry("d1")
1035         assert e1.__class__.__name__ == 'Dir'
1036         match(e1.path, "d1")
1037         match(e1.dir.path, ".")
1038
1039         e2 = fs.Entry("d1/f1")
1040         assert e2.__class__.__name__ == 'File'
1041         match(e2.path, "d1/f1")
1042         match(e2.dir.path, "d1")
1043
1044         e3 = fs.Entry("e3")
1045         assert e3.__class__.__name__ == 'Entry'
1046         match(e3.path, "e3")
1047         match(e3.dir.path, ".")
1048
1049         e4 = fs.Entry("d1/e4")
1050         assert e4.__class__.__name__ == 'Entry'
1051         match(e4.path, "d1/e4")
1052         match(e4.dir.path, "d1")
1053
1054         e5 = fs.Entry("e3/e5")
1055         assert e3.__class__.__name__ == 'Dir'
1056         match(e3.path, "e3")
1057         match(e3.dir.path, ".")
1058         assert e5.__class__.__name__ == 'Entry'
1059         match(e5.path, "e3/e5")
1060         match(e5.dir.path, "e3")
1061
1062         e6 = fs.Dir("d1/e4")
1063         assert e6 is e4
1064         assert e4.__class__.__name__ == 'Dir'
1065         match(e4.path, "d1/e4")
1066         match(e4.dir.path, "d1")
1067
1068         e7 = fs.File("e3/e5")
1069         assert e7 is e5
1070         assert e5.__class__.__name__ == 'File'
1071         match(e5.path, "e3/e5")
1072         match(e5.dir.path, "e3")
1073
1074         fs.chdir(fs.Dir('subdir'))
1075         f11 = fs.File("f11")
1076         match(f11.path, "subdir/f11")
1077         d12 = fs.Dir("d12")
1078         e13 = fs.Entry("subdir/e13")
1079         match(e13.path, "subdir/subdir/e13")
1080         fs.chdir(fs.Dir('..'))
1081
1082         # Test scanning
1083         f1.builder_set(Builder(fs.File))
1084         f1.env_set(Environment())
1085         xyz = fs.File("xyz")
1086         f1.builder.target_scanner = Scanner(xyz)
1087
1088         f1.scan()
1089         assert f1.implicit[0].path == "xyz"
1090         f1.implicit = []
1091         f1.scan()
1092         assert f1.implicit == []
1093         f1.implicit = None
1094         f1.scan()
1095         assert f1.implicit[0].path == "xyz"
1096
1097         # Test underlying scanning functionality in get_found_includes()
1098         env = Environment()
1099         f12 = fs.File("f12")
1100         t1 = fs.File("t1")
1101
1102         deps = f12.get_found_includes(env, None, t1)
1103         assert deps == [], deps
1104
1105         class MyScanner(Scanner):
1106             call_count = 0
1107             def __call__(self, node, env, path):
1108                 self.call_count = self.call_count + 1
1109                 return Scanner.__call__(self, node, env, path)
1110         s = MyScanner(xyz)
1111
1112         deps = f12.get_found_includes(env, s, t1)
1113         assert deps == [xyz], deps
1114         assert s.call_count == 1, s.call_count
1115
1116         f12.built()
1117
1118         deps = f12.get_found_includes(env, s, t1)
1119         assert deps == [xyz], deps
1120         assert s.call_count == 2, s.call_count
1121
1122         env2 = Environment()
1123
1124         deps = f12.get_found_includes(env2, s, t1)
1125         assert deps == [xyz], deps
1126         assert s.call_count == 3, s.call_count
1127
1128
1129
1130         # Make sure we can scan this file even if the target isn't
1131         # a file that has a scanner (it might be an Alias, e.g.).
1132         class DummyNode:
1133             pass
1134
1135         deps = f12.get_found_includes(env, s, DummyNode())
1136         assert deps == [xyz], deps
1137
1138         # Test building a file whose directory is not there yet...
1139         f1 = fs.File(test.workpath("foo/bar/baz/ack"))
1140         assert not f1.dir.exists()
1141         f1.prepare()
1142         f1.build()
1143         assert f1.dir.exists()
1144
1145         os.chdir('..')
1146
1147         # Test getcwd()
1148         fs = SCons.Node.FS.FS()
1149         assert str(fs.getcwd()) == ".", str(fs.getcwd())
1150         fs.chdir(fs.Dir('subdir'))
1151         # The cwd's path is always "."
1152         assert str(fs.getcwd()) == ".", str(fs.getcwd())
1153         assert fs.getcwd().path == 'subdir', fs.getcwd().path
1154         fs.chdir(fs.Dir('../..'))
1155         assert fs.getcwd().path == test.workdir, fs.getcwd().path
1156
1157         f1 = fs.File(test.workpath("do_i_exist"))
1158         assert not f1.exists()
1159         test.write("do_i_exist","\n")
1160         assert not f1.exists(), "exists() call not cached"
1161         f1.built()
1162         assert f1.exists(), "exists() call caching not reset"
1163         test.unlink("do_i_exist")
1164         assert f1.exists()
1165         f1.built()
1166         assert not f1.exists()
1167
1168         # For some reason, in Windows, the \x1a character terminates
1169         # the reading of files in text mode.  This tests that
1170         # get_contents() returns the binary contents.
1171         test.write("binary_file", "Foo\x1aBar")
1172         f1 = fs.File(test.workpath("binary_file"))
1173         assert f1.get_contents() == "Foo\x1aBar", f1.get_contents()
1174
1175         def nonexistent(method, s):
1176             try:
1177                 x = method(s, create = 0)
1178             except SCons.Errors.UserError:
1179                 pass
1180             else:
1181                 raise TestFailed, "did not catch expected UserError"
1182
1183         nonexistent(fs.Entry, 'nonexistent')
1184         nonexistent(fs.Entry, 'nonexistent/foo')
1185
1186         nonexistent(fs.File, 'nonexistent')
1187         nonexistent(fs.File, 'nonexistent/foo')
1188
1189         nonexistent(fs.Dir, 'nonexistent')
1190         nonexistent(fs.Dir, 'nonexistent/foo')
1191
1192         test.write("preserve_me", "\n")
1193         assert os.path.exists(test.workpath("preserve_me"))
1194         f1 = fs.File(test.workpath("preserve_me"))
1195         f1.prepare()
1196         assert os.path.exists(test.workpath("preserve_me"))
1197
1198         test.write("remove_me", "\n")
1199         assert os.path.exists(test.workpath("remove_me"))
1200         f1 = fs.File(test.workpath("remove_me"))
1201         f1.builder = Builder(fs.File)
1202         f1.env_set(Environment())
1203         f1.prepare()
1204         assert not os.path.exists(test.workpath("remove_me"))
1205
1206         e = fs.Entry('e_local')
1207         assert not hasattr(e, '_local')
1208         e.set_local()
1209         assert e._local == 1
1210         f = fs.File('e_local')
1211         assert f._local == 1
1212         f = fs.File('f_local')
1213         assert f._local == 0
1214
1215         #XXX test current() for directories
1216
1217         #XXX test sconsign() for directories
1218
1219         #XXX test set_signature() for directories
1220
1221         #XXX test build() for directories
1222
1223         #XXX test root()
1224
1225         # test Entry.get_contents()
1226         e = fs.Entry('does_not_exist')
1227         c = e.get_contents()
1228         assert c == "", c
1229         assert e.__class__ == SCons.Node.FS.Entry
1230
1231         test.write("file", "file\n")
1232         try:
1233             e = fs.Entry('file')
1234             c = e.get_contents()
1235             assert c == "file\n", c
1236             assert e.__class__ == SCons.Node.FS.File
1237         finally:
1238             test.unlink("file")
1239
1240         test.subdir("dir")
1241         e = fs.Entry('dir')
1242         c = e.get_contents()
1243         assert c == "", c
1244         assert e.__class__ == SCons.Node.FS.Dir
1245
1246         if hasattr(os, 'symlink'):
1247             os.symlink('nonexistent', test.workpath('dangling_symlink'))
1248             e = fs.Entry('dangling_symlink')
1249             c = e.get_contents()
1250             assert e.__class__ == SCons.Node.FS.Entry, e.__class__
1251             assert c == "", c
1252
1253         test.write("tstamp", "tstamp\n")
1254         try:
1255             # Okay, *this* manipulation accomodates Windows FAT file systems
1256             # that only have two-second granularity on their timestamps.
1257             # We round down the current time to the nearest even integer
1258             # value, subtract two to make sure the timestamp is not "now,"
1259             # and then convert it back to a float.
1260             tstamp = float(int(time.time() / 2) * 2) - 2
1261             os.utime(test.workpath("tstamp"), (tstamp - 2.0, tstamp))
1262             f = fs.File("tstamp")
1263             t = f.get_timestamp()
1264             assert t == tstamp, "expected %f, got %f" % (tstamp, t)
1265         finally:
1266             test.unlink("tstamp")
1267
1268         test.subdir('tdir1')
1269         d = fs.Dir('tdir1')
1270         t = d.get_timestamp()
1271         assert t == 0, "expected 0, got %s" % str(t)
1272
1273         test.subdir('tdir2')
1274         f1 = test.workpath('tdir2', 'file1')
1275         f2 = test.workpath('tdir2', 'file2')
1276         test.write(f1, 'file1\n')
1277         test.write(f2, 'file2\n')
1278         current_time = float(int(time.time() / 2) * 2)
1279         t1 = current_time - 4.0
1280         t2 = current_time - 2.0
1281         os.utime(f1, (t1 - 2.0, t1))
1282         os.utime(f2, (t2 - 2.0, t2))
1283         d = fs.Dir('tdir2')
1284         fs.File(f1)
1285         fs.File(f2)
1286         t = d.get_timestamp()
1287         assert t == t2, "expected %f, got %f" % (t2, t)
1288
1289         skey = fs.Entry('eee.x').scanner_key()
1290         assert skey == '.x', skey
1291         skey = fs.Entry('eee.xyz').scanner_key()
1292         assert skey == '.xyz', skey
1293
1294         skey = fs.File('fff.x').scanner_key()
1295         assert skey == '.x', skey
1296         skey = fs.File('fff.xyz').scanner_key()
1297         assert skey == '.xyz', skey
1298
1299         skey = fs.Dir('ddd.x').scanner_key()
1300         assert skey is None, skey
1301
1302         test.write("i_am_not_a_directory", "\n")
1303         try:
1304             exc_caught = 0
1305             try:
1306                 fs.Dir(test.workpath("i_am_not_a_directory"))
1307             except TypeError:
1308                 exc_caught = 1
1309             assert exc_caught, "Should have caught a TypeError"
1310         finally:
1311             test.unlink("i_am_not_a_directory")
1312
1313         exc_caught = 0
1314         try:
1315             fs.File(sub_dir)
1316         except TypeError:
1317             exc_caught = 1
1318         assert exc_caught, "Should have caught a TypeError"
1319
1320         # XXX test calc_signature()
1321
1322         # XXX test current()
1323
1324         d = fs.Dir('dir')
1325         r = d.remove()
1326         assert r is None, r
1327
1328         f = fs.File('does_not_exist')
1329         r = f.remove()
1330         assert r == None, r
1331
1332         test.write('exists', "exists\n")
1333         f = fs.File('exists')
1334         r = f.remove()
1335         assert r, r
1336         assert not os.path.exists(test.workpath('exists')), "exists was not removed"
1337
1338         symlink = test.workpath('symlink')
1339         try:
1340             os.symlink(test.workpath('does_not_exist'), symlink)
1341             assert os.path.islink(symlink)
1342             f = fs.File('symlink')
1343             r = f.remove()
1344             assert r, r
1345             assert not os.path.islink(symlink), "symlink was not removed"
1346         except AttributeError:
1347             pass
1348
1349         test.write('can_not_remove', "can_not_remove\n")
1350         test.writable(test.workpath('.'), 0)
1351         fp = open(test.workpath('can_not_remove'))
1352
1353         f = fs.File('can_not_remove')
1354         exc_caught = 0
1355         try:
1356             r = f.remove()
1357         except OSError:
1358             exc_caught = 1
1359
1360         fp.close()
1361
1362         assert exc_caught, "Should have caught an OSError, r = " + str(r)
1363
1364         f = fs.Entry('foo/bar/baz')
1365         assert f.for_signature() == 'baz', f.for_signature()
1366         assert f.get_string(0) == os.path.normpath('foo/bar/baz'), \
1367                f.get_string(0)
1368         assert f.get_string(1) == 'baz', f.get_string(1)
1369
1370     def test_target_from_source(self):
1371         """Test the method for generating target nodes from sources"""
1372         fs = self.fs
1373
1374         x = fs.File('x.c')
1375         t = x.target_from_source('pre-', '-suf')
1376         assert str(t) == 'pre-x-suf', str(t)
1377         assert t.__class__ == SCons.Node.FS.Entry
1378
1379         y = fs.File('dir/y')
1380         t = y.target_from_source('pre-', '-suf')
1381         assert str(t) == os.path.join('dir', 'pre-y-suf'), str(t)
1382         assert t.__class__ == SCons.Node.FS.Entry
1383
1384         z = fs.File('zz')
1385         t = z.target_from_source('pre-', '-suf', lambda x: x[:-1])
1386         assert str(t) == 'pre-z-suf', str(t)
1387         assert t.__class__ == SCons.Node.FS.Entry
1388
1389         d = fs.Dir('ddd')
1390         t = d.target_from_source('pre-', '-suf')
1391         assert str(t) == 'pre-ddd-suf', str(t)
1392         assert t.__class__ == SCons.Node.FS.Entry
1393
1394         e = fs.Entry('eee')
1395         t = e.target_from_source('pre-', '-suf')
1396         assert str(t) == 'pre-eee-suf', str(t)
1397         assert t.__class__ == SCons.Node.FS.Entry
1398
1399     def test_same_name(self):
1400         """Test that a local same-named file isn't found for a Dir lookup"""
1401         test = self.test
1402         fs = self.fs
1403
1404         test.subdir('subdir')
1405         test.write(['subdir', 'build'], "subdir/build\n")
1406
1407         subdir = fs.Dir('subdir')
1408         fs.chdir(subdir, change_os_dir=1)
1409         path, dir = fs._transformPath('#build/file', subdir)
1410         self.fs._doLookup(SCons.Node.FS.File, path, dir)
1411
1412     def test_above_root(self):
1413         """Testing looking up a path above the root directory"""
1414         test = self.test
1415         fs = self.fs
1416
1417         d1 = fs.Dir('d1')
1418         d2 = d1.Dir('d2')
1419         dirs = string.split(os.path.normpath(d2.abspath), os.sep)
1420         above_path = apply(os.path.join, ['..']*len(dirs) + ['above'])
1421         above = d2.Dir(above_path)
1422
1423     def test_rel_path(self):
1424         """Test the rel_path() method"""
1425         test = self.test
1426         fs = self.fs
1427
1428         d1 = fs.Dir('d1')
1429         d1_f = d1.File('f')
1430         d1_d2 = d1.Dir('d2')
1431         d1_d2_f = d1_d2.File('f')
1432
1433         d3 = fs.Dir('d3')
1434         d3_f = d3.File('f')
1435         d3_d4 = d3.Dir('d4')
1436         d3_d4_f = d3_d4.File('f')
1437
1438         cases = [
1439                 d1,             d1,             '.',
1440                 d1,             d1_f,           'f',
1441                 d1,             d1_d2,          'd2',
1442                 d1,             d1_d2_f,        'd2/f',
1443                 d1,             d3,             '../d3',
1444                 d1,             d3_f,           '../d3/f',
1445                 d1,             d3_d4,          '../d3/d4',
1446                 d1,             d3_d4_f,        '../d3/d4/f',
1447
1448                 d1_f,           d1,             '.',
1449                 d1_f,           d1_f,           'f',
1450                 d1_f,           d1_d2,          'd2',
1451                 d1_f,           d1_d2_f,        'd2/f',
1452                 d1_f,           d3,             '../d3',
1453                 d1_f,           d3_f,           '../d3/f',
1454                 d1_f,           d3_d4,          '../d3/d4',
1455                 d1_f,           d3_d4_f,        '../d3/d4/f',
1456
1457                 d1_d2,          d1,             '..',
1458                 d1_d2,          d1_f,           '../f',
1459                 d1_d2,          d1_d2,          '.',
1460                 d1_d2,          d1_d2_f,        'f',
1461                 d1_d2,          d3,             '../../d3',
1462                 d1_d2,          d3_f,           '../../d3/f',
1463                 d1_d2,          d3_d4,          '../../d3/d4',
1464                 d1_d2,          d3_d4_f,        '../../d3/d4/f',
1465
1466                 d1_d2_f,        d1,             '..',
1467                 d1_d2_f,        d1_f,           '../f',
1468                 d1_d2_f,        d1_d2,          '.',
1469                 d1_d2_f,        d1_d2_f,        'f',
1470                 d1_d2_f,        d3,             '../../d3',
1471                 d1_d2_f,        d3_f,           '../../d3/f',
1472                 d1_d2_f,        d3_d4,          '../../d3/d4',
1473                 d1_d2_f,        d3_d4_f,        '../../d3/d4/f',
1474         ]
1475
1476         if sys.platform in ('win32',):
1477             x_d1        = fs.Dir(r'X:\d1')
1478             x_d1_d2     = x_d1.Dir('d2')
1479             y_d1        = fs.Dir(r'Y:\d1')
1480             y_d1_d2     = y_d1.Dir('d2')
1481             y_d2        = fs.Dir(r'Y:\d2')
1482
1483             win32_cases = [
1484                 x_d1,           x_d1,           '.',
1485                 x_d1,           x_d1_d2,        'd2',
1486                 x_d1,           y_d1,           r'Y:\d1',
1487                 x_d1,           y_d1_d2,        r'Y:\d1\d2',
1488                 x_d1,           y_d2,           r'Y:\d2',
1489             ]
1490
1491             cases.extend(win32_cases)
1492
1493         failed = 0
1494         while cases:
1495             dir, other, expect = cases[:3]
1496             expect = os.path.normpath(expect)
1497             del cases[:3]
1498             result = dir.rel_path(other)
1499             if result != expect:
1500                 if failed == 0: print
1501                 fmt = "    dir_path(%(dir)s, %(other)s) => '%(result)s' did not match '%(expect)s'"
1502                 print fmt % locals()
1503                 failed = failed + 1
1504         assert failed == 0, "%d rel_path() cases failed" % failed
1505
1506     def test_proxy(self):
1507         """Test a Node.FS object wrapped in a proxy instance"""
1508         f1 = self.fs.File('fff')
1509         class Proxy:
1510             # Simplest possibly Proxy class that works for our test,
1511             # this is stripped down from SCons.Util.Proxy.
1512             def __init__(self, subject):
1513                 self.__subject = subject
1514             def __getattr__(self, name):
1515                 return getattr(self.__subject, name)
1516         p = Proxy(f1)
1517         f2 = self.fs.Entry(p)
1518         assert f1 is f2, (f1, f2)
1519
1520
1521
1522 class DirTestCase(_tempdirTestCase):
1523
1524     def test__morph(self):
1525         """Test handling of actions when morphing an Entry into a Dir"""
1526         test = self.test
1527         e = self.fs.Entry('eee')
1528         x = e.get_executor()
1529         x.add_pre_action('pre')
1530         x.add_post_action('post')
1531         e.must_be_same(SCons.Node.FS.Dir)
1532         a = x.get_action_list()
1533         assert a[0] == 'pre', a
1534         assert a[2] == 'post', a
1535
1536     def test_get_env_scanner(self):
1537         """Test the Dir.get_env_scanner() method
1538         """
1539         import SCons.Defaults
1540         d = self.fs.Dir('ddd')
1541         s = d.get_env_scanner(Environment())
1542         assert s is SCons.Defaults.DirEntryScanner, s
1543
1544     def test_get_target_scanner(self):
1545         """Test the Dir.get_target_scanner() method
1546         """
1547         import SCons.Defaults
1548         d = self.fs.Dir('ddd')
1549         s = d.get_target_scanner()
1550         assert s is SCons.Defaults.DirEntryScanner, s
1551
1552     def test_scan(self):
1553         """Test scanning a directory for in-memory entries
1554         """
1555         fs = self.fs
1556
1557         dir = fs.Dir('ddd')
1558         fs.File(os.path.join('ddd', 'f1'))
1559         fs.File(os.path.join('ddd', 'f2'))
1560         fs.File(os.path.join('ddd', 'f3'))
1561         fs.Dir(os.path.join('ddd', 'd1'))
1562         fs.Dir(os.path.join('ddd', 'd1', 'f4'))
1563         fs.Dir(os.path.join('ddd', 'd1', 'f5'))
1564         dir.scan()
1565         kids = map(lambda x: x.path, dir.children(None))
1566         kids.sort()
1567         assert kids == [os.path.join('ddd', 'd1'),
1568                         os.path.join('ddd', 'f1'),
1569                         os.path.join('ddd', 'f2'),
1570                         os.path.join('ddd', 'f3')], kids
1571
1572     def test_entry_exists_on_disk(self):
1573         """Test the Dir.entry_exists_on_disk() method
1574         """
1575         test = self.test
1576
1577         does_not_exist = self.fs.Dir('does_not_exist')
1578         assert not does_not_exist.entry_exists_on_disk('foo')
1579
1580         test.subdir('d')
1581         test.write(['d', 'exists'], "d/exists\n")
1582         test.write(['d', 'Case-Insensitive'], "d/Case-Insensitive\n")
1583
1584         d = self.fs.Dir('d')
1585         assert d.entry_exists_on_disk('exists')
1586         assert not d.entry_exists_on_disk('does_not_exist')
1587
1588         if os.path.normcase("TeSt") != os.path.normpath("TeSt") or sys.platform == "cygwin":
1589             assert d.entry_exists_on_disk('case-insensitive')
1590
1591     def test_srcdir_list(self):
1592         """Test the Dir.srcdir_list() method
1593         """
1594         src = self.fs.Dir('src')
1595         bld = self.fs.Dir('bld')
1596         sub1 = bld.Dir('sub')
1597         sub2 = sub1.Dir('sub')
1598         sub3 = sub2.Dir('sub')
1599         self.fs.BuildDir(bld, src, duplicate=0)
1600         self.fs.BuildDir(sub2, src, duplicate=0)
1601
1602         def check(result, expect):
1603             result = map(str, result)
1604             expect = map(os.path.normpath, expect)
1605             assert result == expect, result
1606
1607         s = src.srcdir_list()
1608         check(s, [])
1609
1610         s = bld.srcdir_list()
1611         check(s, ['src'])
1612
1613         s = sub1.srcdir_list()
1614         check(s, ['src/sub'])
1615
1616         s = sub2.srcdir_list()
1617         check(s, ['src', 'src/sub/sub'])
1618
1619         s = sub3.srcdir_list()
1620         check(s, ['src/sub', 'src/sub/sub/sub'])
1621
1622         self.fs.BuildDir('src/b1/b2', 'src')
1623         b1 = src.Dir('b1')
1624         b1_b2 = b1.Dir('b2')
1625         b1_b2_b1 = b1_b2.Dir('b1')
1626         b1_b2_b1_b2 = b1_b2_b1.Dir('b2')
1627         b1_b2_b1_b2_sub = b1_b2_b1_b2.Dir('sub')
1628
1629         s = b1.srcdir_list()
1630         check(s, [])
1631
1632         s = b1_b2.srcdir_list()
1633         check(s, ['src'])
1634
1635         s = b1_b2_b1.srcdir_list()
1636         check(s, ['src/b1'])
1637
1638         s = b1_b2_b1_b2.srcdir_list()
1639         check(s, [])
1640
1641         s = b1_b2_b1_b2_sub.srcdir_list()
1642         check(s, [])
1643
1644     def test_srcdir_duplicate(self):
1645         """Test the Dir.srcdir_duplicate() method
1646         """
1647         test = self.test
1648
1649         test.subdir('src0')
1650         test.write(['src0', 'exists'], "src0/exists\n")
1651
1652         bld0 = self.fs.Dir('bld0')
1653         src0 = self.fs.Dir('src0')
1654         self.fs.BuildDir(bld0, src0, duplicate=0)
1655
1656         n = bld0.srcdir_duplicate('does_not_exist')
1657         assert n is None, n
1658         assert not os.path.exists(test.workpath('bld0', 'does_not_exist'))
1659
1660         n = bld0.srcdir_duplicate('exists')
1661         assert str(n) == os.path.normpath('src0/exists'), str(n)
1662         assert not os.path.exists(test.workpath('bld0', 'exists'))
1663
1664         test.subdir('src1')
1665         test.write(['src1', 'exists'], "src0/exists\n")
1666
1667         bld1 = self.fs.Dir('bld1')
1668         src1 = self.fs.Dir('src1')
1669         self.fs.BuildDir(bld1, src1, duplicate=1)
1670
1671         n = bld1.srcdir_duplicate('does_not_exist')
1672         assert n is None, n
1673         assert not os.path.exists(test.workpath('bld1', 'does_not_exist'))
1674
1675         n = bld1.srcdir_duplicate('exists')
1676         assert str(n) == os.path.normpath('bld1/exists'), str(n)
1677         assert os.path.exists(test.workpath('bld1', 'exists'))
1678
1679     def test_srcdir_find_file(self):
1680         """Test the Dir.srcdir_find_file() method
1681         """
1682         test = self.test
1683
1684         return_true = lambda: 1
1685
1686         test.subdir('src0')
1687         test.write(['src0', 'on-disk-f1'], "src0/on-disk-f1\n")
1688         test.write(['src0', 'on-disk-f2'], "src0/on-disk-f2\n")
1689         test.write(['src0', 'on-disk-e1'], "src0/on-disk-e1\n")
1690         test.write(['src0', 'on-disk-e2'], "src0/on-disk-e2\n")
1691
1692         bld0 = self.fs.Dir('bld0')
1693         src0 = self.fs.Dir('src0')
1694         self.fs.BuildDir(bld0, src0, duplicate=0)
1695
1696         derived_f = src0.File('derived-f')
1697         derived_f.is_derived = return_true
1698         pseudo_f = src0.File('pseudo-f')
1699         pseudo_f.is_pseudo_derived = return_true
1700         exists_f = src0.File('exists-f')
1701         exists_f.exists = return_true
1702
1703         derived_e = src0.Entry('derived-e')
1704         derived_e.is_derived = return_true
1705         pseudo_e = src0.Entry('pseudo-e')
1706         pseudo_e.is_pseudo_derived = return_true
1707         exists_e = src0.Entry('exists-e')
1708         exists_e.exists = return_true
1709
1710         def check(result, expect):
1711             result = map(str, result)
1712             expect = map(os.path.normpath, expect)
1713             assert result == expect, result
1714
1715         # First check from the source directory.
1716         n = src0.srcdir_find_file('does_not_exist')
1717         assert n == (None, None), n
1718
1719         n = src0.srcdir_find_file('derived-f')
1720         check(n, ['src0/derived-f', 'src0'])
1721         n = src0.srcdir_find_file('pseudo-f')
1722         check(n, ['src0/pseudo-f', 'src0'])
1723         n = src0.srcdir_find_file('exists-f')
1724         check(n, ['src0/exists-f', 'src0'])
1725         n = src0.srcdir_find_file('on-disk-f1')
1726         check(n, ['src0/on-disk-f1', 'src0'])
1727
1728         n = src0.srcdir_find_file('derived-e')
1729         check(n, ['src0/derived-e', 'src0'])
1730         n = src0.srcdir_find_file('pseudo-e')
1731         check(n, ['src0/pseudo-e', 'src0'])
1732         n = src0.srcdir_find_file('exists-e')
1733         check(n, ['src0/exists-e', 'src0'])
1734         n = src0.srcdir_find_file('on-disk-e1')
1735         check(n, ['src0/on-disk-e1', 'src0'])
1736
1737         # Now check from the build directory.
1738         n = bld0.srcdir_find_file('does_not_exist')
1739         assert n == (None, None), n
1740
1741         n = bld0.srcdir_find_file('derived-f')
1742         check(n, ['src0/derived-f', 'bld0'])
1743         n = bld0.srcdir_find_file('pseudo-f')
1744         check(n, ['src0/pseudo-f', 'bld0'])
1745         n = bld0.srcdir_find_file('exists-f')
1746         check(n, ['src0/exists-f', 'bld0'])
1747         n = bld0.srcdir_find_file('on-disk-f2')
1748         check(n, ['src0/on-disk-f2', 'bld0'])
1749
1750         n = bld0.srcdir_find_file('derived-e')
1751         check(n, ['src0/derived-e', 'bld0'])
1752         n = bld0.srcdir_find_file('pseudo-e')
1753         check(n, ['src0/pseudo-e', 'bld0'])
1754         n = bld0.srcdir_find_file('exists-e')
1755         check(n, ['src0/exists-e', 'bld0'])
1756         n = bld0.srcdir_find_file('on-disk-e2')
1757         check(n, ['src0/on-disk-e2', 'bld0'])
1758
1759         test.subdir('src1')
1760         test.write(['src1', 'on-disk-f1'], "src1/on-disk-f1\n")
1761         test.write(['src1', 'on-disk-f2'], "src1/on-disk-f2\n")
1762         test.write(['src1', 'on-disk-e1'], "src1/on-disk-e1\n")
1763         test.write(['src1', 'on-disk-e2'], "src1/on-disk-e2\n")
1764
1765         bld1 = self.fs.Dir('bld1')
1766         src1 = self.fs.Dir('src1')
1767         self.fs.BuildDir(bld1, src1, duplicate=1)
1768
1769         derived_f = src1.File('derived-f')
1770         derived_f.is_derived = return_true
1771         pseudo_f = src1.File('pseudo-f')
1772         pseudo_f.is_pseudo_derived = return_true
1773         exists_f = src1.File('exists-f')
1774         exists_f.exists = return_true
1775
1776         derived_e = src1.Entry('derived-e')
1777         derived_e.is_derived = return_true
1778         pseudo_e = src1.Entry('pseudo-e')
1779         pseudo_e.is_pseudo_derived = return_true
1780         exists_e = src1.Entry('exists-e')
1781         exists_e.exists = return_true
1782
1783         # First check from the source directory.
1784         n = src1.srcdir_find_file('does_not_exist')
1785         assert n == (None, None), n
1786
1787         n = src1.srcdir_find_file('derived-f')
1788         check(n, ['src1/derived-f', 'src1'])
1789         n = src1.srcdir_find_file('pseudo-f')
1790         check(n, ['src1/pseudo-f', 'src1'])
1791         n = src1.srcdir_find_file('exists-f')
1792         check(n, ['src1/exists-f', 'src1'])
1793         n = src1.srcdir_find_file('on-disk-f1')
1794         check(n, ['src1/on-disk-f1', 'src1'])
1795
1796         n = src1.srcdir_find_file('derived-e')
1797         check(n, ['src1/derived-e', 'src1'])
1798         n = src1.srcdir_find_file('pseudo-e')
1799         check(n, ['src1/pseudo-e', 'src1'])
1800         n = src1.srcdir_find_file('exists-e')
1801         check(n, ['src1/exists-e', 'src1'])
1802         n = src1.srcdir_find_file('on-disk-e1')
1803         check(n, ['src1/on-disk-e1', 'src1'])
1804
1805         # Now check from the build directory.
1806         n = bld1.srcdir_find_file('does_not_exist')
1807         assert n == (None, None), n
1808
1809         n = bld1.srcdir_find_file('derived-f')
1810         check(n, ['bld1/derived-f', 'src1'])
1811         n = bld1.srcdir_find_file('pseudo-f')
1812         check(n, ['bld1/pseudo-f', 'src1'])
1813         n = bld1.srcdir_find_file('exists-f')
1814         check(n, ['bld1/exists-f', 'src1'])
1815         n = bld1.srcdir_find_file('on-disk-f2')
1816         check(n, ['bld1/on-disk-f2', 'bld1'])
1817
1818         n = bld1.srcdir_find_file('derived-e')
1819         check(n, ['bld1/derived-e', 'src1'])
1820         n = bld1.srcdir_find_file('pseudo-e')
1821         check(n, ['bld1/pseudo-e', 'src1'])
1822         n = bld1.srcdir_find_file('exists-e')
1823         check(n, ['bld1/exists-e', 'src1'])
1824         n = bld1.srcdir_find_file('on-disk-e2')
1825         check(n, ['bld1/on-disk-e2', 'bld1'])
1826
1827     def test_dir_on_disk(self):
1828         """Test the Dir.dir_on_disk() method"""
1829         self.test.subdir('sub', ['sub', 'exists'])
1830         self.test.write(['sub', 'file'], "self/file\n")
1831         sub = self.fs.Dir('sub')
1832
1833         r = sub.dir_on_disk('does_not_exist')
1834         assert not r, r
1835
1836         r = sub.dir_on_disk('exists')
1837         assert r, r
1838
1839         r = sub.dir_on_disk('file')
1840         assert not r, r
1841
1842     def test_file_on_disk(self):
1843         """Test the Dir.file_on_disk() method"""
1844         self.test.subdir('sub', ['sub', 'dir'])
1845         self.test.write(['sub', 'exists'], "self/exists\n")
1846         sub = self.fs.Dir('sub')
1847
1848         r = sub.file_on_disk('does_not_exist')
1849         assert not r, r
1850
1851         r = sub.file_on_disk('exists')
1852         assert r, r
1853
1854         r = sub.file_on_disk('dir')
1855         assert not r, r
1856
1857 class EntryTestCase(_tempdirTestCase):
1858     def test_runTest(self):
1859         """Test methods specific to the Entry sub-class.
1860         """
1861         test = TestCmd(workdir='')
1862         # FS doesn't like the cwd to be something other than its root.
1863         os.chdir(test.workpath(""))
1864
1865         fs = SCons.Node.FS.FS()
1866
1867         e1 = fs.Entry('e1')
1868         e1.rfile()
1869         assert e1.__class__ is SCons.Node.FS.File, e1.__class__
1870
1871         test.subdir('e3d')
1872         test.write('e3f', "e3f\n")
1873
1874         e3d = fs.Entry('e3d')
1875         e3d.get_contents()
1876         assert e3d.__class__ is SCons.Node.FS.Dir, e3d.__class__
1877
1878         e3f = fs.Entry('e3f')
1879         e3f.get_contents()
1880         assert e3f.__class__ is SCons.Node.FS.File, e3f.__class__
1881
1882         e3n = fs.Entry('e3n')
1883         e3n.get_contents()
1884         assert e3n.__class__ is SCons.Node.FS.Entry, e3n.__class__
1885
1886         test.subdir('e4d')
1887         test.write('e4f', "e4f\n")
1888
1889         e4d = fs.Entry('e4d')
1890         exists = e4d.exists()
1891         assert e4d.__class__ is SCons.Node.FS.Dir, e4d.__class__
1892         assert exists, "e4d does not exist?"
1893
1894         e4f = fs.Entry('e4f')
1895         exists = e4f.exists()
1896         assert e4f.__class__ is SCons.Node.FS.File, e4f.__class__
1897         assert exists, "e4f does not exist?"
1898
1899         e4n = fs.Entry('e4n')
1900         exists = e4n.exists()
1901         assert e4n.__class__ is SCons.Node.FS.File, e4n.__class__
1902         assert not exists, "e4n exists?"
1903
1904         class MyCalc:
1905             def __init__(self, val):
1906                 self.max_drift = 0
1907                 class M:
1908                     def __init__(self, val):
1909                         self.val = val
1910                     def collect(self, args):
1911                         return reduce(lambda x, y: x+y, args)
1912                     def signature(self, executor):
1913                         return self.val + 222
1914                 self.module = M(val)
1915
1916         test.subdir('e5d')
1917         test.write('e5f', "e5f\n")
1918
1919         e5f = fs.Entry('e5f')
1920         sig = e5f.calc_signature(MyCalc(666))
1921         assert e5f.__class__ is SCons.Node.FS.File, e5f.__class__
1922         # This node has no builder, so it just calculates the
1923         # signature once: the source content signature.
1924         assert sig == 888, sig
1925
1926         e5n = fs.Entry('e5n')
1927         sig = e5n.calc_signature(MyCalc(777))
1928         assert e5n.__class__ is SCons.Node.FS.File, e5n.__class__
1929         # Doesn't exist, no sources, and no builder: no sig
1930         assert sig is None, sig
1931
1932     def test_Entry_Entry_lookup(self):
1933         """Test looking up an Entry within another Entry"""
1934         self.fs.Entry('#topdir')
1935         self.fs.Entry('#topdir/a/b/c')
1936
1937
1938
1939 class FileTestCase(_tempdirTestCase):
1940
1941     def test_Dirs(self):
1942         """Test the File.Dirs() method"""
1943         fff = self.fs.File('subdir/fff')
1944         # This simulates that the SConscript file that defined
1945         # fff is in subdir/.
1946         fff.cwd = self.fs.Dir('subdir')
1947         d1 = self.fs.Dir('subdir/d1')
1948         d2 = self.fs.Dir('subdir/d2')
1949         dirs = fff.Dirs(['d1', 'd2'])
1950         assert dirs == [d1, d2], map(str, dirs)
1951
1952     def test_exists(self):
1953         """Test the File.exists() method"""
1954         fs = self.fs
1955         test = self.test
1956
1957         src_f1 = fs.File('src/f1')
1958         assert not src_f1.exists(), "%s apparently exists?" % src_f1
1959
1960         test.subdir('src')
1961         test.write(['src', 'f1'], "src/f1\n")
1962
1963         assert not src_f1.exists(), "%s did not cache previous exists() value" % src_f1
1964         src_f1.clear()
1965         assert src_f1.exists(), "%s apparently does not exist?" % src_f1
1966
1967         test.subdir('build')
1968         fs.BuildDir('build', 'src')
1969         build_f1 = fs.File('build/f1')
1970
1971         assert build_f1.exists(), "%s did not realize that %s exists" % (build_f1, src_f1)
1972         assert os.path.exists(build_f1.abspath), "%s did not get duplicated on disk" % build_f1.abspath
1973
1974         test.unlink(['src', 'f1'])
1975         src_f1.clear()  # so the next exists() call will look on disk again
1976
1977         assert build_f1.exists(), "%s did not cache previous exists() value" % build_f1
1978         build_f1.clear()
1979         build_f1.linked = None
1980         assert not build_f1.exists(), "%s did not realize that %s disappeared" % (build_f1, src_f1)
1981         assert not os.path.exists(build_f1.abspath), "%s did not get removed after %s was removed" % (build_f1, src_f1)
1982
1983
1984
1985 class RepositoryTestCase(_tempdirTestCase):
1986
1987     def setUp(self):
1988         _tempdirTestCase.setUp(self)
1989
1990         self.test.subdir('rep1', 'rep2', 'rep3', 'work')
1991
1992         self.rep1 = self.test.workpath('rep1')
1993         self.rep2 = self.test.workpath('rep2')
1994         self.rep3 = self.test.workpath('rep3')
1995
1996         os.chdir(self.test.workpath('work'))
1997
1998         self.fs = SCons.Node.FS.FS()
1999         self.fs.Repository(self.rep1, self.rep2, self.rep3)
2000
2001     def test_getRepositories(self):
2002         """Test the Dir.getRepositories() method"""
2003         self.fs.Repository('foo')
2004         self.fs.Repository(os.path.join('foo', 'bar'))
2005         self.fs.Repository('bar/foo')
2006         self.fs.Repository('bar')
2007
2008         expect = [
2009             self.rep1,
2010             self.rep2,
2011             self.rep3,
2012             'foo',
2013             os.path.join('foo', 'bar'),
2014             os.path.join('bar', 'foo'),
2015             'bar'
2016         ]
2017
2018         rep = self.fs.Dir('#').getRepositories()
2019         r = map(lambda x, np=os.path.normpath: np(str(x)), rep)
2020         assert r == expect, r
2021
2022     def test_get_all_rdirs(self):
2023         """Test the Dir.get_all_rdirs() method"""
2024         self.fs.Repository('foo')
2025         self.fs.Repository(os.path.join('foo', 'bar'))
2026         self.fs.Repository('bar/foo')
2027         self.fs.Repository('bar')
2028
2029         expect = [
2030             '.',
2031             self.rep1,
2032             self.rep2,
2033             self.rep3,
2034             'foo',
2035             os.path.join('foo', 'bar'),
2036             os.path.join('bar', 'foo'),
2037             'bar'
2038         ]
2039
2040         rep = self.fs.Dir('#').get_all_rdirs()
2041         r = map(lambda x, np=os.path.normpath: np(str(x)), rep)
2042         assert r == expect, r
2043
2044     def test_rdir(self):
2045         """Test the Dir.rdir() method"""
2046         return_true = lambda: 1
2047         return_false = lambda: 0
2048
2049         d1 = self.fs.Dir('d1')
2050         d2 = self.fs.Dir('d2')
2051         d3 = self.fs.Dir('d3')
2052
2053         self.test.subdir([self.rep1, 'd2'])
2054         self.test.write([self.rep2, 'd3'], "")
2055         self.test.subdir([self.rep3, 'd3'])
2056
2057         r = d1.rdir()
2058         assert r is d1, r
2059
2060         r = d2.rdir()
2061         assert not r is d2, r
2062         r = str(r)
2063         assert r == os.path.join(self.rep1, 'd2'), r
2064
2065         r = d3.rdir()
2066         assert not r is d3, r
2067         r = str(r)
2068         assert r == os.path.join(self.rep3, 'd3'), r
2069
2070         e1 = self.fs.Dir('e1')
2071         e1.exists = return_false
2072         e2 = self.fs.Dir('e2')
2073         e2.exists = return_false
2074
2075         # Make sure we match entries in repositories,
2076         # regardless of whether they're derived or not.
2077
2078         re1 = self.fs.Entry(os.path.join(self.rep1, 'e1'))
2079         re1.exists = return_true
2080         re1.is_derived = return_true
2081         re2 = self.fs.Entry(os.path.join(self.rep2, 'e2'))
2082         re2.exists = return_true
2083         re2.is_derived = return_false
2084
2085         r = e1.rdir()
2086         assert r is re1, r
2087
2088         r = e2.rdir()
2089         assert r is re2, r
2090
2091     def test_rfile(self):
2092         """Test the File.rfile() method"""
2093         return_true = lambda: 1
2094         return_false = lambda: 0
2095
2096         f1 = self.fs.File('f1')
2097         f2 = self.fs.File('f2')
2098         f3 = self.fs.File('f3')
2099
2100         self.test.write([self.rep1, 'f2'], "")
2101         self.test.subdir([self.rep2, 'f3'])
2102         self.test.write([self.rep3, 'f3'], "")
2103
2104         r = f1.rfile()
2105         assert r is f1, r
2106
2107         r = f2.rfile()
2108         assert not r is f2, r
2109         r = str(r)
2110         assert r == os.path.join(self.rep1, 'f2'), r
2111
2112         r = f3.rfile()
2113         assert not r is f3, r
2114         r = f3.rstr()
2115         assert r == os.path.join(self.rep3, 'f3'), r
2116
2117         e1 = self.fs.File('e1')
2118         e1.exists = return_false
2119         e2 = self.fs.File('e2')
2120         e2.exists = return_false
2121
2122         # Make sure we match entries in repositories,
2123         # regardless of whether they're derived or not.
2124
2125         re1 = self.fs.Entry(os.path.join(self.rep1, 'e1'))
2126         re1.exists = return_true
2127         re1.is_derived = return_true
2128         re2 = self.fs.Entry(os.path.join(self.rep2, 'e2'))
2129         re2.exists = return_true
2130         re2.is_derived = return_false
2131
2132         r = e1.rfile()
2133         assert r is re1, r
2134
2135         r = e2.rfile()
2136         assert r is re2, r
2137
2138     def test_Rfindalldirs(self):
2139         """Test the Rfindalldirs() methods"""
2140         fs = self.fs
2141         test = self.test
2142
2143         d1 = fs.Dir('d1')
2144         d2 = fs.Dir('d2')
2145         rep1_d1 = fs.Dir(test.workpath('rep1', 'd1'))
2146         rep2_d1 = fs.Dir(test.workpath('rep2', 'd1'))
2147         rep3_d1 = fs.Dir(test.workpath('rep3', 'd1'))
2148         sub = fs.Dir('sub')
2149         sub_d1 = sub.Dir('d1')
2150         rep1_sub_d1 = fs.Dir(test.workpath('rep1', 'sub', 'd1'))
2151         rep2_sub_d1 = fs.Dir(test.workpath('rep2', 'sub', 'd1'))
2152         rep3_sub_d1 = fs.Dir(test.workpath('rep3', 'sub', 'd1'))
2153
2154         r = fs.Top.Rfindalldirs((d1,))
2155         assert r == [d1], map(str, r)
2156
2157         r = fs.Top.Rfindalldirs((d1, d2))
2158         assert r == [d1, d2], map(str, r)
2159
2160         r = fs.Top.Rfindalldirs(('d1',))
2161         assert r == [d1, rep1_d1, rep2_d1, rep3_d1], map(str, r)
2162
2163         r = fs.Top.Rfindalldirs(('#d1',))
2164         assert r == [d1, rep1_d1, rep2_d1, rep3_d1], map(str, r)
2165
2166         r = sub.Rfindalldirs(('d1',))
2167         assert r == [sub_d1, rep1_sub_d1, rep2_sub_d1, rep3_sub_d1], map(str, r)
2168
2169         r = sub.Rfindalldirs(('#d1',))
2170         assert r == [d1, rep1_d1, rep2_d1, rep3_d1], map(str, r)
2171
2172         r = fs.Top.Rfindalldirs(('d1', d2))
2173         assert r == [d1, rep1_d1, rep2_d1, rep3_d1, d2], map(str, r)
2174
2175     def test_rexists(self):
2176         """Test the Entry.rexists() method"""
2177         fs = self.fs
2178         test = self.test
2179
2180         test.write([self.rep1, 'f2'], "")
2181         test.write([self.rep2, "i_exist"], "\n")
2182         test.write(["work", "i_exist_too"], "\n")
2183
2184         fs.BuildDir('build', '.')
2185
2186         f = fs.File(test.workpath("work", "i_do_not_exist"))
2187         assert not f.rexists()
2188
2189         f = fs.File(test.workpath("work", "i_exist"))
2190         assert f.rexists()
2191
2192         f = fs.File(test.workpath("work", "i_exist_too"))
2193         assert f.rexists()
2194
2195         f1 = fs.File(os.path.join('build', 'f1'))
2196         assert not f1.rexists()
2197
2198         f2 = fs.File(os.path.join('build', 'f2'))
2199         assert f2.rexists()
2200
2201     def test_FAT_timestamps(self):
2202         """Test repository timestamps on FAT file systems"""
2203         fs = self.fs
2204         test = self.test
2205
2206         test.write(["rep2", "tstamp"], "tstamp\n")
2207         try:
2208             # Okay, *this* manipulation accomodates Windows FAT file systems
2209             # that only have two-second granularity on their timestamps.
2210             # We round down the current time to the nearest even integer
2211             # value, subtract two to make sure the timestamp is not "now,"
2212             # and then convert it back to a float.
2213             tstamp = float(int(time.time() / 2) * 2) - 2
2214             os.utime(test.workpath("rep2", "tstamp"), (tstamp - 2.0, tstamp))
2215             f = fs.File("tstamp")
2216             t = f.get_timestamp()
2217             assert t == tstamp, "expected %f, got %f" % (tstamp, t)
2218         finally:
2219             test.unlink(["rep2", "tstamp"])
2220
2221     def test_get_contents(self):
2222         """Ensure get_contents() returns binary contents from Repositories"""
2223         fs = self.fs
2224         test = self.test
2225
2226         test.write(["rep3", "contents"], "Con\x1aTents\n")
2227         try:
2228             c = fs.File("contents").get_contents()
2229             assert c == "Con\x1aTents\n", "got '%s'" % c
2230         finally:
2231             test.unlink(["rep3", "contents"])
2232
2233     #def test calc_signature(self):
2234
2235     #def test current(self):
2236
2237
2238
2239 class find_fileTestCase(unittest.TestCase):
2240     def runTest(self):
2241         """Testing find_file function"""
2242         test = TestCmd(workdir = '')
2243         test.write('./foo', 'Some file\n')
2244         test.write('./foo2', 'Another file\n')
2245         test.subdir('same')
2246         test.subdir('bar')
2247         test.write(['bar', 'on_disk'], 'Another file\n')
2248         test.write(['bar', 'same'], 'bar/same\n')
2249
2250         fs = SCons.Node.FS.FS(test.workpath(""))
2251         # FS doesn't like the cwd to be something other than its root.
2252         os.chdir(test.workpath(""))
2253
2254         node_derived = fs.File(test.workpath('bar/baz'))
2255         node_derived.builder_set(1) # Any non-zero value.
2256         node_pseudo = fs.File(test.workpath('pseudo'))
2257         node_pseudo.set_src_builder(1) # Any non-zero value.
2258
2259         paths = tuple(map(fs.Dir, ['.', 'same', './bar']))
2260         nodes = [SCons.Node.FS.find_file('foo', paths)]
2261         nodes.append(SCons.Node.FS.find_file('baz', paths))
2262         nodes.append(SCons.Node.FS.find_file('pseudo', paths))
2263         nodes.append(SCons.Node.FS.find_file('same', paths))
2264
2265         file_names = map(str, nodes)
2266         file_names = map(os.path.normpath, file_names)
2267         expect = ['./foo', './bar/baz', './pseudo', './bar/same']
2268         expect = map(os.path.normpath, expect)
2269         assert file_names == expect, file_names
2270
2271         # Make sure we don't blow up if there's already a File in place
2272         # of a directory that we'd otherwise try to search.  If this
2273         # is broken, we'll see an exception like "Tried to lookup File
2274         # 'bar/baz' as a Dir.
2275         SCons.Node.FS.find_file('baz/no_file_here', paths)
2276
2277         import StringIO
2278         save_sys_stdout = sys.stdout
2279
2280         try:
2281             sio = StringIO.StringIO()
2282             sys.stdout = sio
2283             SCons.Node.FS.find_file('foo2', paths, verbose="xyz")
2284             expect = "  xyz: looking for 'foo2' in '.' ...\n" + \
2285                      "  xyz: ... FOUND 'foo2' in '.'\n"
2286             c = sio.getvalue()
2287             assert c == expect, c
2288
2289             sio = StringIO.StringIO()
2290             sys.stdout = sio
2291             SCons.Node.FS.find_file('baz2', paths, verbose=1)
2292             expect = "  find_file: looking for 'baz2' in '.' ...\n" + \
2293                      "  find_file: looking for 'baz2' in 'same' ...\n" + \
2294                      "  find_file: looking for 'baz2' in 'bar' ...\n"
2295             c = sio.getvalue()
2296             assert c == expect, c
2297
2298             sio = StringIO.StringIO()
2299             sys.stdout = sio
2300             SCons.Node.FS.find_file('on_disk', paths, verbose=1)
2301             expect = "  find_file: looking for 'on_disk' in '.' ...\n" + \
2302                      "  find_file: looking for 'on_disk' in 'same' ...\n" + \
2303                      "  find_file: looking for 'on_disk' in 'bar' ...\n" + \
2304                      "  find_file: ... FOUND 'on_disk' in 'bar'\n"
2305             c = sio.getvalue()
2306             assert c == expect, c
2307         finally:
2308             sys.stdout = save_sys_stdout
2309
2310 class StringDirTestCase(unittest.TestCase):
2311     def runTest(self):
2312         """Test using a string as the second argument of
2313         File() and Dir()"""
2314
2315         test = TestCmd(workdir = '')
2316         test.subdir('sub')
2317         fs = SCons.Node.FS.FS(test.workpath(''))
2318
2319         d = fs.Dir('sub', '.')
2320         assert str(d) == 'sub'
2321         assert d.exists()
2322         f = fs.File('file', 'sub')
2323         assert str(f) == os.path.join('sub', 'file')
2324         assert not f.exists()
2325
2326 class stored_infoTestCase(unittest.TestCase):
2327     def runTest(self):
2328         """Test how we store build information"""
2329         test = TestCmd(workdir = '')
2330         test.subdir('sub')
2331         fs = SCons.Node.FS.FS(test.workpath(''))
2332
2333         d = fs.Dir('sub')
2334         f = fs.File('file1', d)
2335         bi = f.get_stored_info()
2336         assert bi.ninfo.timestamp == 0, bi.ninfo.timestamp
2337         assert bi.ninfo.size == None, bi.ninfo.size
2338
2339         class MySConsign:
2340             class Null:
2341                 def __init__(self):
2342                     self.xyzzy = 7
2343             def get_entry(self, name):
2344                 return self.Null()
2345
2346         f = fs.File('file2', d)
2347         f.dir.sconsign = MySConsign
2348         bi = f.get_stored_info()
2349         assert bi.xyzzy == 7, bi
2350
2351 class has_src_builderTestCase(unittest.TestCase):
2352     def runTest(self):
2353         """Test the has_src_builder() method"""
2354         test = TestCmd(workdir = '')
2355         fs = SCons.Node.FS.FS(test.workpath(''))
2356         os.chdir(test.workpath(''))
2357         test.subdir('sub1')
2358         test.subdir('sub2', ['sub2', 'SCCS'], ['sub2', 'RCS'])
2359
2360         sub1 = fs.Dir('sub1', '.')
2361         f1 = fs.File('f1', sub1)
2362         f2 = fs.File('f2', sub1)
2363         f3 = fs.File('f3', sub1)
2364         sub2 = fs.Dir('sub2', '.')
2365         f4 = fs.File('f4', sub2)
2366         f5 = fs.File('f5', sub2)
2367         f6 = fs.File('f6', sub2)
2368         f7 = fs.File('f7', sub2)
2369         f8 = fs.File('f8', sub2)
2370
2371         h = f1.has_src_builder()
2372         assert not h, h
2373         h = f1.has_builder()
2374         assert not h, h
2375
2376         b1 = Builder(fs.File)
2377         sub1.set_src_builder(b1)
2378
2379         test.write(['sub1', 'f2'], "sub1/f2\n")
2380         h = f1.has_src_builder()        # cached from previous call
2381         assert not h, h
2382         h = f1.has_builder()            # cached from previous call
2383         assert not h, h
2384         h = f2.has_src_builder()
2385         assert not h, h
2386         h = f2.has_builder()
2387         assert not h, h
2388         h = f3.has_src_builder()
2389         assert h, h
2390         h = f3.has_builder()
2391         assert h, h
2392         assert f3.builder is b1, f3.builder
2393
2394         f7.set_src_builder(b1)
2395         f8.builder_set(b1)
2396
2397         test.write(['sub2', 'SCCS', 's.f5'], "sub2/SCCS/s.f5\n")
2398         test.write(['sub2', 'RCS', 'f6,v'], "sub2/RCS/f6,v\n")
2399         h = f4.has_src_builder()
2400         assert not h, h
2401         h = f4.has_builder()
2402         assert not h, h
2403         h = f5.has_src_builder()
2404         assert h, h
2405         h = f5.has_builder()
2406         assert h, h
2407         h = f6.has_src_builder()
2408         assert h, h
2409         h = f6.has_builder()
2410         assert h, h
2411         h = f7.has_src_builder()
2412         assert h, h
2413         h = f7.has_builder()
2414         assert h, h
2415         h = f8.has_src_builder()
2416         assert not h, h
2417         h = f8.has_builder()
2418         assert h, h
2419
2420 class prepareTestCase(unittest.TestCase):
2421     def runTest(self):
2422         """Test the prepare() method"""
2423
2424         class MyFile(SCons.Node.FS.File):
2425             def _createDir(self, update=None):
2426                 raise SCons.Errors.StopError
2427             def exists(self):
2428                 return None
2429
2430         fs = SCons.Node.FS.FS()
2431         file = MyFile('foo', fs.Dir('.'), fs)
2432
2433         exc_caught = 0
2434         try:
2435             file.prepare()
2436         except SCons.Errors.StopError:
2437             exc_caught = 1
2438         assert exc_caught, "Should have caught a StopError."
2439
2440         class MkdirAction(Action):
2441             def __init__(self, dir_made):
2442                 self.dir_made = dir_made
2443             def __call__(self, target, source, env, errfunc):
2444                 self.dir_made.extend(target)
2445
2446         dir_made = []
2447         new_dir = fs.Dir("new_dir")
2448         new_dir.builder = Builder(fs.Dir, action=MkdirAction(dir_made))
2449         new_dir.reset_executor()
2450         xyz = fs.File(os.path.join("new_dir", "xyz"))
2451
2452         xyz.set_state(SCons.Node.up_to_date)
2453         xyz.prepare()
2454         assert dir_made == [], dir_made
2455
2456         xyz.set_state(0)
2457         xyz.prepare()
2458         assert dir_made[0].path == "new_dir", dir_made[0]
2459
2460         dir = fs.Dir("dir")
2461         dir.prepare()
2462
2463 class SConstruct_dirTestCase(unittest.TestCase):
2464     def runTest(self):
2465         """Test setting the SConstruct directory"""
2466
2467         fs = SCons.Node.FS.FS()
2468         fs.set_SConstruct_dir(fs.Dir('xxx'))
2469         assert fs.SConstruct_dir.path == 'xxx'
2470
2471 class CacheDirTestCase(unittest.TestCase):
2472     def runTest(self):
2473         """Test CacheDir functionality"""
2474         test = TestCmd(workdir='')
2475
2476         global built_it
2477
2478         fs = SCons.Node.FS.FS()
2479         assert fs.CachePath is None, fs.CachePath
2480         assert fs.cache_force is None, fs.cache_force
2481         assert fs.cache_show is None, fs.cache_show
2482
2483         fs.CacheDir('cache')
2484         assert fs.CachePath == 'cache', fs.CachePath
2485
2486         save_CacheRetrieve = SCons.Node.FS.CacheRetrieve
2487         self.retrieved = []
2488         def retrieve_succeed(target, source, env, self=self, execute=1):
2489             self.retrieved.append(target)
2490             return 0
2491         def retrieve_fail(target, source, env, self=self, execute=1):
2492             self.retrieved.append(target)
2493             return 1
2494
2495         f1 = fs.File("cd.f1")
2496         f1.builder_set(Builder(fs.File))
2497         f1.env_set(Environment())
2498         try:
2499             SCons.Node.FS.CacheRetrieve = retrieve_succeed
2500             self.retrieved = []
2501             built_it = None
2502
2503             r = f1.retrieve_from_cache()
2504             assert r == 1, r
2505             assert self.retrieved == [f1], self.retrieved
2506             assert built_it is None, built_it
2507
2508             SCons.Node.FS.CacheRetrieve = retrieve_fail
2509             self.retrieved = []
2510             built_it = None
2511
2512             r = f1.retrieve_from_cache()
2513             assert r is None, r
2514             assert self.retrieved == [f1], self.retrieved
2515             assert built_it is None, built_it
2516         finally:
2517             SCons.Node.FS.CacheRetrieve = save_CacheRetrieve
2518
2519         save_CacheRetrieveSilent = SCons.Node.FS.CacheRetrieveSilent
2520
2521         fs.cache_show = 1
2522
2523         f2 = fs.File("cd.f2")
2524         f2.builder_set(Builder(fs.File))
2525         f2.env_set(Environment())
2526         try:
2527             SCons.Node.FS.CacheRetrieveSilent = retrieve_succeed
2528             self.retrieved = []
2529             built_it = None
2530
2531             r = f2.retrieve_from_cache()
2532             assert r == 1, r
2533             assert self.retrieved == [f2], self.retrieved
2534             assert built_it is None, built_it
2535
2536             SCons.Node.FS.CacheRetrieveSilent = retrieve_fail
2537             self.retrieved = []
2538             built_it = None
2539
2540             r = f2.retrieve_from_cache()
2541             assert r is None, r
2542             assert self.retrieved == [f2], self.retrieved
2543             assert built_it is None, built_it
2544         finally:
2545             SCons.Node.FS.CacheRetrieveSilent = save_CacheRetrieveSilent
2546
2547         save_CachePush = SCons.Node.FS.CachePush
2548         def push(target, source, env, self=self):
2549             self.pushed.append(target)
2550             return 0
2551         SCons.Node.FS.CachePush = push
2552
2553         try:
2554             self.pushed = []
2555
2556             cd_f3 = test.workpath("cd.f3")
2557             f3 = fs.File(cd_f3)
2558             f3.built()
2559             assert self.pushed == [], self.pushed
2560             test.write(cd_f3, "cd.f3\n")
2561             f3.built()
2562             assert self.pushed == [f3], self.pushed
2563
2564             self.pushed = []
2565
2566             cd_f4 = test.workpath("cd.f4")
2567             f4 = fs.File(cd_f4)
2568             f4.visited()
2569             assert self.pushed == [], self.pushed
2570             test.write(cd_f4, "cd.f4\n")
2571             f4.visited()
2572             assert self.pushed == [], self.pushed
2573             fs.cache_force = 1
2574             f4.visited()
2575             assert self.pushed == [f4], self.pushed
2576         finally:
2577             SCons.Node.FS.CachePush = save_CachePush
2578
2579         # Verify how the cachepath() method determines the name
2580         # of the file in cache.
2581         def my_collect(list):
2582             return list[0]
2583         save_collect = SCons.Sig.MD5.collect
2584         SCons.Sig.MD5.collect = my_collect
2585         try:
2586             f5 = fs.File("cd.f5")
2587             f5.binfo = f5.BuildInfo(f5)
2588             f5.binfo.ninfo.bsig = 'a_fake_bsig'
2589             cp = f5.cachepath()
2590             dirname = os.path.join('cache', 'A')
2591             filename = os.path.join(dirname, 'a_fake_bsig')
2592             assert cp == (dirname, filename), cp
2593         finally:
2594             SCons.Sig.MD5.collect = save_collect
2595
2596         # Verify that no bsig raises an InternalERror
2597         f6 = fs.File("cd.f6")
2598         f6.binfo = f6.BuildInfo(f6)
2599         exc_caught = 0
2600         try:
2601             cp = f6.cachepath()
2602         except SCons.Errors.InternalError:
2603             exc_caught = 1
2604         assert exc_caught
2605
2606         # Verify that we raise a warning if we can't copy a file to cache.
2607         save_copy2 = shutil.copy2
2608         def copy2(src, dst):
2609             raise OSError
2610         shutil.copy2 = copy2
2611         save_mkdir = os.mkdir
2612         def mkdir(dir, mode=0):
2613             pass
2614         os.mkdir = mkdir
2615         old_warn_exceptions = SCons.Warnings.warningAsException(1)
2616         SCons.Warnings.enableWarningClass(SCons.Warnings.CacheWriteErrorWarning)
2617
2618         try:
2619             cd_f7 = test.workpath("cd.f7")
2620             test.write(cd_f7, "cd.f7\n")
2621             f7 = fs.File(cd_f7)
2622             f7.binfo = f7.BuildInfo(f7)
2623             f7.binfo.ninfo.bsig = 'f7_bsig'
2624
2625             warn_caught = 0
2626             try:
2627                 f7.built()
2628             except SCons.Warnings.CacheWriteErrorWarning:
2629                 warn_caught = 1
2630             assert warn_caught
2631         finally:
2632             shutil.copy2 = save_copy2
2633             os.mkdir = save_mkdir
2634             SCons.Warnings.warningAsException(old_warn_exceptions)
2635             SCons.Warnings.suppressWarningClass(SCons.Warnings.CacheWriteErrorWarning)
2636
2637         # Verify that we don't blow up if there's no strfunction()
2638         # for an action.
2639         act = Action()
2640         act.strfunction = None
2641         f8 = fs.File("cd.f8")
2642         f8.builder_set(Builder(fs.File, action=act))
2643         f8.env_set(Environment())
2644         try:
2645             SCons.Node.FS.CacheRetrieveSilent = retrieve_succeed
2646             self.retrieved = []
2647             built_it = None
2648
2649             r = f8.retrieve_from_cache()
2650             assert r == 1, r
2651             assert self.retrieved == [f8], self.retrieved
2652             assert built_it is None, built_it
2653
2654             SCons.Node.FS.CacheRetrieveSilent = retrieve_fail
2655             self.retrieved = []
2656             built_it = None
2657
2658             r = f8.retrieve_from_cache()
2659             assert r is None, r
2660             assert self.retrieved == [f8], self.retrieved
2661             assert built_it is None, built_it
2662         finally:
2663             SCons.Node.FS.CacheRetrieveSilent = save_CacheRetrieveSilent
2664
2665 class clearTestCase(unittest.TestCase):
2666     def runTest(self):
2667         """Test clearing FS nodes of cached data."""
2668         fs = SCons.Node.FS.FS()
2669         test = TestCmd(workdir='')
2670
2671         e = fs.Entry('e')
2672         assert not e.exists()
2673         assert not e.rexists()
2674         assert str(e) == 'e', str(d)
2675         e.clear()
2676         assert not e.exists()
2677         assert not e.rexists()
2678         assert str(e) == 'e', str(d)
2679
2680         d = fs.Dir(test.workpath('d'))
2681         test.subdir('d')
2682         assert d.exists()
2683         assert d.rexists()
2684         assert str(d) == test.workpath('d'), str(d)
2685         fs.rename(test.workpath('d'), test.workpath('gone'))
2686         # Verify caching is active
2687         assert d.exists(), 'caching not active'
2688         assert d.rexists()
2689         assert str(d) == test.workpath('d'), str(d)
2690         # Now verify clear() resets the cache
2691         d.clear()
2692         assert not d.exists()      
2693         assert not d.rexists()
2694         assert str(d) == test.workpath('d'), str(d)
2695         
2696         f = fs.File(test.workpath('f'))
2697         test.write(test.workpath('f'), 'file f')
2698         assert f.exists()
2699         assert f.rexists()
2700         assert str(f) == test.workpath('f'), str(f)
2701         # Verify caching is active
2702         test.unlink(test.workpath('f'))
2703         assert f.exists()
2704         assert f.rexists()
2705         assert str(f) == test.workpath('f'), str(f)
2706         # Now verify clear() resets the cache
2707         f.clear()
2708         assert not f.exists()
2709         assert not f.rexists()
2710         assert str(f) == test.workpath('f'), str(f)
2711
2712 class disambiguateTestCase(unittest.TestCase):
2713     def runTest(self):
2714         """Test calling the disambiguate() method."""
2715         test = TestCmd(workdir='')
2716
2717         fs = SCons.Node.FS.FS()
2718
2719         ddd = fs.Dir('ddd')
2720         d = ddd.disambiguate()
2721         assert d is ddd, d
2722
2723         fff = fs.File('fff')
2724         f = fff.disambiguate()
2725         assert f is fff, f
2726
2727         test.subdir('edir')
2728         test.write('efile', "efile\n")
2729
2730         edir = fs.Entry(test.workpath('edir'))
2731         d = edir.disambiguate()
2732         assert d.__class__ is ddd.__class__, d.__class__
2733
2734         efile = fs.Entry(test.workpath('efile'))
2735         f = efile.disambiguate()
2736         assert f.__class__ is fff.__class__, f.__class__
2737
2738         test.subdir('build')
2739         test.subdir(['build', 'bdir'])
2740         test.write(['build', 'bfile'], "build/bfile\n")
2741
2742         test.subdir('src')
2743         test.write(['src', 'bdir'], "src/bdir\n")
2744         test.subdir(['src', 'bfile'])
2745
2746         test.subdir(['src', 'edir'])
2747         test.write(['src', 'efile'], "src/efile\n")
2748
2749         fs.BuildDir(test.workpath('build'), test.workpath('src'))
2750
2751         build_bdir = fs.Entry(test.workpath('build/bdir'))
2752         d = build_bdir.disambiguate()
2753         assert d is build_bdir, d
2754         assert d.__class__ is ddd.__class__, d.__class__
2755
2756         build_bfile = fs.Entry(test.workpath('build/bfile'))
2757         f = build_bfile.disambiguate()
2758         assert f is build_bfile, f
2759         assert f.__class__ is fff.__class__, f.__class__
2760
2761         build_edir = fs.Entry(test.workpath('build/edir'))
2762         d = build_edir.disambiguate()
2763         assert d.__class__ is ddd.__class__, d.__class__
2764
2765         build_efile = fs.Entry(test.workpath('build/efile'))
2766         f = build_efile.disambiguate()
2767         assert f.__class__ is fff.__class__, f.__class__
2768
2769         build_nonexistant = fs.Entry(test.workpath('build/nonexistant'))
2770         f = build_nonexistant.disambiguate()
2771         assert f.__class__ is fff.__class__, f.__class__
2772
2773 class postprocessTestCase(unittest.TestCase):
2774     def runTest(self):
2775         """Test calling the postprocess() method."""
2776         fs = SCons.Node.FS.FS()
2777
2778         e = fs.Entry('e')
2779         e.postprocess()
2780
2781         d = fs.Dir('d')
2782         d.postprocess()
2783
2784         f = fs.File('f')
2785         f.postprocess()
2786
2787 class SpecialAttrTestCase(unittest.TestCase):
2788     def runTest(self):
2789         """Test special attributes of file nodes."""
2790         test=TestCmd(workdir='')
2791         fs = SCons.Node.FS.FS(test.workpath('work'))
2792
2793         f = fs.Entry('foo/bar/baz.blat').get_subst_proxy()
2794
2795         s = str(f.dir)
2796         assert s == os.path.normpath('foo/bar'), s
2797         assert f.dir.is_literal(), f.dir
2798         for_sig = f.dir.for_signature()
2799         assert for_sig == 'bar', for_sig
2800
2801         s = str(f.file)
2802         assert s == 'baz.blat', s
2803         assert f.file.is_literal(), f.file
2804         for_sig = f.file.for_signature()
2805         assert for_sig == 'baz.blat_file', for_sig
2806
2807         s = str(f.base)
2808         assert s == os.path.normpath('foo/bar/baz'), s
2809         assert f.base.is_literal(), f.base
2810         for_sig = f.base.for_signature()
2811         assert for_sig == 'baz.blat_base', for_sig
2812
2813         s = str(f.filebase)
2814         assert s == 'baz', s
2815         assert f.filebase.is_literal(), f.filebase
2816         for_sig = f.filebase.for_signature()
2817         assert for_sig == 'baz.blat_filebase', for_sig
2818
2819         s = str(f.suffix)
2820         assert s == '.blat', s
2821         assert f.suffix.is_literal(), f.suffix
2822         for_sig = f.suffix.for_signature()
2823         assert for_sig == 'baz.blat_suffix', for_sig
2824
2825         s = str(f.abspath)
2826         assert s == test.workpath('work', 'foo', 'bar', 'baz.blat'), s
2827         assert f.abspath.is_literal(), f.abspath
2828         for_sig = f.abspath.for_signature()
2829         assert for_sig == 'baz.blat_abspath', for_sig
2830
2831         s = str(f.posix)
2832         assert s == 'foo/bar/baz.blat', s
2833         assert f.posix.is_literal(), f.posix
2834         if f.posix != f:
2835             for_sig = f.posix.for_signature()
2836             assert for_sig == 'baz.blat_posix', for_sig
2837
2838         s = str(f.windows)
2839         assert s == 'foo\\bar\\baz.blat', repr(s)
2840         assert f.windows.is_literal(), f.windows
2841         if f.windows != f:
2842             for_sig = f.windows.for_signature()
2843             assert for_sig == 'baz.blat_windows', for_sig
2844
2845         # Deprecated synonym for the .windows suffix.
2846         s = str(f.win32)
2847         assert s == 'foo\\bar\\baz.blat', repr(s)
2848         assert f.win32.is_literal(), f.win32
2849         if f.win32 != f:
2850             for_sig = f.win32.for_signature()
2851             assert for_sig == 'baz.blat_windows', for_sig
2852
2853         # And now, combinations!!!
2854         s = str(f.srcpath.base)
2855         assert s == os.path.normpath('foo/bar/baz'), s
2856         s = str(f.srcpath.dir)
2857         assert s == str(f.srcdir), s
2858         s = str(f.srcpath.posix)
2859         assert s == 'foo/bar/baz.blat', s
2860         s = str(f.srcpath.windows)
2861         assert s == 'foo\\bar\\baz.blat', s
2862         s = str(f.srcpath.win32)
2863         assert s == 'foo\\bar\\baz.blat', s
2864
2865         # Test what happens with BuildDir()
2866         fs.BuildDir('foo', 'baz')
2867
2868         s = str(f.srcpath)
2869         assert s == os.path.normpath('baz/bar/baz.blat'), s
2870         assert f.srcpath.is_literal(), f.srcpath
2871         g = f.srcpath.get()
2872         assert isinstance(g, SCons.Node.FS.Entry), g.__class__
2873
2874         s = str(f.srcdir)
2875         assert s == os.path.normpath('baz/bar'), s
2876         assert f.srcdir.is_literal(), f.srcdir
2877         g = f.srcdir.get()
2878         assert isinstance(g, SCons.Node.FS.Dir), g.__class__
2879
2880         # And now what happens with BuildDir() + Repository()
2881         fs.Repository(test.workpath('repository'))
2882
2883         f = fs.Entry('foo/sub/file.suffix').get_subst_proxy()
2884         test.subdir('repository',
2885                     ['repository', 'baz'],
2886                     ['repository', 'baz', 'sub'])
2887
2888         rd = test.workpath('repository', 'baz', 'sub')
2889         rf = test.workpath('repository', 'baz', 'sub', 'file.suffix')
2890         test.write(rf, "\n")
2891
2892         s = str(f.srcpath)
2893         assert s == os.path.normpath('baz/sub/file.suffix'), s
2894         assert f.srcpath.is_literal(), f.srcpath
2895         g = f.srcpath.get()
2896         assert isinstance(g, SCons.Node.FS.Entry), g.__class__
2897
2898         s = str(f.srcdir)
2899         assert s == os.path.normpath('baz/sub'), s
2900         assert f.srcdir.is_literal(), f.srcdir
2901         g = f.srcdir.get()
2902         assert isinstance(g, SCons.Node.FS.Dir), g.__class__
2903
2904         s = str(f.rsrcpath)
2905         assert s == rf, s
2906         assert f.rsrcpath.is_literal(), f.rsrcpath
2907         g = f.rsrcpath.get()
2908         assert isinstance(g, SCons.Node.FS.File), g.__class__
2909
2910         s = str(f.rsrcdir)
2911         assert s == rd, s
2912         assert f.rsrcdir.is_literal(), f.rsrcdir
2913         g = f.rsrcdir.get()
2914         assert isinstance(g, SCons.Node.FS.Dir), g.__class__
2915
2916         # Check that attempts to access non-existent attributes of the
2917         # subst proxy generate the right exceptions and messages.
2918         caught = None
2919         try:
2920             fs.Dir('ddd').get_subst_proxy().no_such_attr
2921         except AttributeError, e:
2922             assert str(e) == "Dir instance 'ddd' has no attribute 'no_such_attr'", e
2923             caught = 1
2924         assert caught, "did not catch expected AttributeError"
2925
2926         caught = None
2927         try:
2928             fs.Entry('eee').get_subst_proxy().no_such_attr
2929         except AttributeError, e:
2930             assert str(e) == "Entry instance 'eee' has no attribute 'no_such_attr'", e
2931             caught = 1
2932         assert caught, "did not catch expected AttributeError"
2933
2934         caught = None
2935         try:
2936             fs.File('fff').get_subst_proxy().no_such_attr
2937         except AttributeError, e:
2938             assert str(e) == "File instance 'fff' has no attribute 'no_such_attr'", e
2939             caught = 1
2940         assert caught, "did not catch expected AttributeError"
2941
2942 class SaveStringsTestCase(unittest.TestCase):
2943     def runTest(self):
2944         """Test caching string values of nodes."""
2945         test=TestCmd(workdir='')
2946
2947         def setup(fs):
2948             fs.Dir('src')
2949             fs.Dir('d0')
2950             fs.Dir('d1')
2951
2952             d0_f = fs.File('d0/f')
2953             d1_f = fs.File('d1/f')
2954             d0_b = fs.File('d0/b')
2955             d1_b = fs.File('d1/b')
2956             d1_f.duplicate = 1
2957             d1_b.duplicate = 1
2958             d0_b.builder = 1
2959             d1_b.builder = 1
2960
2961             return [d0_f, d1_f, d0_b, d1_b]
2962
2963         def modify(nodes):
2964             d0_f, d1_f, d0_b, d1_b = nodes
2965             d1_f.duplicate = 0
2966             d1_b.duplicate = 0
2967             d0_b.builder = 0
2968             d1_b.builder = 0
2969
2970         fs1 = SCons.Node.FS.FS(test.workpath('fs1'))
2971         nodes = setup(fs1)
2972         fs1.BuildDir('d0', 'src', duplicate=0)
2973         fs1.BuildDir('d1', 'src', duplicate=1)
2974
2975         s = map(str, nodes)
2976         expect = map(os.path.normpath, ['src/f', 'd1/f', 'd0/b', 'd1/b'])
2977         assert s == expect, s
2978
2979         modify(nodes)
2980
2981         s = map(str, nodes)
2982         expect = map(os.path.normpath, ['src/f', 'src/f', 'd0/b', 'd1/b'])
2983         assert s == expect, s
2984
2985         SCons.Node.FS.save_strings(1)
2986         fs2 = SCons.Node.FS.FS(test.workpath('fs2'))
2987         nodes = setup(fs2)
2988         fs2.BuildDir('d0', 'src', duplicate=0)
2989         fs2.BuildDir('d1', 'src', duplicate=1)
2990
2991         s = map(str, nodes)
2992         expect = map(os.path.normpath, ['src/f', 'd1/f', 'd0/b', 'd1/b'])
2993         assert s == expect, s
2994
2995         modify(nodes)
2996
2997         s = map(str, nodes)
2998         expect = map(os.path.normpath, ['src/f', 'd1/f', 'd0/b', 'd1/b'])
2999         assert s == expect, 'node str() not cached: %s'%s
3000
3001 if __name__ == "__main__":
3002     suite = unittest.TestSuite()
3003     suite.addTest(BuildDirTestCase())
3004     suite.addTest(find_fileTestCase())
3005     suite.addTest(StringDirTestCase())
3006     suite.addTest(stored_infoTestCase())
3007     suite.addTest(has_src_builderTestCase())
3008     suite.addTest(prepareTestCase())
3009     suite.addTest(SConstruct_dirTestCase())
3010     suite.addTest(CacheDirTestCase())
3011     suite.addTest(clearTestCase())
3012     suite.addTest(disambiguateTestCase())
3013     suite.addTest(postprocessTestCase())
3014     suite.addTest(SpecialAttrTestCase())
3015     suite.addTest(SaveStringsTestCase())
3016     tclasses = [
3017         BaseTestCase,
3018         DirTestCase,
3019         DirBuildInfoTestCase,
3020         DirNodeInfoTestCase,
3021         EntryTestCase,
3022         FileTestCase,
3023         FileBuildInfoTestCase,
3024         FileNodeInfoTestCase,
3025         FSTestCase,
3026         RepositoryTestCase,
3027     ]
3028     for tclass in tclasses:
3029         names = unittest.getTestCaseNames(tclass, 'test_')
3030         suite.addTests(map(tclass, names))
3031     if not unittest.TextTestRunner().run(suite).wasSuccessful():
3032         sys.exit(1)