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