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