1#!/usr/bin/env python
2#
3# Python-bindings handle type test script
4#
5# Copyright (C) 2010-2021, Joachim Metz <joachim.metz@gmail.com>
6#
7# Refer to AUTHORS for acknowledgements.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU Lesser General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU Lesser General Public License
20# along with this program.  If not, see <https://www.gnu.org/licenses/>.
21
22import argparse
23import random
24import os
25import sys
26import unittest
27
28import pysmraw
29
30
31class HandleTypeTests(unittest.TestCase):
32  """Tests the handle type."""
33
34  def test_signal_abort(self):
35    """Tests the signal_abort function."""
36    smraw_handle = pysmraw.handle()
37
38    smraw_handle.signal_abort()
39
40  def test_open(self):
41    """Tests the open function."""
42    test_source = unittest.source
43    if not test_source:
44      raise unittest.SkipTest("missing source")
45
46    smraw_handle = pysmraw.handle()
47    filenames = pysmraw.glob(test_source)
48
49    smraw_handle.open(filenames)
50
51    with self.assertRaises(IOError):
52      smraw_handle.open(filenames)
53
54    smraw_handle.close()
55
56    with self.assertRaises(TypeError):
57      smraw_handle.open(None)
58
59    # TODO: add open write test.
60
61  def test_open_file_objects(self):
62    """Tests the open_file_objects function."""
63    test_source = unittest.source
64    if not test_source:
65      raise unittest.SkipTest("missing source")
66
67    smraw_handle = pysmraw.handle()
68
69    filenames = pysmraw.glob(test_source)
70    file_objects = []
71    for filename in filenames:
72      file_object = open(filename, "rb")
73      file_objects.append(file_object)
74
75    smraw_handle.open_file_objects(file_objects)
76
77    with self.assertRaises(IOError):
78      smraw_handle.open_file_objects(file_objects)
79
80    smraw_handle.close()
81
82    # TODO: change IOError into TypeError
83    with self.assertRaises(IOError):
84      smraw_handle.open_file_objects(None)
85
86    with self.assertRaises(ValueError):
87      smraw_handle.open_file_objects(file_objects, mode="w")
88
89  def test_close(self):
90    """Tests the close function."""
91    test_source = unittest.source
92    if not test_source:
93      raise unittest.SkipTest("missing source")
94
95    smraw_handle = pysmraw.handle()
96
97    with self.assertRaises(IOError):
98      smraw_handle.close()
99
100  def test_open_close(self):
101    """Tests the open and close functions."""
102    test_source = unittest.source
103    if not test_source:
104      return
105
106    smraw_handle = pysmraw.handle()
107    filenames = pysmraw.glob(test_source)
108
109    # Test open and close.
110    smraw_handle.open(filenames)
111    smraw_handle.close()
112
113    # Test open and close a second time to validate clean up on close.
114    smraw_handle.open(filenames)
115    smraw_handle.close()
116
117    file_object = open(test_source, "rb")
118
119    # Test open_file_objects and close.
120    smraw_handle.open_file_objects([file_object])
121    smraw_handle.close()
122
123    # Test open_file_objects and close a second time to validate clean up on close.
124    smraw_handle.open_file_objects([file_object])
125    smraw_handle.close()
126
127    # Test open_file_objects and close and dereferencing file_object.
128    smraw_handle.open_file_objects([file_object])
129    del file_object
130    smraw_handle.close()
131
132  def test_read_buffer(self):
133    """Tests the read_buffer function."""
134    test_source = unittest.source
135    if not test_source:
136      raise unittest.SkipTest("missing source")
137
138    smraw_handle = pysmraw.handle()
139    filenames = pysmraw.glob(test_source)
140
141    smraw_handle.open(filenames)
142
143    media_size = smraw_handle.get_media_size()
144
145    # Test read without maximum size.
146    smraw_handle.seek_offset(0, os.SEEK_SET)
147
148    data = smraw_handle.read_buffer()
149
150    self.assertIsNotNone(data)
151    self.assertEqual(len(data), media_size)
152
153    # Test read with maximum size.
154    smraw_handle.seek_offset(0, os.SEEK_SET)
155
156    data = smraw_handle.read_buffer(size=4096)
157
158    self.assertIsNotNone(data)
159    self.assertEqual(len(data), min(media_size, 4096))
160
161    if media_size > 8:
162      smraw_handle.seek_offset(-8, os.SEEK_END)
163
164      # Read buffer on media_size boundary.
165      data = smraw_handle.read_buffer(size=4096)
166
167      self.assertIsNotNone(data)
168      self.assertEqual(len(data), 8)
169
170      # Read buffer beyond media_size boundary.
171      data = smraw_handle.read_buffer(size=4096)
172
173      self.assertIsNotNone(data)
174      self.assertEqual(len(data), 0)
175
176    # Stress test read buffer.
177    smraw_handle.seek_offset(0, os.SEEK_SET)
178
179    remaining_media_size = media_size
180
181    for _ in range(1024):
182      read_size = int(random.random() * 4096)
183
184      data = smraw_handle.read_buffer(size=read_size)
185
186      self.assertIsNotNone(data)
187
188      data_size = len(data)
189
190      if read_size > remaining_media_size:
191        read_size = remaining_media_size
192
193      self.assertEqual(data_size, read_size)
194
195      remaining_media_size -= data_size
196
197      if not remaining_media_size:
198        smraw_handle.seek_offset(0, os.SEEK_SET)
199
200        remaining_media_size = media_size
201
202    with self.assertRaises(ValueError):
203      smraw_handle.read_buffer(size=-1)
204
205    smraw_handle.close()
206
207    # Test the read without open.
208    with self.assertRaises(IOError):
209      smraw_handle.read_buffer(size=4096)
210
211  def test_read_buffer_file_objects(self):
212    """Tests the read_buffer function on file-like objects."""
213    test_source = unittest.source
214    if not test_source:
215      raise unittest.SkipTest("missing source")
216
217    file_object = open(test_source, "rb")
218
219    smraw_handle = pysmraw.handle()
220
221    smraw_handle.open_file_objects([file_object])
222
223    media_size = smraw_handle.get_media_size()
224
225    # Test normal read.
226    data = smraw_handle.read_buffer(size=4096)
227
228    self.assertIsNotNone(data)
229    self.assertEqual(len(data), min(media_size, 4096))
230
231    smraw_handle.close()
232
233  def test_read_buffer_at_offset(self):
234    """Tests the read_buffer_at_offset function."""
235    test_source = unittest.source
236    if not test_source:
237      raise unittest.SkipTest("missing source")
238
239    smraw_handle = pysmraw.handle()
240    filenames = pysmraw.glob(test_source)
241
242    smraw_handle.open(filenames)
243
244    media_size = smraw_handle.get_media_size()
245
246    # Test normal read.
247    data = smraw_handle.read_buffer_at_offset(4096, 0)
248
249    self.assertIsNotNone(data)
250    self.assertEqual(len(data), min(media_size, 4096))
251
252    if media_size > 8:
253      # Read buffer on media_size boundary.
254      data = smraw_handle.read_buffer_at_offset(4096, media_size - 8)
255
256      self.assertIsNotNone(data)
257      self.assertEqual(len(data), 8)
258
259      # Read buffer beyond media_size boundary.
260      data = smraw_handle.read_buffer_at_offset(4096, media_size + 8)
261
262      self.assertIsNotNone(data)
263      self.assertEqual(len(data), 0)
264
265    # Stress test read buffer.
266    for _ in range(1024):
267      random_number = random.random()
268
269      media_offset = int(random_number * media_size)
270      read_size = int(random_number * 4096)
271
272      data = smraw_handle.read_buffer_at_offset(read_size, media_offset)
273
274      self.assertIsNotNone(data)
275
276      remaining_media_size = media_size - media_offset
277
278      data_size = len(data)
279
280      if read_size > remaining_media_size:
281        read_size = remaining_media_size
282
283      self.assertEqual(data_size, read_size)
284
285      remaining_media_size -= data_size
286
287      if not remaining_media_size:
288        smraw_handle.seek_offset(0, os.SEEK_SET)
289
290    with self.assertRaises(ValueError):
291      smraw_handle.read_buffer_at_offset(-1, 0)
292
293    with self.assertRaises(ValueError):
294      smraw_handle.read_buffer_at_offset(4096, -1)
295
296    smraw_handle.close()
297
298    # Test the read without open.
299    with self.assertRaises(IOError):
300      smraw_handle.read_buffer_at_offset(4096, 0)
301
302  def test_seek_offset(self):
303    """Tests the seek_offset function."""
304    test_source = unittest.source
305    if not test_source:
306      raise unittest.SkipTest("missing source")
307
308    smraw_handle = pysmraw.handle()
309    filenames = pysmraw.glob(test_source)
310
311    smraw_handle.open(filenames)
312
313    media_size = smraw_handle.get_media_size()
314
315    smraw_handle.seek_offset(16, os.SEEK_SET)
316
317    offset = smraw_handle.get_offset()
318    self.assertEqual(offset, 16)
319
320    smraw_handle.seek_offset(16, os.SEEK_CUR)
321
322    offset = smraw_handle.get_offset()
323    self.assertEqual(offset, 32)
324
325    smraw_handle.seek_offset(-16, os.SEEK_CUR)
326
327    offset = smraw_handle.get_offset()
328    self.assertEqual(offset, 16)
329
330    if media_size > 16:
331      smraw_handle.seek_offset(-16, os.SEEK_END)
332
333      offset = smraw_handle.get_offset()
334      self.assertEqual(offset, media_size - 16)
335
336    smraw_handle.seek_offset(16, os.SEEK_END)
337
338    offset = smraw_handle.get_offset()
339    self.assertEqual(offset, media_size + 16)
340
341    # TODO: change IOError into ValueError
342    with self.assertRaises(IOError):
343      smraw_handle.seek_offset(-1, os.SEEK_SET)
344
345    # TODO: change IOError into ValueError
346    with self.assertRaises(IOError):
347      smraw_handle.seek_offset(-32 - media_size, os.SEEK_CUR)
348
349    # TODO: change IOError into ValueError
350    with self.assertRaises(IOError):
351      smraw_handle.seek_offset(-32 - media_size, os.SEEK_END)
352
353    # TODO: change IOError into ValueError
354    with self.assertRaises(IOError):
355      smraw_handle.seek_offset(0, -1)
356
357    smraw_handle.close()
358
359    # Test the seek without open.
360    with self.assertRaises(IOError):
361      smraw_handle.seek_offset(16, os.SEEK_SET)
362
363  def test_get_offset(self):
364    """Tests the get_offset function and offset property."""
365    test_source = unittest.source
366    if not test_source:
367      raise unittest.SkipTest("missing source")
368
369    smraw_handle = pysmraw.handle()
370    filenames = pysmraw.glob(test_source)
371    smraw_handle.open(filenames)
372
373    offset = smraw_handle.get_offset()
374    self.assertIsNotNone(offset)
375
376    smraw_handle.close()
377
378  def test_get_media_size(self):
379    """Tests the get_media_size function and media_size property."""
380    test_source = unittest.source
381    if not test_source:
382      raise unittest.SkipTest("missing source")
383
384    smraw_handle = pysmraw.handle()
385    filenames = pysmraw.glob(test_source)
386    smraw_handle.open(filenames)
387
388    media_size = smraw_handle.get_media_size()
389    self.assertIsNotNone(media_size)
390
391    self.assertIsNotNone(smraw_handle.media_size)
392
393    smraw_handle.close()
394
395
396if __name__ == "__main__":
397  argument_parser = argparse.ArgumentParser()
398
399  argument_parser.add_argument(
400      "source", nargs="?", action="store", metavar="PATH",
401      default=None, help="path of the source file.")
402
403  options, unknown_options = argument_parser.parse_known_args()
404  unknown_options.insert(0, sys.argv[0])
405
406  setattr(unittest, "source", options.source)
407
408  unittest.main(argv=unknown_options, verbosity=2)
409