1fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan# Copyright (c) 2011 The Chromium OS Authors. All rights reserved. 2fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan# Use of this source code is governed by a BSD-style license that can be 3fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan# found in the LICENSE file. 4fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan 5fb1049af45d812e87ae0918c9febe46fef9c024cBen Chanimport logging 6fb1049af45d812e87ae0918c9febe46fef9c024cBen Chanimport os 7cf0668dd4d93484764f3d1b62cecffcb6ebb63c8Ben Chanimport shutil 8fb1049af45d812e87ae0918c9febe46fef9c024cBen Chanimport zipfile 9fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan 10fb1049af45d812e87ae0918c9febe46fef9c024cBen Chanfrom autotest_lib.client.bin import test 11fb1049af45d812e87ae0918c9febe46fef9c024cBen Chanfrom autotest_lib.client.common_lib import autotemp, error 12fb1049af45d812e87ae0918c9febe46fef9c024cBen Chanfrom autotest_lib.client.cros.cros_disks import CrosDisksTester 13fb1049af45d812e87ae0918c9febe46fef9c024cBen Chanfrom autotest_lib.client.cros.cros_disks import VirtualFilesystemImage 14fb1049af45d812e87ae0918c9febe46fef9c024cBen Chanfrom autotest_lib.client.cros.cros_disks import DefaultFilesystemTestContent 15fb1049af45d812e87ae0918c9febe46fef9c024cBen Chanfrom collections import deque 16fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan 17fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan 18fb1049af45d812e87ae0918c9febe46fef9c024cBen Chanclass CrosDisksArchiveTester(CrosDisksTester): 19fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan """A tester to verify archive support in CrosDisks. 20fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan """ 21fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan def __init__(self, test, archive_types): 22fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan super(CrosDisksArchiveTester, self).__init__(test) 23cf0668dd4d93484764f3d1b62cecffcb6ebb63c8Ben Chan self._data_dir = os.path.join(test.bindir, 'data') 24fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan self._archive_types = archive_types 25fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan 26fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan def _find_all_files(self, root_dir): 27fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan """Returns all files under a directory and its sub-directories. 28fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan 29fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan This is a generator that performs a breadth-first-search of 30fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan all files under a specified directory and its sub-directories. 31fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan 32fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan Args: 33fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan root_dir: The root directory where the search starts from. 34fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan Yields: 35fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan Path of any found file relative to the root directory. 36fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan """ 37fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan dirs_to_explore = deque(['']) 38fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan while len(dirs_to_explore) > 0: 39fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan current_dir = dirs_to_explore.popleft() 40fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan for path in os.listdir(os.path.join(root_dir, current_dir)): 41fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan expanded_path = os.path.join(root_dir, current_dir, path) 42fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan relative_path = os.path.join(current_dir, path) 43fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan if os.path.isdir(expanded_path): 44fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan dirs_to_explore.append(relative_path) 45fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan else: 46fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan yield relative_path 47fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan 48fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan def _make_zip_archive(self, archive_path, root_dir, 49fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan compression=zipfile.ZIP_DEFLATED): 50fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan """Archives a specified directory into a ZIP file. 51fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan 52fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan The created ZIP file contains all files and sub-directories 53fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan under the specified root directory, but not the root directory 54fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan itself. 55fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan 56fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan Args: 57fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan archive_path: Path of the output archive. 58fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan root_dir: The root directory to archive. 59fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan compression: The ZIP compression method. 60fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan """ 61fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan # ZipFile in Python 2.6 does not work with the 'with' statement. 62fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan archive = zipfile.ZipFile(archive_path, 'w', compression) 63fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan for path in self._find_all_files(root_dir): 64fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan archive.write(os.path.join(root_dir, path), path) 65fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan archive.close() 66fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan 67cf0668dd4d93484764f3d1b62cecffcb6ebb63c8Ben Chan def _make_rar_archive(self, archive_path, root_dir): 68cf0668dd4d93484764f3d1b62cecffcb6ebb63c8Ben Chan """Archives a specified directory into a RAR file. 69cf0668dd4d93484764f3d1b62cecffcb6ebb63c8Ben Chan 70cf0668dd4d93484764f3d1b62cecffcb6ebb63c8Ben Chan The created RAR file contains all files and sub-directories 71cf0668dd4d93484764f3d1b62cecffcb6ebb63c8Ben Chan under the specified root directory, but not the root directory 72cf0668dd4d93484764f3d1b62cecffcb6ebb63c8Ben Chan itself. 73cf0668dd4d93484764f3d1b62cecffcb6ebb63c8Ben Chan 74cf0668dd4d93484764f3d1b62cecffcb6ebb63c8Ben Chan Args: 75cf0668dd4d93484764f3d1b62cecffcb6ebb63c8Ben Chan archive_path: Path of the output archive. 76cf0668dd4d93484764f3d1b62cecffcb6ebb63c8Ben Chan root_dir: The root directory to archive. 77cf0668dd4d93484764f3d1b62cecffcb6ebb63c8Ben Chan """ 78cf0668dd4d93484764f3d1b62cecffcb6ebb63c8Ben Chan # DESPICABLE HACK: app-arch/rar provides only pre-compiled rar binaries 79cf0668dd4d93484764f3d1b62cecffcb6ebb63c8Ben Chan # for x86/amd64. As a workaround, we pretend the RAR creation here 80cf0668dd4d93484764f3d1b62cecffcb6ebb63c8Ben Chan # using a precanned RAR file. 81cf0668dd4d93484764f3d1b62cecffcb6ebb63c8Ben Chan shutil.copyfile(os.path.join(self._data_dir, 'test.rar'), archive_path) 82cf0668dd4d93484764f3d1b62cecffcb6ebb63c8Ben Chan 83fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan def _make_archive(self, archive_type, archive_path, root_dir): 84fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan """Archives a specified directory into an archive of specified type. 85fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan 86fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan The created archive file contains all files and sub-directories 87fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan under the specified root directory, but not the root directory 88fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan itself. 89fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan 90fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan Args: 91fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan archive_type: Type of the output archive. 92fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan archive_path: Path of the output archive. 93fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan root_dir: The root directory to archive. 94fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan """ 95fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan if archive_type in ['zip']: 96fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan self._make_zip_archive(archive_path, root_dir) 97cf0668dd4d93484764f3d1b62cecffcb6ebb63c8Ben Chan elif archive_type in ['rar']: 98cf0668dd4d93484764f3d1b62cecffcb6ebb63c8Ben Chan self._make_rar_archive(archive_path, root_dir) 99fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan else: 100fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan raise error.TestFail("Unsupported archive type " + archive_type) 101fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan 102fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan def _test_archive(self, archive_type): 103fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan # Create the archive file content in a temporary directory. 104fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan archive_dir = autotemp.tempdir(unique_id='CrosDisks') 105fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan test_content = DefaultFilesystemTestContent() 106fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan if not test_content.create(archive_dir.name): 107fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan raise error.TestFail("Failed to create archive test content") 108fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan 109fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan # Create a FAT-formatted virtual filesystem image containing an 110fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan # archive file to help stimulate mounting an archive file on a 111fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan # removable drive. 112fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan with VirtualFilesystemImage( 113fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan block_size=1024, 114fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan block_count=65536, 115fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan filesystem_type='vfat', 116fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan mkfs_options=[ '-F', '32', '-n', 'ARCHIVE' ]) as image: 117fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan image.format() 118fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan image.mount(options=['sync']) 119fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan # Create the archive file on the virtual filesystem image. 120fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan archive_name = 'test.' + archive_type 121fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan archive_path = os.path.join(image.mount_dir, archive_name) 122fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan self._make_archive(archive_type, archive_path, archive_dir.name) 123fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan image.unmount() 124fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan 125fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan # Mount the virtual filesystem image via CrosDisks. 126fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan device_file = image.loop_device 127fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan self.cros_disks.mount(device_file, '', 128fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan [ "ro", "nodev", "noexec", "nosuid" ]) 129fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan result = self.cros_disks.expect_mount_completion({ 130fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan 'status': 0, 131fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan 'source_path': device_file 132fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan }) 133fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan 134fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan # Mount the archive file on the mounted filesystem via CrosDisks. 135fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan archive_path = os.path.join(result['mount_path'], archive_name) 136fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan expected_mount_path = os.path.join('/media/archive', archive_name) 137fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan self.cros_disks.mount(archive_path) 138fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan result = self.cros_disks.expect_mount_completion({ 139fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan 'status': 0, 140fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan 'source_path': archive_path, 141fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan 'mount_path': expected_mount_path 142fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan }) 143fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan 144fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan # Verify the content of the mounted archive file. 145fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan if not test_content.verify(expected_mount_path): 146fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan raise error.TestFail("Failed to verify filesystem test content") 147fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan 148fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan self.cros_disks.unmount(expected_mount_path, ['lazy']) 149fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan self.cros_disks.unmount(device_file, ['lazy']) 150fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan 151fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan def test_archives(self): 152fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan for archive_type in self._archive_types: 153fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan self._test_archive(archive_type) 154fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan 155fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan def get_tests(self): 156fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan return [self.test_archives] 157fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan 158fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan 159fb1049af45d812e87ae0918c9febe46fef9c024cBen Chanclass platform_CrosDisksArchive(test.test): 160fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan version = 1 161fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan 162fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan def run_once(self, *args, **kwargs): 163fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan tester = CrosDisksArchiveTester(self, kwargs['archive_types']) 164fb1049af45d812e87ae0918c9febe46fef9c024cBen Chan tester.run(*args, **kwargs) 165