1# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import os
6from autotest_lib.client.bin import utils
7from autotest_lib.client.cros import storage as storage_mod
8from autotest_lib.client.common_lib import autotemp, error
9
10
11class hardware_UsbBasicFileOperations(storage_mod.StorageTester):
12    version = 1
13    preserve_srcdir = True
14    _src, _dst = None, None
15
16
17    def run_once(self, volume_filter={'bus':'usb'}):
18        storage = self.wait_for_device(volume_filter, cycles=1,
19                                       mount_volume=True)[0]
20        mount_point = storage['mountpoint']
21
22        # -> Megabytes
23        size = 1*1024*1024
24
25        self._src = autotemp.tempfile(unique_id='tmpfile',
26                                      dir=mount_point)
27        self._dst = autotemp.tempfile(unique_id='autotest',
28                                      dir=self.tmpdir)
29        # Step 1: check if file creation works
30        try:
31            storage_mod.create_file(self._src.name, size)
32        except error.CmdError, e:
33            msg = ('fatal error occurred during file creation: '
34                   'basic file operation failed: %s' % e)
35            raise error.TestFail(msg)
36
37        # not part of current check, remember the value for later use
38        src_md5 = storage_mod.checksum_file(self._src.name)
39
40        # Step 2: check if open works
41        try:
42            f = open(self._src.name, 'rb')
43        except Exception, e:
44            msg = ('fatal error occurred during open(): '
45                   'basic file operation failed: %s' % e)
46            raise error.TestFail(msg)
47
48        try:
49            f.read()
50        except Exception, e:
51            msg = ('fatal error occurred during read(): '
52                   'basic file operation failed: %s' % e)
53            raise error.TestFail(msg)
54
55        try:
56            f.close()
57        except Exception, e:
58            msg = ('fatal error occurred during close(): '
59                   'basic file operation failed: %s' % e)
60            raise error.TestFail(msg)
61
62
63        # Step 3: check if file copy works
64        try:
65            utils.force_copy(self._src.name, self._dst.name)
66        except Exception, e:
67            msg = ('fatal error occurred during a file copy: '
68                   'basic file operation failed: %s' % e)
69            raise error.TestFail(msg)
70
71        if src_md5 != storage_mod.checksum_file(self._dst.name):
72            msg = ('fatal error occurred during a file copy, '
73                   'md5 from origin and from destination are different: '
74                   'basic file operation failed')
75            raise error.TestFail(msg)
76
77
78        # Step 4: check if file removal works
79        try:
80            os.remove(self._src.name)
81        except OSError, e:
82            msg = ('fatal error occurred during file removal: '
83                   'basic file operation failed: %s' % e)
84            raise error.TestFail(msg)
85
86        if os.path.isfile(self._src.name):
87            msg = ('fatal error occurred during file removal: '
88                   'file still present after command, '
89                   'basic file operation failed')
90            raise error.TestFail(msg)
91
92        utils.drop_caches()
93
94        if os.path.isfile(self._src.name):
95            msg = ('fatal error occurred during file removal: '
96                   'file still present after command issued and '
97                   'disk cached flushed), '
98                   'basic file operation failed')
99            raise error.TestFail(msg)
100
101        # Step 5: check if modification to a file are persistent
102        # copy file, modify src and modify dst the same way, checksum
103        storage_mod.create_file(self._src.name, size)
104        utils.force_copy(self._src.name, self._dst.name)
105
106        # apply the same change to both files (which are identical in origin)
107        src_md5 = modify_file(self._src.name)
108        dst_md5 = modify_file(self._dst.name)
109
110        # both copy of they file have to be the same
111        if src_md5 != dst_md5:
112            msg = ('fatal error occurred after modifying src and dst: '
113                   'md5 checksums differ - %s / %s ,'
114                   'basic file operation failed' % (src_md5, dst_md5))
115            raise error.TestFail(msg)
116
117
118    def cleanup(self):
119        if self._src:
120            self._src.clean()
121        if self._dst:
122            self._dst.clean()
123
124        self.scanner.unmount_all()
125
126        super(hardware_UsbBasicFileOperations, self).cleanup()
127
128
129def modify_file(path):
130    '''Modify a file returning its new MD5
131
132    Open |path|, change a byte within the file and return the new md5.
133
134    The change applied to the file is based on the file content and size.
135    This means that identical files will result in identical changes and thus
136    will return the same MD5.
137
138    @param path: a path to the file to be modified
139    @return the MD5 of |path| after the modification
140    '''
141    position = os.path.getsize(path) / 2
142
143    # modify the file means: read a char, increase its value and write it back
144    # given the same file (identical in size and bytes) it will apply the same
145    # change
146    f = open(path, 'r+b')
147    f.seek(position)
148    c = f.read(1)
149    f.seek(position)
150    f.write(chr(ord(c)+1))
151    f.close()
152    return storage_mod.checksum_file(path)
153