def test_symlink(self): # This test will fail if the checkout is in a symlink. actual = trace_inputs.split_at_symlink(None, ROOT_DIR) expected = (ROOT_DIR, None, None) self.assertEqual(expected, actual) actual = trace_inputs.split_at_symlink( None, os.path.join(BASE_DIR, 'trace_inputs')) expected = ( os.path.join(BASE_DIR, 'trace_inputs'), None, None) self.assertEqual(expected, actual) actual = trace_inputs.split_at_symlink( None, os.path.join(BASE_DIR, 'trace_inputs', 'files2')) expected = ( os.path.join(BASE_DIR, 'trace_inputs'), 'files2', '') self.assertEqual(expected, actual) actual = trace_inputs.split_at_symlink( ROOT_DIR, os.path.join('tests', 'trace_inputs', 'files2')) expected = ( os.path.join('tests', 'trace_inputs'), 'files2', '') self.assertEqual(expected, actual) actual = trace_inputs.split_at_symlink( ROOT_DIR, os.path.join('tests', 'trace_inputs', 'files2', 'bar')) expected = ( os.path.join('tests', 'trace_inputs'), 'files2', '/bar') self.assertEqual(expected, actual)
def expand_directory_and_symlink(indir, relfile, blacklist): """Expands a single input. It can result in multiple outputs. This function is recursive when relfile is a directory or a symlink. Note: this code doesn't properly handle recursive symlink like one created with: ln -s .. foo """ if os.path.isabs(relfile): raise run_test_from_archive.MappingError( 'Can\'t map absolute path %s' % relfile) infile = normpath(os.path.join(indir, relfile)) if not infile.startswith(indir): raise run_test_from_archive.MappingError( 'Can\'t map file %s outside %s' % (infile, indir)) if sys.platform != 'win32': # Look if any item in relfile is a symlink. base, symlink, rest = trace_inputs.split_at_symlink(indir, relfile) if symlink: # Append everything pointed by the symlink. If the symlink is recursive, # this code blows up. symlink_relfile = os.path.join(base, symlink) symlink_path = os.path.join(indir, symlink_relfile) pointed = os.readlink(symlink_path) dest_infile = normpath( os.path.join(os.path.dirname(symlink_path), pointed)) if rest: dest_infile = trace_inputs.safe_join(dest_infile, rest) if not dest_infile.startswith(indir): raise run_test_from_archive.MappingError( 'Can\'t map symlink reference %s (from %s) ->%s outside of %s' % (symlink_relfile, relfile, dest_infile, indir)) if infile.startswith(dest_infile): raise run_test_from_archive.MappingError( 'Can\'t map recursive symlink reference %s->%s' % (symlink_relfile, dest_infile)) dest_relfile = dest_infile[len(indir)+1:] logging.info('Found symlink: %s -> %s' % (symlink_relfile, dest_relfile)) out = expand_directory_and_symlink(indir, dest_relfile, blacklist) # Add the symlink itself. out.append(symlink_relfile) return out if relfile.endswith(os.path.sep): if not os.path.isdir(infile): raise run_test_from_archive.MappingError( '%s is not a directory but ends with "%s"' % (infile, os.path.sep)) outfiles = [] for filename in os.listdir(infile): inner_relfile = os.path.join(relfile, filename) if blacklist(inner_relfile): continue if os.path.isdir(os.path.join(indir, inner_relfile)): inner_relfile += os.path.sep outfiles.extend( expand_directory_and_symlink(indir, inner_relfile, blacklist)) return outfiles else: # Always add individual files even if they were blacklisted. if os.path.isdir(infile): raise run_test_from_archive.MappingError( 'Input directory %s must have a trailing slash' % infile) if not os.path.isfile(infile): raise run_test_from_archive.MappingError( 'Input file %s doesn\'t exist' % infile) return [relfile]