test_mio5_utils.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. """ Testing mio5_utils Cython module
  2. """
  3. import sys
  4. from io import BytesIO
  5. cStringIO = BytesIO
  6. import numpy as np
  7. from numpy.testing import assert_array_equal, assert_equal, assert_
  8. from pytest import raises as assert_raises
  9. import scipy.io.matlab._byteordercodes as boc
  10. import scipy.io.matlab._streams as streams
  11. import scipy.io.matlab._mio5_params as mio5p
  12. import scipy.io.matlab._mio5_utils as m5u
  13. def test_byteswap():
  14. for val in (
  15. 1,
  16. 0x100,
  17. 0x10000):
  18. a = np.array(val, dtype=np.uint32)
  19. b = a.byteswap()
  20. c = m5u.byteswap_u4(a)
  21. assert_equal(b.item(), c)
  22. d = m5u.byteswap_u4(c)
  23. assert_equal(a.item(), d)
  24. def _make_tag(base_dt, val, mdtype, sde=False):
  25. ''' Makes a simple matlab tag, full or sde '''
  26. base_dt = np.dtype(base_dt)
  27. bo = boc.to_numpy_code(base_dt.byteorder)
  28. byte_count = base_dt.itemsize
  29. if not sde:
  30. udt = bo + 'u4'
  31. padding = 8 - (byte_count % 8)
  32. all_dt = [('mdtype', udt),
  33. ('byte_count', udt),
  34. ('val', base_dt)]
  35. if padding:
  36. all_dt.append(('padding', 'u1', padding))
  37. else: # is sde
  38. udt = bo + 'u2'
  39. padding = 4-byte_count
  40. if bo == '<': # little endian
  41. all_dt = [('mdtype', udt),
  42. ('byte_count', udt),
  43. ('val', base_dt)]
  44. else: # big endian
  45. all_dt = [('byte_count', udt),
  46. ('mdtype', udt),
  47. ('val', base_dt)]
  48. if padding:
  49. all_dt.append(('padding', 'u1', padding))
  50. tag = np.zeros((1,), dtype=all_dt)
  51. tag['mdtype'] = mdtype
  52. tag['byte_count'] = byte_count
  53. tag['val'] = val
  54. return tag
  55. def _write_stream(stream, *strings):
  56. stream.truncate(0)
  57. stream.seek(0)
  58. for s in strings:
  59. stream.write(s)
  60. stream.seek(0)
  61. def _make_readerlike(stream, byte_order=boc.native_code):
  62. class R:
  63. pass
  64. r = R()
  65. r.mat_stream = stream
  66. r.byte_order = byte_order
  67. r.struct_as_record = True
  68. r.uint16_codec = sys.getdefaultencoding()
  69. r.chars_as_strings = False
  70. r.mat_dtype = False
  71. r.squeeze_me = False
  72. return r
  73. def test_read_tag():
  74. # mainly to test errors
  75. # make reader-like thing
  76. str_io = BytesIO()
  77. r = _make_readerlike(str_io)
  78. c_reader = m5u.VarReader5(r)
  79. # This works for StringIO but _not_ cStringIO
  80. assert_raises(OSError, c_reader.read_tag)
  81. # bad SDE
  82. tag = _make_tag('i4', 1, mio5p.miINT32, sde=True)
  83. tag['byte_count'] = 5
  84. _write_stream(str_io, tag.tobytes())
  85. assert_raises(ValueError, c_reader.read_tag)
  86. def test_read_stream():
  87. tag = _make_tag('i4', 1, mio5p.miINT32, sde=True)
  88. tag_str = tag.tobytes()
  89. str_io = cStringIO(tag_str)
  90. st = streams.make_stream(str_io)
  91. s = streams._read_into(st, tag.itemsize)
  92. assert_equal(s, tag.tobytes())
  93. def test_read_numeric():
  94. # make reader-like thing
  95. str_io = cStringIO()
  96. r = _make_readerlike(str_io)
  97. # check simplest of tags
  98. for base_dt, val, mdtype in (('u2', 30, mio5p.miUINT16),
  99. ('i4', 1, mio5p.miINT32),
  100. ('i2', -1, mio5p.miINT16)):
  101. for byte_code in ('<', '>'):
  102. r.byte_order = byte_code
  103. c_reader = m5u.VarReader5(r)
  104. assert_equal(c_reader.little_endian, byte_code == '<')
  105. assert_equal(c_reader.is_swapped, byte_code != boc.native_code)
  106. for sde_f in (False, True):
  107. dt = np.dtype(base_dt).newbyteorder(byte_code)
  108. a = _make_tag(dt, val, mdtype, sde_f)
  109. a_str = a.tobytes()
  110. _write_stream(str_io, a_str)
  111. el = c_reader.read_numeric()
  112. assert_equal(el, val)
  113. # two sequential reads
  114. _write_stream(str_io, a_str, a_str)
  115. el = c_reader.read_numeric()
  116. assert_equal(el, val)
  117. el = c_reader.read_numeric()
  118. assert_equal(el, val)
  119. def test_read_numeric_writeable():
  120. # make reader-like thing
  121. str_io = cStringIO()
  122. r = _make_readerlike(str_io, '<')
  123. c_reader = m5u.VarReader5(r)
  124. dt = np.dtype('<u2')
  125. a = _make_tag(dt, 30, mio5p.miUINT16, 0)
  126. a_str = a.tobytes()
  127. _write_stream(str_io, a_str)
  128. el = c_reader.read_numeric()
  129. assert_(el.flags.writeable is True)
  130. def test_zero_byte_string():
  131. # Tests hack to allow chars of non-zero length, but 0 bytes
  132. # make reader-like thing
  133. str_io = cStringIO()
  134. r = _make_readerlike(str_io, boc.native_code)
  135. c_reader = m5u.VarReader5(r)
  136. tag_dt = np.dtype([('mdtype', 'u4'), ('byte_count', 'u4')])
  137. tag = np.zeros((1,), dtype=tag_dt)
  138. tag['mdtype'] = mio5p.miINT8
  139. tag['byte_count'] = 1
  140. hdr = m5u.VarHeader5()
  141. # Try when string is 1 length
  142. hdr.set_dims([1,])
  143. _write_stream(str_io, tag.tobytes() + b' ')
  144. str_io.seek(0)
  145. val = c_reader.read_char(hdr)
  146. assert_equal(val, ' ')
  147. # Now when string has 0 bytes 1 length
  148. tag['byte_count'] = 0
  149. _write_stream(str_io, tag.tobytes())
  150. str_io.seek(0)
  151. val = c_reader.read_char(hdr)
  152. assert_equal(val, ' ')
  153. # Now when string has 0 bytes 4 length
  154. str_io.seek(0)
  155. hdr.set_dims([4,])
  156. val = c_reader.read_char(hdr)
  157. assert_array_equal(val, [' '] * 4)