test_mio5_utils.py 5.4 KB

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