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