# Copyright (C) 2010, 2011 Lars Wirzenius # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import stat import unittest import summainlib class FakeStatResult(object): def __init__(self, *args, **kwargs): for name, value in kwargs.iteritems(): setattr(self, name, value) class FakeSha1(object): def update(self, data): pass def hexdigest(self): return 'abc' class FakeOpenFile(object): def __call__(self, filename): self.data = 'some data' return self def read(self, amount): data = self.data[:amount] self.data = self.data[len(data):] return data def close(self): pass class FakeReadlink(object): def __init__(self, parent): self.parent = parent def __call__(self, filename): if stat.S_ISLNK(self.parent.st.st_mode): self.target = 'symlink' else: self.target = '' return self.target class FilesystemObjectTests(unittest.TestCase): def setUp(self): self.st = FakeStatResult(st_mtime=1262307723, st_mode=stat.S_IFREG | 0644, st_ino=12765, st_dev=42, st_nlink=2, st_size=1, st_uid=0, st_gid=0) self.nn = summainlib.NumberNormalizer() self.pn = summainlib.SamePath() self.exclude = [] def new(self, name, mode=None): if mode is not None: self.st.st_mode = mode return summainlib.FilesystemObject(name, self.nn, self.pn, self.exclude, stat_result=self.st, sha1=FakeSha1(), open_file=FakeOpenFile(), readlink=FakeReadlink(self)) def test_formats_simple_name_identically(self): self.assertEqual(self.new('foo')['Name'], 'foo') def test_formats_space_correctly(self): self.assertEqual(self.new('foo bar')['Name'], 'foo%20bar') def test_formats_mtime_correctly(self): self.assertEqual(self.new('foo')['Mtime'], '2010-01-01 01:02:03 +0000') def test_formats_mode_for_regular_file_correctly(self): self.assertEqual(self.new('foo')['Mode'], '100644') def test_formats_inode_number_correctly(self): # Note: normalization makes the result be 1. self.assertEqual(self.new('foo')['Ino'], '1') def test_formats_device_number_correctly(self): # Note: normalization makes the result be 1. self.assertEqual(self.new('foo')['Dev'], '1') def test_formats_link_count_correctly(self): self.assertEqual(self.new('foo')['Nlink'], '2') def test_formats_size_correctly(self): self.assertEqual(self.new('foo')['Size'], '1') def test_formats_uid_correctly(self): self.assertEqual(self.new('foo')['Uid'], '0') def test_formats_username_correctly(self): self.assertEqual(self.new('foo')['Username'], 'root') def test_formats_gid_correctly(self): self.assertEqual(self.new('foo')['Gid'], '0') def test_formats_group_correctly(self): self.assertEqual(self.new('foo')['Group'], 'root') def test_formats_sha1_correctly_for_regular_file(self): self.assertEqual(self.new('foo')['Sha-1'], 'abc') def test_formats_sha1_correctly_for_special_file(self): self.st.st_mode = stat.S_IFDIR | 0755 self.assertEqual(self.new('foo')['Sha-1'], '') def test_formats_target_correctly_for_symlink(self): self.st.st_mode = stat.S_IFLNK | 0777 self.assertEqual(self.new('foo')['Target'], 'symlink') def test_formats_target_correctly_for_regular_file(self): self.assertEqual(self.new('foo')['Target'], '') def test_does_not_output_size_for_directory(self): fso = self.new('foo', mode=stat.S_IFDIR | 0777) output = fso.format() self.assert_('Size:' not in output) def test_relative_path_returns_path_if_not_starting_with_root(self): fso = self.new('/foo/bar') self.assertEqual(fso.relative_path('/yo'), '/foo/bar') def test_relative_path_returns_partial_path_if_starting_with_root(self): fso = self.new('/foo/bar') self.assertEqual(fso.relative_path('/foo'), 'bar') def test_relative_path_returns_dot_if_same_as_root_and_dir(self): fso = self.new('/foo/bar', mode=stat.S_IFDIR) self.assertEqual(fso.relative_path('/foo/bar'), '.') def test_relative_path_returns_path_if_same_as_root_and_not_dir(self): fso = self.new('/foo/bar', mode=stat.S_IFREG) self.assertEqual(fso.relative_path('/foo/bar'), '/foo/bar') def test_relative_path_returns_path_if_root_is_slashless_prefix(self): fso = self.new('/foobar', mode=stat.S_IFREG) self.assertEqual(fso.relative_path('/foo'), '/foobar') def test_relative_path_returns_partial_path_if_root_ends_in_slash(self): fso = self.new('/foo/bar', mode=stat.S_IFREG) self.assertEqual(fso.relative_path('/foo/'), 'bar') class FilesystemObjectNormalizedNumbersTests(unittest.TestCase): def setUp(self): self.ino = 0 self.dev = 0 self.nn = summainlib.NumberNormalizer() self.pn = summainlib.SamePath() self.exclude = [] def reset(self): self.dev += 1 self.nn.reset() def new(self, name): st = FakeStatResult(st_ino=self.ino, st_dev=self.dev, st_mtime=0, st_mode=stat.S_IFREG|0, st_nlink=1, st_size=0, st_uid=0, st_gid=0) self.ino += 1 return summainlib.FilesystemObject(name, self.nn, self.pn, self.exclude, stat_result=st, sha1=FakeSha1(), open_file=FakeOpenFile(), readlink=FakeReadlink(self)) def test_inode_numbers_are_repeatable(self): a1 = self.new('foo') a2 = self.new('bar') self.reset() b1 = self.new('foo') b2 = self.new('bar') self.assertEqual(a1['Dev'], b1['Dev']) self.assertEqual(a1['Ino'], b1['Ino']) self.assertEqual(a2['Dev'], b2['Dev']) self.assertEqual(a2['Ino'], b2['Ino']) class NumberNormalizerTests(unittest.TestCase): def setUp(self): self.nn = summainlib.NumberNormalizer() def test_returns_1_2_3_regardless_of_input_numbers(self): self.assertEqual([self.nn.get_ino(i) for i in [10, 11, 12]], [1, 2, 3]) def test_returns_1_1_1_when_input_number_is_repeated(self): self.assertEqual([self.nn.get_ino(i) for i in [10, 10, 10]], [1, 1, 1]) class PathNormalizerTests(unittest.TestCase): def setUp(self): self.pn = summainlib.PathNormalizer() def test_returns_different_paths_for_different_inputs(self): self.assertEqual(self.pn.normalize('/foo/bar'), '/a/b') self.assertEqual(self.pn.normalize('/ping/pong'), '/c/d') def test_returns_same_paths_for_same_input(self): self.assertEqual(self.pn.normalize('/foo/bar'), '/a/b') self.assertEqual(self.pn.normalize('/foo/bar'), '/a/b') def test_returns_same_parent_path_for_siblings(self): self.assertEqual(self.pn.normalize('/foo/bar'), '/a/b') self.assertEqual(self.pn.normalize('/foo/yo'), '/a/c') self.assertEqual(self.pn.normalize('/foo'), '/a') def test_handles_trailing_slashes(self): self.assertEqual(self.pn.normalize('/foo/bar'), '/a/b') self.assertEqual(self.pn.normalize('/foo/bar/'), '/a/b/') def test_handles_relative_paths(self): self.assertEqual(self.pn.normalize('foo/bar'), 'a/b') self.assertEqual(self.pn.normalize('/foo/bar'), '/a/b') def test_handles_dot(self): self.assertEqual(self.pn.normalize('.'), '.') def test_handles_dotdot(self): self.assertEqual(self.pn.normalize('..'), '..') def test_splits_root(self): self.assertEqual(self.pn.split('/'), ['/']) def test_splits_absolute_path(self): self.assertEqual(self.pn.split('/foo/bar'), ['/', 'foo', 'bar']) def test_splits_trailing_slash(self): self.assertEqual(self.pn.split('/foo/'), ['/', 'foo', '/']) self.assertEqual(self.pn.split('/foo/bar/'), ['/', 'foo', 'bar', '/']) def test_splits_relative_path(self): self.assertEqual(self.pn.split('foo/bar'), ['foo', 'bar']) def test_normalizes_slash_to_itself(self): self.assertEqual(self.pn.normalize_part('/'), '/') def test_normalizes_first_part_to_a(self): self.assertEqual(self.pn.normalize_part('foo'), 'a') def test_normalizes_second_part_to_b(self): self.pn.normalize_part('foo') self.assertEqual(self.pn.normalize_part('bar'), 'b') def test_normalizes_same_part_twice_to_same_result(self): self.pn.normalize_part('foo') self.assertEqual(self.pn.normalize_part('foo'), 'a')