def test_rename_path_with_directory_containing_file_end_points(self): file_path = join_path([self.test_dir, self.p('foo')]) content_old = self.p('klink\n') content_new = self.p('klank\n') self._create_file(file_path, content_old) self._init( DelegateMultiMetaStore([ContentMetaStore(), PathMetaStore()]), u'/%c/%f', ) self.source_tree_rep.start() try: self.source_tree_rep.rename_path( join_path_abs([content_old.rstrip(u'\n')]), join_path_abs([content_new.rstrip(u'\n')]), ) self.source_tree_rep.update_source_file(file_path) self.assertEqual( self.source_tree_rep.get_entries(u'/'), [content_new.rstrip(u'\n')], ) self.assertEqual( self.source_tree_rep.get_entries( join_path_abs([content_new.rstrip(u'\n')])), [self.p('foo')], ) self.assertEqual(self._get_file_content(file_path), content_new) finally: self.source_tree_rep.stop() self._remove_file(file_path)
def test_rename_path_with_directory_containing_file_end_points(self): file_path = join_path([self.test_dir, self.p('foo')]) content_old = self.p('klink\n') content_new = self.p('klank\n') self._create_file(file_path, content_old) self._init( DelegateMultiMetaStore([ContentMetaStore(), PathMetaStore()]), u'/%c/%f', ) self.source_tree_rep.start() try: self.source_tree_rep.rename_path( join_path_abs([content_old.rstrip(u'\n')]), join_path_abs([content_new.rstrip(u'\n')]), ) self.source_tree_rep.update_source_file(file_path) self.assertEqual( self.source_tree_rep.get_entries(u'/'), [content_new.rstrip(u'\n')], ) self.assertEqual( self.source_tree_rep.get_entries(join_path_abs([content_new.rstrip(u'\n')])), [self.p('foo')], ) self.assertEqual(self._get_file_content(file_path), content_new) finally: self.source_tree_rep.stop() self._remove_file(file_path)
def test_add_remove_directory(self): self._init(ContentMetaStore(), u'/%c/%c') self.source_tree_rep.start() try: self.source_tree_rep.add_directory(join_path_abs([self.p('foo')])) self.source_tree_rep.remove_directory(join_path_abs([self.p('foo')])) self.assertEqual(self.source_tree_rep.get_entries(u'/'), []) finally: self.source_tree_rep.stop()
def test_add_remove_directory(self): self._init(ContentMetaStore(), u'/%c/%c') self.source_tree_rep.start() try: self.source_tree_rep.add_directory(join_path_abs([self.p('foo')])) self.source_tree_rep.remove_directory( join_path_abs([self.p('foo')])) self.assertEqual(self.source_tree_rep.get_entries(u'/'), []) finally: self.source_tree_rep.stop()
def test_rename_path_with_nonexistent_path(self): self._init(ContentMetaStore(), u'/%c') self.source_tree_rep.start() try: self.assertRaises( PathNotFound, self.source_tree_rep.rename_path, join_path_abs([self.p('foo')]), join_path_abs([self.p('bar')]), ) finally: self.source_tree_rep.stop()
def test_rename_path_with_empty_directory_end_point(self): self._init(ContentMetaStore(), u'/%c/%c') self.source_tree_rep.start() try: self.source_tree_rep.add_directory(join_path_abs([self.p('foo')])) self.source_tree_rep.rename_path( join_path_abs([self.p('foo')]), join_path_abs([self.p('bar')]), ) self.assertEqual(self.source_tree_rep.get_entries(u'/'), [self.p('bar')]) self.assertEqual(self.source_tree_rep.get_entries(self.p(u'/bar')), []) finally: self.source_tree_rep.stop()
def test_rename_path_with_empty_directory_end_point(self): self._init(ContentMetaStore(), u'/%c/%c') self.source_tree_rep.start() try: self.source_tree_rep.add_directory(join_path_abs([self.p('foo')])) self.source_tree_rep.rename_path( join_path_abs([self.p('foo')]), join_path_abs([self.p('bar')]), ) self.assertEqual( self.source_tree_rep.get_entries(u'/'), [self.p('bar')]) self.assertEqual( self.source_tree_rep.get_entries(self.p(u'/bar')), []) finally: self.source_tree_rep.stop()
def test_file_end_point(self): filename = self.p('foo') real_path = join_path([self.test_dir, filename]) self._create_file(real_path) try: self._init(PathMetaStore(), u'/%f/%f') fake_path = join_path_abs([filename, filename]) self.source_tree_rep.start() try: self.assertEqual(self.source_tree_rep.get_real_path(fake_path), real_path) self.assertEqual( self.source_tree_rep.get_fake_paths(real_path), [fake_path]) self.assertRaises(NotADirectory, self.source_tree_rep.get_entries, fake_path) self.assertTrue(self.source_tree_rep.path_exists(fake_path)) self.assertTrue(self.source_tree_rep.is_file(fake_path)) self.assertFalse(self.source_tree_rep.is_dir(fake_path)) self.assertFalse(self.source_tree_rep.is_empty_dir(fake_path)) finally: self.source_tree_rep.stop() finally: self._remove_file(real_path)
def test_directory_mid_point(self): filename = self.p('foo') real_path = join_path([self.test_dir, filename]) self._create_file(real_path) try: self._init(PathMetaStore(), u'/%f/%f') fake_path = join_path_abs([filename]) self.source_tree_rep.start() try: self.assertRaises( IsADirectory, self.source_tree_rep.get_real_path, fake_path) self.assertEqual( self.source_tree_rep.get_entries(fake_path), [filename]) self.assertTrue( self.source_tree_rep.path_exists(fake_path)) self.assertFalse( self.source_tree_rep.is_file(fake_path)) self.assertTrue( self.source_tree_rep.is_dir(fake_path)) self.assertFalse( self.source_tree_rep.is_empty_dir(fake_path)) finally: self.source_tree_rep.stop() finally: self._remove_file(real_path)
def test_fill_path_with_missing_conditional(self): self._init(NullMetaStore(), u'/%a/%b/%?%c%:Default%?') values = {'a': self.p('foo'), 'b': self.p('bar')} self.assertEqual( self.source_tree_rep.fill_path(values), join_path_abs([self.p('foo'), self.p('bar'), u'Default']), )
def test_getattr_with_file_end_point(self): filename = self.p('foo') real_path = join_path([self.test_dir, filename]) self._create_file(real_path) try: self._init(PathMetaStore(), u'/%f/%f') fake_path = join_path_abs([filename, filename]) self.source_tree_rep.start() try: lstat_result = os.lstat(real_path) getattr_result = self.source_tree_rep.getattr(fake_path) self.assertTrue(isinstance(getattr_result, os.stat_result)) self.assertTrue(stat.S_ISREG(getattr_result.st_mode)) self.assertEqual( stat.S_IMODE(lstat_result.st_mode), stat.S_IMODE(getattr_result.st_mode), ) for attr in ('st_nlink', 'st_uid', 'st_gid', 'st_size'): self._assert_lstat_getattr_attrs_are_equal( attr, lstat_result, getattr_result) for attr in ('st_atime', 'st_mtime', 'st_ctime'): self._assert_lstat_getattr_attrs_are_equal_plus_or_minus( attr, lstat_result, getattr_result, 2) finally: self.source_tree_rep.stop() finally: self._remove_file(real_path)
def test_getattr_with_directory_end_point(self): self._init(PathMetaStore(), u'/%f/%f') fake_path = join_path_abs([self.p('foo')]) self.source_tree_rep.start() try: self.source_tree_rep.add_directory(fake_path) lstat_result = os.lstat(self.test_dir) getattr_result = self.source_tree_rep.getattr(fake_path) self.assertTrue(isinstance(getattr_result, os.stat_result)) self.assertEqual(getattr_result.st_size, 0) # Empty directories have two links. self.assertEqual(getattr_result.st_nlink, 2) self.assertTrue(stat.S_ISDIR(getattr_result.st_mode)) self.assertEqual( stat.S_IMODE(lstat_result.st_mode), stat.S_IMODE(getattr_result.st_mode), ) for attr in ('st_uid', 'st_gid'): self._assert_lstat_getattr_attrs_are_equal( attr, lstat_result, getattr_result) for attr in ('st_atime', 'st_mtime', 'st_ctime'): self._assert_lstat_getattr_attrs_are_equal_plus_or_minus( attr, lstat_result, getattr_result, 2) finally: self.source_tree_rep.stop()
def build_source_tree(self): super(GetattrWithSymlinkTestCase, self).build_source_tree() self.filename = self.p('foo') self.source_file = os.path.join(self.source_dir, self.filename) self.dest_file = join_path_abs([self.filename]) self.dest_file_encoded = self.dest_file.encode(ENCODING) os.symlink('/dev/null', self.source_file)
def test_utime_with_non_existent_path(self): self._init(PathMetaStore(), u'/%f/%f') fake_path = join_path_abs([self.p('foo')]) self.source_tree_rep.start() try: self.assertRaises( PathNotFound, self.source_tree_rep.utime, fake_path, (1, 2)) finally: self.source_tree_rep.stop()
def test_utime_with_non_existent_path(self): self._init(PathMetaStore(), u'/%f/%f') fake_path = join_path_abs([self.p('foo')]) self.source_tree_rep.start() try: self.assertRaises(PathNotFound, self.source_tree_rep.utime, fake_path, (1, 2)) finally: self.source_tree_rep.stop()
def test_fill_path(self): self._init(NullMetaStore(), u'/%a/%b/%c') values = { 'a': self.p('foo'), 'b': self.p('bar'), 'c': self.p('baz'), } self.assertEqual( self.source_tree_rep.fill_path(values), join_path_abs([self.p('foo'), self.p('bar'), self.p('baz')]), )
def test_rename_path_with_file_end_point(self): file_path = join_path([self.test_dir, self.p('foo')]) content_old = self.p('bar\n') content_new = self.p('baz\n') self._create_file(file_path, content_old) self._init(ContentMetaStore(), u'/%c') self.source_tree_rep.start() try: self.source_tree_rep.rename_path( join_path_abs([content_old.rstrip(u'\n')]), join_path_abs([content_new.rstrip(u'\n')]), ) self.assertEqual( self.source_tree_rep.get_entries(u'/'), [content_old.rstrip(u'\n')], ) self.assertEqual(self._get_file_content(file_path), content_new) finally: self.source_tree_rep.stop() self._remove_file(file_path)
def init(self): self._special_file_classes_by_path = {} for cls in self.special_file_classes: self._special_file_classes_by_path[join_path_abs([cls.filename ])] = cls cls.filesystem = self log_debug( 'SpecialFileFileSystemMixin: _special_file_classes_by_path = %r', self._special_file_classes_by_path, ) return super(SpecialFileFileSystemMixin, self).init()
def get_end_points(self, fake_path): if self.is_file(fake_path): return [fake_path] entries = self.get_entries(fake_path) if entries == []: return [fake_path] end_points = [] for entry in entries: entry_path = join_path_abs([fake_path, entry]) end_points.extend(self.get_end_points(entry_path)) return end_points
def test_utime_with_directory_end_point(self): self._init(PathMetaStore(), u'/%f/%f') fake_path = join_path_abs([self.p('foo')]) self.source_tree_rep.start() try: self.source_tree_rep.add_directory(fake_path) times = (1, 2) self.source_tree_rep.utime(fake_path, times) lstat_result = os.lstat(self.test_dir) self.assertEqual(lstat_result.st_atime, times[0]) self.assertEqual(lstat_result.st_mtime, times[1]) finally: self.source_tree_rep.stop()
def build_source_tree(self): super(_BaseSingleFileOperationTestCase, self).build_source_tree() self.filename = self.p('foo') self.source_file = os.path.join(self.source_dir, self.filename) self.dest_file = join_path_abs([self.filename]) self.dest_file_encoded = self.dest_file.encode(ENCODING) self.content = self.get_content() f = open(self.source_file, 'w') try: f.write(self.content) finally: f.close()
def test_real_path_with_multiple_tag_values(self): real_path = join_path([self.test_dir, self.p('foo')]) content = self.p('bar\nbaz\nqux\n') fake_path1 = join_path_abs([self.p('bar')]) fake_path2 = join_path_abs([self.p('baz')]) fake_path3 = join_path_abs([self.p('qux')]) self._create_file(real_path, content) try: self._init(ContentMetaStore(), u'/%c') self.source_tree_rep.start() try: self.assertEqual( self.source_tree_rep.get_real_path(fake_path1), real_path) self.assertEqual( self.source_tree_rep.get_real_path(fake_path2), real_path) self.assertEqual( self.source_tree_rep.get_real_path(fake_path3), real_path) self.assertEqual( self.source_tree_rep.get_fake_paths(real_path), [fake_path1, fake_path2, fake_path3]) finally: self.source_tree_rep.stop() finally: self._remove_file(real_path)
def test_real_path_with_multiple_tag_values(self): real_path = join_path([self.test_dir, self.p('foo')]) content = self.p('bar\nbaz\nqux\n') fake_path1 = join_path_abs([self.p('bar')]) fake_path2 = join_path_abs([self.p('baz')]) fake_path3 = join_path_abs([self.p('qux')]) self._create_file(real_path, content) try: self._init(ContentMetaStore(), u'/%c') self.source_tree_rep.start() try: self.assertEqual( self.source_tree_rep.get_real_path(fake_path1), real_path) self.assertEqual( self.source_tree_rep.get_real_path(fake_path2), real_path) self.assertEqual( self.source_tree_rep.get_real_path(fake_path3), real_path) self.assertEqual( self.source_tree_rep.get_fake_paths( real_path), [fake_path1, fake_path2, fake_path3]) finally: self.source_tree_rep.stop() finally: self._remove_file(real_path)
def test_non_existent_path(self): self._init(PathMetaStore(), u'/%f/%f') fake_path = join_path_abs([self.p('foo')]) self.source_tree_rep.start() try: self.assertRaises(PathNotFound, self.source_tree_rep.get_real_path, fake_path) self.assertRaises(PathNotFound, self.source_tree_rep.get_entries, fake_path) self.assertFalse(self.source_tree_rep.path_exists(fake_path)) self.assertFalse(self.source_tree_rep.is_file(fake_path)) self.assertFalse(self.source_tree_rep.is_dir(fake_path)) self.assertFalse(self.source_tree_rep.is_empty_dir(fake_path)) finally: self.source_tree_rep.stop()
def test_directory_end_point(self): self._init(PathMetaStore(), u'/%f/%f') fake_path = join_path_abs([self.p('foo')]) self.source_tree_rep.start() try: self.source_tree_rep.add_directory(fake_path) self.assertRaises(IsADirectory, self.source_tree_rep.get_real_path, fake_path) self.assertEqual(self.source_tree_rep.get_entries(fake_path), []) self.assertTrue(self.source_tree_rep.path_exists(fake_path)) self.assertFalse(self.source_tree_rep.is_file(fake_path)) self.assertTrue(self.source_tree_rep.is_dir(fake_path)) self.assertTrue(self.source_tree_rep.is_empty_dir(fake_path)) finally: self.source_tree_rep.stop()
def test_getattr_with_directory_mid_point(self): filename = self.p('foo') real_path = join_path([self.test_dir, filename]) self._create_file(real_path) try: self._init(PathMetaStore(), u'/%f/%f') fake_path = join_path_abs([self.p('foo')]) fake_path_parent = os.path.dirname(fake_path) self.source_tree_rep.start() try: for test_path in (fake_path_parent, fake_path): dir_lstat_result = os.lstat(self.test_dir) file_lstat_result = os.lstat(real_path) getattr_result = self.source_tree_rep.getattr(test_path) self.assertTrue(isinstance(getattr_result, os.stat_result)) self.assertEqual(getattr_result.st_size, 0) if test_path == fake_path: # A directory containing one file has two links. self.assertEqual(getattr_result.st_nlink, 2) else: # A directory containing one subdirectory has three links. self.assertEqual(getattr_result.st_nlink, 3) self.assertTrue(stat.S_ISDIR(getattr_result.st_mode)) self.assertEqual( stat.S_IMODE(dir_lstat_result.st_mode), stat.S_IMODE(getattr_result.st_mode), ) # Ownership should be the same as the source directory. for attr in ('st_uid', 'st_gid'): self._assert_lstat_getattr_attrs_are_equal( attr, dir_lstat_result, getattr_result) # Times should be the same as the contained file. for attr in ('st_atime', 'st_mtime', 'st_ctime'): self._assert_lstat_getattr_attrs_are_equal_plus_or_minus( attr, file_lstat_result, getattr_result, 2) finally: self.source_tree_rep.stop() finally: self._remove_file(real_path)
def test_utime_with_file_end_point(self): filename = self.p('foo') real_path = join_path([self.test_dir, filename]) self._create_file(real_path) self._init(PathMetaStore(), u'/%f/%f') fake_path = join_path_abs([filename, filename]) self.source_tree_rep.start() try: times = (1, 2) self.source_tree_rep.utime(fake_path, times) lstat_result = os.lstat(real_path) self.assertEqual(lstat_result.st_atime, times[0]) self.assertEqual(lstat_result.st_mtime, times[1]) finally: self.source_tree_rep.stop() self._remove_file(real_path)
def test_non_existent_path(self): self._init(PathMetaStore(), u'/%f/%f') fake_path = join_path_abs([self.p('foo')]) self.source_tree_rep.start() try: self.assertRaises( PathNotFound, self.source_tree_rep.get_real_path, fake_path) self.assertRaises( PathNotFound, self.source_tree_rep.get_entries, fake_path) self.assertFalse( self.source_tree_rep.path_exists(fake_path)) self.assertFalse( self.source_tree_rep.is_file(fake_path)) self.assertFalse( self.source_tree_rep.is_dir(fake_path)) self.assertFalse( self.source_tree_rep.is_empty_dir(fake_path)) finally: self.source_tree_rep.stop()
def test_directory_end_point(self): self._init(PathMetaStore(), u'/%f/%f') fake_path = join_path_abs([self.p('foo')]) self.source_tree_rep.start() try: self.source_tree_rep.add_directory(fake_path) self.assertRaises( IsADirectory, self.source_tree_rep.get_real_path, fake_path) self.assertEqual( self.source_tree_rep.get_entries(fake_path), []) self.assertTrue( self.source_tree_rep.path_exists(fake_path)) self.assertFalse( self.source_tree_rep.is_file(fake_path)) self.assertTrue( self.source_tree_rep.is_dir(fake_path)) self.assertTrue( self.source_tree_rep.is_empty_dir(fake_path)) finally: self.source_tree_rep.stop()
def test_path_conflict_then_remove_second(self): self._init(PathMetaStore(), u'/%f') filename = self.p('foo') real_path1 = join_path([self.test_dir, self.p('bar'), filename]) real_path2 = join_path([self.test_dir, self.p('baz'), filename]) files = (real_path1, real_path2) dirs = [os.path.dirname(p) for p in files] fake_path = join_path_abs([filename]) self.source_tree_rep.start() try: self._create_dirs(dirs) try: self._create_files(files) self.source_tree_rep.add_source_file(real_path1) self.source_tree_rep.add_source_file(real_path2) self._remove_file(real_path2) self.source_tree_rep.remove_source_file(real_path2) self.assertEqual( self.source_tree_rep.get_real_path(fake_path), real_path1, ) self._remove_file(real_path1) self.source_tree_rep.remove_source_file(real_path1) self.assertRaises( PathNotFound, self.source_tree_rep.get_real_path, fake_path, ) finally: self._remove_dirs(dirs) finally: self.source_tree_rep.stop()
def fgetattr(self): return self.getattr(join_path_abs([self.filename]))
def rename_path(self, old_fake_path, new_fake_path): self.validate_fake_path(old_fake_path) self.validate_fake_path(new_fake_path) log_info( u'SourceTreeRepresentation.rename_path: renaming %s to %s', old_fake_path, new_fake_path, ) old_path_parts = split_path(old_fake_path) new_path_parts = split_path(new_fake_path) if len(old_path_parts) != len(new_path_parts): log_error( (u'rename_path: old path and new path have ' u'differing directory depths: %s, %s'), old_fake_path, new_fake_path, ) raise InvalidArgument # Find the index of the path segment that changed: for index, (old_path_part, new_path_part) in enumerate( zip(old_path_parts, new_path_parts)): if old_path_part != new_path_part: break old_node_path = join_path_abs(old_path_parts[:index + 1]) # Here's our approach: # 1. Separate the affected end points into files and directories. # 2. For each directory end point, remove the old directory and add the # new one. # 3. For each file end point: # a. We know which path segment changed, so we can use that # information to get old tag values for that segment and new tag # values for that segment. These values represent the total tag # change for that particular end point. # b. Group the values by real path and then combine each group. # c. For each affected real path, use Values.diff3 to calculate a # final values delta and apply it to the real path. end_points = self.path_store.get_end_points(old_fake_path) file_end_points = [] directory_end_points = [] for end_point in end_points: if self.is_file(end_point): file_end_points.append(end_point) else: directory_end_points.append(end_point) del end_points # Handle directory end points: for end_point in directory_end_points: self.remove_directory(end_point) end_point_parts = split_path(end_point) end_point_parts[index] = new_path_parts[index] self.add_directory_with_parents(join_path_abs(end_point_parts)) del directory_end_points # Get old values, new values for each file end point: old_values_by_end_point = {} new_values_by_end_point = {} for end_point in file_end_points: meta_data = self.path_store.get_meta_data(end_point) end_point_splitters = meta_data['splitters'] splitter = end_point_splitters[index] old_values = Values.from_flat_dict(splitter.split(old_path_part)) try: new_values = Values.from_flat_dict( splitter.split(new_path_part)) except PatternError, e: log_error(u'rename_path: %s', e) raise InvalidArgument old_values_by_end_point[end_point] = old_values new_values_by_end_point[end_point] = new_values
class SourceTreeRepresentation(object): meta_store = None substitution_patterns = None path_store = None source_tree = None monitor = None cache = None filters = None def __init__( self, meta_store, substitution_patterns, path_store, source_tree, monitor, cache=None, filters=(), debug=False, ): for substitution_pattern in substitution_patterns: if substitution_pattern.expression == '': raise ValueError('substitution pattern string cannot be empty') self.debug = debug self.meta_store = meta_store self.substitution_patterns = substitution_patterns self.path_store = path_store self.source_tree = source_tree self.monitor = monitor self.monitor.add_cb = self.add_cb self.monitor.remove_cb = self.remove_cb self.monitor.update_cb = self.update_cb self.cache = cache self.filters = [] for expr, real in filters: self.add_filter(expr, real) def start(self): self.monitor.start(debug=self.debug) self.populate() def stop(self): self.monitor.stop() def populate(self): log_info('populating source tree representation...') self.add_source_dir(self.source_tree.root) ################################################################################ def validate_source_path(self, real_path): self.validate_path(real_path) def validate_fake_path(self, fake_path): self.validate_path(fake_path) def validate_path(self, path): if type(path) is not unicode: raise AssertionError(u'path object %s is not a unicode string' % repr(path)) if not path.startswith(unicode_path_sep): raise AssertionError(u'path %s does not start with "%s"' % (repr(path), unicode_path_sep)) if path.endswith(unicode_path_sep) and (path != unicode_path_sep): raise AssertionError(u'path %s ends with "%s"' % (repr(path), unicode_path_sep)) def fill_path(self, substitutions): log_debug(u'fill_path: substitutions = %r', substitutions) if isinstance(substitutions, Values): raise TypeError('substitutions must not be Values instance') fake_path_parts = [] for substitution_pattern in self.substitution_patterns: try: fake_path_part = substitution_pattern.fill(substitutions) except PatternError, e: raise UnrepresentablePath(unicode(e)) if fake_path_part == '': # This is potentially caused by a bad format string. We # need to handle it here because it could be caused by a # format string directory segment that is a single # conditional expression (and that is valid). If it is # caused by a segment that is empty ("//"), it is better to # handle the problem at initialization time by rejecting # the format string. raise UnrepresentablePath( u'fake path would have a path segment with length zero') if unicode_path_sep in fake_path_part: # The tag value has a slash in it. Proceeding would result # in a fake path that has too many levels of directories. raise UnrepresentablePath( u'fake path would have a path segment with a slash') fake_path_parts.append(fake_path_part) return join_path_abs(fake_path_parts)