test_largefile.py revision 180510d29b369b88b0eb8815086162d2d6ef60a7
1"""Test largefile support on system where this makes sense.
2"""
3
4import os
5import stat
6import sys
7import unittest
8from test.test_support import run_unittest, TESTFN, verbose, requires, \
9                              TestSkipped, unlink
10
11try:
12    import signal
13    # The default handler for SIGXFSZ is to abort the process.
14    # By ignoring it, system calls exceeding the file size resource
15    # limit will raise IOError instead of crashing the interpreter.
16    oldhandler = signal.signal(signal.SIGXFSZ, signal.SIG_IGN)
17except (ImportError, AttributeError):
18    pass
19
20# create >2GB file (2GB = 2147483648 bytes)
21size = 2500000000
22
23
24class TestCase(unittest.TestCase):
25    """Test that each file function works as expected for a large
26    (i.e. > 2GB, do  we have to check > 4GB) files.
27
28    NOTE: the order of execution of the test methods is important! test_seek
29    must run first to create the test file. File cleanup must also be handled
30    outside the test instances because of this.
31
32    """
33
34    def test_seek(self):
35        if verbose:
36            print('create large file via seek (may be sparse file) ...')
37        with open(TESTFN, 'wb') as f:
38            f.write(b'z')
39            f.seek(0)
40            f.seek(size)
41            f.write(b'a')
42            f.flush()
43            if verbose:
44                print('check file size with os.fstat')
45            self.assertEqual(os.fstat(f.fileno())[stat.ST_SIZE], size+1)
46
47    def test_osstat(self):
48        if verbose:
49            print('check file size with os.stat')
50        self.assertEqual(os.stat(TESTFN)[stat.ST_SIZE], size+1)
51
52    def test_seek_read(self):
53        if verbose:
54            print('play around with seek() and read() with the built largefile')
55        with open(TESTFN, 'rb') as f:
56            self.assertEqual(f.tell(), 0)
57            self.assertEqual(f.read(1), b'z')
58            self.assertEqual(f.tell(), 1)
59            f.seek(0)
60            self.assertEqual(f.tell(), 0)
61            f.seek(0, 0)
62            self.assertEqual(f.tell(), 0)
63            f.seek(42)
64            self.assertEqual(f.tell(), 42)
65            f.seek(42, 0)
66            self.assertEqual(f.tell(), 42)
67            f.seek(42, 1)
68            self.assertEqual(f.tell(), 84)
69            f.seek(0, 1)
70            self.assertEqual(f.tell(), 84)
71            f.seek(0, 2)  # seek from the end
72            self.assertEqual(f.tell(), size + 1 + 0)
73            f.seek(-10, 2)
74            self.assertEqual(f.tell(), size + 1 - 10)
75            f.seek(-size-1, 2)
76            self.assertEqual(f.tell(), 0)
77            f.seek(size)
78            self.assertEqual(f.tell(), size)
79            # the 'a' that was written at the end of file above
80            self.assertEqual(f.read(1), b'a')
81            f.seek(-size-1, 1)
82            self.assertEqual(f.read(1), b'z')
83            self.assertEqual(f.tell(), 1)
84
85    def test_lseek(self):
86        if verbose:
87            print('play around with os.lseek() with the built largefile')
88        with open(TESTFN, 'rb') as f:
89            self.assertEqual(os.lseek(f.fileno(), 0, 0), 0)
90            self.assertEqual(os.lseek(f.fileno(), 42, 0), 42)
91            self.assertEqual(os.lseek(f.fileno(), 42, 1), 84)
92            self.assertEqual(os.lseek(f.fileno(), 0, 1), 84)
93            self.assertEqual(os.lseek(f.fileno(), 0, 2), size+1+0)
94            self.assertEqual(os.lseek(f.fileno(), -10, 2), size+1-10)
95            self.assertEqual(os.lseek(f.fileno(), -size-1, 2), 0)
96            self.assertEqual(os.lseek(f.fileno(), size, 0), size)
97            # the 'a' that was written at the end of file above
98            self.assertEqual(f.read(1), b'a')
99
100    def test_truncate(self):
101        if verbose:
102            print('try truncate')
103        with open(TESTFN, 'r+b') as f:
104            # this is already decided before start running the test suite
105            # but we do it anyway for extra protection
106            if not hasattr(f, 'truncate'):
107                raise TestSkipped("open().truncate() not available on this system")
108            f.seek(0, 2)
109            # else we've lost track of the true size
110            self.assertEqual(f.tell(), size+1)
111            # Cut it back via seek + truncate with no argument.
112            newsize = size - 10
113            f.seek(newsize)
114            f.truncate()
115            self.assertEqual(f.tell(), newsize)  # else pointer moved
116            f.seek(0, 2)
117            self.assertEqual(f.tell(), newsize)  # else wasn't truncated
118            # Ensure that truncate(smaller than true size) shrinks
119            # the file.
120            newsize -= 1
121            f.seek(42)
122            f.truncate(newsize)
123            self.assertEqual(f.tell(), 42)       # else pointer moved
124            f.seek(0, 2)
125            self.assertEqual(f.tell(), newsize)  # else wasn't truncated
126            # XXX truncate(larger than true size) is ill-defined
127            # across platform; cut it waaaaay back
128            f.seek(0)
129            f.truncate(1)
130            self.assertEqual(f.tell(), 0)       # else pointer moved
131            self.assertEqual(len(f.read()), 1)  # else wasn't truncated
132
133def test_main():
134    # On Windows and Mac OSX this test comsumes large resources; It
135    # takes a long time to build the >2GB file and takes >2GB of disk
136    # space therefore the resource must be enabled to run this test.
137    # If not, nothing after this line stanza will be executed.
138    if sys.platform[:3] == 'win' or sys.platform == 'darwin':
139        requires('largefile',
140                 'test requires %s bytes and a long time to run' % str(size))
141    else:
142        # Only run if the current filesystem supports large files.
143        # (Skip this test on Windows, since we now always support
144        # large files.)
145        f = open(TESTFN, 'wb')
146        try:
147            # 2**31 == 2147483648
148            f.seek(2147483649)
149            # Seeking is not enough of a test: you must write and
150            # flush, too!
151            f.write(b'x')
152            f.flush()
153        except (IOError, OverflowError):
154            f.close()
155            unlink(TESTFN)
156            raise TestSkipped("filesystem does not have largefile support")
157        else:
158            f.close()
159    suite = unittest.TestSuite()
160    suite.addTest(TestCase('test_seek'))
161    suite.addTest(TestCase('test_osstat'))
162    suite.addTest(TestCase('test_seek_read'))
163    suite.addTest(TestCase('test_lseek'))
164    with open(TESTFN, 'w') as f:
165        if hasattr(f, 'truncate'):
166            suite.addTest(TestCase('test_truncate'))
167    unlink(TESTFN)
168    try:
169        run_unittest(suite)
170    finally:
171        unlink(TESTFN)
172
173if __name__ == '__main__':
174    test_main()
175