summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/analysis/storage/storage.py81
-rw-r--r--tests/arch/instruction.py52
-rw-r--r--tests/arch/operand.py72
-rw-r--r--tests/arch/operands/immediate.py76
-rw-r--r--tests/common/leb128.py80
-rw-r--r--tests/glibext/secstorage.py26
-rw-r--r--tests/glibext/storage.py74
7 files changed, 296 insertions, 165 deletions
diff --git a/tests/analysis/storage/storage.py b/tests/analysis/storage/storage.py
deleted file mode 100644
index 612d500..0000000
--- a/tests/analysis/storage/storage.py
+++ /dev/null
@@ -1,81 +0,0 @@
-
-from chrysacase import ChrysalideTestCase
-from pychrysalide import core
-from pychrysalide.analysis.contents import FileContent
-from pychrysalide.analysis.storage import ObjectStorage
-from pychrysalide.common import PackedBuffer
-import os
-import shutil
-import tempfile
-
-
-class TestObjectStorage(ChrysalideTestCase):
- """TestCase for analysis.storage."""
-
- @classmethod
- def setUpClass(cls):
-
- super(TestObjectStorage, cls).setUpClass()
-
- cls._tmp_path = tempfile.mkdtemp()
-
- config = core.get_main_configuration()
- param = config.search(core.MainParameterKeys.TMPDIR)
-
- cls._old_tmpdir = param.value
- param.value = cls._tmp_path
-
- cls.log('Using temporary directory "%s"' % cls._tmp_path)
-
-
- @classmethod
- def tearDownClass(cls):
-
- super(TestObjectStorage, cls).tearDownClass()
-
- config = core.get_main_configuration()
- param = config.search(core.MainParameterKeys.TMPDIR)
-
- param.value = cls._old_tmpdir
-
- # import os
- # os.system('ls -laihR %s' % cls._tmp_path)
-
- cls.log('Delete directory "%s"' % cls._tmp_path)
-
- shutil.rmtree(cls._tmp_path)
-
-
- def testFileContentStorage(self):
- """Store and load file binary content."""
-
- storage = ObjectStorage('my-storage-hash')
- self.assertIsNotNone(storage)
-
- filename = os.path.join(self._tmp_path, 'test.bin')
-
- with open(filename, 'wb') as fd:
- fd.write(b'ABC')
-
- cnt = FileContent(filename)
- self.assertIsNotNone(cnt)
-
- ret = storage.store_object('contents', cnt)
- self.assertEqual(ret, 0)
-
- pbuf = PackedBuffer()
-
- ret = storage.store(pbuf)
- self.assertTrue(ret)
-
- self.assertTrue(pbuf.payload_length > 0)
-
- pbuf.rewind()
-
- storage2 = ObjectStorage.load(pbuf)
- self.assertIsNotNone(storage2)
-
- cnt2 = storage2.load_object('contents', 0)
- self.assertIsNotNone(cnt2)
-
- self.assertEqual(cnt.data, cnt2.data)
diff --git a/tests/arch/instruction.py b/tests/arch/instruction.py
new file mode 100644
index 0000000..da4d8c1
--- /dev/null
+++ b/tests/arch/instruction.py
@@ -0,0 +1,52 @@
+
+import pychrysalide
+from chrysacase import ChrysalideTestCase
+from pychrysalide.arch import ArchInstruction
+
+
+class TestProcessor(ChrysalideTestCase):
+ """TestCase for arch.ArchProcessor."""
+
+
+ def testAbstractClass(self):
+ """Forbid instruction class instance."""
+
+ with self.assertRaisesRegex(RuntimeError, 'pychrysalide.arch.ArchInstruction is an abstract class'):
+ ins = ArchInstruction()
+
+
+ def testInstructionBasicImplementation(self):
+ """Implement basic custom instructions."""
+
+
+ class TodoInstruction(ArchInstruction):
+
+ def __init__(self):
+ super().__init__(0x123)
+
+
+ ins = TodoInstruction()
+
+ with self.assertRaisesRegex(NotImplementedError, 'unexpected NULL value as encoding'):
+ print(ins.encoding)
+
+ with self.assertRaisesRegex(NotImplementedError, 'unexpected NULL value as keyword'):
+ print(ins.keyword)
+
+
+ class CustomInstruction(ArchInstruction):
+
+ def __init__(self):
+ super().__init__(0x123)
+
+ def _get_encoding(self):
+ return 'custom'
+
+ def _get_keyword(self):
+ return 'kw'
+
+
+ ins = CustomInstruction()
+
+ self.assertEqual('custom', ins.encoding)
+ self.assertEqual('kw', ins.keyword)
diff --git a/tests/arch/operand.py b/tests/arch/operand.py
new file mode 100644
index 0000000..e8df8b5
--- /dev/null
+++ b/tests/arch/operand.py
@@ -0,0 +1,72 @@
+
+import pychrysalide
+
+from chrysacase import ChrysalideTestCase
+from pychrysalide.arch import ArchOperand
+from pychrysalide.glibext import HashableObject, StringBuilder
+
+
+class TestOperand(ChrysalideTestCase):
+ """TestCase for arch.ArchOperand."""
+
+
+ def testAbstractClass(self):
+ """Forbid operand class instance."""
+
+ with self.assertRaisesRegex(RuntimeError, 'pychrysalide.arch.ArchOperand is an abstract class'):
+ pc = ArchOperand()
+
+
+ def testStringBuilderMethodsOverriding(self):
+ """Override the StringBuilder interface provided by native implementations."""
+
+ class MyOperand(ArchOperand, StringBuilder):
+
+ def __init__(self):
+ super().__init__(self)
+
+ def _to_string(self, flags=0):
+ return 'my-op'
+
+ op = MyOperand()
+
+ self.assertEqual(op.to_string(), 'my-op')
+ self.assertEqual(str(op), 'my-op')
+ self.assertEqual(f'{op}', 'my-op')
+
+
+ def testHashableObjectMethods(self):
+ """Test the HashableObject methods implemantation for operands."""
+
+ # Pas d'implementation de particulière de HashableObject,
+ # c'est donc la définition d'implémentation d'ArchOperand
+ # qui s'applique.
+
+ # Spécificité de l'implémentation GLib : hash 32 bits
+
+ class DefaultHashableOperand(ArchOperand):
+ pass
+
+ def_op = DefaultHashableOperand()
+
+ h = hash(def_op)
+
+ self.assertEqual(0, h & ~0xffffffff)
+
+
+ # Définition particulière de l'opérande, sur la base de
+ # l'implémentation parente.
+
+ class CustomHashableOperand(ArchOperand, HashableObject):
+
+ def _hash(self):
+ h = self.parent_hash()
+ h &= ~0xffff
+ return h
+
+ cust_op = CustomHashableOperand()
+
+ h = hash(cust_op)
+
+ self.assertEqual(0, h & 0xffff)
+ self.assertEqual(0, h & ~0xffffffff)
diff --git a/tests/arch/operands/immediate.py b/tests/arch/operands/immediate.py
index 74b8069..c3fcb84 100644
--- a/tests/arch/operands/immediate.py
+++ b/tests/arch/operands/immediate.py
@@ -1,35 +1,59 @@
-#!/usr/bin/python3-dbg
-# -*- coding: utf-8 -*-
-
import pychrysalide
-from chrysacase import ChrysalideTestCase
-from pychrysalide import arch
-from pychrysalide.arch import ImmOperand
+from chrysacase import ChrysalideTestCase
+from pychrysalide import MemoryDataSize
+from pychrysalide.arch.operands import ImmediateOperand
+from pychrysalide.glibext import StringBuilder
class TestImmediate(ChrysalideTestCase):
- """TestCase for arch.ImmOperand."""
+ """TestCase for arch.ImmediateOperand."""
+
+
+ def testBasicImmediate(self):
+ """Check basic properties of immediate values."""
+
+ imm = ImmediateOperand(MemoryDataSize._32_BITS_UNSIGNED, 0x123)
+
+ self.assertEqual(imm.size, MemoryDataSize._32_BITS_UNSIGNED)
+ self.assertEqual(imm.value, 0x123)
+ self.assertFalse(imm.is_negative)
+
+
+ def testStringBuilderForImmediatesOverriding(self):
+ """Override the StringBuilder interface for immediate values."""
+
+ class MyImmOperand(ImmediateOperand, StringBuilder):
+
+ def __init__(self):
+ super().__init__(MemoryDataSize._32_BITS_UNSIGNED, 0x123)
+
+ def _to_string(self, flags=0):
+ return 'NaN'
+
+ op = MyImmOperand()
+
+ self.assertEqual(op.to_string(), 'NaN')
+ self.assertEqual(str(op), 'NaN')
+ self.assertEqual(f'{op}', 'NaN')
def validateValue(self, value, size, padding, strings):
"""Check all kinds of things with a given immediate operand."""
- display = [
- ImmOperand.IOD_BIN, ImmOperand.IOD_OCT,
- ImmOperand.IOD_DEC,
- ImmOperand.IOD_HEX
- ]
+ for d in strings.keys():
- for d in display:
-
- op = ImmOperand(size, value)
+ op = ImmediateOperand(size, value)
self.assertTrue(op.size == size)
self.assertTrue(op.value == value)
- op.padding = padding
+ if padding:
+ op.set_flag(ImmediateOperand.ImmOperandFlag.ZERO_PADDING)
+ else:
+ op.unset_flag(ImmediateOperand.ImmOperandFlag.ZERO_PADDING_BY_DEFAULT)
+
op.display = d
string = op.to_string()
@@ -40,23 +64,23 @@ class TestImmediate(ChrysalideTestCase):
"""Run sanity checks on immediate operand with value 1."""
strings = {
- ImmOperand.IOD_BIN: 'b1',
- ImmOperand.IOD_OCT: '01',
- ImmOperand.IOD_DEC: '1',
- ImmOperand.IOD_HEX: '0x1'
+ ImmediateOperand.ImmOperandDisplay.BIN: 'b1',
+ ImmediateOperand.ImmOperandDisplay.OCT: '01',
+ ImmediateOperand.ImmOperandDisplay.DEC: '1',
+ ImmediateOperand.ImmOperandDisplay.HEX: '0x1'
}
- self.validateValue(1, arch.MDS_8_BITS_UNSIGNED, False, strings)
+ self.validateValue(1, pychrysalide.MemoryDataSize._8_BITS_UNSIGNED, False, strings)
def testByteOnePadded(self):
"""Run sanity checks on immediate operand with padded value 1."""
strings = {
- ImmOperand.IOD_BIN: 'b00000001',
- ImmOperand.IOD_OCT: '01',
- ImmOperand.IOD_DEC: '1',
- ImmOperand.IOD_HEX: '0x01'
+ ImmediateOperand.ImmOperandDisplay.BIN: 'b00000001',
+ ImmediateOperand.ImmOperandDisplay.OCT: '01',
+ ImmediateOperand.ImmOperandDisplay.DEC: '1',
+ ImmediateOperand.ImmOperandDisplay.HEX: '0x01'
}
- self.validateValue(1, arch.MDS_8_BITS_UNSIGNED, True, strings)
+ self.validateValue(1, pychrysalide.MemoryDataSize._8_BITS_UNSIGNED, True, strings)
diff --git a/tests/common/leb128.py b/tests/common/leb128.py
index db3013e..037af4d 100644
--- a/tests/common/leb128.py
+++ b/tests/common/leb128.py
@@ -1,7 +1,6 @@
from chrysacase import ChrysalideTestCase
from pychrysalide.common import pack_uleb128, unpack_uleb128, pack_leb128, unpack_leb128
-from pychrysalide.common import PackedBuffer
class TestLEB128Values(ChrysalideTestCase):
@@ -16,34 +15,30 @@ class TestLEB128Values(ChrysalideTestCase):
128: b'\x80\x01',
}
- for value, encoding in cases.items():
-
- pbuf = PackedBuffer()
+ # Lecture depuis des blocs individuels
- status = pack_uleb128(value, pbuf)
- self.assertTrue(status)
+ for value, encoding in cases.items():
- self.assertEqual(pbuf.payload_length, len(encoding))
+ self.assertEqual(pack_uleb128(value), encoding)
- pbuf.rewind()
+ v, r = unpack_uleb128(encoding)
- got = pbuf.extract(len(encoding))
+ self.assertEqual(value, v)
+ self.assertEqual(b'', r)
- self.assertEqual(got, encoding)
+ # Lecture depuis un bloc commun
- self.assertFalse(pbuf.more_data)
+ data = b''.join(cases.values())
- for value, encoding in cases.items():
+ values = []
- pbuf = PackedBuffer()
- pbuf.extend(encoding, False)
+ while len(data) > 0:
- pbuf.rewind()
+ val, data = unpack_uleb128(data)
- got = unpack_uleb128(pbuf)
- self.assertIsNotNone(got)
+ values.append(val)
- self.assertEqual(got, value)
+ self.assertEqual(values, [ k for k in cases.keys() ])
def testSignedLeb128Encoding(self):
@@ -55,54 +50,39 @@ class TestLEB128Values(ChrysalideTestCase):
-9001: b'\xd7\xb9\x7f',
}
- for value, encoding in cases.items():
+ # Lecture depuis des blocs individuels
- pbuf = PackedBuffer()
+ for value, encoding in cases.items():
- status = pack_leb128(value, pbuf)
- self.assertTrue(status)
+ self.assertEqual(pack_leb128(value), encoding)
- self.assertEqual(pbuf.payload_length, len(encoding))
+ v, r = unpack_leb128(encoding)
- pbuf.rewind()
+ self.assertEqual(value, v)
+ self.assertEqual(b'', r)
- got = pbuf.extract(len(encoding))
+ # Lecture depuis un bloc commun
- self.assertEqual(got, encoding)
+ data = b''.join(cases.values())
- self.assertFalse(pbuf.more_data)
+ values = []
- for value, encoding in cases.items():
+ while len(data) > 0:
- pbuf = PackedBuffer()
- pbuf.extend(encoding, False)
+ val, data = unpack_leb128(data)
- pbuf.rewind()
+ values.append(val)
- got = unpack_leb128(pbuf)
- self.assertIsNotNone(got)
-
- self.assertEqual(got, value)
+ self.assertEqual(values, [ k for k in cases.keys() ])
def testTooBigLeb128Encodings(self):
"""Prevent overflow for LEB128 values."""
- pbuf = PackedBuffer()
- pbuf.extend(b'\x80' * 10 + b'\x7f', False)
-
- pbuf.rewind()
-
- got = unpack_uleb128(pbuf)
-
- self.assertIsNone(got)
-
- pbuf = PackedBuffer()
- pbuf.extend(b'\x80' * 10 + b'\x7f', False)
-
- pbuf.rewind()
+ v = unpack_uleb128(b'\x80' * 10 + b'\x7f')
- got = unpack_leb128(pbuf)
+ self.assertIsNone(v)
- self.assertIsNone(got)
+ v = unpack_leb128(b'\x80' * 10 + b'\x7f')
+ self.assertIsNone(v)
diff --git a/tests/glibext/secstorage.py b/tests/glibext/secstorage.py
index 248b3e3..5b8f680 100644
--- a/tests/glibext/secstorage.py
+++ b/tests/glibext/secstorage.py
@@ -22,8 +22,6 @@ class TestSecretStorage(ChrysalideTestCase):
subprocess.run([ 'glib-compile-schemas', path ])
- os.environ['GSETTINGS_SCHEMA_DIR'] = path + ':' + os.environ['GSETTINGS_SCHEMA_DIR']
-
@classmethod
def tearDownClass(cls):
@@ -32,8 +30,6 @@ class TestSecretStorage(ChrysalideTestCase):
cls.log('Removing compiled GSettings schema...')
- os.environ['GSETTINGS_SCHEMA_DIR'] = ':'.join(os.environ['GSETTINGS_SCHEMA_DIR'].split(':')[1:])
-
path = os.path.dirname(os.path.realpath(__file__))
filename = os.path.join(path, 'gschemas.compiled')
@@ -42,10 +38,24 @@ class TestSecretStorage(ChrysalideTestCase):
os.remove(filename)
+ def _get_settings(self, sid):
+ """Provide local GSettings instance."""
+
+ path = os.path.dirname(os.path.realpath(__file__))
+
+ source = Gio.SettingsSchemaSource.new_from_directory(path, None, True)
+
+ schema = Gio.SettingsSchemaSource.lookup(source, sid, False)
+
+ settings = Gio.Settings.new_full(schema, None, None)
+
+ return settings
+
+
def testMasterKeyDefinition(self):
"""Check for cryptographic parameters for secret storage."""
- settings = Gio.Settings.new('re.chrysalide.tests.secstorage')
+ settings = self._get_settings('re.chrysalide.tests.secstorage')
storage = SecretStorage(settings)
@@ -67,7 +77,7 @@ class TestSecretStorage(ChrysalideTestCase):
def testMasterKeyCreation(self):
"""Create and update cryptographic parameters for secret storage."""
- settings = Gio.Settings.new('re.chrysalide.tests.secstorage')
+ settings = self._get_settings('re.chrysalide.tests.secstorage')
storage = SecretStorage(settings)
@@ -101,9 +111,9 @@ class TestSecretStorage(ChrysalideTestCase):
def testDataEncryption(self):
- """Create and update cryptographic parameters for secret storage."""
+ """Encrypt and decrypt data with the secret storage."""
- settings = Gio.Settings.new('re.chrysalide.tests.secstorage')
+ settings = self._get_settings('re.chrysalide.tests.secstorage')
storage = SecretStorage(settings)
diff --git a/tests/glibext/storage.py b/tests/glibext/storage.py
new file mode 100644
index 0000000..b60377a
--- /dev/null
+++ b/tests/glibext/storage.py
@@ -0,0 +1,74 @@
+
+from chrysacase import ChrysalideTestCase
+from pychrysalide.glibext import ObjectStorage, SerializableObject
+import gi
+import os
+import tempfile
+
+
+class TestObjectStorage(ChrysalideTestCase):
+ """TestCase for analysis.storage."""
+
+ @classmethod
+ def setUpClass(cls):
+
+ super(TestObjectStorage, cls).setUpClass()
+
+ _, cls._tmp_filename = tempfile.mkstemp()
+
+ cls.log('Using temporary filename "%s"' % cls._tmp_filename)
+
+
+ @classmethod
+ def tearDownClass(cls):
+
+ super(TestObjectStorage, cls).tearDownClass()
+
+ cls.log('Delete filename "%s"' % cls._tmp_filename)
+
+ os.unlink(cls._tmp_filename)
+
+
+ def testGenericStorage(self):
+ """Store and load basic objects."""
+
+
+ class SimpleObject(gi._gi.GObject, SerializableObject):
+
+ def __init__(self, b=None):
+ super().__init__()
+ self._b = b
+
+ def _load(self, storage, fd):
+ assert(self._b is None)
+ self._b = os.read(fd, 1)[0]
+ return True
+
+ def _store(self, storage, fd):
+ os.write(fd, bytes([ self._b ]))
+ return True
+
+ def __eq__(self, other):
+ return self._b == other._b
+
+
+ # Store
+
+ storage = ObjectStorage('TestStorage', 0, 'my-storage-hash')
+ self.assertIsNotNone(storage)
+
+ so = SimpleObject(0x23)
+
+ pos = storage.store_object('simple', so)
+ self.assertIsNotNone(pos)
+
+ status = storage.store(self._tmp_filename)
+ self.assertTrue(status)
+
+ # Reload
+
+ storage2 = ObjectStorage.load(self._tmp_filename)
+
+ so2 = storage2.load_object('simple', pos)
+
+ self.assertEqual(so, so2)