import py import os import commands from fat import FAT, Attrib, FatError def pytest_funcarg__rawdisk(request): def setup(): tmpdir = request.getfuncargvalue('tmpdir') disk = tmpdir.join('disk') os.system('dd if=/dev/zero of=%s bs=33M count=1' % disk) return disk return request.cached_setup(setup, scope='session') def pytest_funcarg__disk(request): disk = request.getfuncargvalue('rawdisk') os.system('mkfs.vfat -F 32 %s' % disk) return disk def pytest_funcarg__disk16(request): disk = request.getfuncargvalue('rawdisk') os.system('mkfs.vfat -F 16 %s' % disk) return disk def pytest_funcarg__mtools(request): disk = request.getfuncargvalue('disk') class MTools(object): def __getattr__(self, cmd): def fn(*args): args = ' '.join(['"%s"' % arg for arg in args]) ret, out = commands.getstatusoutput('%s -i %s %s' % (cmd, disk, args)) assert ret == 0 return out return fn return MTools() def pytest_funcarg__fat(request): disk = request.getfuncargvalue('disk') return FAT(disk.open('r+b')) def putfile(tmpdir, mtools, name, content=None, dest='::'): myfile = tmpdir.join(name) f = myfile.open('w') if content: f.write(content) f.close() mtools.mcopy(str(myfile), dest) # ------------------------- def test_mtools(mtools): out = mtools.minfo('::') assert 'disk type="FAT32 "' in out def test_boot_sector(fat): bs = fat.bs assert bs.oem_name == 'mkdosfs\x00' assert bs.sector_size == 512 assert bs.sectors_per_cluster == 1 assert bs.reserved_sectors == 32 assert bs.number_of_fats == 2 assert bs.root_start == 2 def test_reject_non_fat_32(disk16): f = disk16.open('rb') py.test.raises(FatError, "FAT(f)") def test_read_root(tmpdir, mtools, fat): putfile(tmpdir, mtools, 'test1.txt') putfile(tmpdir, mtools, 'test2.txt') entries = fat.read_root() assert len(entries) == 2 longname, pos, entry = entries[0] assert longname == 'test1.txt' assert entry.short_name == 'TEST1 ' assert entry.short_ext == 'TXT' assert entry.attrib == Attrib.ARCHIVE longname, pos, entry = entries[1] assert longname == 'test2.txt' assert entry.short_name == 'TEST2 ' assert entry.short_ext == 'TXT' assert entry.attrib == Attrib.ARCHIVE def test_longname(tmpdir, mtools, fat): NAME = 'very long filename.html' putfile(tmpdir, mtools, NAME) entries = fat.read_root() assert len(entries) == 1 name, pos, entry = entries[0] assert name == NAME assert entry.short_name == 'VERYLO~1' assert entry.short_ext == 'HTM' assert entry.attrib == Attrib.ARCHIVE def test_cluster_chain(tmpdir, mtools, fat): # make a file that occupies 2 clusters SIZE = fat.cluster_size + 16 content = 'x' * SIZE putfile(tmpdir, mtools, 'test.txt', content=content) entries = fat.read_root() name, pos, entry = entries[0] first_cluster = fat.get_first_cluster(entry) chain = fat.get_cluster_chain(first_cluster) assert len(chain) == 2 # it is not strictly guaranteed that the clusters are consecutive, but in # practice it happens assert chain[1] == chain[0] + 1 content2 = fat.read_file(entry) assert content == content2 def test_long_directory(tmpdir, mtools, fat): N = 10 for i in range(N): putfile(tmpdir, mtools, 'test%d.txt' % i) entries = fat.read_root() assert len(entries) == N def test_filename_26_chars(tmpdir, mtools, fat): filename = '26_chars_long_filename.txt' assert len(filename) == 26 putfile(tmpdir, mtools, filename) filename2, _, entry = fat.read_root()[0] assert filename2 == filename def test_filename_spans_two_clusters(tmpdir, mtools, fat): # fill the first 8 entries for i in range(7): putfile(tmpdir, mtools, 'test%d.txt' % i) putfile(tmpdir, mtools, 'long_filename_that_takes_three_entries.txt') entries = fat.read_root() assert len(entries) == 8 name, pos, entry = entries[-1] assert name == 'long_filename_that_takes_three_entries.txt' def test_file_lookup(tmpdir, mtools, fat): mtools.mmd('foo') mtools.mmd('foo/bar') putfile(tmpdir, mtools, 'test.txt', dest='::/foo/bar', content='this is a test') filename, pos, entry = fat.lookup('/foo/bar/test.txt') assert filename == 'test.txt' assert entry.short_name == 'TEST ' assert entry.short_ext == 'TXT' first_cluster = fat.get_first_cluster(entry) chain = fat.get_cluster_chain(first_cluster) assert len(chain) == 1 content = fat.read_cluster(chain[0]) assert content[:entry.file_size] == 'this is a test' def test_file_lookup_nonabs(fat): py.test.raises(FatError, "fat.lookup('test.txt')") def test_file_lookup_not_found(tmpdir, mtools, fat): mtools.mmd('foo') mtools.mmd('foo/bar') putfile(tmpdir, mtools, 'test.txt', dest='::/foo/bar', content='this is a test') py.test.raises(FatError, "fat.lookup('/foo/bar2/test.txt')") py.test.raises(FatError, "fat.lookup('/foo/bar/test2.txt')") def test_file_lookup_deleted(tmpdir, mtools, fat): putfile(tmpdir, mtools, 'test.txt') mtools.mdel('test.txt') py.test.raises(FatError, "fat.lookup('/test.txt')") def test_modify_entry(tmpdir, mtools, fat): putfile(tmpdir, mtools, 'test1.txt') putfile(tmpdir, mtools, 'test2.txt') out = mtools.mdir() assert 'test1.txt' in out assert 'test2.txt' in out _, pos, entry = fat.lookup('/test1.txt') assert entry.attrib == Attrib.ARCHIVE newentry = entry._replace(attrib = Attrib.ARCHIVE | Attrib.HIDDEN) fat.write_entry(pos, newentry) fat.f.flush() out = mtools.mdir() assert 'test1.txt' not in out assert 'test2.txt' in out out = mtools.mdir('-a') assert 'test1.txt' in out assert 'test2.txt' in out def test_hard_link(tmpdir, mtools, fat): putfile(tmpdir, mtools, 'test1.txt', content='this is a test') putfile(tmpdir, mtools, 'test2.txt') fat.hard_link('/test1.txt', '/test2.txt') fat.f.flush() out = mtools.mdir() assert 'test1.txt' in out assert 'test2.txt' in out _, _, entry = fat.lookup('/test1.txt') content = fat.read_file(entry) assert content == 'this is a test' _, _, entry = fat.lookup('/test2.txt') content = fat.read_file(entry) assert content == 'this is a test' def test_hard_link_fail(tmpdir, mtools, fat): putfile(tmpdir, mtools, 'test1.txt', content='this is a test') putfile(tmpdir, mtools, 'test2.txt', content='foobar') py.test.raises(FatError, "fat.hard_link('/test1.txt', '/test2.txt')") py.test.raises(FatError, "fat.hard_link('/test1.txt', '/test3.txt')") def test_unlink(tmpdir, mtools, fat): SIZE = fat.cluster_size + 16 content = 'x' * SIZE putfile(tmpdir, mtools, 'test.txt', content=content) putfile(tmpdir, mtools, 'empty.txt') test2 = tmpdir.join('test2.txt') fat.hard_link('/test.txt', '/empty.txt') fat.f.flush() fat.unlink('/empty.txt') fat.f.flush() out = mtools.mdir() assert 'empty.txt' not in out mtools.mcopy('::test.txt', str(test2)) assert test2.open().read() == content