http://scons.tigris.org/issues/show_bug.cgi?id=2329
[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 = sorted([x.path for x in dir.children(None)])
1726         assert kids == [os.path.join('ddd', 'd1'),
1727                         os.path.join('ddd', 'f1'),
1728                         os.path.join('ddd', 'f2'),
1729                         os.path.join('ddd', 'f3')], kids
1730
1731     def test_get_contents(self):
1732         """Test getting the contents for a directory.
1733         """
1734         test = self.test
1735
1736         test.subdir('d')
1737         test.write(['d', 'g'], "67890\n")
1738         test.write(['d', 'f'], "12345\n")
1739         test.subdir(['d','sub'])
1740         test.write(['d', 'sub','h'], "abcdef\n")
1741         test.subdir(['d','empty'])
1742
1743         d = self.fs.Dir('d')
1744         g = self.fs.File(os.path.join('d', 'g'))
1745         f = self.fs.File(os.path.join('d', 'f'))
1746         h = self.fs.File(os.path.join('d', 'sub', 'h'))
1747         e = self.fs.Dir(os.path.join('d', 'empty'))
1748         s = self.fs.Dir(os.path.join('d', 'sub'))
1749
1750         #TODO(1.5) files = d.get_contents().split('\n')
1751         files = d.get_contents().split('\n')
1752
1753         assert e.get_contents() == '', e.get_contents()
1754         assert e.get_text_contents() == '', e.get_text_contents()
1755         assert e.get_csig()+" empty" == files[0], files
1756         assert f.get_csig()+" f" == files[1], files
1757         assert g.get_csig()+" g" == files[2], files
1758         assert s.get_csig()+" sub" == files[3], files
1759
1760     def test_implicit_re_scans(self):
1761         """Test that adding entries causes a directory to be re-scanned
1762         """
1763
1764         fs = self.fs
1765
1766         dir = fs.Dir('ddd')
1767
1768         fs.File(os.path.join('ddd', 'f1'))
1769         dir.scan()
1770         kids = sorted([x.path for x in dir.children()])
1771         assert kids == [os.path.join('ddd', 'f1')], kids
1772
1773         fs.File(os.path.join('ddd', 'f2'))
1774         dir.scan()
1775         kids = sorted([x.path for x in dir.children()])
1776         assert kids == [os.path.join('ddd', 'f1'),
1777                         os.path.join('ddd', 'f2')], kids
1778
1779     def test_entry_exists_on_disk(self):
1780         """Test the Dir.entry_exists_on_disk() method
1781         """
1782         test = self.test
1783
1784         does_not_exist = self.fs.Dir('does_not_exist')
1785         assert not does_not_exist.entry_exists_on_disk('foo')
1786
1787         test.subdir('d')
1788         test.write(['d', 'exists'], "d/exists\n")
1789         test.write(['d', 'Case-Insensitive'], "d/Case-Insensitive\n")
1790
1791         d = self.fs.Dir('d')
1792         assert d.entry_exists_on_disk('exists')
1793         assert not d.entry_exists_on_disk('does_not_exist')
1794
1795         if os.path.normcase("TeSt") != os.path.normpath("TeSt") or sys.platform == "cygwin":
1796             assert d.entry_exists_on_disk('case-insensitive')
1797
1798     def test_srcdir_list(self):
1799         """Test the Dir.srcdir_list() method
1800         """
1801         src = self.fs.Dir('src')
1802         bld = self.fs.Dir('bld')
1803         sub1 = bld.Dir('sub')
1804         sub2 = sub1.Dir('sub')
1805         sub3 = sub2.Dir('sub')
1806         self.fs.VariantDir(bld, src, duplicate=0)
1807         self.fs.VariantDir(sub2, src, duplicate=0)
1808
1809         def check(result, expect):
1810             result = list(map(str, result))
1811             expect = list(map(os.path.normpath, expect))
1812             assert result == expect, result
1813
1814         s = src.srcdir_list()
1815         check(s, [])
1816
1817         s = bld.srcdir_list()
1818         check(s, ['src'])
1819
1820         s = sub1.srcdir_list()
1821         check(s, ['src/sub'])
1822
1823         s = sub2.srcdir_list()
1824         check(s, ['src', 'src/sub/sub'])
1825
1826         s = sub3.srcdir_list()
1827         check(s, ['src/sub', 'src/sub/sub/sub'])
1828
1829         self.fs.VariantDir('src/b1/b2', 'src')
1830         b1 = src.Dir('b1')
1831         b1_b2 = b1.Dir('b2')
1832         b1_b2_b1 = b1_b2.Dir('b1')
1833         b1_b2_b1_b2 = b1_b2_b1.Dir('b2')
1834         b1_b2_b1_b2_sub = b1_b2_b1_b2.Dir('sub')
1835
1836         s = b1.srcdir_list()
1837         check(s, [])
1838
1839         s = b1_b2.srcdir_list()
1840         check(s, ['src'])
1841
1842         s = b1_b2_b1.srcdir_list()
1843         check(s, ['src/b1'])
1844
1845         s = b1_b2_b1_b2.srcdir_list()
1846         check(s, ['src/b1/b2'])
1847
1848         s = b1_b2_b1_b2_sub.srcdir_list()
1849         check(s, ['src/b1/b2/sub'])
1850
1851     def test_srcdir_duplicate(self):
1852         """Test the Dir.srcdir_duplicate() method
1853         """
1854         test = self.test
1855
1856         test.subdir('src0')
1857         test.write(['src0', 'exists'], "src0/exists\n")
1858
1859         bld0 = self.fs.Dir('bld0')
1860         src0 = self.fs.Dir('src0')
1861         self.fs.VariantDir(bld0, src0, duplicate=0)
1862
1863         n = bld0.srcdir_duplicate('does_not_exist')
1864         assert n is None, n
1865         assert not os.path.exists(test.workpath('bld0', 'does_not_exist'))
1866
1867         n = bld0.srcdir_duplicate('exists')
1868         assert str(n) == os.path.normpath('src0/exists'), str(n)
1869         assert not os.path.exists(test.workpath('bld0', 'exists'))
1870
1871         test.subdir('src1')
1872         test.write(['src1', 'exists'], "src0/exists\n")
1873
1874         bld1 = self.fs.Dir('bld1')
1875         src1 = self.fs.Dir('src1')
1876         self.fs.VariantDir(bld1, src1, duplicate=1)
1877
1878         n = bld1.srcdir_duplicate('does_not_exist')
1879         assert n is None, n
1880         assert not os.path.exists(test.workpath('bld1', 'does_not_exist'))
1881
1882         n = bld1.srcdir_duplicate('exists')
1883         assert str(n) == os.path.normpath('bld1/exists'), str(n)
1884         assert os.path.exists(test.workpath('bld1', 'exists'))
1885
1886     def test_srcdir_find_file(self):
1887         """Test the Dir.srcdir_find_file() method
1888         """
1889         test = self.test
1890
1891         return_true = lambda: 1
1892
1893         test.subdir('src0')
1894         test.write(['src0', 'on-disk-f1'], "src0/on-disk-f1\n")
1895         test.write(['src0', 'on-disk-f2'], "src0/on-disk-f2\n")
1896         test.write(['src0', 'on-disk-e1'], "src0/on-disk-e1\n")
1897         test.write(['src0', 'on-disk-e2'], "src0/on-disk-e2\n")
1898
1899         bld0 = self.fs.Dir('bld0')
1900         src0 = self.fs.Dir('src0')
1901         self.fs.VariantDir(bld0, src0, duplicate=0)
1902
1903         derived_f = src0.File('derived-f')
1904         derived_f.is_derived = return_true
1905         exists_f = src0.File('exists-f')
1906         exists_f.exists = return_true
1907
1908         derived_e = src0.Entry('derived-e')
1909         derived_e.is_derived = return_true
1910         exists_e = src0.Entry('exists-e')
1911         exists_e.exists = return_true
1912
1913         def check(result, expect):
1914             result = list(map(str, result))
1915             expect = list(map(os.path.normpath, expect))
1916             assert result == expect, result
1917
1918         # First check from the source directory.
1919         n = src0.srcdir_find_file('does_not_exist')
1920         assert n == (None, None), n
1921
1922         n = src0.srcdir_find_file('derived-f')
1923         check(n, ['src0/derived-f', 'src0'])
1924         n = src0.srcdir_find_file('exists-f')
1925         check(n, ['src0/exists-f', 'src0'])
1926         n = src0.srcdir_find_file('on-disk-f1')
1927         check(n, ['src0/on-disk-f1', 'src0'])
1928
1929         n = src0.srcdir_find_file('derived-e')
1930         check(n, ['src0/derived-e', 'src0'])
1931         n = src0.srcdir_find_file('exists-e')
1932         check(n, ['src0/exists-e', 'src0'])
1933         n = src0.srcdir_find_file('on-disk-e1')
1934         check(n, ['src0/on-disk-e1', 'src0'])
1935
1936         # Now check from the variant directory.
1937         n = bld0.srcdir_find_file('does_not_exist')
1938         assert n == (None, None), n
1939
1940         n = bld0.srcdir_find_file('derived-f')
1941         check(n, ['src0/derived-f', 'bld0'])
1942         n = bld0.srcdir_find_file('exists-f')
1943         check(n, ['src0/exists-f', 'bld0'])
1944         n = bld0.srcdir_find_file('on-disk-f2')
1945         check(n, ['src0/on-disk-f2', 'bld0'])
1946
1947         n = bld0.srcdir_find_file('derived-e')
1948         check(n, ['src0/derived-e', 'bld0'])
1949         n = bld0.srcdir_find_file('exists-e')
1950         check(n, ['src0/exists-e', 'bld0'])
1951         n = bld0.srcdir_find_file('on-disk-e2')
1952         check(n, ['src0/on-disk-e2', 'bld0'])
1953
1954         test.subdir('src1')
1955         test.write(['src1', 'on-disk-f1'], "src1/on-disk-f1\n")
1956         test.write(['src1', 'on-disk-f2'], "src1/on-disk-f2\n")
1957         test.write(['src1', 'on-disk-e1'], "src1/on-disk-e1\n")
1958         test.write(['src1', 'on-disk-e2'], "src1/on-disk-e2\n")
1959
1960         bld1 = self.fs.Dir('bld1')
1961         src1 = self.fs.Dir('src1')
1962         self.fs.VariantDir(bld1, src1, duplicate=1)
1963
1964         derived_f = src1.File('derived-f')
1965         derived_f.is_derived = return_true
1966         exists_f = src1.File('exists-f')
1967         exists_f.exists = return_true
1968
1969         derived_e = src1.Entry('derived-e')
1970         derived_e.is_derived = return_true
1971         exists_e = src1.Entry('exists-e')
1972         exists_e.exists = return_true
1973
1974         # First check from the source directory.
1975         n = src1.srcdir_find_file('does_not_exist')
1976         assert n == (None, None), n
1977
1978         n = src1.srcdir_find_file('derived-f')
1979         check(n, ['src1/derived-f', 'src1'])
1980         n = src1.srcdir_find_file('exists-f')
1981         check(n, ['src1/exists-f', 'src1'])
1982         n = src1.srcdir_find_file('on-disk-f1')
1983         check(n, ['src1/on-disk-f1', 'src1'])
1984
1985         n = src1.srcdir_find_file('derived-e')
1986         check(n, ['src1/derived-e', 'src1'])
1987         n = src1.srcdir_find_file('exists-e')
1988         check(n, ['src1/exists-e', 'src1'])
1989         n = src1.srcdir_find_file('on-disk-e1')
1990         check(n, ['src1/on-disk-e1', 'src1'])
1991
1992         # Now check from the variant directory.
1993         n = bld1.srcdir_find_file('does_not_exist')
1994         assert n == (None, None), n
1995
1996         n = bld1.srcdir_find_file('derived-f')
1997         check(n, ['bld1/derived-f', 'src1'])
1998         n = bld1.srcdir_find_file('exists-f')
1999         check(n, ['bld1/exists-f', 'src1'])
2000         n = bld1.srcdir_find_file('on-disk-f2')
2001         check(n, ['bld1/on-disk-f2', 'bld1'])
2002
2003         n = bld1.srcdir_find_file('derived-e')
2004         check(n, ['bld1/derived-e', 'src1'])
2005         n = bld1.srcdir_find_file('exists-e')
2006         check(n, ['bld1/exists-e', 'src1'])
2007         n = bld1.srcdir_find_file('on-disk-e2')
2008         check(n, ['bld1/on-disk-e2', 'bld1'])
2009
2010     def test_dir_on_disk(self):
2011         """Test the Dir.dir_on_disk() method"""
2012         self.test.subdir('sub', ['sub', 'exists'])
2013         self.test.write(['sub', 'file'], "self/file\n")
2014         sub = self.fs.Dir('sub')
2015
2016         r = sub.dir_on_disk('does_not_exist')
2017         assert not r, r
2018
2019         r = sub.dir_on_disk('exists')
2020         assert r, r
2021
2022         r = sub.dir_on_disk('file')
2023         assert not r, r
2024
2025     def test_file_on_disk(self):
2026         """Test the Dir.file_on_disk() method"""
2027         self.test.subdir('sub', ['sub', 'dir'])
2028         self.test.write(['sub', 'exists'], "self/exists\n")
2029         sub = self.fs.Dir('sub')
2030
2031         r = sub.file_on_disk('does_not_exist')
2032         assert not r, r
2033
2034         r = sub.file_on_disk('exists')
2035         assert r, r
2036
2037         r = sub.file_on_disk('dir')
2038         assert not r, r
2039
2040 class EntryTestCase(_tempdirTestCase):
2041     def test_runTest(self):
2042         """Test methods specific to the Entry sub-class.
2043         """
2044         test = TestCmd(workdir='')
2045         # FS doesn't like the cwd to be something other than its root.
2046         os.chdir(test.workpath(""))
2047
2048         fs = SCons.Node.FS.FS()
2049
2050         e1 = fs.Entry('e1')
2051         e1.rfile()
2052         assert e1.__class__ is SCons.Node.FS.File, e1.__class__
2053
2054         test.subdir('e3d')
2055         test.write('e3f', "e3f\n")
2056
2057         e3d = fs.Entry('e3d')
2058         e3d.get_contents()
2059         assert e3d.__class__ is SCons.Node.FS.Dir, e3d.__class__
2060
2061         e3f = fs.Entry('e3f')
2062         e3f.get_contents()
2063         assert e3f.__class__ is SCons.Node.FS.File, e3f.__class__
2064
2065         e3n = fs.Entry('e3n')
2066         e3n.get_contents()
2067         assert e3n.__class__ is SCons.Node.FS.Entry, e3n.__class__
2068
2069         test.subdir('e4d')
2070         test.write('e4f', "e4f\n")
2071
2072         e4d = fs.Entry('e4d')
2073         exists = e4d.exists()
2074         assert e4d.__class__ is SCons.Node.FS.Dir, e4d.__class__
2075         assert exists, "e4d does not exist?"
2076
2077         e4f = fs.Entry('e4f')
2078         exists = e4f.exists()
2079         assert e4f.__class__ is SCons.Node.FS.File, e4f.__class__
2080         assert exists, "e4f does not exist?"
2081
2082         e4n = fs.Entry('e4n')
2083         exists = e4n.exists()
2084         assert e4n.__class__ is SCons.Node.FS.File, e4n.__class__
2085         assert not exists, "e4n exists?"
2086
2087         class MyCalc:
2088             def __init__(self, val):
2089                 self.max_drift = 0
2090                 class M:
2091                     def __init__(self, val):
2092                         self.val = val
2093                     def collect(self, args):
2094                         return reduce(lambda x, y: x+y, args)
2095                     def signature(self, executor):
2096                         return self.val + 222
2097                 self.module = M(val)
2098
2099         test.subdir('e5d')
2100         test.write('e5f', "e5f\n")
2101
2102     def test_Entry_Entry_lookup(self):
2103         """Test looking up an Entry within another Entry"""
2104         self.fs.Entry('#topdir')
2105         self.fs.Entry('#topdir/a/b/c')
2106
2107
2108
2109 class FileTestCase(_tempdirTestCase):
2110
2111     def test_subclass(self):
2112         """Test looking up subclass of File nodes"""
2113         class FileSubclass(SCons.Node.FS.File):
2114             pass
2115         sd = self.fs._lookup('special_file', None, FileSubclass, create=1)
2116         sd.must_be_same(SCons.Node.FS.File)
2117
2118     def test_Dirs(self):
2119         """Test the File.Dirs() method"""
2120         fff = self.fs.File('subdir/fff')
2121         # This simulates that the SConscript file that defined
2122         # fff is in subdir/.
2123         fff.cwd = self.fs.Dir('subdir')
2124         d1 = self.fs.Dir('subdir/d1')
2125         d2 = self.fs.Dir('subdir/d2')
2126         dirs = fff.Dirs(['d1', 'd2'])
2127         assert dirs == [d1, d2], list(map(str, dirs))
2128
2129     def test_exists(self):
2130         """Test the File.exists() method"""
2131         fs = self.fs
2132         test = self.test
2133
2134         src_f1 = fs.File('src/f1')
2135         assert not src_f1.exists(), "%s apparently exists?" % src_f1
2136
2137         test.subdir('src')
2138         test.write(['src', 'f1'], "src/f1\n")
2139
2140         assert not src_f1.exists(), "%s did not cache previous exists() value" % src_f1
2141         src_f1.clear()
2142         assert src_f1.exists(), "%s apparently does not exist?" % src_f1
2143
2144         test.subdir('build')
2145         fs.VariantDir('build', 'src')
2146         build_f1 = fs.File('build/f1')
2147
2148         assert build_f1.exists(), "%s did not realize that %s exists" % (build_f1, src_f1)
2149         assert os.path.exists(build_f1.abspath), "%s did not get duplicated on disk" % build_f1.abspath
2150
2151         test.unlink(['src', 'f1'])
2152         src_f1.clear()  # so the next exists() call will look on disk again
2153
2154         assert build_f1.exists(), "%s did not cache previous exists() value" % build_f1
2155         build_f1.clear()
2156         build_f1.linked = None
2157         assert not build_f1.exists(), "%s did not realize that %s disappeared" % (build_f1, src_f1)
2158         assert not os.path.exists(build_f1.abspath), "%s did not get removed after %s was removed" % (build_f1, src_f1)
2159
2160
2161
2162 class GlobTestCase(_tempdirTestCase):
2163     def setUp(self):
2164         _tempdirTestCase.setUp(self)
2165
2166         fs = SCons.Node.FS.FS()
2167         self.fs = fs
2168
2169         # Make entries on disk that will not have Nodes, so we can verify
2170         # the behavior of looking for things on disk.
2171         self.test.write('disk-bbb', "disk-bbb\n")
2172         self.test.write('disk-aaa', "disk-aaa\n")
2173         self.test.write('disk-ccc', "disk-ccc\n")
2174         self.test.write('#disk-hash', "#disk-hash\n")
2175         self.test.subdir('disk-sub')
2176         self.test.write(['disk-sub', 'disk-ddd'], "disk-sub/disk-ddd\n")
2177         self.test.write(['disk-sub', 'disk-eee'], "disk-sub/disk-eee\n")
2178         self.test.write(['disk-sub', 'disk-fff'], "disk-sub/disk-fff\n")
2179
2180         # Make some entries that have both Nodes and on-disk entries,
2181         # so we can verify what we do with
2182         self.test.write('both-aaa', "both-aaa\n")
2183         self.test.write('both-bbb', "both-bbb\n")
2184         self.test.write('both-ccc', "both-ccc\n")
2185         self.test.write('#both-hash', "#both-hash\n")
2186         self.test.subdir('both-sub1')
2187         self.test.write(['both-sub1', 'both-ddd'], "both-sub1/both-ddd\n")
2188         self.test.write(['both-sub1', 'both-eee'], "both-sub1/both-eee\n")
2189         self.test.write(['both-sub1', 'both-fff'], "both-sub1/both-fff\n")
2190         self.test.subdir('both-sub2')
2191         self.test.write(['both-sub2', 'both-ddd'], "both-sub2/both-ddd\n")
2192         self.test.write(['both-sub2', 'both-eee'], "both-sub2/both-eee\n")
2193         self.test.write(['both-sub2', 'both-fff'], "both-sub2/both-fff\n")
2194
2195         self.both_aaa = fs.File('both-aaa')
2196         self.both_bbb = fs.File('both-bbb')
2197         self.both_ccc = fs.File('both-ccc')
2198         self._both_hash = fs.File('./#both-hash')
2199         self.both_sub1 = fs.Dir('both-sub1')
2200         self.both_sub1_both_ddd = self.both_sub1.File('both-ddd')
2201         self.both_sub1_both_eee = self.both_sub1.File('both-eee')
2202         self.both_sub1_both_fff = self.both_sub1.File('both-fff')
2203         self.both_sub2 = fs.Dir('both-sub2')
2204         self.both_sub2_both_ddd = self.both_sub2.File('both-ddd')
2205         self.both_sub2_both_eee = self.both_sub2.File('both-eee')
2206         self.both_sub2_both_fff = self.both_sub2.File('both-fff')
2207
2208         # Make various Nodes (that don't have on-disk entries) so we
2209         # can verify how we match them.
2210         self.ggg = fs.File('ggg')
2211         self.hhh = fs.File('hhh')
2212         self.iii = fs.File('iii')
2213         self._hash = fs.File('./#hash')
2214         self.subdir1 = fs.Dir('subdir1')
2215         self.subdir1_lll = self.subdir1.File('lll')
2216         self.subdir1_jjj = self.subdir1.File('jjj')
2217         self.subdir1_kkk = self.subdir1.File('kkk')
2218         self.subdir2 = fs.Dir('subdir2')
2219         self.subdir2_lll = self.subdir2.File('lll')
2220         self.subdir2_kkk = self.subdir2.File('kkk')
2221         self.subdir2_jjj = self.subdir2.File('jjj')
2222         self.sub = fs.Dir('sub')
2223         self.sub_dir3 = self.sub.Dir('dir3')
2224         self.sub_dir3_kkk = self.sub_dir3.File('kkk')
2225         self.sub_dir3_jjj = self.sub_dir3.File('jjj')
2226         self.sub_dir3_lll = self.sub_dir3.File('lll')
2227
2228
2229     def do_cases(self, cases, **kwargs):
2230
2231         # First, execute all of the cases with string=True and verify
2232         # that we get the expected strings returned.  We do this first
2233         # so the Glob() calls don't add Nodes to the self.fs file system
2234         # hierarchy.
2235
2236         import copy
2237         strings_kwargs = copy.copy(kwargs)
2238         strings_kwargs['strings'] = True
2239         for input, string_expect, node_expect in cases:
2240             r = sorted(self.fs.Glob(input, **strings_kwargs))
2241             assert r == string_expect, "Glob(%s, strings=True) expected %s, got %s" % (input, string_expect, r)
2242
2243         # Now execute all of the cases without string=True and look for
2244         # the expected Nodes to be returned.  If we don't have a list of
2245         # actual expected Nodes, that means we're expecting a search for
2246         # on-disk-only files to have returned some newly-created nodes.
2247         # Verify those by running the list through str() before comparing
2248         # them with the expected list of strings.
2249         for input, string_expect, node_expect in cases:
2250             r = self.fs.Glob(input, **kwargs)
2251             if node_expect:
2252                 r.sort(lambda a,b: cmp(a.path, b.path))
2253                 result = []
2254                 for n in node_expect:
2255                     if isinstance(n, str):
2256                         n = self.fs.Entry(n)
2257                     result.append(n)
2258                 fmt = lambda n: "%s %s" % (repr(n), repr(str(n)))
2259             else:
2260                 r = sorted(map(str, r))
2261                 result = string_expect
2262                 fmt = lambda n: n
2263             if r != result:
2264                 import pprint
2265                 print "Glob(%s) expected:" % repr(input)
2266                 pprint.pprint(list(map(fmt, result)))
2267                 print "Glob(%s) got:" % repr(input)
2268                 pprint.pprint(list(map(fmt, r)))
2269                 self.fail()
2270
2271     def test_exact_match(self):
2272         """Test globbing for exact Node matches"""
2273         join = os.path.join
2274
2275         cases = (
2276             ('ggg',         ['ggg'],                    [self.ggg]),
2277
2278             ('subdir1',     ['subdir1'],                [self.subdir1]),
2279
2280             ('subdir1/jjj', [join('subdir1', 'jjj')],   [self.subdir1_jjj]),
2281
2282             ('disk-aaa',    ['disk-aaa'],               None),
2283
2284             ('disk-sub',    ['disk-sub'],               None),
2285
2286             ('both-aaa',    ['both-aaa'],               []),
2287         )
2288
2289         self.do_cases(cases)
2290
2291     def test_subdir_matches(self):
2292         """Test globbing for exact Node matches in subdirectories"""
2293         join = os.path.join
2294
2295         cases = (
2296             ('*/jjj',
2297              [join('subdir1', 'jjj'), join('subdir2', 'jjj')],
2298              [self.subdir1_jjj, self.subdir2_jjj]),
2299
2300             ('*/disk-ddd',
2301              [join('disk-sub', 'disk-ddd')],
2302              None),
2303         )
2304
2305         self.do_cases(cases)
2306
2307     def test_asterisk1(self):
2308         """Test globbing for simple asterisk Node matches (1)"""
2309         cases = (
2310             ('h*',
2311              ['hhh'],
2312              [self.hhh]),
2313
2314             ('*',
2315              ['#both-hash', '#hash',
2316               'both-aaa', 'both-bbb', 'both-ccc',
2317               'both-sub1', 'both-sub2',
2318               'ggg', 'hhh', 'iii',
2319               'sub', 'subdir1', 'subdir2'],
2320              [self._both_hash, self._hash,
2321               self.both_aaa, self.both_bbb, self.both_ccc, 'both-hash',
2322               self.both_sub1, self.both_sub2,
2323               self.ggg, 'hash', self.hhh, self.iii,
2324               self.sub, self.subdir1, self.subdir2]),
2325         )
2326
2327         self.do_cases(cases, ondisk=False)
2328
2329     def test_asterisk2(self):
2330         """Test globbing for simple asterisk Node matches (2)"""
2331         cases = (
2332             ('disk-b*',
2333              ['disk-bbb'],
2334              None),
2335
2336             ('*',
2337              ['#both-hash', '#disk-hash', '#hash',
2338               'both-aaa', 'both-bbb', 'both-ccc',
2339               'both-sub1', 'both-sub2',
2340               'disk-aaa', 'disk-bbb', 'disk-ccc', 'disk-sub',
2341               'ggg', 'hhh', 'iii',
2342               'sub', 'subdir1', 'subdir2'],
2343              ['./#both-hash', './#disk-hash', './#hash',
2344               'both-aaa', 'both-bbb', 'both-ccc', 'both-hash',
2345               'both-sub1', 'both-sub2',
2346               'disk-aaa', 'disk-bbb', 'disk-ccc', 'disk-sub',
2347               'ggg', 'hash', 'hhh', 'iii',
2348               'sub', 'subdir1', 'subdir2']),
2349         )
2350
2351         self.do_cases(cases)
2352
2353     def test_question_mark(self):
2354         """Test globbing for simple question-mark Node matches"""
2355         join = os.path.join
2356
2357         cases = (
2358             ('ii?',
2359              ['iii'],
2360              [self.iii]),
2361
2362             ('both-sub?/both-eee',
2363              [join('both-sub1', 'both-eee'), join('both-sub2', 'both-eee')],
2364              [self.both_sub1_both_eee, self.both_sub2_both_eee]),
2365
2366             ('subdir?/jjj',
2367              [join('subdir1', 'jjj'), join('subdir2', 'jjj')],
2368              [self.subdir1_jjj, self.subdir2_jjj]),
2369
2370             ('disk-cc?',
2371              ['disk-ccc'],
2372              None),
2373         )
2374
2375         self.do_cases(cases)
2376
2377     def test_does_not_exist(self):
2378         """Test globbing for things that don't exist"""
2379
2380         cases = (
2381             ('does_not_exist',  [], []),
2382             ('no_subdir/*',     [], []),
2383             ('subdir?/no_file', [], []),
2384         )
2385
2386         self.do_cases(cases)
2387
2388     def test_subdir_asterisk(self):
2389         """Test globbing for asterisk Node matches in subdirectories"""
2390         join = os.path.join
2391
2392         cases = (
2393             ('*/k*',
2394              [join('subdir1', 'kkk'), join('subdir2', 'kkk')],
2395              [self.subdir1_kkk, self.subdir2_kkk]),
2396
2397             ('both-sub?/*',
2398              [join('both-sub1', 'both-ddd'),
2399               join('both-sub1', 'both-eee'),
2400               join('both-sub1', 'both-fff'),
2401               join('both-sub2', 'both-ddd'),
2402               join('both-sub2', 'both-eee'),
2403               join('both-sub2', 'both-fff')],
2404              [self.both_sub1_both_ddd,
2405               self.both_sub1_both_eee,
2406               self.both_sub1_both_fff,
2407               self.both_sub2_both_ddd,
2408               self.both_sub2_both_eee,
2409               self.both_sub2_both_fff],
2410              ),
2411
2412             ('subdir?/*',
2413              [join('subdir1', 'jjj'),
2414               join('subdir1', 'kkk'),
2415               join('subdir1', 'lll'),
2416               join('subdir2', 'jjj'),
2417               join('subdir2', 'kkk'),
2418               join('subdir2', 'lll')],
2419              [self.subdir1_jjj, self.subdir1_kkk, self.subdir1_lll,
2420               self.subdir2_jjj, self.subdir2_kkk, self.subdir2_lll]),
2421
2422             ('sub/*/*',
2423              [join('sub', 'dir3', 'jjj'),
2424               join('sub', 'dir3', 'kkk'),
2425               join('sub', 'dir3', 'lll')],
2426              [self.sub_dir3_jjj, self.sub_dir3_kkk, self.sub_dir3_lll]),
2427
2428             ('*/k*',
2429              [join('subdir1', 'kkk'), join('subdir2', 'kkk')],
2430              None),
2431
2432             ('subdir?/*',
2433              [join('subdir1', 'jjj'),
2434               join('subdir1', 'kkk'),
2435               join('subdir1', 'lll'),
2436               join('subdir2', 'jjj'),
2437               join('subdir2', 'kkk'),
2438               join('subdir2', 'lll')],
2439              None),
2440
2441             ('sub/*/*',
2442              [join('sub', 'dir3', 'jjj'),
2443               join('sub', 'dir3', 'kkk'),
2444               join('sub', 'dir3', 'lll')],
2445              None),
2446         )
2447
2448         self.do_cases(cases)
2449
2450     def test_subdir_question(self):
2451         """Test globbing for question-mark Node matches in subdirectories"""
2452         join = os.path.join
2453
2454         cases = (
2455             ('*/?kk',
2456              [join('subdir1', 'kkk'), join('subdir2', 'kkk')],
2457              [self.subdir1_kkk, self.subdir2_kkk]),
2458
2459             ('subdir?/l?l',
2460              [join('subdir1', 'lll'), join('subdir2', 'lll')],
2461              [self.subdir1_lll, self.subdir2_lll]),
2462
2463             ('*/disk-?ff',
2464              [join('disk-sub', 'disk-fff')],
2465              None),
2466
2467             ('subdir?/l?l',
2468              [join('subdir1', 'lll'), join('subdir2', 'lll')],
2469              None),
2470         )
2471
2472         self.do_cases(cases)
2473
2474     def test_sort(self):
2475         """Test whether globbing sorts"""
2476         join = os.path.join
2477         # At least sometimes this should return out-of-order items
2478         # if Glob doesn't sort.
2479         # It's not a very good test though since it depends on the
2480         # order returned by glob, which might already be sorted.
2481         g = self.fs.Glob('disk-sub/*', strings=True)
2482         expect = [
2483             os.path.join('disk-sub', 'disk-ddd'),
2484             os.path.join('disk-sub', 'disk-eee'),
2485             os.path.join('disk-sub', 'disk-fff'),
2486         ]
2487         assert g == expect, str(g) + " is not sorted, but should be!"
2488
2489         g = self.fs.Glob('disk-*', strings=True)
2490         expect = [ 'disk-aaa', 'disk-bbb', 'disk-ccc', 'disk-sub' ]
2491         assert g == expect, str(g) + " is not sorted, but should be!"
2492
2493
2494 class RepositoryTestCase(_tempdirTestCase):
2495
2496     def setUp(self):
2497         _tempdirTestCase.setUp(self)
2498
2499         self.test.subdir('rep1', 'rep2', 'rep3', 'work')
2500
2501         self.rep1 = self.test.workpath('rep1')
2502         self.rep2 = self.test.workpath('rep2')
2503         self.rep3 = self.test.workpath('rep3')
2504
2505         os.chdir(self.test.workpath('work'))
2506
2507         self.fs = SCons.Node.FS.FS()
2508         self.fs.Repository(self.rep1, self.rep2, self.rep3)
2509
2510     def test_getRepositories(self):
2511         """Test the Dir.getRepositories() method"""
2512         self.fs.Repository('foo')
2513         self.fs.Repository(os.path.join('foo', 'bar'))
2514         self.fs.Repository('bar/foo')
2515         self.fs.Repository('bar')
2516
2517         expect = [
2518             self.rep1,
2519             self.rep2,
2520             self.rep3,
2521             'foo',
2522             os.path.join('foo', 'bar'),
2523             os.path.join('bar', 'foo'),
2524             'bar'
2525         ]
2526
2527         rep = self.fs.Dir('#').getRepositories()
2528         r = [os.path.normpath(str(x)) for x in rep]
2529         assert r == expect, r
2530
2531     def test_get_all_rdirs(self):
2532         """Test the Dir.get_all_rdirs() method"""
2533         self.fs.Repository('foo')
2534         self.fs.Repository(os.path.join('foo', 'bar'))
2535         self.fs.Repository('bar/foo')
2536         self.fs.Repository('bar')
2537
2538         expect = [
2539             '.',
2540             self.rep1,
2541             self.rep2,
2542             self.rep3,
2543             'foo',
2544             os.path.join('foo', 'bar'),
2545             os.path.join('bar', 'foo'),
2546             'bar'
2547         ]
2548
2549         rep = self.fs.Dir('#').get_all_rdirs()
2550         r = [os.path.normpath(str(x)) for x in rep]
2551         assert r == expect, r
2552
2553     def test_rentry(self):
2554         """Test the Base.entry() method"""
2555         return_true = lambda: 1
2556         return_false = lambda: 0
2557
2558         d1 = self.fs.Dir('d1')
2559         d2 = self.fs.Dir('d2')
2560         d3 = self.fs.Dir('d3')
2561
2562         e1 = self.fs.Entry('e1')
2563         e2 = self.fs.Entry('e2')
2564         e3 = self.fs.Entry('e3')
2565
2566         f1 = self.fs.File('f1')
2567         f2 = self.fs.File('f2')
2568         f3 = self.fs.File('f3')
2569
2570         self.test.write([self.rep1, 'd2'], "")
2571         self.test.subdir([self.rep2, 'd3'])
2572         self.test.write([self.rep3, 'd3'], "")
2573
2574         self.test.write([self.rep1, 'e2'], "")
2575         self.test.subdir([self.rep2, 'e3'])
2576         self.test.write([self.rep3, 'e3'], "")
2577
2578         self.test.write([self.rep1, 'f2'], "")
2579         self.test.subdir([self.rep2, 'f3'])
2580         self.test.write([self.rep3, 'f3'], "")
2581
2582         r = d1.rentry()
2583         assert r is d1, r
2584
2585         r = d2.rentry()
2586         assert not r is d2, r
2587         r = str(r)
2588         assert r == os.path.join(self.rep1, 'd2'), r
2589
2590         r = d3.rentry()
2591         assert not r is d3, r
2592         r = str(r)
2593         assert r == os.path.join(self.rep2, 'd3'), r
2594
2595         r = e1.rentry()
2596         assert r is e1, r
2597
2598         r = e2.rentry()
2599         assert not r is e2, r
2600         r = str(r)
2601         assert r == os.path.join(self.rep1, 'e2'), r
2602
2603         r = e3.rentry()
2604         assert not r is e3, r
2605         r = str(r)
2606         assert r == os.path.join(self.rep2, 'e3'), r
2607
2608         r = f1.rentry()
2609         assert r is f1, r
2610
2611         r = f2.rentry()
2612         assert not r is f2, r
2613         r = str(r)
2614         assert r == os.path.join(self.rep1, 'f2'), r
2615
2616         r = f3.rentry()
2617         assert not r is f3, r
2618         r = str(r)
2619         assert r == os.path.join(self.rep2, 'f3'), r
2620
2621     def test_rdir(self):
2622         """Test the Dir.rdir() method"""
2623         return_true = lambda: 1
2624         return_false = lambda: 0
2625
2626         d1 = self.fs.Dir('d1')
2627         d2 = self.fs.Dir('d2')
2628         d3 = self.fs.Dir('d3')
2629
2630         self.test.subdir([self.rep1, 'd2'])
2631         self.test.write([self.rep2, 'd3'], "")
2632         self.test.subdir([self.rep3, 'd3'])
2633
2634         r = d1.rdir()
2635         assert r is d1, r
2636
2637         r = d2.rdir()
2638         assert not r is d2, r
2639         r = str(r)
2640         assert r == os.path.join(self.rep1, 'd2'), r
2641
2642         r = d3.rdir()
2643         assert not r is d3, r
2644         r = str(r)
2645         assert r == os.path.join(self.rep3, 'd3'), r
2646
2647         e1 = self.fs.Dir('e1')
2648         e1.exists = return_false
2649         e2 = self.fs.Dir('e2')
2650         e2.exists = return_false
2651
2652         # Make sure we match entries in repositories,
2653         # regardless of whether they're derived or not.
2654
2655         re1 = self.fs.Entry(os.path.join(self.rep1, 'e1'))
2656         re1.exists = return_true
2657         re1.is_derived = return_true
2658         re2 = self.fs.Entry(os.path.join(self.rep2, 'e2'))
2659         re2.exists = return_true
2660         re2.is_derived = return_false
2661
2662         r = e1.rdir()
2663         assert r is re1, r
2664
2665         r = e2.rdir()
2666         assert r is re2, r
2667
2668     def test_rfile(self):
2669         """Test the File.rfile() method"""
2670         return_true = lambda: 1
2671         return_false = lambda: 0
2672
2673         f1 = self.fs.File('f1')
2674         f2 = self.fs.File('f2')
2675         f3 = self.fs.File('f3')
2676
2677         self.test.write([self.rep1, 'f2'], "")
2678         self.test.subdir([self.rep2, 'f3'])
2679         self.test.write([self.rep3, 'f3'], "")
2680
2681         r = f1.rfile()
2682         assert r is f1, r
2683
2684         r = f2.rfile()
2685         assert not r is f2, r
2686         r = str(r)
2687         assert r == os.path.join(self.rep1, 'f2'), r
2688
2689         r = f3.rfile()
2690         assert not r is f3, r
2691         r = f3.rstr()
2692         assert r == os.path.join(self.rep3, 'f3'), r
2693
2694         e1 = self.fs.File('e1')
2695         e1.exists = return_false
2696         e2 = self.fs.File('e2')
2697         e2.exists = return_false
2698
2699         # Make sure we match entries in repositories,
2700         # regardless of whether they're derived or not.
2701
2702         re1 = self.fs.Entry(os.path.join(self.rep1, 'e1'))
2703         re1.exists = return_true
2704         re1.is_derived = return_true
2705         re2 = self.fs.Entry(os.path.join(self.rep2, 'e2'))
2706         re2.exists = return_true
2707         re2.is_derived = return_false
2708
2709         r = e1.rfile()
2710         assert r is re1, r
2711
2712         r = e2.rfile()
2713         assert r is re2, r
2714
2715     def test_Rfindalldirs(self):
2716         """Test the Rfindalldirs() methods"""
2717         fs = self.fs
2718         test = self.test
2719
2720         d1 = fs.Dir('d1')
2721         d2 = fs.Dir('d2')
2722         rep1_d1 = fs.Dir(test.workpath('rep1', 'd1'))
2723         rep2_d1 = fs.Dir(test.workpath('rep2', 'd1'))
2724         rep3_d1 = fs.Dir(test.workpath('rep3', 'd1'))
2725         sub = fs.Dir('sub')
2726         sub_d1 = sub.Dir('d1')
2727         rep1_sub_d1 = fs.Dir(test.workpath('rep1', 'sub', 'd1'))
2728         rep2_sub_d1 = fs.Dir(test.workpath('rep2', 'sub', 'd1'))
2729         rep3_sub_d1 = fs.Dir(test.workpath('rep3', 'sub', 'd1'))
2730
2731         r = fs.Top.Rfindalldirs((d1,))
2732         assert r == [d1], list(map(str, r))
2733
2734         r = fs.Top.Rfindalldirs((d1, d2))
2735         assert r == [d1, d2], list(map(str, r))
2736
2737         r = fs.Top.Rfindalldirs(('d1',))
2738         assert r == [d1, rep1_d1, rep2_d1, rep3_d1], list(map(str, r))
2739
2740         r = fs.Top.Rfindalldirs(('#d1',))
2741         assert r == [d1, rep1_d1, rep2_d1, rep3_d1], list(map(str, r))
2742
2743         r = sub.Rfindalldirs(('d1',))
2744         assert r == [sub_d1, rep1_sub_d1, rep2_sub_d1, rep3_sub_d1], list(map(str, r))
2745
2746         r = sub.Rfindalldirs(('#d1',))
2747         assert r == [d1, rep1_d1, rep2_d1, rep3_d1], list(map(str, r))
2748
2749         r = fs.Top.Rfindalldirs(('d1', d2))
2750         assert r == [d1, rep1_d1, rep2_d1, rep3_d1, d2], list(map(str, r))
2751
2752     def test_rexists(self):
2753         """Test the Entry.rexists() method"""
2754         fs = self.fs
2755         test = self.test
2756
2757         test.write([self.rep1, 'f2'], "")
2758         test.write([self.rep2, "i_exist"], "\n")
2759         test.write(["work", "i_exist_too"], "\n")
2760
2761         fs.VariantDir('build', '.')
2762
2763         f = fs.File(test.workpath("work", "i_do_not_exist"))
2764         assert not f.rexists()
2765
2766         f = fs.File(test.workpath("work", "i_exist"))
2767         assert f.rexists()
2768
2769         f = fs.File(test.workpath("work", "i_exist_too"))
2770         assert f.rexists()
2771
2772         f1 = fs.File(os.path.join('build', 'f1'))
2773         assert not f1.rexists()
2774
2775         f2 = fs.File(os.path.join('build', 'f2'))
2776         assert f2.rexists()
2777
2778     def test_FAT_timestamps(self):
2779         """Test repository timestamps on FAT file systems"""
2780         fs = self.fs
2781         test = self.test
2782
2783         test.write(["rep2", "tstamp"], "tstamp\n")
2784         try:
2785             # Okay, *this* manipulation accomodates Windows FAT file systems
2786             # that only have two-second granularity on their timestamps.
2787             # We round down the current time to the nearest even integer
2788             # value, subtract two to make sure the timestamp is not "now,"
2789             # and then convert it back to a float.
2790             tstamp = float(int(time.time() / 2) * 2) - 2
2791             os.utime(test.workpath("rep2", "tstamp"), (tstamp - 2.0, tstamp))
2792             f = fs.File("tstamp")
2793             t = f.get_timestamp()
2794             assert t == tstamp, "expected %f, got %f" % (tstamp, t)
2795         finally:
2796             test.unlink(["rep2", "tstamp"])
2797
2798     def test_get_contents(self):
2799         """Ensure get_contents() returns binary contents from Repositories"""
2800         fs = self.fs
2801         test = self.test
2802
2803         test.write(["rep3", "contents"], "Con\x1aTents\n")
2804         try:
2805             c = fs.File("contents").get_contents()
2806             assert c == "Con\x1aTents\n", "got '%s'" % c
2807         finally:
2808             test.unlink(["rep3", "contents"])
2809
2810     def test_get_text_contents(self):
2811         """Ensure get_text_contents() returns text contents from
2812         Repositories"""
2813         fs = self.fs
2814         test = self.test
2815
2816         # Use a test string that has a file terminator in it to make
2817         # sure we read the entire file, regardless of its contents.
2818         try:
2819             eval('test_string = u"Con\x1aTents\n"')
2820         except SyntaxError:
2821             import UserString
2822             class FakeUnicodeString(UserString.UserString):
2823                 def encode(self, encoding):
2824                     return str(self)
2825             test_string = FakeUnicodeString("Con\x1aTents\n")
2826
2827
2828         # Test with ASCII.
2829         test.write(["rep3", "contents"], test_string.encode('ascii'))
2830         try:
2831             c = fs.File("contents").get_text_contents()
2832             assert test_string == c, "got %s" % repr(c)
2833         finally:
2834             test.unlink(["rep3", "contents"])
2835
2836         # Test with utf-8
2837         test.write(["rep3", "contents"], test_string.encode('utf-8'))
2838         try:
2839             c = fs.File("contents").get_text_contents()
2840             assert test_string == c, "got %s" % repr(c)
2841         finally:
2842             test.unlink(["rep3", "contents"])
2843
2844         # Test with utf-16
2845         test.write(["rep3", "contents"], test_string.encode('utf-16'))
2846         try:
2847             c = fs.File("contents").get_text_contents()
2848             assert test_string == c, "got %s" % repr(c)
2849         finally:
2850             test.unlink(["rep3", "contents"])
2851
2852     #def test_is_up_to_date(self):
2853
2854
2855
2856 class find_fileTestCase(unittest.TestCase):
2857     def runTest(self):
2858         """Testing find_file function"""
2859         test = TestCmd(workdir = '')
2860         test.write('./foo', 'Some file\n')
2861         test.write('./foo2', 'Another file\n')
2862         test.subdir('same')
2863         test.subdir('bar')
2864         test.write(['bar', 'on_disk'], 'Another file\n')
2865         test.write(['bar', 'same'], 'bar/same\n')
2866
2867         fs = SCons.Node.FS.FS(test.workpath(""))
2868         # FS doesn't like the cwd to be something other than its root.
2869         os.chdir(test.workpath(""))
2870
2871         node_derived = fs.File(test.workpath('bar/baz'))
2872         node_derived.builder_set(1) # Any non-zero value.
2873         node_pseudo = fs.File(test.workpath('pseudo'))
2874         node_pseudo.set_src_builder(1) # Any non-zero value.
2875
2876         paths = tuple(map(fs.Dir, ['.', 'same', './bar']))
2877         nodes = [SCons.Node.FS.find_file('foo', paths)]
2878         nodes.append(SCons.Node.FS.find_file('baz', paths))
2879         nodes.append(SCons.Node.FS.find_file('pseudo', paths))
2880         nodes.append(SCons.Node.FS.find_file('same', paths))
2881
2882         file_names = list(map(str, nodes))
2883         file_names = list(map(os.path.normpath, file_names))
2884         expect = ['./foo', './bar/baz', './pseudo', './bar/same']
2885         expect = list(map(os.path.normpath, expect))
2886         assert file_names == expect, file_names
2887
2888         # Make sure we don't blow up if there's already a File in place
2889         # of a directory that we'd otherwise try to search.  If this
2890         # is broken, we'll see an exception like "Tried to lookup File
2891         # 'bar/baz' as a Dir.
2892         SCons.Node.FS.find_file('baz/no_file_here', paths)
2893
2894         import StringIO
2895         save_sys_stdout = sys.stdout
2896
2897         try:
2898             sio = StringIO.StringIO()
2899             sys.stdout = sio
2900             SCons.Node.FS.find_file('foo2', paths, verbose="xyz")
2901             expect = "  xyz: looking for 'foo2' in '.' ...\n" + \
2902                      "  xyz: ... FOUND 'foo2' in '.'\n"
2903             c = sio.getvalue()
2904             assert c == expect, c
2905
2906             sio = StringIO.StringIO()
2907             sys.stdout = sio
2908             SCons.Node.FS.find_file('baz2', paths, verbose=1)
2909             expect = "  find_file: looking for 'baz2' in '.' ...\n" + \
2910                      "  find_file: looking for 'baz2' in 'same' ...\n" + \
2911                      "  find_file: looking for 'baz2' in 'bar' ...\n"
2912             c = sio.getvalue()
2913             assert c == expect, c
2914
2915             sio = StringIO.StringIO()
2916             sys.stdout = sio
2917             SCons.Node.FS.find_file('on_disk', paths, verbose=1)
2918             expect = "  find_file: looking for 'on_disk' in '.' ...\n" + \
2919                      "  find_file: looking for 'on_disk' in 'same' ...\n" + \
2920                      "  find_file: looking for 'on_disk' in 'bar' ...\n" + \
2921                      "  find_file: ... FOUND 'on_disk' in 'bar'\n"
2922             c = sio.getvalue()
2923             assert c == expect, c
2924         finally:
2925             sys.stdout = save_sys_stdout
2926
2927 class StringDirTestCase(unittest.TestCase):
2928     def runTest(self):
2929         """Test using a string as the second argument of
2930         File() and Dir()"""
2931
2932         test = TestCmd(workdir = '')
2933         test.subdir('sub')
2934         fs = SCons.Node.FS.FS(test.workpath(''))
2935
2936         d = fs.Dir('sub', '.')
2937         assert str(d) == 'sub', str(d)
2938         assert d.exists()
2939         f = fs.File('file', 'sub')
2940         assert str(f) == os.path.join('sub', 'file')
2941         assert not f.exists()
2942
2943 class stored_infoTestCase(unittest.TestCase):
2944     def runTest(self):
2945         """Test how we store build information"""
2946         test = TestCmd(workdir = '')
2947         test.subdir('sub')
2948         fs = SCons.Node.FS.FS(test.workpath(''))
2949
2950         d = fs.Dir('sub')
2951         f = fs.File('file1', d)
2952         bi = f.get_stored_info()
2953         assert hasattr(bi, 'ninfo')
2954
2955         class MySConsign:
2956             class Null:
2957                 def __init__(self):
2958                     self.xyzzy = 7
2959             def get_entry(self, name):
2960                 return self.Null()
2961
2962         f = fs.File('file2', d)
2963         f.dir.sconsign = MySConsign
2964         bi = f.get_stored_info()
2965         assert bi.xyzzy == 7, bi
2966
2967 class has_src_builderTestCase(unittest.TestCase):
2968     def runTest(self):
2969         """Test the has_src_builder() method"""
2970         test = TestCmd(workdir = '')
2971         fs = SCons.Node.FS.FS(test.workpath(''))
2972         os.chdir(test.workpath(''))
2973         test.subdir('sub1')
2974         test.subdir('sub2', ['sub2', 'SCCS'], ['sub2', 'RCS'])
2975
2976         sub1 = fs.Dir('sub1', '.')
2977         f1 = fs.File('f1', sub1)
2978         f2 = fs.File('f2', sub1)
2979         f3 = fs.File('f3', sub1)
2980         sub2 = fs.Dir('sub2', '.')
2981         f4 = fs.File('f4', sub2)
2982         f5 = fs.File('f5', sub2)
2983         f6 = fs.File('f6', sub2)
2984         f7 = fs.File('f7', sub2)
2985         f8 = fs.File('f8', sub2)
2986
2987         h = f1.has_src_builder()
2988         assert not h, h
2989         h = f1.has_builder()
2990         assert not h, h
2991
2992         b1 = Builder(fs.File)
2993         sub1.set_src_builder(b1)
2994
2995         test.write(['sub1', 'f2'], "sub1/f2\n")
2996         h = f1.has_src_builder()        # cached from previous call
2997         assert not h, h
2998         h = f1.has_builder()            # cached from previous call
2999         assert not h, h
3000         h = f2.has_src_builder()
3001         assert not h, h
3002         h = f2.has_builder()
3003         assert not h, h
3004         h = f3.has_src_builder()
3005         assert h, h
3006         h = f3.has_builder()
3007         assert h, h
3008         assert f3.builder is b1, f3.builder
3009
3010         f7.set_src_builder(b1)
3011         f8.builder_set(b1)
3012
3013         test.write(['sub2', 'SCCS', 's.f5'], "sub2/SCCS/s.f5\n")
3014         test.write(['sub2', 'RCS', 'f6,v'], "sub2/RCS/f6,v\n")
3015         h = f4.has_src_builder()
3016         assert not h, h
3017         h = f4.has_builder()
3018         assert not h, h
3019         h = f5.has_src_builder()
3020         assert h, h
3021         h = f5.has_builder()
3022         assert h, h
3023         h = f6.has_src_builder()
3024         assert h, h
3025         h = f6.has_builder()
3026         assert h, h
3027         h = f7.has_src_builder()
3028         assert h, h
3029         h = f7.has_builder()
3030         assert h, h
3031         h = f8.has_src_builder()
3032         assert not h, h
3033         h = f8.has_builder()
3034         assert h, h
3035
3036 class prepareTestCase(unittest.TestCase):
3037     def runTest(self):
3038         """Test the prepare() method"""
3039
3040         class MyFile(SCons.Node.FS.File):
3041             def _createDir(self, update=None):
3042                 raise SCons.Errors.StopError
3043             def exists(self):
3044                 return None
3045
3046         fs = SCons.Node.FS.FS()
3047         file = MyFile('foo', fs.Dir('.'), fs)
3048
3049         exc_caught = 0
3050         try:
3051             file.prepare()
3052         except SCons.Errors.StopError:
3053             exc_caught = 1
3054         assert exc_caught, "Should have caught a StopError."
3055
3056         class MkdirAction(Action):
3057             def __init__(self, dir_made):
3058                 self.dir_made = dir_made
3059             def __call__(self, target, source, env, executor=None):
3060                 if executor:
3061                     target = executor.get_all_targets()
3062                     source = executor.get_all_sources()
3063                 self.dir_made.extend(target)
3064
3065         dir_made = []
3066         new_dir = fs.Dir("new_dir")
3067         new_dir.builder = Builder(fs.Dir, action=MkdirAction(dir_made))
3068         new_dir.reset_executor()
3069         xyz = fs.File(os.path.join("new_dir", "xyz"))
3070
3071         xyz.set_state(SCons.Node.up_to_date)
3072         xyz.prepare()
3073         assert dir_made == [], dir_made
3074
3075         xyz.set_state(0)
3076         xyz.prepare()
3077         assert dir_made[0].path == "new_dir", dir_made[0]
3078
3079         dir = fs.Dir("dir")
3080         dir.prepare()
3081
3082
3083
3084 class SConstruct_dirTestCase(unittest.TestCase):
3085     def runTest(self):
3086         """Test setting the SConstruct directory"""
3087
3088         fs = SCons.Node.FS.FS()
3089         fs.set_SConstruct_dir(fs.Dir('xxx'))
3090         assert fs.SConstruct_dir.path == 'xxx'
3091
3092
3093
3094 class CacheDirTestCase(unittest.TestCase):
3095
3096     def test_get_cachedir_csig(self):
3097         fs = SCons.Node.FS.FS()
3098
3099         f9 = fs.File('f9')
3100         r = f9.get_cachedir_csig()
3101         assert r == 'd41d8cd98f00b204e9800998ecf8427e', r
3102
3103
3104
3105 class clearTestCase(unittest.TestCase):
3106     def runTest(self):
3107         """Test clearing FS nodes of cached data."""
3108         fs = SCons.Node.FS.FS()
3109         test = TestCmd(workdir='')
3110
3111         e = fs.Entry('e')
3112         assert not e.exists()
3113         assert not e.rexists()
3114         assert str(e) == 'e', str(d)
3115         e.clear()
3116         assert not e.exists()
3117         assert not e.rexists()
3118         assert str(e) == 'e', str(d)
3119
3120         d = fs.Dir(test.workpath('d'))
3121         test.subdir('d')
3122         assert d.exists()
3123         assert d.rexists()
3124         assert str(d) == test.workpath('d'), str(d)
3125         fs.rename(test.workpath('d'), test.workpath('gone'))
3126         # Verify caching is active
3127         assert d.exists(), 'caching not active'
3128         assert d.rexists()
3129         assert str(d) == test.workpath('d'), str(d)
3130         # Now verify clear() resets the cache
3131         d.clear()
3132         assert not d.exists()      
3133         assert not d.rexists()
3134         assert str(d) == test.workpath('d'), str(d)
3135         
3136         f = fs.File(test.workpath('f'))
3137         test.write(test.workpath('f'), 'file f')
3138         assert f.exists()
3139         assert f.rexists()
3140         assert str(f) == test.workpath('f'), str(f)
3141         # Verify caching is active
3142         test.unlink(test.workpath('f'))
3143         assert f.exists()
3144         assert f.rexists()
3145         assert str(f) == test.workpath('f'), str(f)
3146         # Now verify clear() resets the cache
3147         f.clear()
3148         assert not f.exists()
3149         assert not f.rexists()
3150         assert str(f) == test.workpath('f'), str(f)
3151
3152
3153
3154 class disambiguateTestCase(unittest.TestCase):
3155     def runTest(self):
3156         """Test calling the disambiguate() method."""
3157         test = TestCmd(workdir='')
3158
3159         fs = SCons.Node.FS.FS()
3160
3161         ddd = fs.Dir('ddd')
3162         d = ddd.disambiguate()
3163         assert d is ddd, d
3164
3165         fff = fs.File('fff')
3166         f = fff.disambiguate()
3167         assert f is fff, f
3168
3169         test.subdir('edir')
3170         test.write('efile', "efile\n")
3171
3172         edir = fs.Entry(test.workpath('edir'))
3173         d = edir.disambiguate()
3174         assert d.__class__ is ddd.__class__, d.__class__
3175
3176         efile = fs.Entry(test.workpath('efile'))
3177         f = efile.disambiguate()
3178         assert f.__class__ is fff.__class__, f.__class__
3179
3180         test.subdir('build')
3181         test.subdir(['build', 'bdir'])
3182         test.write(['build', 'bfile'], "build/bfile\n")
3183
3184         test.subdir('src')
3185         test.write(['src', 'bdir'], "src/bdir\n")
3186         test.subdir(['src', 'bfile'])
3187
3188         test.subdir(['src', 'edir'])
3189         test.write(['src', 'efile'], "src/efile\n")
3190
3191         fs.VariantDir(test.workpath('build'), test.workpath('src'))
3192
3193         build_bdir = fs.Entry(test.workpath('build/bdir'))
3194         d = build_bdir.disambiguate()
3195         assert d is build_bdir, d
3196         assert d.__class__ is ddd.__class__, d.__class__
3197
3198         build_bfile = fs.Entry(test.workpath('build/bfile'))
3199         f = build_bfile.disambiguate()
3200         assert f is build_bfile, f
3201         assert f.__class__ is fff.__class__, f.__class__
3202
3203         build_edir = fs.Entry(test.workpath('build/edir'))
3204         d = build_edir.disambiguate()
3205         assert d.__class__ is ddd.__class__, d.__class__
3206
3207         build_efile = fs.Entry(test.workpath('build/efile'))
3208         f = build_efile.disambiguate()
3209         assert f.__class__ is fff.__class__, f.__class__
3210
3211         build_nonexistant = fs.Entry(test.workpath('build/nonexistant'))
3212         f = build_nonexistant.disambiguate()
3213         assert f.__class__ is fff.__class__, f.__class__
3214
3215 class postprocessTestCase(unittest.TestCase):
3216     def runTest(self):
3217         """Test calling the postprocess() method."""
3218         fs = SCons.Node.FS.FS()
3219
3220         e = fs.Entry('e')
3221         e.postprocess()
3222
3223         d = fs.Dir('d')
3224         d.postprocess()
3225
3226         f = fs.File('f')
3227         f.postprocess()
3228
3229
3230
3231 class SpecialAttrTestCase(unittest.TestCase):
3232     def runTest(self):
3233         """Test special attributes of file nodes."""
3234         test=TestCmd(workdir='')
3235         fs = SCons.Node.FS.FS(test.workpath('work'))
3236
3237         f = fs.Entry('foo/bar/baz.blat').get_subst_proxy()
3238
3239         s = str(f.dir)
3240         assert s == os.path.normpath('foo/bar'), s
3241         assert f.dir.is_literal(), f.dir
3242         for_sig = f.dir.for_signature()
3243         assert for_sig == 'bar', for_sig
3244
3245         s = str(f.file)
3246         assert s == 'baz.blat', s
3247         assert f.file.is_literal(), f.file
3248         for_sig = f.file.for_signature()
3249         assert for_sig == 'baz.blat_file', for_sig
3250
3251         s = str(f.base)
3252         assert s == os.path.normpath('foo/bar/baz'), s
3253         assert f.base.is_literal(), f.base
3254         for_sig = f.base.for_signature()
3255         assert for_sig == 'baz.blat_base', for_sig
3256
3257         s = str(f.filebase)
3258         assert s == 'baz', s
3259         assert f.filebase.is_literal(), f.filebase
3260         for_sig = f.filebase.for_signature()
3261         assert for_sig == 'baz.blat_filebase', for_sig
3262
3263         s = str(f.suffix)
3264         assert s == '.blat', s
3265         assert f.suffix.is_literal(), f.suffix
3266         for_sig = f.suffix.for_signature()
3267         assert for_sig == 'baz.blat_suffix', for_sig
3268
3269         s = str(f.abspath)
3270         assert s == test.workpath('work', 'foo', 'bar', 'baz.blat'), s
3271         assert f.abspath.is_literal(), f.abspath
3272         for_sig = f.abspath.for_signature()
3273         assert for_sig == 'baz.blat_abspath', for_sig
3274
3275         s = str(f.posix)
3276         assert s == 'foo/bar/baz.blat', s
3277         assert f.posix.is_literal(), f.posix
3278         if f.posix != f:
3279             for_sig = f.posix.for_signature()
3280             assert for_sig == 'baz.blat_posix', for_sig
3281
3282         s = str(f.windows)
3283         assert s == 'foo\\bar\\baz.blat', repr(s)
3284         assert f.windows.is_literal(), f.windows
3285         if f.windows != f:
3286             for_sig = f.windows.for_signature()
3287             assert for_sig == 'baz.blat_windows', for_sig
3288
3289         # Deprecated synonym for the .windows suffix.
3290         s = str(f.win32)
3291         assert s == 'foo\\bar\\baz.blat', repr(s)
3292         assert f.win32.is_literal(), f.win32
3293         if f.win32 != f:
3294             for_sig = f.win32.for_signature()
3295             assert for_sig == 'baz.blat_windows', for_sig
3296
3297         # And now, combinations!!!
3298         s = str(f.srcpath.base)
3299         assert s == os.path.normpath('foo/bar/baz'), s
3300         s = str(f.srcpath.dir)
3301         assert s == str(f.srcdir), s
3302         s = str(f.srcpath.posix)
3303         assert s == 'foo/bar/baz.blat', s
3304         s = str(f.srcpath.windows)
3305         assert s == 'foo\\bar\\baz.blat', s
3306         s = str(f.srcpath.win32)
3307         assert s == 'foo\\bar\\baz.blat', s
3308
3309         # Test what happens with VariantDir()
3310         fs.VariantDir('foo', 'baz')
3311
3312         s = str(f.srcpath)
3313         assert s == os.path.normpath('baz/bar/baz.blat'), s
3314         assert f.srcpath.is_literal(), f.srcpath
3315         g = f.srcpath.get()
3316         assert isinstance(g, SCons.Node.FS.File), g.__class__
3317
3318         s = str(f.srcdir)
3319         assert s == os.path.normpath('baz/bar'), s
3320         assert f.srcdir.is_literal(), f.srcdir
3321         g = f.srcdir.get()
3322         assert isinstance(g, SCons.Node.FS.Dir), g.__class__
3323
3324         # And now what happens with VariantDir() + Repository()
3325         fs.Repository(test.workpath('repository'))
3326
3327         f = fs.Entry('foo/sub/file.suffix').get_subst_proxy()
3328         test.subdir('repository',
3329                     ['repository', 'baz'],
3330                     ['repository', 'baz', 'sub'])
3331
3332         rd = test.workpath('repository', 'baz', 'sub')
3333         rf = test.workpath('repository', 'baz', 'sub', 'file.suffix')
3334         test.write(rf, "\n")
3335
3336         s = str(f.srcpath)
3337         assert s == os.path.normpath('baz/sub/file.suffix'), s
3338         assert f.srcpath.is_literal(), f.srcpath
3339         g = f.srcpath.get()
3340         # Gets disambiguated to SCons.Node.FS.File by get_subst_proxy().
3341         assert isinstance(g, SCons.Node.FS.File), g.__class__
3342
3343         s = str(f.srcdir)
3344         assert s == os.path.normpath('baz/sub'), s
3345         assert f.srcdir.is_literal(), f.srcdir
3346         g = f.srcdir.get()
3347         assert isinstance(g, SCons.Node.FS.Dir), g.__class__
3348
3349         s = str(f.rsrcpath)
3350         assert s == rf, s
3351         assert f.rsrcpath.is_literal(), f.rsrcpath
3352         g = f.rsrcpath.get()
3353         assert isinstance(g, SCons.Node.FS.File), g.__class__
3354
3355         s = str(f.rsrcdir)
3356         assert s == rd, s
3357         assert f.rsrcdir.is_literal(), f.rsrcdir
3358         g = f.rsrcdir.get()
3359         assert isinstance(g, SCons.Node.FS.Dir), g.__class__
3360
3361         # Check that attempts to access non-existent attributes of the
3362         # subst proxy generate the right exceptions and messages.
3363         caught = None
3364         try:
3365             fs.Dir('ddd').get_subst_proxy().no_such_attr
3366         except AttributeError, e:
3367             assert str(e) == "Dir instance 'ddd' has no attribute 'no_such_attr'", e
3368             caught = 1
3369         assert caught, "did not catch expected AttributeError"
3370
3371         caught = None
3372         try:
3373             fs.Entry('eee').get_subst_proxy().no_such_attr
3374         except AttributeError, e:
3375             # Gets disambiguated to File instance by get_subst_proxy().
3376             assert str(e) == "File instance 'eee' has no attribute 'no_such_attr'", e
3377             caught = 1
3378         assert caught, "did not catch expected AttributeError"
3379
3380         caught = None
3381         try:
3382             fs.File('fff').get_subst_proxy().no_such_attr
3383         except AttributeError, e:
3384             assert str(e) == "File instance 'fff' has no attribute 'no_such_attr'", e
3385             caught = 1
3386         assert caught, "did not catch expected AttributeError"
3387
3388
3389
3390 class SaveStringsTestCase(unittest.TestCase):
3391     def runTest(self):
3392         """Test caching string values of nodes."""
3393         test=TestCmd(workdir='')
3394
3395         def setup(fs):
3396             fs.Dir('src')
3397             fs.Dir('d0')
3398             fs.Dir('d1')
3399
3400             d0_f = fs.File('d0/f')
3401             d1_f = fs.File('d1/f')
3402             d0_b = fs.File('d0/b')
3403             d1_b = fs.File('d1/b')
3404             d1_f.duplicate = 1
3405             d1_b.duplicate = 1
3406             d0_b.builder = 1
3407             d1_b.builder = 1
3408
3409             return [d0_f, d1_f, d0_b, d1_b]
3410
3411         def modify(nodes):
3412             d0_f, d1_f, d0_b, d1_b = nodes
3413             d1_f.duplicate = 0
3414             d1_b.duplicate = 0
3415             d0_b.builder = 0
3416             d1_b.builder = 0
3417
3418         fs1 = SCons.Node.FS.FS(test.workpath('fs1'))
3419         nodes = setup(fs1)
3420         fs1.VariantDir('d0', 'src', duplicate=0)
3421         fs1.VariantDir('d1', 'src', duplicate=1)
3422
3423         s = list(map(str, nodes))
3424         expect = list(map(os.path.normpath, ['src/f', 'd1/f', 'd0/b', 'd1/b']))
3425         assert s == expect, s
3426
3427         modify(nodes)
3428
3429         s = list(map(str, nodes))
3430         expect = list(map(os.path.normpath, ['src/f', 'src/f', 'd0/b', 'd1/b']))
3431         assert s == expect, s
3432
3433         SCons.Node.FS.save_strings(1)
3434         fs2 = SCons.Node.FS.FS(test.workpath('fs2'))
3435         nodes = setup(fs2)
3436         fs2.VariantDir('d0', 'src', duplicate=0)
3437         fs2.VariantDir('d1', 'src', duplicate=1)
3438
3439         s = list(map(str, nodes))
3440         expect = list(map(os.path.normpath, ['src/f', 'd1/f', 'd0/b', 'd1/b']))
3441         assert s == expect, s
3442
3443         modify(nodes)
3444
3445         s = list(map(str, nodes))
3446         expect = list(map(os.path.normpath, ['src/f', 'd1/f', 'd0/b', 'd1/b']))
3447         assert s == expect, 'node str() not cached: %s'%s
3448
3449
3450 class AbsolutePathTestCase(unittest.TestCase):
3451     def test_root_lookup_equivalence(self):
3452         """Test looking up /fff vs. fff in the / directory"""
3453         test=TestCmd(workdir='')
3454
3455         fs = SCons.Node.FS.FS('/')
3456
3457         save_cwd = os.getcwd()
3458         try:
3459             os.chdir('/')
3460             fff1 = fs.File('fff')
3461             fff2 = fs.File('/fff')
3462             assert fff1 is fff2, "fff and /fff returned different Nodes!"
3463         finally:
3464             os.chdir(save_cwd)
3465
3466
3467
3468 if __name__ == "__main__":
3469     suite = unittest.TestSuite()
3470     suite.addTest(VariantDirTestCase())
3471     suite.addTest(find_fileTestCase())
3472     suite.addTest(StringDirTestCase())
3473     suite.addTest(stored_infoTestCase())
3474     suite.addTest(has_src_builderTestCase())
3475     suite.addTest(prepareTestCase())
3476     suite.addTest(SConstruct_dirTestCase())
3477     suite.addTest(clearTestCase())
3478     suite.addTest(disambiguateTestCase())
3479     suite.addTest(postprocessTestCase())
3480     suite.addTest(SpecialAttrTestCase())
3481     suite.addTest(SaveStringsTestCase())
3482     tclasses = [
3483         AbsolutePathTestCase,
3484         BaseTestCase,
3485         CacheDirTestCase,
3486         DirTestCase,
3487         DirBuildInfoTestCase,
3488         DirNodeInfoTestCase,
3489         EntryTestCase,
3490         FileTestCase,
3491         FileBuildInfoTestCase,
3492         FileNodeInfoTestCase,
3493         FSTestCase,
3494         GlobTestCase,
3495         RepositoryTestCase,
3496     ]
3497     for tclass in tclasses:
3498         names = unittest.getTestCaseNames(tclass, 'test_')
3499         suite.addTests(list(map(tclass, names)))
3500     if not unittest.TextTestRunner().run(suite).wasSuccessful():
3501         sys.exit(1)
3502
3503 # Local Variables:
3504 # tab-width:4
3505 # indent-tabs-mode:nil
3506 # End:
3507 # vim: set expandtab tabstop=4 shiftwidth=4: