|
|
|
|
|
from __future__ import absolute_import |
|
|
from __future__ import print_function |
|
|
from __future__ import unicode_literals |
|
|
from io import BytesIO |
|
|
from unittest import TestCase |
|
|
import sys |
|
|
|
|
|
from six import add_metaclass |
|
|
|
|
|
from hwp5.bintype import read_type |
|
|
from hwp5.dataio import ARRAY, N_ARRAY |
|
|
from hwp5.dataio import BSTR |
|
|
from hwp5.dataio import BYTE |
|
|
from hwp5.dataio import Enum |
|
|
from hwp5.dataio import EnumType |
|
|
from hwp5.dataio import Flags |
|
|
from hwp5.dataio import INT8 |
|
|
from hwp5.dataio import INT16 |
|
|
from hwp5.dataio import INT32 |
|
|
from hwp5.dataio import UINT8 |
|
|
from hwp5.dataio import UINT16 |
|
|
from hwp5.dataio import UINT32 |
|
|
from hwp5.dataio import ParseError |
|
|
from hwp5.dataio import Struct |
|
|
from hwp5.dataio import StructType |
|
|
from hwp5.dataio import decode_utf16le_with_hypua |
|
|
from hwp5.dataio import typed_struct_attributes |
|
|
from hwp5.dataio import _parse_flags_args |
|
|
|
|
|
|
|
|
PY3 = sys.version_info.major == 3 |
|
|
if PY3: |
|
|
long = int |
|
|
unicode = str |
|
|
|
|
|
|
|
|
class TestArray(TestCase): |
|
|
def test_new(self): |
|
|
t1 = ARRAY(INT32, 3) |
|
|
t2 = ARRAY(INT32, 3) |
|
|
assert t1 is t2 |
|
|
|
|
|
assert N_ARRAY(INT32, INT32) is N_ARRAY(INT32, INT32) |
|
|
|
|
|
def test_BSTR(self): |
|
|
assert type(BSTR(u'abc')) is unicode |
|
|
|
|
|
def test_hello(self): |
|
|
assert INT32.basetype is int |
|
|
|
|
|
def test_slots(self): |
|
|
a = INT32() |
|
|
self.assertRaises(Exception, setattr, a, 'randomattr', 1) |
|
|
|
|
|
|
|
|
class TestTypedAttributes(TestCase): |
|
|
|
|
|
def test_typed_struct_attributes(self): |
|
|
|
|
|
class SomeRandomStruct(Struct): |
|
|
@staticmethod |
|
|
def attributes(): |
|
|
yield INT32, 'a' |
|
|
yield BSTR, 'b' |
|
|
yield ARRAY(INT32, 3), 'c' |
|
|
|
|
|
attributes = dict(a=1, b=u'abc', c=(4, 5, 6)) |
|
|
typed_attributes = typed_struct_attributes( |
|
|
SomeRandomStruct, attributes, dict() |
|
|
) |
|
|
typed_attributes = list(typed_attributes) |
|
|
expected = [dict(name='a', type=INT32, value=1), |
|
|
dict(name='b', type=BSTR, value='abc'), |
|
|
dict(name='c', type=ARRAY(INT32, 3), value=(4, 5, 6))] |
|
|
self.assertEqual(expected, typed_attributes) |
|
|
|
|
|
def test_typed_struct_attributes_inherited(self): |
|
|
|
|
|
class Hello(Struct): |
|
|
@staticmethod |
|
|
def attributes(): |
|
|
yield INT32, 'a' |
|
|
|
|
|
class Hoho(Hello): |
|
|
@staticmethod |
|
|
def attributes(): |
|
|
yield BSTR, 'b' |
|
|
|
|
|
attributes = dict(a=1, b=u'abc', c=(2, 2)) |
|
|
result = typed_struct_attributes(Hoho, attributes, dict()) |
|
|
result = list(result) |
|
|
expected = [dict(name='a', type=INT32, value=1), |
|
|
dict(name='b', type=BSTR, value='abc'), |
|
|
dict(name='c', type=tuple, value=(2, 2))] |
|
|
self.assertEqual(expected, result) |
|
|
|
|
|
|
|
|
class TestStructType(TestCase): |
|
|
def test_assign_enum_flags_name(self): |
|
|
|
|
|
@add_metaclass(StructType) |
|
|
class Foo(object): |
|
|
bar = Enum() |
|
|
baz = Flags(UINT16) |
|
|
self.assertEqual('bar', Foo.bar.__name__) |
|
|
self.assertEqual(Foo, Foo.bar.scoping_struct) |
|
|
self.assertEqual('baz', Foo.baz.__name__) |
|
|
|
|
|
def test_parse_members(self): |
|
|
|
|
|
@add_metaclass(StructType) |
|
|
class A(object): |
|
|
|
|
|
@classmethod |
|
|
def attributes(cls): |
|
|
yield UINT8, 'uint8' |
|
|
yield UINT16, 'uint16' |
|
|
yield UINT32, 'uint32' |
|
|
|
|
|
values = dict(uint8=8, uint16=16, uint32=32) |
|
|
|
|
|
def getvalue(member): |
|
|
return values[member['name']] |
|
|
|
|
|
context = dict() |
|
|
result = list(A.parse_members(context, getvalue)) |
|
|
self.assertEqual([dict(name='uint8', type=UINT8, value=8), |
|
|
dict(name='uint16', type=UINT16, value=16), |
|
|
dict(name='uint32', type=UINT32, value=32)], result) |
|
|
|
|
|
def test_parse_members_condition(self): |
|
|
|
|
|
def uint32_is_32(context, values): |
|
|
return values['uint32'] == 32 |
|
|
|
|
|
@add_metaclass(StructType) |
|
|
class A(object): |
|
|
|
|
|
@classmethod |
|
|
def attributes(cls): |
|
|
yield UINT8, 'uint8' |
|
|
yield UINT16, 'uint16' |
|
|
yield UINT32, 'uint32' |
|
|
yield dict(type=UINT32, name='extra', condition=uint32_is_32) |
|
|
|
|
|
values = dict(uint8=8, uint16=16, uint32=32, extra=666) |
|
|
|
|
|
def getvalue(member): |
|
|
return values[member['name']] |
|
|
|
|
|
context = dict() |
|
|
result = list(A.parse_members(context, getvalue)) |
|
|
self.assertEqual([dict(name='uint8', type=UINT8, value=8), |
|
|
dict(name='uint16', type=UINT16, value=16), |
|
|
dict(name='uint32', type=UINT32, value=32), |
|
|
dict(name='extra', type=UINT32, value=666, |
|
|
condition=uint32_is_32)], |
|
|
result) |
|
|
|
|
|
def test_parse_members_empty(self): |
|
|
|
|
|
@add_metaclass(StructType) |
|
|
class A(object): |
|
|
pass |
|
|
|
|
|
value = dict() |
|
|
|
|
|
def getvalue(member): |
|
|
return value[member['name']] |
|
|
|
|
|
context = dict() |
|
|
result = list(A.parse_members_with_inherited(context, getvalue)) |
|
|
self.assertEqual([], result) |
|
|
|
|
|
def test_parse_members_inherited(self): |
|
|
|
|
|
@add_metaclass(StructType) |
|
|
class A(object): |
|
|
|
|
|
@classmethod |
|
|
def attributes(cls): |
|
|
yield UINT8, 'uint8' |
|
|
yield UINT16, 'uint16' |
|
|
yield UINT32, 'uint32' |
|
|
|
|
|
class B(A): |
|
|
@classmethod |
|
|
def attributes(cls): |
|
|
yield INT8, 'int8' |
|
|
yield INT16, 'int16' |
|
|
yield INT32, 'int32' |
|
|
|
|
|
value = dict(uint8=8, uint16=16, uint32=32, |
|
|
int8=-1, int16=-16, int32=-32) |
|
|
|
|
|
def getvalue(member): |
|
|
return value[member['name']] |
|
|
|
|
|
context = dict() |
|
|
result = list(B.parse_members_with_inherited(context, getvalue)) |
|
|
self.assertEqual([dict(name='uint8', type=UINT8, value=8), |
|
|
dict(name='uint16', type=UINT16, value=16), |
|
|
dict(name='uint32', type=UINT32, value=32), |
|
|
dict(name='int8', type=INT8, value=-1), |
|
|
dict(name='int16', type=INT16, value=-16), |
|
|
dict(name='int32', type=INT32, value=-32)], |
|
|
result) |
|
|
|
|
|
|
|
|
class TestEnumType(TestCase): |
|
|
def test_enum(self): |
|
|
Foo = EnumType( |
|
|
str('Foo'), |
|
|
(int,), |
|
|
dict(items=['a', 'b', 'c'], moreitems=dict(d=1, e=4)) |
|
|
) |
|
|
|
|
|
self.assertRaises(AttributeError, getattr, Foo, 'items') |
|
|
self.assertRaises(AttributeError, getattr, Foo, 'moreitems') |
|
|
|
|
|
|
|
|
self.assertEqual(0, Foo.a) |
|
|
self.assertEqual(1, Foo.b) |
|
|
self.assertEqual(2, Foo.c) |
|
|
self.assertEqual(1, Foo.d) |
|
|
self.assertEqual(4, Foo.e) |
|
|
self.assertTrue(isinstance(Foo.a, Foo)) |
|
|
|
|
|
|
|
|
self.assertTrue(Foo(0) is Foo(0)) |
|
|
self.assertTrue(Foo(0) is Foo.a) |
|
|
|
|
|
|
|
|
self.assertTrue(Foo(0) is not 0) |
|
|
|
|
|
|
|
|
self.assertEqual('a', Foo.a.name) |
|
|
self.assertEqual('b', Foo.b.name) |
|
|
|
|
|
|
|
|
self.assertEqual('b', Foo.d.name) |
|
|
self.assertTrue(Foo.b is Foo.d) |
|
|
|
|
|
|
|
|
self.assertEqual('Foo.a', repr(Foo(0))) |
|
|
self.assertEqual('Foo.b', repr(Foo(1))) |
|
|
self.assertEqual('Foo.e', repr(Foo(4))) |
|
|
|
|
|
|
|
|
self.assertRaises(AttributeError, setattr, Foo(0), 'bar', 0) |
|
|
if sys.platform.startswith('java'): |
|
|
self.assertRaises(TypeError, setattr, Foo(0), 'name', 'a') |
|
|
else: |
|
|
self.assertRaises(AttributeError, setattr, Foo(0), 'name', 'a') |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
undefined = Foo(5) |
|
|
self.assertTrue(isinstance(undefined, Foo)) |
|
|
self.assertEqual(None, undefined.name) |
|
|
self.assertEqual('Foo(5)', repr(undefined)) |
|
|
|
|
|
|
|
|
self.assertRaises(TypeError, Foo, 5, 'f') |
|
|
|
|
|
|
|
|
self.assertRaises(Exception, Enum, 'a', a=1) |
|
|
|
|
|
|
|
|
class TestFlags(TestCase): |
|
|
def test_parse_args(self): |
|
|
x = list(_parse_flags_args([0, 1, long, 'bit01'])) |
|
|
bit01 = ('bit01', (0, 1, long)) |
|
|
self.assertEqual([bit01], x) |
|
|
|
|
|
x = list(_parse_flags_args([2, 3, 'bit23'])) |
|
|
bit23 = ('bit23', (2, 3, int)) |
|
|
self.assertEqual([bit23], x) |
|
|
|
|
|
x = list(_parse_flags_args([4, long, 'bit4'])) |
|
|
bit4 = ('bit4', (4, 4, long)) |
|
|
self.assertEqual([bit4], x) |
|
|
|
|
|
x = list(_parse_flags_args([5, 'bit5'])) |
|
|
bit5 = ('bit5', (5, 5, int)) |
|
|
|
|
|
x = list(_parse_flags_args([0, 1, long, 'bit01', |
|
|
2, 3, 'bit23', |
|
|
4, long, 'bit4', |
|
|
5, 'bit5'])) |
|
|
self.assertEqual([bit01, bit23, bit4, bit5], x) |
|
|
|
|
|
def test_basetype(self): |
|
|
MyFlags = Flags(UINT32) |
|
|
self.assertEqual(UINT32, MyFlags.basetype) |
|
|
|
|
|
def test_bitfields(self): |
|
|
MyEnum = Enum(a=1, b=2) |
|
|
MyFlags = Flags( |
|
|
UINT32, |
|
|
0, 1, 'field0', |
|
|
2, 4, MyEnum, 'field2' |
|
|
) |
|
|
bitfields = MyFlags.bitfields |
|
|
f = bitfields['field0'] |
|
|
self.assertEqual((0, 1, int), |
|
|
(f.lsb, f.msb, f.valuetype)) |
|
|
f = bitfields['field2'] |
|
|
self.assertEqual((2, 4, MyEnum), |
|
|
(f.lsb, f.msb, f.valuetype)) |
|
|
|
|
|
@property |
|
|
def ByteFlags(self): |
|
|
return Flags(BYTE, |
|
|
0, 3, 'low', |
|
|
4, 7, 'high') |
|
|
|
|
|
def test_dictvalue(self): |
|
|
flags = self.ByteFlags(0xf0) |
|
|
self.assertEqual(dict(low=0, high=0xf), |
|
|
flags.dictvalue()) |
|
|
|
|
|
|
|
|
class TestReadStruct(TestCase): |
|
|
|
|
|
def test_read_parse_error(self): |
|
|
|
|
|
class Foo(Struct): |
|
|
|
|
|
def attributes(): |
|
|
yield INT16, 'a' |
|
|
attributes = staticmethod(attributes) |
|
|
|
|
|
stream = BytesIO() |
|
|
|
|
|
record = dict() |
|
|
context = dict(record=record) |
|
|
try: |
|
|
read_type(Foo, context, stream) |
|
|
assert False, 'ParseError expected' |
|
|
except ParseError as e: |
|
|
self.assertEqual(Foo, e.binevents[0][1]['type']) |
|
|
self.assertEqual('a', e.binevents[-1][1]['name']) |
|
|
self.assertEqual(0, e.offset) |
|
|
|
|
|
|
|
|
class TestBSTR(TestCase): |
|
|
|
|
|
def test_read(self): |
|
|
f = BytesIO(b'\x03\x00' + u'가나다'.encode('utf-16le')) |
|
|
|
|
|
s = BSTR.read(f) |
|
|
self.assertEqual(u'가나다', s) |
|
|
|
|
|
pua = u'\ub098\ub78f\u302e\ub9d0\u302f\uebd4\ubbf8\u302e' |
|
|
pua_utf16le = pua.encode('utf-16le') |
|
|
if PY3: |
|
|
lengthbyte = bytes([len(pua)]) |
|
|
else: |
|
|
lengthbyte = chr(len(pua)) |
|
|
f = BytesIO(lengthbyte + b'\x00' + pua_utf16le) |
|
|
|
|
|
jamo = BSTR.read(f) |
|
|
expected = u'\ub098\ub78f\u302e\ub9d0\u302f\uebd4\ubbf8\u302e' |
|
|
self.assertEqual(expected, jamo) |
|
|
|
|
|
|
|
|
class TestDecodeUTF16LEPUA(TestCase): |
|
|
|
|
|
def test_decode(self): |
|
|
expected = u'가나다' |
|
|
bytes = expected.encode('utf-16le') |
|
|
u = decode_utf16le_with_hypua(bytes) |
|
|
self.assertEqual(expected, u) |
|
|
|