Add Babeltrace 2 Python bindings tests
authorPhilippe Proulx <eeppeliteloop@gmail.com>
Sat, 4 Feb 2017 03:40:47 +0000 (22:40 -0500)
committerJérémie Galarneau <jeremie.galarneau@efficios.com>
Sun, 28 May 2017 16:57:37 +0000 (12:57 -0400)
This patch adds a testing infrastructure for the Python bindings of
Balbetrace, as well as tests for the bt2 package.

To run those tests, you need the tappy Python project
(https://github.com/python-tap/tappy). The configure script checks that
this project is available when you specify the new
--enable-python-bindings-tests option.

A test runner script, tests/utils/python/testrunner.py, loads the test
cases from a directory passed as its first command-line argument, sets
the TAP runner, and runs the tests, exiting with 0 if everything was
successful.

The Python binding tests are in tests/bindings/python/bt2 and are only
executed if the project is configured with --enable-python-bindings.
In this directory, testall.sh is generated from testall.sh.in to
call the test runner script with the appropriate paths, and set the
appropriate PYTHONPATH and LD_LIBRARY_PATH environment variables.

You can run testall.sh directly or with TESTALL_COVERAGE=1 to check the
coverage of the bt2 package with the Python `coverage` tool. You can
also set TESTALL_COVERAGE_HTML=1 to get an HTML report, or
TESTALL_COVERAGE_REPORT=1 to get a plain text report. The .coveragerc
file in this directory is a coverage configuration file which omits
certain files and exclude certain lines. The coverage of most modules
by the the tests is in the 90%-95% range as of this patch:

    __init__.py               100%
    clock_class.py            97%
    component.py              97%
    ctf_writer.py             63%
    event.py                  97%
    event_class.py            98%
    field_types.py            100%
    fields.py                 96%
    notification.py           79%
    notification_iterator.py  91%
    packet.py                 98%
    plugin.py                 30%
    stream.py                 92%
    stream_class.py           97%
    trace.py                  99%
    values.py                 95%

It is expected that ctf_writer.py be tested by the tests of the original
`babeltrace` package which will wrap the bt2 package in a future patch.

test_values.py and test_fields.py test that value and field objects
behave like Python native objects. For this, test methods are generated
to try each possible operator between each possible object. The result
is compared with the same operator applied to native Python objects (or
if the operation raises something, the compared operation must raise the
same thing).

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
22 files changed:
configure.ac
m4/check_tappy.m4 [new file with mode: 0644]
tests/Makefile.am
tests/bindings/Makefile.am [new file with mode: 0644]
tests/bindings/python/Makefile.am [new file with mode: 0644]
tests/bindings/python/bt2/.coveragerc [new file with mode: 0644]
tests/bindings/python/bt2/.gitignore [new file with mode: 0644]
tests/bindings/python/bt2/Makefile.am [new file with mode: 0644]
tests/bindings/python/bt2/test_clock_class.py [new file with mode: 0644]
tests/bindings/python/bt2/test_comp_notif_iter.py [new file with mode: 0644]
tests/bindings/python/bt2/test_ctf_writer_clock.py [new file with mode: 0644]
tests/bindings/python/bt2/test_event.py [new file with mode: 0644]
tests/bindings/python/bt2/test_event_class.py [new file with mode: 0644]
tests/bindings/python/bt2/test_field_types.py [new file with mode: 0644]
tests/bindings/python/bt2/test_fields.py [new file with mode: 0644]
tests/bindings/python/bt2/test_packet.py [new file with mode: 0644]
tests/bindings/python/bt2/test_stream.py [new file with mode: 0644]
tests/bindings/python/bt2/test_stream_class.py [new file with mode: 0644]
tests/bindings/python/bt2/test_trace.py [new file with mode: 0644]
tests/bindings/python/bt2/test_values.py [new file with mode: 0644]
tests/bindings/python/bt2/testall.sh.in [new file with mode: 0644]
tests/utils/python/testrunner.py [new file with mode: 0644]

index bd6f65b6d6e95d782b8f77bcb2df9a99e8e4bec6..554a5b05ef2d273fe68a41344792faee63149188 100644 (file)
@@ -251,8 +251,21 @@ AC_ARG_ENABLE([python-bindings-doc],
                               [generate Python bindings documentation])],
               [enable_python_bindings_doc=yes], [enable_python_bindings_doc=no])
 
-if test "x${enable_python:-no}" = xno && test "x${enable_python_bindings_doc:-yes}" = xyes; then
-  AC_MSG_ERROR([--enable-python-bindings-doc was specified without --enable-python-bindings])
+AC_ARG_ENABLE([python-bindings-tests],
+              [AC_HELP_STRING([--enable-python-bindings-tests],
+                              [test Python bindings])],
+              [enable_python_bindings_tests=yes], [enable_python_bindings_tests=no])
+
+AM_CONDITIONAL([ENABLE_PYTHON_BINDINGS_TESTS], [test "x${enable_python_bindings_tests:-yes}" = xyes])
+
+if test "x${enable_python:-no}" = xno; then
+  if test "x${enable_python_bindings_doc:-yes}" = xyes; then
+    AC_MSG_ERROR([--enable-python-bindings-doc was specified without --enable-python-bindings])
+  fi
+
+  if test "x${enable_python_bindings_tests:-yes}" = xyes; then
+    AC_MSG_ERROR([--enable-python-bindings-tests was specified without --enable-python-bindings])
+  fi
 fi
 
 AM_CONDITIONAL([BUILD_PYTHON_BINDINGS_DOC], [test "x${enable_python_bindings_doc:-yes}" = xyes])
@@ -293,6 +306,13 @@ if test "x${enable_python_bindings_doc:-yes}" = xyes; then
     )
 fi
 
+if test "x${enable_python_bindings_tests:-yes}" = xyes; then
+    AM_CHECK_PYTHON_TAPPY([PYTHON])
+    AS_IF([test "x$PYTHON_TAPPY_EXISTS" = xno],
+        AC_MSG_ERROR([You need the tappy Python project to test the Python bindings (see <https://github.com/python-tap/tappy>)])
+    )
+fi
+
 # Set default enable state for debug info.
 # The _enable_debug_info variable is prepended with an underscore to
 # avoid clashing with the one generated by AC_ARG_ENABLE.
@@ -425,6 +445,9 @@ AC_CONFIG_FILES([
        tests/lib/test-plugin-plugins/Makefile
        tests/utils/Makefile
        tests/utils/tap/Makefile
+       tests/bindings/Makefile
+       tests/bindings/python/Makefile
+       tests/bindings/python/bt2/Makefile
        extras/Makefile
        extras/valgrind/Makefile
        plugins/Makefile
@@ -458,6 +481,13 @@ AC_CONFIG_FILES([tests/lib/writer/bt_python_helper.py])
 AC_CONFIG_FILES([tests/bin/test_packet_seq_num], [chmod +x tests/bin/test_packet_seq_num])
 AC_CONFIG_FILES([tests/bin/test_formats], [chmod +x tests/bin/test_formats])
 
+AS_IF([test "x$enable_python" = "xyes"], [
+       AC_CONFIG_FILES(
+               [tests/bindings/python/bt2/testall.sh],
+               [chmod +x tests/bindings/python/bt2/testall.sh]
+       )
+])
+
 AC_OUTPUT
 
 #
@@ -499,6 +529,10 @@ PPRINT_PROP_BOOL([Python bindings], $value)
 test "x$enable_python_bindings_doc" = "xyes" && value=1 || value=0
 PPRINT_PROP_BOOL([Python bindings doc], $value)
 
+# python bindings tests enabled/disabled
+test "x$enable_python_bindings_tests" = "xyes" && value=1 || value=0
+PPRINT_PROP_BOOL([Python bindings tests], $value)
+
 # debug info enabled/disabled
 test "x$_enable_debug_info" = "xyes" && value=1 || value=0
 PPRINT_PROP_BOOL([Debug information output], $value)
diff --git a/m4/check_tappy.m4 b/m4/check_tappy.m4
new file mode 100644 (file)
index 0000000..317813f
--- /dev/null
@@ -0,0 +1,32 @@
+# check_tappy.m4 -- check for tappy Python package
+#
+# Copyright (C) 2015 - Philippe Proulx <pproulx@efficios.com>
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# Sphinx ships with a script named "sphinx-build", which is usually
+# installed in "/usr/bin". Unfortunately, this script uses
+# "/usr/bin/python" as its interpreter. Since "/usr/bin/python" can
+# be either Python 2 or Python 3, depending on the distribution, and
+# since we absolutely need the Python 3 Sphinx package for Babeltrace
+# because it needs to import our bindings for autodocumentation,
+# there's no way to tell if "sphinx-build" is actually using Python 2
+# or Python 3.
+#
+# This macro checks if the Sphinx package ("sphinx") is installed
+# and visible from the interpreter designated by the PYTHON variable.
+# It sets PYTHON_SPHINX_EXISTS to "yes" if Sphinx is found for the
+# given Python interpreter, otherwise "no".
+
+# AM_CHECK_PYTHON_SPHINX(PYTHON)
+# ---------------------------------------------------------------------------
+AC_DEFUN([AM_CHECK_PYTHON_TAPPY],
+    [prog="
+try:
+    from tap import TAPTestRunner
+    print('yes')
+except ImportError:
+    print('no')"
+    PYTHON_TAPPY_EXISTS=`${$1} -c "$prog"`])
index 90340455a5cda7184abf78b8e60eaf0a7b94cc77..a07b136bbbbddac3c25791545bf2ddb61b7045ad 100644 (file)
@@ -1,4 +1,4 @@
-SUBDIRS = utils bin lib
+SUBDIRS = utils bin lib bindings
 
 LOG_DRIVER_FLAGS='--merge'
 LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) \
@@ -33,5 +33,6 @@ endif
 if USE_PYTHON
 TESTS += bin/intersection/test_multi_trace_intersection.py \
        lib/writer/test_ctf_writer_no_packet_context.py \
-       lib/writer/test_ctf_writer_empty_packet.py
+       lib/writer/test_ctf_writer_empty_packet.py \
+       bindings/python/bt2/testall.sh
 endif
diff --git a/tests/bindings/Makefile.am b/tests/bindings/Makefile.am
new file mode 100644 (file)
index 0000000..4bfc6cf
--- /dev/null
@@ -0,0 +1,3 @@
+if ENABLE_PYTHON_BINDINGS_TESTS
+SUBDIRS = python
+endif
diff --git a/tests/bindings/python/Makefile.am b/tests/bindings/python/Makefile.am
new file mode 100644 (file)
index 0000000..f3cec07
--- /dev/null
@@ -0,0 +1 @@
+SUBDIRS = bt2
diff --git a/tests/bindings/python/bt2/.coveragerc b/tests/bindings/python/bt2/.coveragerc
new file mode 100644 (file)
index 0000000..869bc00
--- /dev/null
@@ -0,0 +1,14 @@
+[run]
+include = */babeltrace/bindings/python/bt2/*.py
+
+omit =
+    *native_*.py
+    */object.py
+    */utils.py
+
+[report]
+exclude_lines =
+    def __repr__
+    raise bt2\.CreationError
+    raise NotImplementedError
+    pass
diff --git a/tests/bindings/python/bt2/.gitignore b/tests/bindings/python/bt2/.gitignore
new file mode 100644 (file)
index 0000000..e86dc59
--- /dev/null
@@ -0,0 +1,3 @@
+htmlcov
+.coverage
+testall.sh
diff --git a/tests/bindings/python/bt2/Makefile.am b/tests/bindings/python/bt2/Makefile.am
new file mode 100644 (file)
index 0000000..78893fd
--- /dev/null
@@ -0,0 +1,15 @@
+check_SCRIPTS = testall.sh
+EXTRA_DIST = \
+       $(check_SCRIPTS) \
+       test_clock_class.py \
+       test_ctf_writer_clock.py \
+       test_event_class.py \
+       test_event.py \
+       test_fields.py \
+       test_field_types.py \
+       test_packet.py \
+       test_stream_class.py \
+       test_stream.py \
+       test_trace.py \
+       test_values.py \
+       .coveragerc
diff --git a/tests/bindings/python/bt2/test_clock_class.py b/tests/bindings/python/bt2/test_clock_class.py
new file mode 100644 (file)
index 0000000..436b2b9
--- /dev/null
@@ -0,0 +1,299 @@
+import unittest
+import uuid
+import copy
+import bt2
+
+
+class ClockClassOffsetTestCase(unittest.TestCase):
+    def test_create_default(self):
+        cco = bt2.ClockClassOffset()
+        self.assertEqual(cco.seconds, 0)
+        self.assertEqual(cco.cycles, 0)
+
+    def test_create(self):
+        cco = bt2.ClockClassOffset(23, 4871232)
+        self.assertEqual(cco.seconds, 23)
+        self.assertEqual(cco.cycles, 4871232)
+
+    def test_create_kwargs(self):
+        cco = bt2.ClockClassOffset(seconds=23, cycles=4871232)
+        self.assertEqual(cco.seconds, 23)
+        self.assertEqual(cco.cycles, 4871232)
+
+    def test_create_invalid_seconds(self):
+        with self.assertRaises(TypeError):
+            bt2.ClockClassOffset('hello', 4871232)
+
+    def test_create_invalid_cycles(self):
+        with self.assertRaises(TypeError):
+            bt2.ClockClassOffset(23, 'hello')
+
+    def test_eq(self):
+        cco1 = bt2.ClockClassOffset(23, 42)
+        cco2 = bt2.ClockClassOffset(23, 42)
+        self.assertEqual(cco1, cco2)
+
+    def test_ne_seconds(self):
+        cco1 = bt2.ClockClassOffset(23, 42)
+        cco2 = bt2.ClockClassOffset(24, 42)
+        self.assertNotEqual(cco1, cco2)
+
+    def test_ne_cycles(self):
+        cco1 = bt2.ClockClassOffset(23, 42)
+        cco2 = bt2.ClockClassOffset(23, 43)
+        self.assertNotEqual(cco1, cco2)
+
+    def test_eq_invalid(self):
+        self.assertFalse(bt2.ClockClassOffset() == 23)
+
+
+class ClockClassTestCase(unittest.TestCase):
+    def setUp(self):
+        self._cc = bt2.ClockClass('salut')
+
+    def test_create_default(self):
+        self.assertEqual(self._cc.name, 'salut')
+
+    def test_create_invalid_no_name(self):
+        with self.assertRaises(TypeError):
+            bt2.ClockClass()
+
+    def test_create_full(self):
+        my_uuid = uuid.uuid1()
+        cc = bt2.ClockClass(name='name', description='some description',
+                            frequency=1001, precision=176,
+                            offset=bt2.ClockClassOffset(45, 3003),
+                            is_absolute=True, uuid=my_uuid)
+        self.assertEqual(cc.name, 'name')
+        self.assertEqual(cc.description, 'some description')
+        self.assertEqual(cc.frequency, 1001)
+        self.assertEqual(cc.precision, 176)
+        self.assertEqual(cc.offset, bt2.ClockClassOffset(45, 3003))
+        self.assertEqual(cc.is_absolute, True)
+        self.assertEqual(cc.uuid, copy.deepcopy(my_uuid))
+
+    def test_assign_name(self):
+        self._cc.name = 'the_clock'
+        self.assertEqual(self._cc.name, 'the_clock')
+
+    def test_assign_invalid_name(self):
+        with self.assertRaises(TypeError):
+            self._cc.name = 23
+
+    def test_assign_description(self):
+        self._cc.description = 'hi people'
+        self.assertEqual(self._cc.description, 'hi people')
+
+    def test_assign_invalid_description(self):
+        with self.assertRaises(TypeError):
+            self._cc.description = 23
+
+    def test_assign_frequency(self):
+        self._cc.frequency = 987654321
+        self.assertEqual(self._cc.frequency, 987654321)
+
+    def test_assign_invalid_frequency(self):
+        with self.assertRaises(TypeError):
+            self._cc.frequency = 'lel'
+
+    def test_assign_precision(self):
+        self._cc.precision = 12
+        self.assertEqual(self._cc.precision, 12)
+
+    def test_assign_invalid_precision(self):
+        with self.assertRaises(TypeError):
+            self._cc.precision = 'lel'
+
+    def test_assign_offset(self):
+        self._cc.offset = bt2.ClockClassOffset(12, 56)
+        self.assertEqual(self._cc.offset, bt2.ClockClassOffset(12, 56))
+
+    def test_assign_invalid_offset(self):
+        with self.assertRaises(TypeError):
+            self._cc.offset = object()
+
+    def test_assign_absolute(self):
+        self._cc.is_absolute = True
+        self.assertTrue(self._cc.is_absolute)
+
+    def test_assign_invalid_absolute(self):
+        with self.assertRaises(TypeError):
+            self._cc.is_absolute = 23
+
+    def test_assign_uuid(self):
+        the_uuid = uuid.uuid1()
+        self._cc.uuid = the_uuid
+        self.assertEqual(self._cc.uuid, the_uuid)
+
+    def test_assign_invalid_uuid(self):
+        with self.assertRaises(TypeError):
+            self._cc.uuid = object()
+
+    def test_create_clock_value(self):
+        cv = self._cc.create_clock_value(756)
+        self.assertEqual(cv.clock_class.addr, self._cc.addr)
+
+    def _test_copy(self, cpy):
+        self.assertIsNot(cpy, self._cc)
+        self.assertNotEqual(cpy.addr, self._cc.addr)
+        self.assertEqual(cpy, self._cc)
+
+    def test_copy(self):
+        cpy = copy.copy(self._cc)
+        self._test_copy(cpy)
+
+    def test_deepcopy(self):
+        cpy = copy.deepcopy(self._cc)
+        self._test_copy(cpy)
+
+    def test_eq(self):
+        my_uuid = uuid.uuid1()
+        cc1 = bt2.ClockClass(name='name', description='some description',
+                             frequency=1001, precision=176,
+                             offset=bt2.ClockClassOffset(45, 3003),
+                             is_absolute=True, uuid=my_uuid)
+        cc2 = bt2.ClockClass(name='name', description='some description',
+                             frequency=1001, precision=176,
+                             offset=bt2.ClockClassOffset(45, 3003),
+                             is_absolute=True, uuid=my_uuid)
+        self.assertEqual(cc1, cc2)
+
+    def test_ne_name(self):
+        my_uuid = uuid.uuid1()
+        cc1 = bt2.ClockClass(name='mane', description='some description',
+                             frequency=1001, precision=176,
+                             offset=bt2.ClockClassOffset(45, 3003),
+                             is_absolute=True, uuid=my_uuid)
+        cc2 = bt2.ClockClass(name='name', description='some description',
+                             frequency=1001, precision=176,
+                             offset=bt2.ClockClassOffset(45, 3003),
+                             is_absolute=True, uuid=my_uuid)
+        self.assertNotEqual(cc1, cc2)
+
+    def test_ne_description(self):
+        my_uuid = uuid.uuid1()
+        cc1 = bt2.ClockClass(name='name', description='some descripti2',
+                             frequency=1001, precision=176,
+                             offset=bt2.ClockClassOffset(45, 3003),
+                             is_absolute=True, uuid=my_uuid)
+        cc2 = bt2.ClockClass(name='name', description='some description',
+                             frequency=1001, precision=176,
+                             offset=bt2.ClockClassOffset(45, 3003),
+                             is_absolute=True, uuid=my_uuid)
+        self.assertNotEqual(cc1, cc2)
+
+    def test_ne_frequency(self):
+        my_uuid = uuid.uuid1()
+        cc1 = bt2.ClockClass(name='name', description='some description',
+                             frequency=1003, precision=176,
+                             offset=bt2.ClockClassOffset(45, 3003),
+                             is_absolute=True, uuid=my_uuid)
+        cc2 = bt2.ClockClass(name='name', description='some description',
+                             frequency=1001, precision=176,
+                             offset=bt2.ClockClassOffset(45, 3003),
+                             is_absolute=True, uuid=my_uuid)
+        self.assertNotEqual(cc1, cc2)
+
+    def test_ne_precision(self):
+        my_uuid = uuid.uuid1()
+        cc1 = bt2.ClockClass(name='name', description='some description',
+                             frequency=1001, precision=171,
+                             offset=bt2.ClockClassOffset(45, 3003),
+                             is_absolute=True, uuid=my_uuid)
+        cc2 = bt2.ClockClass(name='name', description='some description',
+                             frequency=1001, precision=176,
+                             offset=bt2.ClockClassOffset(45, 3003),
+                             is_absolute=True, uuid=my_uuid)
+        self.assertNotEqual(cc1, cc2)
+
+    def test_ne_offset(self):
+        my_uuid = uuid.uuid1()
+        cc1 = bt2.ClockClass(name='name', description='some description',
+                             frequency=1001, precision=176,
+                             offset=bt2.ClockClassOffset(45, 3001),
+                             is_absolute=True, uuid=my_uuid)
+        cc2 = bt2.ClockClass(name='name', description='some description',
+                             frequency=1001, precision=176,
+                             offset=bt2.ClockClassOffset(45, 3003),
+                             is_absolute=True, uuid=my_uuid)
+        self.assertNotEqual(cc1, cc2)
+
+    def test_ne_absolute(self):
+        my_uuid = uuid.uuid1()
+        cc1 = bt2.ClockClass(name='name', description='some description',
+                             frequency=1001, precision=176,
+                             offset=bt2.ClockClassOffset(45, 3003),
+                             is_absolute=True, uuid=my_uuid)
+        cc2 = bt2.ClockClass(name='name', description='some description',
+                             frequency=1001, precision=176,
+                             offset=bt2.ClockClassOffset(45, 3003),
+                             is_absolute=False, uuid=my_uuid)
+        self.assertNotEqual(cc1, cc2)
+
+    def test_ne_uuid(self):
+        cc1 = bt2.ClockClass(name='name', description='some description',
+                             frequency=1001, precision=176,
+                             offset=bt2.ClockClassOffset(45, 3003),
+                             is_absolute=True, uuid=uuid.uuid1())
+        cc2 = bt2.ClockClass(name='name', description='some description',
+                             frequency=1001, precision=176,
+                             offset=bt2.ClockClassOffset(45, 3003),
+                             is_absolute=True, uuid=uuid.uuid1())
+        self.assertNotEqual(cc1, cc2)
+
+    def test_eq_invalid(self):
+        self.assertFalse(self._cc == 23)
+
+
+class ClockClassValueTestCase(unittest.TestCase):
+    def setUp(self):
+        self._cc = bt2.ClockClass('salut')
+        self._cv = self._cc.create_clock_value(123)
+
+    def test_create_default(self):
+        self.assertEqual(self._cv.clock_class.addr, self._cc.addr)
+        self.assertEqual(self._cv.cycles, 123)
+
+    def test_create_invalid_cycles_type(self):
+        with self.assertRaises(TypeError):
+            self._cc.create_clock_value('yes')
+
+    def test_ns_from_epoch(self):
+        self._cv.clock_class.frequency = 1000
+        self._cv.clock_class.offset = bt2.ClockClassOffset(45, 354)
+        s_from_epoch = 45 + ((354 + 123) / 1000)
+        ns_from_epoch = int(s_from_epoch * 1e9)
+        self.assertEqual(self._cv.ns_from_epoch, ns_from_epoch)
+
+    def test_eq(self):
+        cv1 = self._cc.create_clock_value(123)
+        cv2 = self._cc.create_clock_value(123)
+        self.assertEqual(cv1, cv2)
+
+    def test_ne_clock_class(self):
+        cc1 = bt2.ClockClass('yes')
+        cc2 = bt2.ClockClass('yes')
+        cv1 = cc1.create_clock_value(123)
+        cv2 = cc2.create_clock_value(123)
+        self.assertNotEqual(cv1, cv2)
+
+    def test_ne_cycles(self):
+        cv1 = self._cc.create_clock_value(123)
+        cv2 = self._cc.create_clock_value(125)
+        self.assertNotEqual(cv1, cv2)
+
+    def test_eq_invalid(self):
+        self.assertFalse(self._cv == 23)
+
+    def _test_copy(self, cpy):
+        self.assertIsNot(cpy, self._cv)
+        self.assertNotEqual(cpy.addr, self._cv.addr)
+        self.assertEqual(cpy, self._cv)
+
+    def test_copy(self):
+        cpy = copy.copy(self._cv)
+        self._test_copy(cpy)
+
+    def test_deepcopy(self):
+        cpy = copy.deepcopy(self._cv)
+        self._test_copy(cpy)
diff --git a/tests/bindings/python/bt2/test_comp_notif_iter.py b/tests/bindings/python/bt2/test_comp_notif_iter.py
new file mode 100644 (file)
index 0000000..b13294a
--- /dev/null
@@ -0,0 +1,698 @@
+from collections import OrderedDict
+import unittest
+import copy
+import bt2.notification_iterator
+import bt2
+
+
+def _create_ec():
+    # clock class
+    cc = bt2.ClockClass('salut_clock')
+
+    # event header
+    eh = bt2.StructureFieldType()
+    eh += OrderedDict((
+        ('id', bt2.IntegerFieldType(8)),
+        ('ts', bt2.IntegerFieldType(32, mapped_clock_class=cc)),
+    ))
+
+    # packet context
+    pc = bt2.StructureFieldType()
+    pc += OrderedDict((
+        ('something', bt2.IntegerFieldType(8)),
+    ))
+
+    # stream class
+    sc = bt2.StreamClass()
+    sc.packet_context_field_type = pc
+    sc.event_header_field_type = eh
+    sc.event_context_field_type = None
+
+    # event payload
+    ep = bt2.StructureFieldType()
+    ep += OrderedDict((
+        ('mosquito', bt2.StringFieldType()),
+    ))
+
+    # event class
+    event_class = bt2.EventClass('ec', id=0)
+    event_class.context_field_type = None
+    event_class.payload_field_type = ep
+    sc.add_event_class(event_class)
+
+    # packet header
+    ph = bt2.StructureFieldType()
+    ph += OrderedDict((
+        ('magic', bt2.IntegerFieldType(32)),
+        ('stream_id', bt2.IntegerFieldType(16)),
+    ))
+
+    # trace
+    trace = bt2.Trace()
+    trace.packet_header_field_type = ph
+    trace.add_clock_class(cc)
+    trace.add_stream_class(sc)
+    return event_class
+
+
+def _create_event(event_class, msg):
+    ev = event_class()
+    ev.header_field['id'] = 0
+    ev.header_field['ts'] = 19487
+    ev.payload_field['mosquito'] = msg
+    return ev
+
+
+def _create_packet(stream):
+    packet = stream.create_packet()
+    packet.header_field['magic'] = 0xc1fc1fc1
+    packet.header_field['stream_id'] = 0
+    packet.context_field['something'] = 194
+    return packet
+
+
+def _create_source():
+    class MyIter(bt2.UserNotificationIterator):
+        def __init__(self):
+            self._event_class = self.component._event_class
+            self._stream = self.component._stream
+            self._packet = _create_packet(self._stream)
+            self._at = 0
+            self._cur_notif = None
+
+        def _get(self):
+            if self._cur_notif is None:
+                raise bt2.Error('nothing here!')
+
+            return self._cur_notif
+
+        def _next(self):
+            if self._at == 0:
+                notif = bt2.BeginningOfPacketNotification(self._packet)
+            elif self._at < 5:
+                ev = _create_event(self._event_class, 'at {}'.format(self._at))
+                ev.packet = self._packet
+                notif = bt2.TraceEventNotification(ev)
+            elif self._at == 5:
+                notif = bt2.EndOfPacketNotification(self._packet)
+            elif self._at == 6:
+                notif = bt2.EndOfStreamNotification(self._stream)
+            else:
+                raise bt2.Stop
+
+            self._at += 1
+            self._cur_notif = notif
+
+    class MySource(bt2.UserSourceComponent, notification_iterator_class=MyIter):
+        def __init__(self):
+            self._event_class = _create_ec()
+            self._stream = self._event_class.stream_class(name='abcdef')
+
+    return MySource()
+
+
+class GenCompClassTestCase(unittest.TestCase):
+    def test_attr_name(self):
+        class MySink(bt2.UserSinkComponent):
+            def _consume(self):
+                pass
+
+        sink = MySink()
+        self.assertEqual(sink.component_class.name, 'MySink')
+
+    def test_attr_description(self):
+        class MySink(bt2.UserSinkComponent):
+            'hello'
+
+            def _consume(self):
+                pass
+
+        sink = MySink()
+        self.assertEqual(sink.component_class.description, 'hello')
+
+    def test_instantiate(self):
+        class MySink(bt2.UserSinkComponent):
+            'hello'
+
+            def __init__(self, params=None, name=None):
+                if params is None or name is None:
+                    return
+
+                nonlocal nl_params_a, nl_name
+                nl_params_a = params['a']
+                nl_name = name
+
+            def _consume(self):
+                pass
+
+        nl_params_a = None
+        nl_name = None
+        sink = MySink()
+        self.assertIsNone(nl_params_a)
+        self.assertIsNone(nl_name)
+        gen_comp_class = sink.component_class
+        sink2 = gen_comp_class(params={'a': 23}, name='salut')
+        self.assertEqual(nl_params_a, 23)
+        self.assertEqual(nl_name, 'salut')
+
+
+class UserCompClassTestCase(unittest.TestCase):
+    def test_attr_name(self):
+        class MySink(bt2.UserSinkComponent):
+            def _consume(self):
+                pass
+
+        self.assertEqual(MySink.name, 'MySink')
+
+    def test_attr_name_param(self):
+        class MySink(bt2.UserSinkComponent, name='my custom name'):
+            def _consume(self):
+                pass
+
+        self.assertEqual(MySink.name, 'my custom name')
+
+    def test_attr_description(self):
+        class MySink(bt2.UserSinkComponent):
+            'here is your description'
+
+            def _consume(self):
+                pass
+
+        self.assertEqual(MySink.description, 'here is your description')
+
+    def test_init(self):
+        class MySink(bt2.UserSinkComponent):
+            def __init__(self):
+                nonlocal inited
+                inited = True
+
+            def _consume(self):
+                pass
+
+        inited = False
+        sink = MySink()
+        self.assertTrue(inited)
+
+    def test_init_raises(self):
+        class MySink(bt2.UserSinkComponent):
+            def __init__(self):
+                raise RuntimeError('oops')
+
+            def _consume(self):
+                pass
+
+        with self.assertRaises(RuntimeError):
+            sink = MySink()
+
+    def test_destroy(self):
+        class MySink(bt2.UserSinkComponent):
+            def _destroy(self):
+                nonlocal destroyed
+                destroyed = True
+
+            def _consume(self):
+                pass
+
+        destroyed = False
+        sink = MySink()
+        del sink
+        self.assertTrue(destroyed)
+
+    def test_destroy_raises(self):
+        class MySink(bt2.UserSinkComponent):
+            def _destroy(self):
+                raise RuntimeError('oh oh')
+                nonlocal destroyed
+                destroyed = True
+
+            def _consume(self):
+                pass
+
+        destroyed = False
+        sink = MySink()
+        del sink
+        self.assertFalse(destroyed)
+
+    def test_comp_attr_name(self):
+        class MySink(bt2.UserSinkComponent):
+            def _consume(self):
+                pass
+
+        sink = MySink(name='salut')
+        self.assertEqual(sink.name, 'salut')
+
+    def test_comp_attr_no_name(self):
+        class MySink(bt2.UserSinkComponent):
+            def _consume(self):
+                pass
+
+        sink = MySink()
+        self.assertIsNone(sink.name)
+
+    def test_comp_attr_class(self):
+        class MySink(bt2.UserSinkComponent):
+            def _consume(self):
+                pass
+
+        sink = MySink()
+        self.assertEqual(sink.component_class.name, 'MySink')
+
+
+class UserSinkCompClassTestCase(unittest.TestCase):
+    def test_missing_consume(self):
+        with self.assertRaises(bt2.IncompleteUserClassError):
+            class MySink(bt2.UserSinkComponent):
+                pass
+
+    def test_set_min_input(self):
+        class MySink(bt2.UserSinkComponent):
+            def __init__(self):
+                self._maximum_input_notification_iterator_count = 10
+                self._minimum_input_notification_iterator_count = 5
+
+            def _consume(self):
+                pass
+
+        sink = MySink()
+
+    def test_set_max_input(self):
+        class MySink(bt2.UserSinkComponent):
+            def __init__(self):
+                self._maximum_input_notification_iterator_count = 5
+
+            def _consume(self):
+                pass
+
+        sink = MySink()
+
+    def test_consume(self):
+        class MySink(bt2.UserSinkComponent):
+            def __init__(self):
+                pass
+
+            def _consume(self):
+                nonlocal consumed
+                consumed = True
+
+        sink = MySink()
+        consumed = False
+        source = _create_source()
+        notif_iter = source.create_notification_iterator()
+        sink.add_notification_iterator(notif_iter)
+        sink.consume()
+        self.assertTrue(consumed)
+
+    def test_consume_raises(self):
+        class MySink(bt2.UserSinkComponent):
+            def __init__(self):
+                pass
+
+            def _consume(self):
+                raise RuntimeError('hello')
+
+        sink = MySink()
+        source = _create_source()
+        notif_iter = source.create_notification_iterator()
+        sink.add_notification_iterator(notif_iter)
+
+        with self.assertRaises(bt2.Error):
+            sink.consume()
+
+    def test_add_notification_iterator(self):
+        class MySink(bt2.UserSinkComponent):
+            def __init__(self):
+                pass
+
+            def _consume(self):
+                pass
+
+            def _add_notification_iterator(self, notif_iter):
+                nonlocal added
+                added = True
+
+        sink = MySink()
+        added = False
+        source = _create_source()
+        notif_iter = source.create_notification_iterator()
+        sink.add_notification_iterator(notif_iter)
+        self.assertTrue(added)
+
+    def test_input_notif_iterators(self):
+        class MySink(bt2.UserSinkComponent):
+            def __init__(self):
+                self._maximum_input_notification_iterator_count = 5
+
+            def _consume(self):
+                nonlocal count
+                count = 0
+
+                for notif_iter in self._input_notification_iterators:
+                    count += 1
+
+        sink = MySink()
+        count = 0
+        source = _create_source()
+        notif_iter1 = source.create_notification_iterator()
+        notif_iter2 = source.create_notification_iterator()
+        sink.add_notification_iterator(notif_iter1)
+        sink.add_notification_iterator(notif_iter2)
+        sink.consume()
+        self.assertEqual(count, 2)
+
+
+class UserSourceCompClassTestCase(unittest.TestCase):
+    def test_missing_notif_iterator_class(self):
+        with self.assertRaises(bt2.IncompleteUserClassError):
+            class MySource(bt2.UserSourceComponent):
+                pass
+
+    def test_invalid_notif_iterator_class(self):
+        class Lel:
+            pass
+
+        with self.assertRaises(bt2.IncompleteUserClassError):
+            class MySource(bt2.UserSourceComponent, notification_iterator_class=Lel):
+                pass
+
+    def test_notif_iterator_class_missing_get(self):
+        class MyIter(bt2.UserNotificationIterator):
+            def _next(self):
+                pass
+
+        with self.assertRaises(bt2.IncompleteUserClassError):
+            class MySource(bt2.UserSourceComponent, notification_iterator_class=MyIter):
+                pass
+
+    def test_notif_iterator_class_missing_next(self):
+        class MyIter(bt2.UserNotificationIterator):
+            def _get(self):
+                pass
+
+        with self.assertRaises(bt2.IncompleteUserClassError):
+            class MySource(bt2.UserSourceComponent, notification_iterator_class=MyIter):
+                pass
+
+    def test_create_notification_iterator(self):
+        class MyIter(bt2.UserNotificationIterator):
+            def __init__(self):
+                nonlocal created
+                created = True
+
+            def _next(self):
+                pass
+
+            def _get(self):
+                pass
+
+        class MySource(bt2.UserSourceComponent, notification_iterator_class=MyIter):
+            pass
+
+        created = False
+        source = MySource()
+        notif_iter = source.create_notification_iterator()
+        self.assertTrue(created)
+
+
+class UserSourceCompClassTestCase(unittest.TestCase):
+    def test_missing_notif_iterator_class(self):
+        with self.assertRaises(bt2.IncompleteUserClassError):
+            class MyFilter(bt2.UserFilterComponent):
+                pass
+
+    def test_invalid_notif_iterator_class(self):
+        class Lel:
+            pass
+
+        with self.assertRaises(bt2.IncompleteUserClassError):
+            class MyFilter(bt2.UserFilterComponent, notification_iterator_class=Lel):
+                pass
+
+    def test_notif_iterator_class_missing_get(self):
+        class MyIter(bt2.UserNotificationIterator):
+            def _next(self):
+                pass
+
+        with self.assertRaises(bt2.IncompleteUserClassError):
+            class MyFilter(bt2.UserFilterComponent, notification_iterator_class=MyIter):
+                pass
+
+    def test_notif_iterator_class_missing_next(self):
+        class MyIter(bt2.UserNotificationIterator):
+            def _get(self):
+                pass
+
+        with self.assertRaises(bt2.IncompleteUserClassError):
+            class MyFilter(bt2.UserFilterComponent, notification_iterator_class=MyIter):
+                pass
+
+    def test_create_notification_iterator(self):
+        class MyIter(bt2.UserNotificationIterator):
+            def __init__(self):
+                nonlocal created
+                created = True
+
+            def _next(self):
+                pass
+
+            def _get(self):
+                pass
+
+        class MyFilter(bt2.UserFilterComponent, notification_iterator_class=MyIter):
+            pass
+
+        created = False
+        filter = MyFilter()
+        notif_iter = filter.create_notification_iterator()
+        self.assertTrue(created)
+
+    def test_set_min_input(self):
+        class MyIter(bt2.UserNotificationIterator):
+            def _next(self):
+                pass
+
+            def _get(self):
+                pass
+
+        class MyFilter(bt2.UserFilterComponent, notification_iterator_class=MyIter):
+            def __init__(self):
+                self._maximum_input_notification_iterator_count = 10
+                self._minimum_input_notification_iterator_count = 5
+
+        filter = MyFilter()
+
+    def test_set_max_input(self):
+        class MyIter(bt2.UserNotificationIterator):
+            def _next(self):
+                pass
+
+            def _get(self):
+                pass
+
+        class MyFilter(bt2.UserFilterComponent, notification_iterator_class=MyIter):
+            def __init__(self):
+                self._maximum_input_notification_iterator_count = 5
+
+        filter = MyFilter()
+
+    def test_add_notification_iterator(self):
+        class MyIter(bt2.UserNotificationIterator):
+            def _next(self):
+                pass
+
+            def _get(self):
+                pass
+
+        class MyFilter(bt2.UserFilterComponent, notification_iterator_class=MyIter):
+            def __init__(self):
+                pass
+
+            def _add_notification_iterator(self, notif_iter):
+                nonlocal added
+                added = True
+
+        filter = MyFilter()
+        added = False
+        source = _create_source()
+        notif_iter = source.create_notification_iterator()
+        filter.add_notification_iterator(notif_iter)
+        self.assertTrue(added)
+
+    def test_input_notif_iterators(self):
+        class MyIter(bt2.UserNotificationIterator):
+            def _next(self):
+                pass
+
+            def _get(self):
+                pass
+
+        class MyFilter(bt2.UserFilterComponent, notification_iterator_class=MyIter):
+            def __init__(self):
+                self._maximum_input_notification_iterator_count = 5
+
+            def _test(self):
+                nonlocal count
+                count = 0
+
+                for notif_iter in self._input_notification_iterators:
+                    count += 1
+
+        filter = MyFilter()
+        count = 0
+        source = _create_source()
+        notif_iter1 = source.create_notification_iterator()
+        notif_iter2 = source.create_notification_iterator()
+        filter.add_notification_iterator(notif_iter1)
+        filter.add_notification_iterator(notif_iter2)
+        filter._test()
+        self.assertEqual(count, 2)
+
+
+class UserNotificationIteratorTestCase(unittest.TestCase):
+    def test_init(self):
+        class MyIter(bt2.UserNotificationIterator):
+            def __init__(self):
+                nonlocal inited
+                inited = True
+
+            def _next(self):
+                pass
+
+            def _get(self):
+                pass
+
+        class MySource(bt2.UserSourceComponent, notification_iterator_class=MyIter):
+            pass
+
+        inited = False
+        source = MySource()
+        notif_iter = source.create_notification_iterator()
+        self.assertTrue(inited)
+
+    def test_destroy(self):
+        class MyIter(bt2.UserNotificationIterator):
+            def _next(self):
+                pass
+
+            def _get(self):
+                pass
+
+            def _destroy(self):
+                nonlocal destroyed
+                destroyed = True
+
+        class MySource(bt2.UserSourceComponent, notification_iterator_class=MyIter):
+            pass
+
+        source = MySource()
+        notif_iter = source.create_notification_iterator()
+        destroyed = False
+        del notif_iter
+        self.assertTrue(destroyed)
+
+    def test_attr_component(self):
+        class MyIter(bt2.UserNotificationIterator):
+            def __init__(self):
+                nonlocal source, is_same
+                is_same = self.component is source
+
+            def _next(self):
+                pass
+
+            def _get(self):
+                pass
+
+        class MySource(bt2.UserSourceComponent, notification_iterator_class=MyIter):
+            pass
+
+        source = MySource()
+        is_same = False
+        notif_iter = source.create_notification_iterator()
+        self.assertTrue(is_same)
+
+    def test_next_raises_stop(self):
+        class MyIter(bt2.UserNotificationIterator):
+            def _next(self):
+                raise bt2.Stop
+
+            def _get(self):
+                pass
+
+        class MySource(bt2.UserSourceComponent, notification_iterator_class=MyIter):
+            pass
+
+        source = MySource()
+        is_same = False
+        notif_iter = source.create_notification_iterator()
+
+        with self.assertRaises(bt2.Stop):
+            notif_iter.next()
+
+    def test_next_raises_unsupported_feature(self):
+        class MyIter(bt2.UserNotificationIterator):
+            def _next(self):
+                raise bt2.UnsupportedFeature
+
+            def _get(self):
+                pass
+
+        class MySource(bt2.UserSourceComponent, notification_iterator_class=MyIter):
+            pass
+
+        source = MySource()
+        is_same = False
+        notif_iter = source.create_notification_iterator()
+
+        with self.assertRaises(bt2.UnsupportedFeature):
+            notif_iter.next()
+
+    def test_next_raises_unknown(self):
+        class MyIter(bt2.UserNotificationIterator):
+            def _next(self):
+                raise TypeError('lel')
+
+            def _get(self):
+                pass
+
+        class MySource(bt2.UserSourceComponent, notification_iterator_class=MyIter):
+            pass
+
+        source = MySource()
+        is_same = False
+        notif_iter = source.create_notification_iterator()
+
+        with self.assertRaises(bt2.Error):
+            notif_iter.next()
+
+    def test_iteration(self):
+        source = _create_source()
+        notif_iter = source.create_notification_iterator()
+        pkt_addr = None
+
+        for index, notif in enumerate(notif_iter):
+            if index == 0:
+                self.assertIsInstance(notif, bt2.BeginningOfPacketNotification)
+                self.assertEqual(notif.packet.header_field['magic'], 0xc1fc1fc1)
+                self.assertEqual(notif.packet.context_field['something'], 194)
+                pkt_addr = notif.packet.addr
+            elif index < 5:
+                self.assertIsInstance(notif, bt2.TraceEventNotification)
+                self.assertEqual(notif.event.header_field['ts'], 19487)
+                self.assertEqual(notif.event.payload_field['mosquito'], 'at {}'.format(index))
+            elif index == 5:
+                self.assertIsInstance(notif, bt2.EndOfPacketNotification)
+                self.assertEqual(notif.packet.header_field['magic'], 0xc1fc1fc1)
+                self.assertEqual(notif.packet.context_field['something'], 194)
+                self.assertEqual(notif.packet.addr, pkt_addr)
+            elif index == 6:
+                self.assertIsInstance(notif, bt2.EndOfStreamNotification)
+                self.assertEqual(notif.stream.name, 'abcdef')
+            else:
+                raise RuntimeError('FAIL')
+
+
+class GenNotificationIteratorTestCase(unittest.TestCase):
+    def test_attr_component(self):
+        source = _create_source()
+        notif_iter = source.create_notification_iterator()
+        self.assertIsInstance(notif_iter, bt2.notification_iterator._GenericNotificationIterator)
+        self.assertEqual(notif_iter.component.addr, source.addr)
diff --git a/tests/bindings/python/bt2/test_ctf_writer_clock.py b/tests/bindings/python/bt2/test_ctf_writer_clock.py
new file mode 100644 (file)
index 0000000..1479146
--- /dev/null
@@ -0,0 +1,197 @@
+import unittest
+import uuid
+import copy
+import bt2
+
+
+class CtfWriterClockTestCase(unittest.TestCase):
+    def setUp(self):
+        self._clock = bt2.CtfWriterClock('salut')
+
+    def test_create_default(self):
+        self.assertEqual(self._clock.name, 'salut')
+
+    def test_create_invalid_no_name(self):
+        with self.assertRaises(TypeError):
+            bt2.CtfWriterClock()
+
+    def test_create_full(self):
+        my_uuid = uuid.uuid1()
+        cc = bt2.CtfWriterClock(name='name', description='some description',
+                                frequency=1001, precision=176,
+                                offset=bt2.ClockClassOffset(45, 3003),
+                                is_absolute=True, uuid=my_uuid)
+        self.assertEqual(cc.name, 'name')
+        self.assertEqual(cc.description, 'some description')
+        self.assertEqual(cc.frequency, 1001)
+        self.assertEqual(cc.precision, 176)
+        self.assertEqual(cc.offset, bt2.ClockClassOffset(45, 3003))
+        self.assertEqual(cc.is_absolute, True)
+        self.assertEqual(cc.uuid, copy.deepcopy(my_uuid))
+
+    def test_assign_description(self):
+        self._clock.description = 'hi people'
+        self.assertEqual(self._clock.description, 'hi people')
+
+    def test_assign_invalid_description(self):
+        with self.assertRaises(TypeError):
+            self._clock.description = 23
+
+    def test_assign_frequency(self):
+        self._clock.frequency = 987654321
+        self.assertEqual(self._clock.frequency, 987654321)
+
+    def test_assign_invalid_frequency(self):
+        with self.assertRaises(TypeError):
+            self._clock.frequency = 'lel'
+
+    def test_assign_precision(self):
+        self._clock.precision = 12
+        self.assertEqual(self._clock.precision, 12)
+
+    def test_assign_invalid_precision(self):
+        with self.assertRaises(TypeError):
+            self._clock.precision = 'lel'
+
+    def test_assign_offset(self):
+        self._clock.offset = bt2.ClockClassOffset(12, 56)
+        self.assertEqual(self._clock.offset, bt2.ClockClassOffset(12, 56))
+
+    def test_assign_invalid_offset(self):
+        with self.assertRaises(TypeError):
+            self._clock.offset = object()
+
+    def test_assign_absolute(self):
+        self._clock.is_absolute = True
+        self.assertTrue(self._clock.is_absolute)
+
+    def test_assign_invalid_absolute(self):
+        with self.assertRaises(TypeError):
+            self._clock.is_absolute = 23
+
+    def test_assign_uuid(self):
+        the_uuid = uuid.uuid1()
+        self._clock.uuid = the_uuid
+        self.assertEqual(self._clock.uuid, the_uuid)
+
+    def test_assign_invalid_uuid(self):
+        with self.assertRaises(TypeError):
+            self._clock.uuid = object()
+
+    def test_assign_time(self):
+        self._clock.time = 41232
+
+    def test_assign_invalid_time(self):
+        with self.assertRaises(TypeError):
+            self._clock.time = object()
+
+    def _test_copy(self, cpy):
+        self.assertIsNot(cpy, self._clock)
+        self.assertNotEqual(cpy.addr, self._clock.addr)
+        self.assertEqual(cpy, self._clock)
+
+    def test_copy(self):
+        cpy = copy.copy(self._clock)
+        self._test_copy(cpy)
+
+    def test_deepcopy(self):
+        cpy = copy.deepcopy(self._clock)
+        self._test_copy(cpy)
+
+    def test_eq(self):
+        my_uuid = uuid.uuid1()
+        cc1 = bt2.CtfWriterClock(name='name', description='some description',
+                                 frequency=1001, precision=176,
+                                 offset=bt2.ClockClassOffset(45, 3003),
+                                 is_absolute=True, uuid=my_uuid)
+        cc2 = bt2.CtfWriterClock(name='name', description='some description',
+                                 frequency=1001, precision=176,
+                                 offset=bt2.ClockClassOffset(45, 3003),
+                                 is_absolute=True, uuid=my_uuid)
+        self.assertEqual(cc1, cc2)
+
+    def test_ne_name(self):
+        my_uuid = uuid.uuid1()
+        cc1 = bt2.CtfWriterClock(name='mane', description='some description',
+                                 frequency=1001, precision=176,
+                                 offset=bt2.ClockClassOffset(45, 3003),
+                                 is_absolute=True, uuid=my_uuid)
+        cc2 = bt2.CtfWriterClock(name='name', description='some description',
+                                 frequency=1001, precision=176,
+                                 offset=bt2.ClockClassOffset(45, 3003),
+                                 is_absolute=True, uuid=my_uuid)
+        self.assertNotEqual(cc1, cc2)
+
+    def test_ne_description(self):
+        my_uuid = uuid.uuid1()
+        cc1 = bt2.CtfWriterClock(name='name', description='some descripti2',
+                                 frequency=1001, precision=176,
+                                 offset=bt2.ClockClassOffset(45, 3003),
+                                 is_absolute=True, uuid=my_uuid)
+        cc2 = bt2.CtfWriterClock(name='name', description='some description',
+                                 frequency=1001, precision=176,
+                                 offset=bt2.ClockClassOffset(45, 3003),
+                                 is_absolute=True, uuid=my_uuid)
+        self.assertNotEqual(cc1, cc2)
+
+    def test_ne_frequency(self):
+        my_uuid = uuid.uuid1()
+        cc1 = bt2.CtfWriterClock(name='name', description='some description',
+                                 frequency=1003, precision=176,
+                                 offset=bt2.ClockClassOffset(45, 3003),
+                                 is_absolute=True, uuid=my_uuid)
+        cc2 = bt2.CtfWriterClock(name='name', description='some description',
+                                 frequency=1001, precision=176,
+                                 offset=bt2.ClockClassOffset(45, 3003),
+                                 is_absolute=True, uuid=my_uuid)
+        self.assertNotEqual(cc1, cc2)
+
+    def test_ne_precision(self):
+        my_uuid = uuid.uuid1()
+        cc1 = bt2.CtfWriterClock(name='name', description='some description',
+                                 frequency=1001, precision=171,
+                                 offset=bt2.ClockClassOffset(45, 3003),
+                                 is_absolute=True, uuid=my_uuid)
+        cc2 = bt2.CtfWriterClock(name='name', description='some description',
+                                 frequency=1001, precision=176,
+                                 offset=bt2.ClockClassOffset(45, 3003),
+                                 is_absolute=True, uuid=my_uuid)
+        self.assertNotEqual(cc1, cc2)
+
+    def test_ne_offset(self):
+        my_uuid = uuid.uuid1()
+        cc1 = bt2.CtfWriterClock(name='name', description='some description',
+                                 frequency=1001, precision=176,
+                                 offset=bt2.ClockClassOffset(45, 3001),
+                                 is_absolute=True, uuid=my_uuid)
+        cc2 = bt2.CtfWriterClock(name='name', description='some description',
+                                 frequency=1001, precision=176,
+                                 offset=bt2.ClockClassOffset(45, 3003),
+                                 is_absolute=True, uuid=my_uuid)
+        self.assertNotEqual(cc1, cc2)
+
+    def test_ne_absolute(self):
+        my_uuid = uuid.uuid1()
+        cc1 = bt2.CtfWriterClock(name='name', description='some description',
+                                 frequency=1001, precision=176,
+                                 offset=bt2.ClockClassOffset(45, 3003),
+                                 is_absolute=True, uuid=my_uuid)
+        cc2 = bt2.CtfWriterClock(name='name', description='some description',
+                                 frequency=1001, precision=176,
+                                 offset=bt2.ClockClassOffset(45, 3003),
+                                 is_absolute=False, uuid=my_uuid)
+        self.assertNotEqual(cc1, cc2)
+
+    def test_ne_uuid(self):
+        cc1 = bt2.CtfWriterClock(name='name', description='some description',
+                                 frequency=1001, precision=176,
+                                 offset=bt2.ClockClassOffset(45, 3003),
+                                 is_absolute=True, uuid=uuid.uuid1())
+        cc2 = bt2.CtfWriterClock(name='name', description='some description',
+                                 frequency=1001, precision=176,
+                                 offset=bt2.ClockClassOffset(45, 3003),
+                                 is_absolute=True, uuid=uuid.uuid1())
+        self.assertNotEqual(cc1, cc2)
+
+    def test_eq_invalid(self):
+        self.assertFalse(self._clock == 23)
diff --git a/tests/bindings/python/bt2/test_event.py b/tests/bindings/python/bt2/test_event.py
new file mode 100644 (file)
index 0000000..6e7bdc6
--- /dev/null
@@ -0,0 +1,370 @@
+from collections import OrderedDict
+from bt2 import values
+import unittest
+import copy
+import bt2
+
+
+class EventTestCase(unittest.TestCase):
+    def setUp(self):
+        self._ec = self._create_ec()
+
+    def _create_ec(self, with_eh=True, with_sec=True, with_ec=True, with_ep=True):
+        # event header
+        if with_eh:
+            eh = bt2.StructureFieldType()
+            eh += OrderedDict((
+                ('id', bt2.IntegerFieldType(8)),
+                ('ts', bt2.IntegerFieldType(32)),
+            ))
+        else:
+            eh = None
+
+        # stream event context
+        if with_sec:
+            sec = bt2.StructureFieldType()
+            sec += OrderedDict((
+                ('cpu_id', bt2.IntegerFieldType(8)),
+                ('stuff', bt2.FloatingPointNumberFieldType()),
+            ))
+        else:
+            sec = None
+
+        # packet context
+        pc = bt2.StructureFieldType()
+        pc += OrderedDict((
+            ('something', bt2.IntegerFieldType(8)),
+            ('something_else', bt2.FloatingPointNumberFieldType()),
+        ))
+
+        # stream class
+        sc = bt2.StreamClass()
+        sc.packet_context_field_type = pc
+        sc.event_header_field_type = eh
+        sc.event_context_field_type = sec
+
+        # event context
+        if with_ec:
+            ec = bt2.StructureFieldType()
+            ec += OrderedDict((
+                ('ant', bt2.IntegerFieldType(16, is_signed=True)),
+                ('msg', bt2.StringFieldType()),
+            ))
+        else:
+            ec = None
+
+        # event payload
+        if with_ep:
+            ep = bt2.StructureFieldType()
+            ep += OrderedDict((
+                ('giraffe', bt2.IntegerFieldType(32)),
+                ('gnu', bt2.IntegerFieldType(8)),
+                ('mosquito', bt2.IntegerFieldType(8)),
+            ))
+        else:
+            ep = None
+
+        # event class
+        event_class = bt2.EventClass('ec')
+        event_class.context_field_type = ec
+        event_class.payload_field_type = ep
+        sc.add_event_class(event_class)
+        return event_class
+
+    def test_attr_event_class(self):
+        ev = self._ec()
+        self.assertEqual(ev.event_class.addr, self._ec.addr)
+
+    def test_attr_name(self):
+        ev = self._ec()
+        self.assertEqual(ev.name, self._ec.name)
+
+    def test_attr_id(self):
+        ev = self._ec()
+        self.assertEqual(ev.id, self._ec.id)
+
+    def test_get_event_header_field(self):
+        ev = self._ec()
+        ev.header_field['id'] = 23
+        ev.header_field['ts'] = 1234
+        self.assertEqual(ev.header_field['id'], 23)
+        self.assertEqual(ev.header_field['ts'], 1234)
+
+    def test_set_event_header_field(self):
+        eh = self._ec.stream_class.event_header_field_type()
+        eh['id'] = 17
+        eh['ts'] = 188
+        ev = self._ec()
+        ev.header_field = eh
+        self.assertEqual(ev.header_field['id'], 17)
+        self.assertEqual(ev.header_field['ts'], 188)
+
+    def test_get_stream_event_context_field(self):
+        ev = self._ec()
+        ev.stream_event_context_field['cpu_id'] = 1
+        ev.stream_event_context_field['stuff'] = 13.194
+        self.assertEqual(ev.stream_event_context_field['cpu_id'], 1)
+        self.assertEqual(ev.stream_event_context_field['stuff'], 13.194)
+
+    def test_set_stream_event_context_field(self):
+        sec = self._ec.stream_class.event_context_field_type()
+        sec['cpu_id'] = 2
+        sec['stuff'] = 19.19
+        ev = self._ec()
+        ev.stream_event_context_field = sec
+        self.assertEqual(ev.stream_event_context_field['cpu_id'], 2)
+        self.assertEqual(ev.stream_event_context_field['stuff'], 19.19)
+
+    def test_no_stream_event_context(self):
+        ec = self._create_ec(with_sec=False)
+        ev = ec()
+        self.assertIsNone(ev.stream_event_context_field)
+
+    def test_get_event_context_field(self):
+        ev = self._ec()
+        ev.context_field['ant'] = -1
+        ev.context_field['msg'] = 'hellooo'
+        self.assertEqual(ev.context_field['ant'], -1)
+        self.assertEqual(ev.context_field['msg'], 'hellooo')
+
+    def test_set_event_context_field(self):
+        ec = self._ec.context_field_type()
+        ec['ant'] = 2
+        ec['msg'] = 'hi there'
+        ev = self._ec()
+        ev.context_field = ec
+        self.assertEqual(ev.context_field['ant'], 2)
+        self.assertEqual(ev.context_field['msg'], 'hi there')
+
+    def test_no_event_context(self):
+        ec = self._create_ec(with_ec=False)
+        ev = ec()
+        self.assertIsNone(ev.context_field)
+
+    def test_get_event_payload_field(self):
+        ev = self._ec()
+        ev.payload_field['giraffe'] = 1
+        ev.payload_field['gnu'] = 23
+        ev.payload_field['mosquito'] = 42
+        self.assertEqual(ev.payload_field['giraffe'], 1)
+        self.assertEqual(ev.payload_field['gnu'], 23)
+        self.assertEqual(ev.payload_field['mosquito'], 42)
+
+    def test_set_event_payload_field(self):
+        ep = self._ec.payload_field_type()
+        ep['giraffe'] = 2
+        ep['gnu'] = 124
+        ep['mosquito'] = 17
+        ev = self._ec()
+        ev.payload_field = ep
+        self.assertEqual(ev.payload_field['giraffe'], 2)
+        self.assertEqual(ev.payload_field['gnu'], 124)
+        self.assertEqual(ev.payload_field['mosquito'], 17)
+
+    def test_clock_value(self):
+        tc = bt2.Trace()
+        tc.add_stream_class(self._ec.stream_class)
+        cc = bt2.ClockClass('hi')
+        tc.add_clock_class(cc)
+        ev = self._ec()
+        ev.set_clock_value(cc.create_clock_value(177))
+        self.assertEqual(ev.get_clock_value(cc).cycles, 177)
+
+    def test_no_clock_value(self):
+        tc = bt2.Trace()
+        tc.add_stream_class(self._ec.stream_class)
+        cc = bt2.ClockClass('hi')
+        tc.add_clock_class(cc)
+        ev = self._ec()
+        self.assertIsNone(ev.get_clock_value(cc))
+
+    def test_no_packet(self):
+        ev = self._ec()
+        self.assertIsNone(ev.packet)
+
+    def test_packet(self):
+        tc = bt2.Trace()
+        tc.packet_header_field_type = bt2.StructureFieldType()
+        tc.packet_header_field_type.append_field('magic', bt2.IntegerFieldType(32))
+        tc.packet_header_field_type.append_field('stream_id', bt2.IntegerFieldType(16))
+        tc.add_stream_class(self._ec.stream_class)
+        ev = self._ec()
+        self._fill_ev(ev)
+        stream = self._ec.stream_class()
+        packet = stream.create_packet()
+        packet.header_field['magic'] = 0xc1fc1fc1
+        packet.header_field['stream_id'] = 0
+        packet.context_field['something'] = 154
+        packet.context_field['something_else'] = 17.2
+        ev.packet = packet
+        self.assertEqual(ev.packet.addr, packet.addr)
+
+    def test_no_stream(self):
+        ev = self._ec()
+        self.assertIsNone(ev.stream)
+
+    def test_stream(self):
+        tc = bt2.Trace()
+        tc.packet_header_field_type = bt2.StructureFieldType()
+        tc.packet_header_field_type.append_field('magic', bt2.IntegerFieldType(32))
+        tc.packet_header_field_type.append_field('stream_id', bt2.IntegerFieldType(16))
+        tc.add_stream_class(self._ec.stream_class)
+        ev = self._ec()
+        self._fill_ev(ev)
+        stream = self._ec.stream_class()
+        packet = stream.create_packet()
+        packet.header_field['magic'] = 0xc1fc1fc1
+        packet.header_field['stream_id'] = 0
+        packet.context_field['something'] = 154
+        packet.context_field['something_else'] = 17.2
+        ev.packet = packet
+        self.assertEqual(ev.stream.addr, stream.addr)
+
+    def _fill_ev(self, ev):
+        ev.header_field['id'] = 23
+        ev.header_field['ts'] = 1234
+        ev.stream_event_context_field['cpu_id'] = 1
+        ev.stream_event_context_field['stuff'] = 13.194
+        ev.context_field['ant'] = -1
+        ev.context_field['msg'] = 'hellooo'
+        ev.payload_field['giraffe'] = 1
+        ev.payload_field['gnu'] = 23
+        ev.payload_field['mosquito'] = 42
+
+    def _get_full_ev(self):
+        tc = bt2.Trace()
+        tc.add_stream_class(self._ec.stream_class)
+        cc = bt2.ClockClass('hi')
+        tc.add_clock_class(cc)
+        ev = self._ec()
+        self._fill_ev(ev)
+        ev.clock_value(cc, 234)
+        return ev
+
+    def test_getitem(self):
+        tc = bt2.Trace()
+        tc.packet_header_field_type = bt2.StructureFieldType()
+        tc.packet_header_field_type.append_field('magic', bt2.IntegerFieldType(32))
+        tc.packet_header_field_type.append_field('stream_id', bt2.IntegerFieldType(16))
+        tc.add_stream_class(self._ec.stream_class)
+        ev = self._ec()
+        self._fill_ev(ev)
+        stream = self._ec.stream_class()
+        packet = stream.create_packet()
+        packet.header_field['magic'] = 0xc1fc1fc1
+        packet.header_field['stream_id'] = 0
+        packet.context_field['something'] = 154
+        packet.context_field['something_else'] = 17.2
+
+        with self.assertRaises(KeyError):
+            ev['magic']
+
+        ev.packet = packet
+        self.assertEqual(ev['mosquito'], 42)
+        self.assertEqual(ev['gnu'], 23)
+        self.assertEqual(ev['giraffe'], 1)
+        self.assertEqual(ev['msg'], 'hellooo')
+        self.assertEqual(ev['ant'], -1)
+        self.assertEqual(ev['stuff'], 13.194)
+        self.assertEqual(ev['cpu_id'], 1)
+        self.assertEqual(ev['ts'], 1234)
+        self.assertEqual(ev['id'], 23)
+        self.assertEqual(ev['something_else'], 17.2)
+        self.assertEqual(ev['something'], 154)
+        self.assertEqual(ev['stream_id'], 0)
+        self.assertEqual(ev['magic'], 0xc1fc1fc1)
+
+        with self.assertRaises(KeyError):
+            ev['yes']
+
+    def test_eq(self):
+        tc = bt2.Trace()
+        tc.add_stream_class(self._ec.stream_class)
+        cc = bt2.ClockClass('hi')
+        tc.add_clock_class(cc)
+        ev1 = self._ec()
+        self._fill_ev(ev1)
+        ev1.set_clock_value(cc.create_clock_value(234))
+        ev2 = self._ec()
+        self._fill_ev(ev2)
+        ev2.set_clock_value(cc.create_clock_value(234))
+        self.assertEqual(ev1, ev2)
+
+    def test_ne_header_field(self):
+        tc = bt2.Trace()
+        tc.add_stream_class(self._ec.stream_class)
+        cc = bt2.ClockClass('hi')
+        tc.add_clock_class(cc)
+        ev1 = self._ec()
+        self._fill_ev(ev1)
+        ev1.header_field['id'] = 19
+        ev1.set_clock_value(cc.create_clock_value(234))
+        ev2 = self._ec()
+        self._fill_ev(ev2)
+        ev2.set_clock_value(cc.create_clock_value(234))
+        self.assertNotEqual(ev1, ev2)
+
+    def test_ne_stream_event_context_field(self):
+        tc = bt2.Trace()
+        tc.add_stream_class(self._ec.stream_class)
+        cc = bt2.ClockClass('hi')
+        tc.add_clock_class(cc)
+        ev1 = self._ec()
+        self._fill_ev(ev1)
+        ev1.stream_event_context_field['cpu_id'] = 3
+        ev1.set_clock_value(cc.create_clock_value(234))
+        ev2 = self._ec()
+        self._fill_ev(ev2)
+        ev2.set_clock_value(cc.create_clock_value(234))
+        self.assertNotEqual(ev1, ev2)
+
+    def test_ne_context_field(self):
+        tc = bt2.Trace()
+        tc.add_stream_class(self._ec.stream_class)
+        cc = bt2.ClockClass('hi')
+        tc.add_clock_class(cc)
+        ev1 = self._ec()
+        self._fill_ev(ev1)
+        ev1.context_field['ant'] = -3
+        ev1.set_clock_value(cc.create_clock_value(234))
+        ev2 = self._ec()
+        self._fill_ev(ev2)
+        ev2.set_clock_value(cc.create_clock_value(234))
+        self.assertNotEqual(ev1, ev2)
+
+    def test_ne_payload_field(self):
+        tc = bt2.Trace()
+        tc.add_stream_class(self._ec.stream_class)
+        cc = bt2.ClockClass('hi')
+        tc.add_clock_class(cc)
+        ev1 = self._ec()
+        self._fill_ev(ev1)
+        ev1.payload_field['mosquito'] = 98
+        ev1.set_clock_value(cc.create_clock_value(234))
+        ev2 = self._ec()
+        self._fill_ev(ev2)
+        ev2.set_clock_value(cc.create_clock_value(234))
+        self.assertNotEqual(ev1, ev2)
+
+    def test_eq_invalid(self):
+        ev = self._ec()
+        self.assertFalse(ev == 23)
+
+    def _test_copy(self, func):
+        tc = bt2.Trace()
+        tc.add_stream_class(self._ec.stream_class)
+        cc = bt2.ClockClass('hi')
+        tc.add_clock_class(cc)
+        ev = self._ec()
+        self._fill_ev(ev)
+        ev.set_clock_value(cc.create_clock_value(234))
+        cpy = func(ev)
+        self.assertIsNot(ev, cpy)
+        self.assertNotEqual(ev.addr, cpy.addr)
+        self.assertEqual(ev, cpy)
+
+    def test_copy(self):
+        self._test_copy(copy.copy)
+
+    def test_deepcopy(self):
+        self._test_copy(copy.deepcopy)
diff --git a/tests/bindings/python/bt2/test_event_class.py b/tests/bindings/python/bt2/test_event_class.py
new file mode 100644 (file)
index 0000000..8d21b84
--- /dev/null
@@ -0,0 +1,184 @@
+from bt2 import values
+import unittest
+import copy
+import bt2
+
+
+class EventClassTestCase(unittest.TestCase):
+    def setUp(self):
+        self._context_ft = bt2.StructureFieldType()
+        self._context_ft.append_field('allo', bt2.StringFieldType())
+        self._context_ft.append_field('zola', bt2.IntegerFieldType(18))
+        self._payload_ft = bt2.StructureFieldType()
+        self._payload_ft.append_field('zoom', bt2.StringFieldType())
+        self._ec = bt2.EventClass('my_event')
+        self._ec.id = 18
+        self._ec.context_field_type = self._context_ft
+        self._ec.payload_field_type = self._payload_ft
+
+    def test_create(self):
+        self.assertEqual(self._ec.name, 'my_event')
+        self.assertEqual(self._ec.id, 18)
+        self.assertEqual(self._ec.context_field_type, self._context_ft)
+        self.assertEqual(self._ec.payload_field_type, self._payload_ft)
+
+    def test_create_invalid_no_name(self):
+        with self.assertRaises(TypeError):
+            bt2.EventClass()
+
+    def test_create_full(self):
+        ec = bt2.EventClass(name='name', id=23,
+                            context_field_type=self._context_ft,
+                            payload_field_type=self._payload_ft,
+                            attributes={'model.emf.uri': 'my URI'})
+        self.assertEqual(ec.name, 'name')
+        self.assertEqual(ec.id, 23)
+        self.assertEqual(ec.context_field_type, self._context_ft)
+        self.assertEqual(ec.payload_field_type, self._payload_ft)
+        self.assertEqual(ec.attributes['model.emf.uri'], 'my URI')
+
+    def test_assign_id(self):
+        self._ec.id = 1717
+        self.assertEqual(self._ec.id, 1717)
+
+    def test_assign_invalid_id(self):
+        with self.assertRaises(TypeError):
+            self._ec.id = 'lel'
+
+    def test_assign_context_field_type(self):
+        self._ec.context_field_type = self._payload_ft
+        self.assertEqual(self._ec.context_field_type, self._payload_ft)
+
+    def test_assign_no_context_field_type(self):
+        self._ec.context_field_type = None
+        self.assertIsNone(self._ec.context_field_type)
+
+    def test_assign_invalid_context_field_type(self):
+        with self.assertRaises(TypeError):
+            self._ec.context_field_type = 'lel'
+
+    def test_assign_payload_field_type(self):
+        self._ec.payload_field_type = self._payload_ft
+        self.assertEqual(self._ec.payload_field_type, self._payload_ft)
+
+    def test_assign_no_payload_field_type(self):
+        self._ec.payload_field_type = None
+        self.assertIsNone(self._ec.payload_field_type)
+
+    def test_assign_invalid_payload_field_type(self):
+        with self.assertRaises(TypeError):
+            self._ec.payload_field_type = 'lel'
+
+    def test_stream_class_prop_no_sc(self):
+        self.assertIsNone(self._ec.stream_class)
+
+    def test_stream_class_prop(self):
+        sc = bt2.StreamClass()
+        sc.add_event_class(self._ec)
+        self.assertEqual(self._ec.stream_class.addr, sc.addr)
+
+    def _test_copy(self, cpy):
+        self.assertIsNot(cpy, self._ec)
+        self.assertNotEqual(cpy.addr, self._ec.addr)
+        self.assertEqual(cpy, self._ec)
+
+    def test_copy(self):
+        cpy = copy.copy(self._ec)
+        self._test_copy(cpy)
+        self.assertEqual(self._ec.context_field_type.addr, cpy.context_field_type.addr)
+        self.assertEqual(self._ec.payload_field_type.addr, cpy.payload_field_type.addr)
+
+    def test_deepcopy(self):
+        cpy = copy.deepcopy(self._ec)
+        self._test_copy(cpy)
+        self.assertNotEqual(self._ec.context_field_type.addr, cpy.context_field_type.addr)
+        self.assertNotEqual(self._ec.payload_field_type.addr, cpy.payload_field_type.addr)
+
+    def test_attr_getitem(self):
+        self.assertEqual(self._ec.attributes['id'], 18)
+        self.assertEqual(self._ec.attributes['name'], 'my_event')
+
+    def test_attr_setitem(self):
+        self._ec.attributes['model.emf.uri'] = 'my url'
+        self.assertEqual(self._ec.attributes['model.emf.uri'], 'my url')
+
+    def test_attr_len(self):
+        self.assertTrue(len(self._ec.attributes) != 0)
+
+    def test_attr_iter(self):
+        for name, value in self._ec.attributes.items():
+            self.assertIsInstance(value, values._Value)
+
+            if name == 'name':
+                self.assertEqual(value, 'my_event')
+            elif name == 'id':
+                self.assertEqual(value, 18)
+
+    def test_eq(self):
+        ec1 = bt2.EventClass(name='name', id=23,
+                             context_field_type=self._context_ft,
+                             payload_field_type=self._payload_ft,
+                             attributes={'model.emf.uri': 'my URI'})
+        ec2 = bt2.EventClass(name='name', id=23,
+                             context_field_type=self._context_ft,
+                             payload_field_type=self._payload_ft,
+                             attributes={'model.emf.uri': 'my URI'})
+        self.assertEqual(ec1, ec2)
+
+    def test_ne_name(self):
+        ec1 = bt2.EventClass(name='name1', id=23,
+                             context_field_type=self._context_ft,
+                             payload_field_type=self._payload_ft,
+                             attributes={'model.emf.uri': 'my URI'})
+        ec2 = bt2.EventClass(name='name', id=23,
+                             context_field_type=self._context_ft,
+                             payload_field_type=self._payload_ft,
+                             attributes={'model.emf.uri': 'my URI'})
+        self.assertNotEqual(ec1, ec2)
+
+    def test_ne_id(self):
+        ec1 = bt2.EventClass(name='name', id=24,
+                             context_field_type=self._context_ft,
+                             payload_field_type=self._payload_ft,
+                             attributes={'model.emf.uri': 'my URI'})
+        ec2 = bt2.EventClass(name='name', id=23,
+                             context_field_type=self._context_ft,
+                             payload_field_type=self._payload_ft,
+                             attributes={'model.emf.uri': 'my URI'})
+        self.assertNotEqual(ec1, ec2)
+
+    def test_ne_context_field_type(self):
+        ec1 = bt2.EventClass(name='name', id=23,
+                             context_field_type=self._payload_ft,
+                             payload_field_type=self._payload_ft,
+                             attributes={'model.emf.uri': 'my URI'})
+        ec2 = bt2.EventClass(name='name', id=23,
+                             context_field_type=self._context_ft,
+                             payload_field_type=self._payload_ft,
+                             attributes={'model.emf.uri': 'my URI'})
+        self.assertNotEqual(ec1, ec2)
+
+    def test_ne_payload_field_type(self):
+        ec1 = bt2.EventClass(name='name', id=23,
+                             context_field_type=self._context_ft,
+                             payload_field_type=self._context_ft,
+                             attributes={'model.emf.uri': 'my URI'})
+        ec2 = bt2.EventClass(name='name', id=23,
+                             context_field_type=self._context_ft,
+                             payload_field_type=self._payload_ft,
+                             attributes={'model.emf.uri': 'my URI'})
+        self.assertNotEqual(ec1, ec2)
+
+    def test_ne_attribute(self):
+        ec1 = bt2.EventClass(name='name', id=23,
+                             context_field_type=self._context_ft,
+                             payload_field_type=self._payload_ft,
+                             attributes={'model.emf.uri': 'my URI'})
+        ec2 = bt2.EventClass(name='name', id=23,
+                             context_field_type=self._context_ft,
+                             payload_field_type=self._payload_ft,
+                             attributes={'model.emf.uri': 'my UR'})
+        self.assertNotEqual(ec1, ec2)
+
+    def test_eq_invalid(self):
+        self.assertFalse(self._ec == 23)
diff --git a/tests/bindings/python/bt2/test_field_types.py b/tests/bindings/python/bt2/test_field_types.py
new file mode 100644 (file)
index 0000000..b265402
--- /dev/null
@@ -0,0 +1,666 @@
+import bt2.fields
+import unittest
+import copy
+import bt2
+
+
+class _TestCopySimple:
+    def _test_copy(self, cpy):
+        self.assertIsNot(cpy, self._ft)
+        self.assertNotEqual(cpy.addr, self._ft.addr)
+        self.assertEqual(cpy, self._ft)
+
+    def test_copy(self):
+        cpy = copy.copy(self._ft)
+        self._test_copy(cpy)
+
+    def test_deepcopy(self):
+        cpy = copy.deepcopy(self._ft)
+        self._test_copy(cpy)
+
+
+class _TestAlignmentProp:
+    def test_assign_alignment(self):
+        self._ft.alignment = 32
+        self.assertEqual(self._ft.alignment, 32)
+
+    def test_assign_invalid_alignment(self):
+        with self.assertRaises(ValueError):
+            self._ft.alignment = 23
+
+
+class _TestByteOrderProp:
+    def test_assign_byte_order(self):
+        self._ft.byte_order = bt2.ByteOrder.LITTLE_ENDIAN
+        self.assertEqual(self._ft.byte_order, bt2.ByteOrder.LITTLE_ENDIAN)
+
+    def test_assign_invalid_byte_order(self):
+        with self.assertRaises(TypeError):
+            self._ft.byte_order = 'hey'
+
+
+class _TestInvalidEq:
+    def test_eq_invalid(self):
+        self.assertFalse(self._ft == 23)
+
+
+class _TestIntegerFieldTypeProps:
+    def test_size_prop(self):
+        self.assertEqual(self._ft.size, 35)
+
+    def test_assign_signed(self):
+        self._ft.is_signed = True
+        self.assertTrue(self._ft.is_signed)
+
+    def test_assign_invalid_signed(self):
+        with self.assertRaises(TypeError):
+            self._ft.is_signed = 23
+
+    def test_assign_base(self):
+        self._ft.base = bt2.Base.HEXADECIMAL
+        self.assertEqual(self._ft.base, bt2.Base.HEXADECIMAL)
+
+    def test_assign_invalid_base(self):
+        with self.assertRaises(TypeError):
+            self._ft.base = 'hey'
+
+    def test_assign_encoding(self):
+        self._ft.encoding = bt2.Encoding.UTF8
+        self.assertEqual(self._ft.encoding, bt2.Encoding.UTF8)
+
+    def test_assign_invalid_encoding(self):
+        with self.assertRaises(TypeError):
+            self._ft.encoding = 'hey'
+
+    def test_assign_mapped_clock_class(self):
+        cc = bt2.ClockClass('name')
+        self._ft.mapped_clock_class = cc
+        self.assertEqual(self._ft.mapped_clock_class, cc)
+
+    def test_assign_invalid_mapped_clock_class(self):
+        with self.assertRaises(TypeError):
+            self._ft.mapped_clock_class = object()
+
+
+class IntegerFieldTypeTestCase(_TestIntegerFieldTypeProps, _TestCopySimple,
+                               _TestAlignmentProp, _TestByteOrderProp,
+                               _TestInvalidEq, unittest.TestCase):
+    def setUp(self):
+        self._ft = bt2.IntegerFieldType(35)
+
+    def test_create_default(self):
+        self.assertEqual(self._ft.size, 35)
+
+    def test_create_invalid_size(self):
+        with self.assertRaises(TypeError):
+            ft = bt2.IntegerFieldType('yes')
+
+    def test_create_neg_size(self):
+        with self.assertRaises(ValueError):
+            ft = bt2.IntegerFieldType(-2)
+
+    def test_create_neg_zero(self):
+        with self.assertRaises(ValueError):
+            ft = bt2.IntegerFieldType(0)
+
+    def test_create_full(self):
+        cc = bt2.ClockClass('name')
+        ft = bt2.IntegerFieldType(24, alignment=16,
+                                  byte_order=bt2.ByteOrder.BIG_ENDIAN,
+                                  is_signed=True, base=bt2.Base.OCTAL,
+                                  encoding=bt2.Encoding.NONE,
+                                  mapped_clock_class=cc)
+        self.assertEqual(ft.size, 24)
+        self.assertEqual(ft.alignment, 16)
+        self.assertEqual(ft.byte_order, bt2.ByteOrder.BIG_ENDIAN)
+        self.assertTrue(ft.is_signed)
+        self.assertEqual(ft.base, bt2.Base.OCTAL)
+        self.assertEqual(ft.encoding, bt2.Encoding.NONE)
+        self.assertEqual(ft.mapped_clock_class, cc)
+
+    def test_create_field(self):
+        field = self._ft()
+        self.assertIsInstance(field, bt2.fields._IntegerField)
+
+    def test_create_field_init(self):
+        field = self._ft(23)
+        self.assertEqual(field, 23)
+
+
+class FloatingPointNumberFieldTypeTestCase(_TestCopySimple, _TestAlignmentProp,
+                                           _TestByteOrderProp, _TestInvalidEq,
+                                           unittest.TestCase):
+    def setUp(self):
+        self._ft = bt2.FloatingPointNumberFieldType()
+
+    def test_create_default(self):
+        pass
+
+    def test_create_full(self):
+        ft = bt2.FloatingPointNumberFieldType(alignment=16,
+                                              byte_order=bt2.ByteOrder.BIG_ENDIAN,
+                                              exponent_size=11,
+                                              mantissa_size=53)
+        self.assertEqual(ft.alignment, 16)
+        self.assertEqual(ft.byte_order, bt2.ByteOrder.BIG_ENDIAN)
+        self.assertEqual(ft.exponent_size, 11)
+        self.assertEqual(ft.mantissa_size, 53)
+
+    def test_assign_exponent_size(self):
+        self._ft.exponent_size = 8
+        self.assertEqual(self._ft.exponent_size, 8)
+
+    def test_assign_invalid_exponent_size(self):
+        with self.assertRaises(TypeError):
+            self._ft.exponent_size = 'yes'
+
+    def test_assign_mantissa_size(self):
+        self._ft.mantissa_size = 24
+        self.assertEqual(self._ft.mantissa_size, 24)
+
+    def test_assign_invalid_mantissa_size(self):
+        with self.assertRaises(TypeError):
+            self._ft.mantissa_size = 'no'
+
+    def test_create_field(self):
+        field = self._ft()
+        self.assertIsInstance(field, bt2.fields._FloatingPointNumberField)
+
+    def test_create_field_init(self):
+        field = self._ft(17.5)
+        self.assertEqual(field, 17.5)
+
+
+class EnumerationFieldTypeTestCase(_TestIntegerFieldTypeProps, _TestInvalidEq,
+                                   _TestCopySimple, _TestAlignmentProp,
+                                   _TestByteOrderProp, unittest.TestCase):
+    def setUp(self):
+        self._ft = bt2.EnumerationFieldType(size=35)
+
+    def test_create_from_int_ft(self):
+        int_ft = bt2.IntegerFieldType(23)
+        self._ft = bt2.EnumerationFieldType(int_ft)
+
+    def test_create_from_invalid_type(self):
+        with self.assertRaises(TypeError):
+            self._ft = bt2.EnumerationFieldType('coucou')
+
+    def test_create_from_invalid_ft(self):
+        with self.assertRaises(TypeError):
+            ft = bt2.FloatingPointNumberFieldType()
+            self._ft = bt2.EnumerationFieldType(ft)
+
+    def test_create_full(self):
+        ft = bt2.EnumerationFieldType(size=24, alignment=16,
+                                      byte_order=bt2.ByteOrder.BIG_ENDIAN,
+                                      is_signed=True, base=bt2.Base.OCTAL,
+                                      encoding=bt2.Encoding.NONE,
+                                      mapped_clock_class=None)
+        self.assertEqual(ft.size, 24)
+        self.assertEqual(ft.alignment, 16)
+        self.assertEqual(ft.byte_order, bt2.ByteOrder.BIG_ENDIAN)
+        self.assertTrue(ft.is_signed)
+        self.assertEqual(ft.base, bt2.Base.OCTAL)
+        self.assertEqual(ft.encoding, bt2.Encoding.NONE)
+        #self.assertIsNone(ft.mapped_clock_class)
+
+    def test_integer_field_type_prop(self):
+        int_ft = bt2.IntegerFieldType(23)
+        enum_ft = bt2.EnumerationFieldType(int_ft)
+        self.assertEqual(enum_ft.integer_field_type.addr, int_ft.addr)
+
+    def test_append_mapping_simple(self):
+        self._ft.append_mapping('hello', 24)
+        mapping = self._ft[0]
+        self.assertEqual(mapping.name, 'hello')
+        self.assertEqual(mapping.lower, 24)
+        self.assertEqual(mapping.upper, 24)
+
+    def test_append_mapping_simple_kwargs(self):
+        self._ft.append_mapping(name='hello', lower=17, upper=23)
+        mapping = self._ft[0]
+        self.assertEqual(mapping.name, 'hello')
+        self.assertEqual(mapping.lower, 17)
+        self.assertEqual(mapping.upper, 23)
+
+    def test_append_mapping_range(self):
+        self._ft.append_mapping('hello', 21, 199)
+        mapping = self._ft[0]
+        self.assertEqual(mapping.name, 'hello')
+        self.assertEqual(mapping.lower, 21)
+        self.assertEqual(mapping.upper, 199)
+
+    def test_append_mapping_invalid_name(self):
+        with self.assertRaises(TypeError):
+            self._ft.append_mapping(17, 21, 199)
+
+    def test_append_mapping_invalid_signedness_lower(self):
+        with self.assertRaises(ValueError):
+            self._ft.append_mapping('hello', -21, 199)
+
+    def test_append_mapping_invalid_signedness_upper(self):
+        with self.assertRaises(ValueError):
+            self._ft.append_mapping('hello', 21, -199)
+
+    def test_append_mapping_simple_signed(self):
+        self._ft.is_signed = True
+        self._ft.append_mapping('hello', -24)
+        mapping = self._ft[0]
+        self.assertEqual(mapping.name, 'hello')
+        self.assertEqual(mapping.lower, -24)
+        self.assertEqual(mapping.upper, -24)
+
+    def test_append_mapping_range_signed(self):
+        self._ft.is_signed = True
+        self._ft.append_mapping('hello', -21, 199)
+        mapping = self._ft[0]
+        self.assertEqual(mapping.name, 'hello')
+        self.assertEqual(mapping.lower, -21)
+        self.assertEqual(mapping.upper, 199)
+
+    def test_iadd(self):
+        enum_ft = bt2.EnumerationFieldType(size=16)
+        enum_ft.append_mapping('c', 4, 5)
+        enum_ft.append_mapping('d', 6, 18)
+        enum_ft.append_mapping('e', 20, 27)
+        self._ft.append_mapping('a', 0, 2)
+        self._ft.append_mapping('b', 3)
+        self._ft += enum_ft
+        self.assertEqual(self._ft[0].name, 'a')
+        self.assertEqual(self._ft[0].lower, 0)
+        self.assertEqual(self._ft[0].upper, 2)
+        self.assertEqual(self._ft[1].name, 'b')
+        self.assertEqual(self._ft[1].lower, 3)
+        self.assertEqual(self._ft[1].upper, 3)
+        self.assertEqual(self._ft[2].name, 'c')
+        self.assertEqual(self._ft[2].lower, 4)
+        self.assertEqual(self._ft[2].upper, 5)
+        self.assertEqual(self._ft[3].name, 'd')
+        self.assertEqual(self._ft[3].lower, 6)
+        self.assertEqual(self._ft[3].upper, 18)
+        self.assertEqual(self._ft[4].name, 'e')
+        self.assertEqual(self._ft[4].lower, 20)
+        self.assertEqual(self._ft[4].upper, 27)
+
+    def test_bool_op(self):
+        self.assertFalse(self._ft)
+        self._ft.append_mapping('a', 0)
+        self.assertTrue(self._ft)
+
+    def test_len(self):
+        self._ft.append_mapping('a', 0)
+        self._ft.append_mapping('b', 1)
+        self._ft.append_mapping('c', 2)
+        self.assertEqual(len(self._ft), 3)
+
+    def test_getitem(self):
+        self._ft.append_mapping('a', 0)
+        self._ft.append_mapping('b', 1, 3)
+        self._ft.append_mapping('c', 5)
+        mapping = self._ft[1]
+        self.assertEqual(mapping.name, 'b')
+        self.assertEqual(mapping.lower, 1)
+        self.assertEqual(mapping.upper, 3)
+
+    def test_iter(self):
+        mappings = (
+            ('a', 1, 5),
+            ('b', 10, 17),
+            ('c', 20, 1504),
+            ('d', 22510, 99999),
+        )
+
+        for mapping in mappings:
+            self._ft.append_mapping(*mapping)
+
+        for ft_mapping, mapping in zip(self._ft, mappings):
+            self.assertEqual(ft_mapping.name, mapping[0])
+            self.assertEqual(ft_mapping.lower, mapping[1])
+            self.assertEqual(ft_mapping.upper, mapping[2])
+
+    def test_mapping_eq(self):
+        enum1 = bt2.EnumerationFieldType(size=32)
+        enum2 = bt2.EnumerationFieldType(size=16)
+        enum1.append_mapping('b', 1, 3)
+        enum2.append_mapping('b', 1, 3)
+        self.assertEqual(enum1[0], enum2[0])
+
+    def test_mapping_eq_invalid(self):
+        enum1 = bt2.EnumerationFieldType(size=32)
+        enum1.append_mapping('b', 1, 3)
+        self.assertNotEqual(enum1[0], 23)
+
+    def _test_find_by_name(self, ft):
+        ft.append_mapping('a', 0)
+        ft.append_mapping('b', 1, 3)
+        ft.append_mapping('a', 5)
+        ft.append_mapping('a', 17, 144)
+        ft.append_mapping('C', 5)
+        mapping_iter = ft.mappings_by_name('a')
+        mappings = list(mapping_iter)
+        a0 = False
+        a5 = False
+        a17_144 = False
+        i = 0
+
+        for mapping in mappings:
+            i += 1
+            self.assertEqual(mapping.name, 'a')
+
+            if mapping.lower == 0 and mapping.upper == 0:
+                a0 = True
+            elif mapping.lower == 5 and mapping.upper == 5:
+                a5 = True
+            elif mapping.lower == 17 and mapping.upper == 144:
+                a17_144 = True
+
+        self.assertEqual(i, 3)
+        self.assertTrue(a0)
+        self.assertTrue(a5)
+        self.assertTrue(a17_144)
+
+    def test_find_by_name_signed(self):
+        self._test_find_by_name(bt2.EnumerationFieldType(size=8, is_signed=True))
+
+    def test_find_by_name_unsigned(self):
+        self._test_find_by_name(bt2.EnumerationFieldType(size=8))
+
+    def _test_find_by_value(self, ft):
+        ft.append_mapping('a', 0)
+        ft.append_mapping('b', 1, 3)
+        ft.append_mapping('c', 5, 19)
+        ft.append_mapping('d', 8, 15)
+        ft.append_mapping('e', 10, 21)
+        ft.append_mapping('f', 0)
+        ft.append_mapping('g', 14)
+        mapping_iter = ft.mappings_by_value(14)
+        mappings = list(mapping_iter)
+        c = False
+        d = False
+        e = False
+        g = False
+        i = 0
+
+        for mapping in mappings:
+            i += 1
+
+            if mapping.name == 'c':
+                c = True
+            elif mapping.name == 'd':
+                d = True
+            elif mapping.name == 'e':
+                e = True
+            elif mapping.name == 'g':
+                g = True
+
+        self.assertEqual(i, 4)
+        self.assertTrue(c)
+        self.assertTrue(d)
+        self.assertTrue(e)
+        self.assertTrue(g)
+
+    def test_find_by_value_signed(self):
+        self._test_find_by_value(bt2.EnumerationFieldType(size=8, is_signed=True))
+
+    def test_find_by_value_unsigned(self):
+        self._test_find_by_value(bt2.EnumerationFieldType(size=8))
+
+    def test_create_field(self):
+        self._ft.append_mapping('c', 4, 5)
+        field = self._ft()
+        self.assertIsInstance(field, bt2.fields._EnumerationField)
+
+    def test_create_field_init(self):
+        self._ft.append_mapping('c', 4, 5)
+        field = self._ft(4)
+        self.assertEqual(field, 4)
+
+
+class StringFieldTypeTestCase(_TestCopySimple, _TestInvalidEq,
+                              unittest.TestCase):
+    def setUp(self):
+        self._ft = bt2.StringFieldType()
+
+    def test_create_default(self):
+        pass
+
+    def test_create_full(self):
+        ft = bt2.StringFieldType(encoding=bt2.Encoding.UTF8)
+        self.assertEqual(ft.encoding, bt2.Encoding.UTF8)
+
+    def test_assign_encoding(self):
+        self._ft.encoding = bt2.Encoding.UTF8
+        self.assertEqual(self._ft.encoding, bt2.Encoding.UTF8)
+
+    def test_assign_invalid_encoding(self):
+        with self.assertRaises(TypeError):
+            self._ft.encoding = 'yes'
+
+    def test_create_field(self):
+        field = self._ft()
+        self.assertIsInstance(field, bt2.fields._StringField)
+
+    def test_create_field_init(self):
+        field = self._ft('hola')
+        self.assertEqual(field, 'hola')
+
+
+class _TestFieldContainer(_TestInvalidEq, _TestCopySimple):
+    def test_append_field(self):
+        int_field_type = bt2.IntegerFieldType(32)
+        self._ft.append_field('int32', int_field_type)
+        field_type = self._ft['int32']
+        self.assertEqual(field_type, int_field_type)
+
+    def test_append_field_kwargs(self):
+        int_field_type = bt2.IntegerFieldType(32)
+        self._ft.append_field(name='int32', field_type=int_field_type)
+        field_type = self._ft['int32']
+        self.assertEqual(field_type, int_field_type)
+
+    def test_append_field_invalid_name(self):
+        with self.assertRaises(TypeError):
+            self._ft.append_field(23, bt2.StringFieldType())
+
+    def test_append_field_invalid_field_type(self):
+        with self.assertRaises(TypeError):
+            self._ft.append_field('yes', object())
+
+    def test_iadd(self):
+        struct_ft = bt2.StructureFieldType()
+        c_field_type = bt2.StringFieldType()
+        d_field_type = bt2.EnumerationFieldType(size=32)
+        e_field_type = bt2.StructureFieldType()
+        struct_ft.append_field('c_string', c_field_type)
+        struct_ft.append_field('d_enum', d_field_type)
+        struct_ft.append_field('e_struct', e_field_type)
+        a_field_type = bt2.FloatingPointNumberFieldType()
+        b_field_type = bt2.IntegerFieldType(17)
+        self._ft.append_field('a_float', a_field_type)
+        self._ft.append_field('b_int', b_field_type)
+        self._ft += struct_ft
+        self.assertEqual(self._ft['a_float'], a_field_type)
+        self.assertEqual(self._ft['b_int'], b_field_type)
+        self.assertEqual(self._ft['c_string'], c_field_type)
+        self.assertEqual(self._ft['d_enum'], d_field_type)
+        self.assertEqual(self._ft['e_struct'], e_field_type)
+
+    def test_bool_op(self):
+        self.assertFalse(self._ft)
+        self._ft.append_field('a', bt2.StringFieldType())
+        self.assertTrue(self._ft)
+
+    def test_len(self):
+        ft = bt2.StringFieldType()
+        self._ft.append_field('a', ft)
+        self._ft.append_field('b', ft)
+        self._ft.append_field('c', ft)
+        self.assertEqual(len(self._ft), 3)
+
+    def test_getitem(self):
+        a_ft = bt2.IntegerFieldType(32)
+        b_ft = bt2.StringFieldType()
+        c_ft = bt2.FloatingPointNumberFieldType()
+        self._ft.append_field('a', a_ft)
+        self._ft.append_field('b', b_ft)
+        self._ft.append_field('c', c_ft)
+        self.assertEqual(self._ft['b'], b_ft)
+
+    def test_getitem_invalid_key_type(self):
+        with self.assertRaises(TypeError):
+            self._ft[0]
+
+    def test_getitem_invalid_key(self):
+        with self.assertRaises(KeyError):
+            self._ft['no way']
+
+    def test_contains(self):
+        self.assertFalse('a' in self._ft)
+        self._ft.append_field('a', bt2.StringFieldType())
+        self.assertTrue('a' in self._ft)
+
+    def test_iter(self):
+        a_ft = bt2.IntegerFieldType(32)
+        b_ft = bt2.StringFieldType()
+        c_ft = bt2.FloatingPointNumberFieldType()
+        fields = (
+            ('a', a_ft),
+            ('b', b_ft),
+            ('c', c_ft),
+        )
+
+        for field in fields:
+            self._ft.append_field(*field)
+
+        for (name, ft_field_type), field in zip(self._ft.items(), fields):
+            self.assertEqual(name, field[0])
+            self.assertEqual(ft_field_type, field[1])
+
+    def test_at_index(self):
+        a_ft = bt2.IntegerFieldType(32)
+        b_ft = bt2.StringFieldType()
+        c_ft = bt2.FloatingPointNumberFieldType()
+        self._ft.append_field('c', c_ft)
+        self._ft.append_field('a', a_ft)
+        self._ft.append_field('b', b_ft)
+        self.assertEqual(self._ft.at_index(1), a_ft)
+
+    def test_at_index_invalid(self):
+        self._ft.append_field('c', bt2.IntegerFieldType(32))
+
+        with self.assertRaises(TypeError):
+            self._ft.at_index('yes')
+
+
+class StructureFieldTypeTestCase(_TestFieldContainer, unittest.TestCase):
+    def setUp(self):
+        self._ft = bt2.StructureFieldType()
+
+    def test_create_default(self):
+        self.assertEqual(self._ft.alignment, 1)
+
+    def test_create_with_min_alignment(self):
+        ft = bt2.StructureFieldType(8)
+        self.assertEqual(ft.alignment, 8)
+
+    def test_assign_alignment(self):
+        with self.assertRaises(AttributeError):
+            self._ft.alignment = 32
+
+    def test_assign_min_alignment(self):
+        self._ft.min_alignment = 64
+        self.assertTrue(self._ft.alignment >= 64)
+
+    def test_assign_invalid_min_alignment(self):
+        with self.assertRaises(ValueError):
+            self._ft.min_alignment = 23
+
+    def test_assign_get_min_alignment(self):
+        with self.assertRaises(AttributeError):
+            self._ft.min_alignment
+
+    def test_create_field(self):
+        field = self._ft()
+        self.assertIsInstance(field, bt2.fields._StructureField)
+
+    def test_create_field_init_invalid(self):
+        with self.assertRaises(bt2.Error):
+            field = self._ft(23)
+
+
+class VariantFieldTypeTestCase(_TestFieldContainer, unittest.TestCase):
+    def setUp(self):
+        self._ft = bt2.VariantFieldType('path.to.tag')
+
+    def test_create_default(self):
+        self.assertEqual(self._ft.tag_name, 'path.to.tag')
+
+    def test_create_invalid_tag_name(self):
+        with self.assertRaises(TypeError):
+            self._ft = bt2.VariantFieldType(23)
+
+    def test_assign_tag_name(self):
+        self._ft.tag_name = 'a.different.tag'
+        self.assertEqual(self._ft.tag_name, 'a.different.tag')
+
+    def test_assign_invalid_tag_name(self):
+        with self.assertRaises(TypeError):
+            self._ft.tag_name = -17
+
+
+class ArrayFieldTypeTestCase(_TestInvalidEq, _TestCopySimple,
+                             unittest.TestCase):
+    def setUp(self):
+        self._elem_ft = bt2.IntegerFieldType(23)
+        self._ft = bt2.ArrayFieldType(self._elem_ft, 45)
+
+    def test_create_default(self):
+        self.assertEqual(self._ft.element_field_type, self._elem_ft)
+        self.assertEqual(self._ft.length, 45)
+
+    def test_create_invalid_field_type(self):
+        with self.assertRaises(TypeError):
+            self._ft = bt2.ArrayFieldType(object(), 45)
+
+    def test_create_invalid_length(self):
+        with self.assertRaises(ValueError):
+            self._ft = bt2.ArrayFieldType(bt2.StringFieldType(), -17)
+
+    def test_create_invalid_length_type(self):
+        with self.assertRaises(TypeError):
+            self._ft = bt2.ArrayFieldType(bt2.StringFieldType(), 'the length')
+
+    def test_create_field(self):
+        field = self._ft()
+        self.assertIsInstance(field, bt2.fields._ArrayField)
+
+    def test_create_field_init_invalid(self):
+        with self.assertRaises(bt2.Error):
+            field = self._ft(23)
+
+
+class SequenceFieldTypeTestCase(_TestInvalidEq, _TestCopySimple,
+                                unittest.TestCase):
+    def setUp(self):
+        self._elem_ft = bt2.IntegerFieldType(23)
+        self._ft = bt2.SequenceFieldType(self._elem_ft, 'the.length')
+
+    def test_create_default(self):
+        self.assertEqual(self._ft.element_field_type, self._elem_ft)
+        self.assertEqual(self._ft.length_name, 'the.length')
+
+    def test_create_invalid_field_type(self):
+        with self.assertRaises(TypeError):
+            self._ft = bt2.ArrayFieldType(object(), 'the.length')
+
+    def test_create_invalid_length_type(self):
+        with self.assertRaises(TypeError):
+            self._ft = bt2.SequenceFieldType(bt2.StringFieldType(), 17)
+
+    def test_create_field(self):
+        field = self._ft()
+        self.assertIsInstance(field, bt2.fields._SequenceField)
+
+    def test_create_field_init_invalid(self):
+        with self.assertRaises(bt2.Error):
+            field = self._ft(23)
diff --git a/tests/bindings/python/bt2/test_fields.py b/tests/bindings/python/bt2/test_fields.py
new file mode 100644 (file)
index 0000000..c14e0de
--- /dev/null
@@ -0,0 +1,1210 @@
+from functools import partial, partialmethod
+import operator
+import unittest
+import numbers
+import math
+import copy
+import bt2
+
+
+class _TestCopySimple:
+    def test_copy(self):
+        cpy = copy.copy(self._def)
+        self.assertIsNot(cpy, self._def)
+        self.assertNotEqual(cpy.addr, self._def.addr)
+        self.assertEqual(cpy, self._def)
+
+    def test_deepcopy(self):
+        cpy = copy.deepcopy(self._def)
+        self.assertIsNot(cpy, self._def)
+        self.assertNotEqual(cpy.addr, self._def.addr)
+        self.assertEqual(cpy, self._def)
+
+
+_COMP_BINOPS = (
+    operator.eq,
+    operator.ne,
+)
+
+
+class _TestNumericField(_TestCopySimple):
+    def _binop(self, op, rhs):
+        rexc = None
+        rvexc = None
+        comp_value = rhs
+
+        if isinstance(rhs, (bt2.fields._IntegerField, bt2.fields._FloatingPointNumberField)):
+            comp_value = rhs.value
+
+        try:
+            r = op(self._def, rhs)
+        except Exception as e:
+            rexc = e
+
+        try:
+            rv = op(self._def_value, comp_value)
+        except Exception as e:
+            rvexc = e
+
+        if rexc is not None or rvexc is not None:
+            # at least one of the operations raised an exception: in
+            # this case both operations should have raised the same
+            # type of exception (division by zero, bit shift with a
+            # floating point number operand, etc.)
+            self.assertIs(type(rexc), type(rvexc))
+            return None, None
+
+        return r, rv
+
+    def _unaryop(self, op):
+        rexc = None
+        rvexc = None
+
+        try:
+            r = op(self._def)
+        except Exception as e:
+            rexc = e
+
+        try:
+            rv = op(self._def_value)
+        except Exception as e:
+            rvexc = e
+
+        if rexc is not None or rvexc is not None:
+            # at least one of the operations raised an exception: in
+            # this case both operations should have raised the same
+            # type of exception (division by zero, bit shift with a
+            # floating point number operand, etc.)
+            self.assertIs(type(rexc), type(rvexc))
+            return None, None
+
+        return r, rv
+
+    def _test_unaryop_type(self, op):
+        r, rv = self._unaryop(op)
+
+        if r is None:
+            return
+
+        self.assertIsInstance(r, type(rv))
+
+    def _test_unaryop_value(self, op):
+        r, rv = self._unaryop(op)
+
+        if r is None:
+            return
+
+        self.assertEqual(r, rv)
+
+    def _test_unaryop_addr_same(self, op):
+        addr_before = self._def.addr
+        self._unaryop(op)
+        self.assertEqual(self._def.addr, addr_before)
+
+    def _test_unaryop_value_same(self, op):
+        value_before = self._def.value
+        self._unaryop(op)
+        self.assertEqual(self._def.value, value_before)
+
+    def _test_binop_type(self, op, rhs):
+        r, rv = self._binop(op, rhs)
+
+        if r is None:
+            return
+
+        if op in _COMP_BINOPS:
+            # __eq__() and __ne__() always return a 'bool' object
+            self.assertIsInstance(r, bool)
+        else:
+            self.assertIsInstance(r, type(rv))
+
+    def _test_binop_value(self, op, rhs):
+        r, rv = self._binop(op, rhs)
+
+        if r is None:
+            return
+
+        self.assertEqual(r, rv)
+
+    def _test_binop_lhs_addr_same(self, op, rhs):
+        addr_before = self._def.addr
+        r, rv = self._binop(op, rhs)
+        self.assertEqual(self._def.addr, addr_before)
+
+    def _test_binop_lhs_value_same(self, op, rhs):
+        value_before = self._def.value
+        r, rv = self._binop(op, rhs)
+        self.assertEqual(self._def.value, value_before)
+
+    def _test_binop_invalid_unknown(self, op):
+        if op in _COMP_BINOPS:
+            self.skipTest('not testing')
+
+        class A:
+            pass
+
+        with self.assertRaises(TypeError):
+            op(self._def, A())
+
+    def _test_binop_invalid_none(self, op):
+        if op in _COMP_BINOPS:
+            self.skipTest('not testing')
+
+        with self.assertRaises(TypeError):
+            op(self._def, None)
+
+    def _test_ibinop_value(self, op, rhs):
+        r, rv = self._binop(op, rhs)
+
+        if r is None:
+            return
+
+        # The inplace operators are special for field objects because
+        # they do not return a new, immutable object like it's the case
+        # for Python numbers. In Python, `a += 2`, where `a` is a number
+        # object, assigns a new number object reference to `a`, dropping
+        # the old reference. Since BT's field objects are mutable, we
+        # modify their internal value with the inplace operators. This
+        # means however that we can lose data in the process, for
+        # example:
+        #
+        #     int_value_obj += 3.3
+        #
+        # Here, if `int_value_obj` is a Python `int` with the value 2,
+        # it would be a `float` object after this, holding the value
+        # 5.3. In our case, if `int_value_obj` is an integer field
+        # object, 3.3 is converted to an `int` object (3) and added to
+        # the current value of `int_value_obj`, so after this the value
+        # of the object is 5. This does not compare to 5.3, which is
+        # why we also use the `int()` type here.
+        if isinstance(self._def, bt2.fields._IntegerField):
+            rv = int(rv)
+
+        self.assertEqual(r, rv)
+
+    def _test_ibinop_type(self, op, rhs):
+        r, rv = self._binop(op, rhs)
+
+        if r is None:
+            return
+
+        self.assertIs(r, self._def)
+
+    def _test_ibinop_invalid_unknown(self, op):
+        class A:
+            pass
+
+        with self.assertRaises(TypeError):
+            op(self._def, A())
+
+    def _test_ibinop_invalid_none(self, op):
+        with self.assertRaises(TypeError):
+            op(self._def, None)
+
+    def _test_binop_rhs_false(self, test_cb, op):
+        test_cb(op, False)
+
+    def _test_binop_rhs_true(self, test_cb, op):
+        test_cb(op, True)
+
+    def _test_binop_rhs_pos_int(self, test_cb, op):
+        test_cb(op, 2)
+
+    def _test_binop_rhs_neg_int(self, test_cb, op):
+        test_cb(op, -23)
+
+    def _test_binop_rhs_zero_int(self, test_cb, op):
+        test_cb(op, 0)
+
+    def _test_binop_rhs_pos_vint(self, test_cb, op):
+        test_cb(op, bt2.create_value(2))
+
+    def _test_binop_rhs_neg_vint(self, test_cb, op):
+        test_cb(op, bt2.create_value(-23))
+
+    def _test_binop_rhs_zero_vint(self, test_cb, op):
+        test_cb(op, bt2.create_value(0))
+
+    def _test_binop_rhs_pos_float(self, test_cb, op):
+        test_cb(op, 2.2)
+
+    def _test_binop_rhs_neg_float(self, test_cb, op):
+        test_cb(op, -23.4)
+
+    def _test_binop_rhs_zero_float(self, test_cb, op):
+        test_cb(op, 0.0)
+
+    def _test_binop_rhs_pos_vfloat(self, test_cb, op):
+        test_cb(op, bt2.create_value(2.2))
+
+    def _test_binop_rhs_neg_vfloat(self, test_cb, op):
+        test_cb(op, bt2.create_value(-23.4))
+
+    def _test_binop_rhs_zero_vfloat(self, test_cb, op):
+        test_cb(op, bt2.create_value(0.0))
+
+    def _test_binop_type_false(self, op):
+        self._test_binop_rhs_false(self._test_binop_type, op)
+
+    def _test_binop_type_true(self, op):
+        self._test_binop_rhs_true(self._test_binop_type, op)
+
+    def _test_binop_type_pos_int(self, op):
+        self._test_binop_rhs_pos_int(self._test_binop_type, op)
+
+    def _test_binop_type_neg_int(self, op):
+        self._test_binop_rhs_neg_int(self._test_binop_type, op)
+
+    def _test_binop_type_zero_int(self, op):
+        self._test_binop_rhs_zero_int(self._test_binop_type, op)
+
+    def _test_binop_type_pos_vint(self, op):
+        self._test_binop_rhs_pos_vint(self._test_binop_type, op)
+
+    def _test_binop_type_neg_vint(self, op):
+        self._test_binop_rhs_neg_vint(self._test_binop_type, op)
+
+    def _test_binop_type_zero_vint(self, op):
+        self._test_binop_rhs_zero_vint(self._test_binop_type, op)
+
+    def _test_binop_type_pos_float(self, op):
+        self._test_binop_rhs_pos_float(self._test_binop_type, op)
+
+    def _test_binop_type_neg_float(self, op):
+        self._test_binop_rhs_neg_float(self._test_binop_type, op)
+
+    def _test_binop_type_zero_float(self, op):
+        self._test_binop_rhs_zero_float(self._test_binop_type, op)
+
+    def _test_binop_type_pos_vfloat(self, op):
+        self._test_binop_rhs_pos_vfloat(self._test_binop_type, op)
+
+    def _test_binop_type_neg_vfloat(self, op):
+        self._test_binop_rhs_neg_vfloat(self._test_binop_type, op)
+
+    def _test_binop_type_zero_vfloat(self, op):
+        self._test_binop_rhs_zero_vfloat(self._test_binop_type, op)
+
+    def _test_binop_value_false(self, op):
+        self._test_binop_rhs_false(self._test_binop_value, op)
+
+    def _test_binop_value_true(self, op):
+        self._test_binop_rhs_true(self._test_binop_value, op)
+
+    def _test_binop_value_pos_int(self, op):
+        self._test_binop_rhs_pos_int(self._test_binop_value, op)
+
+    def _test_binop_value_neg_int(self, op):
+        self._test_binop_rhs_neg_int(self._test_binop_value, op)
+
+    def _test_binop_value_zero_int(self, op):
+        self._test_binop_rhs_zero_int(self._test_binop_value, op)
+
+    def _test_binop_value_pos_vint(self, op):
+        self._test_binop_rhs_pos_vint(self._test_binop_value, op)
+
+    def _test_binop_value_neg_vint(self, op):
+        self._test_binop_rhs_neg_vint(self._test_binop_value, op)
+
+    def _test_binop_value_zero_vint(self, op):
+        self._test_binop_rhs_zero_vint(self._test_binop_value, op)
+
+    def _test_binop_value_pos_float(self, op):
+        self._test_binop_rhs_pos_float(self._test_binop_value, op)
+
+    def _test_binop_value_neg_float(self, op):
+        self._test_binop_rhs_neg_float(self._test_binop_value, op)
+
+    def _test_binop_value_zero_float(self, op):
+        self._test_binop_rhs_zero_float(self._test_binop_value, op)
+
+    def _test_binop_value_pos_vfloat(self, op):
+        self._test_binop_rhs_pos_vfloat(self._test_binop_value, op)
+
+    def _test_binop_value_neg_vfloat(self, op):
+        self._test_binop_rhs_neg_vfloat(self._test_binop_value, op)
+
+    def _test_binop_value_zero_vfloat(self, op):
+        self._test_binop_rhs_zero_vfloat(self._test_binop_value, op)
+
+    def _test_binop_lhs_addr_same_false(self, op):
+        self._test_binop_rhs_false(self._test_binop_lhs_addr_same, op)
+
+    def _test_binop_lhs_addr_same_true(self, op):
+        self._test_binop_rhs_true(self._test_binop_lhs_addr_same, op)
+
+    def _test_binop_lhs_addr_same_pos_int(self, op):
+        self._test_binop_rhs_pos_int(self._test_binop_lhs_addr_same, op)
+
+    def _test_binop_lhs_addr_same_neg_int(self, op):
+        self._test_binop_rhs_neg_int(self._test_binop_lhs_addr_same, op)
+
+    def _test_binop_lhs_addr_same_zero_int(self, op):
+        self._test_binop_rhs_zero_int(self._test_binop_lhs_addr_same, op)
+
+    def _test_binop_lhs_addr_same_pos_vint(self, op):
+        self._test_binop_rhs_pos_vint(self._test_binop_lhs_addr_same, op)
+
+    def _test_binop_lhs_addr_same_neg_vint(self, op):
+        self._test_binop_rhs_neg_vint(self._test_binop_lhs_addr_same, op)
+
+    def _test_binop_lhs_addr_same_zero_vint(self, op):
+        self._test_binop_rhs_zero_vint(self._test_binop_lhs_addr_same, op)
+
+    def _test_binop_lhs_addr_same_pos_float(self, op):
+        self._test_binop_rhs_pos_float(self._test_binop_lhs_addr_same, op)
+
+    def _test_binop_lhs_addr_same_neg_float(self, op):
+        self._test_binop_rhs_neg_float(self._test_binop_lhs_addr_same, op)
+
+    def _test_binop_lhs_addr_same_zero_float(self, op):
+        self._test_binop_rhs_zero_float(self._test_binop_lhs_addr_same, op)
+
+    def _test_binop_lhs_addr_same_pos_vfloat(self, op):
+        self._test_binop_rhs_pos_vfloat(self._test_binop_lhs_addr_same, op)
+
+    def _test_binop_lhs_addr_same_neg_vfloat(self, op):
+        self._test_binop_rhs_neg_vfloat(self._test_binop_lhs_addr_same, op)
+
+    def _test_binop_lhs_addr_same_zero_vfloat(self, op):
+        self._test_binop_rhs_zero_vfloat(self._test_binop_lhs_addr_same, op)
+
+    def _test_binop_lhs_value_same_false(self, op):
+        self._test_binop_rhs_false(self._test_binop_lhs_value_same, op)
+
+    def _test_binop_lhs_value_same_true(self, op):
+        self._test_binop_rhs_true(self._test_binop_lhs_value_same, op)
+
+    def _test_binop_lhs_value_same_pos_int(self, op):
+        self._test_binop_rhs_pos_int(self._test_binop_lhs_value_same, op)
+
+    def _test_binop_lhs_value_same_neg_int(self, op):
+        self._test_binop_rhs_neg_int(self._test_binop_lhs_value_same, op)
+
+    def _test_binop_lhs_value_same_zero_int(self, op):
+        self._test_binop_rhs_zero_int(self._test_binop_lhs_value_same, op)
+
+    def _test_binop_lhs_value_same_pos_vint(self, op):
+        self._test_binop_rhs_pos_vint(self._test_binop_lhs_value_same, op)
+
+    def _test_binop_lhs_value_same_neg_vint(self, op):
+        self._test_binop_rhs_neg_vint(self._test_binop_lhs_value_same, op)
+
+    def _test_binop_lhs_value_same_zero_vint(self, op):
+        self._test_binop_rhs_zero_vint(self._test_binop_lhs_value_same, op)
+
+    def _test_binop_lhs_value_same_pos_float(self, op):
+        self._test_binop_rhs_pos_float(self._test_binop_lhs_value_same, op)
+
+    def _test_binop_lhs_value_same_neg_float(self, op):
+        self._test_binop_rhs_neg_float(self._test_binop_lhs_value_same, op)
+
+    def _test_binop_lhs_value_same_zero_float(self, op):
+        self._test_binop_rhs_zero_float(self._test_binop_lhs_value_same, op)
+
+    def _test_binop_lhs_value_same_pos_vfloat(self, op):
+        self._test_binop_rhs_pos_vfloat(self._test_binop_lhs_value_same, op)
+
+    def _test_binop_lhs_value_same_neg_vfloat(self, op):
+        self._test_binop_rhs_neg_vfloat(self._test_binop_lhs_value_same, op)
+
+    def _test_binop_lhs_value_same_zero_vfloat(self, op):
+        self._test_binop_rhs_zero_vfloat(self._test_binop_lhs_value_same, op)
+
+    def _test_ibinop_type_false(self, op):
+        self._test_binop_rhs_false(self._test_ibinop_type, op)
+
+    def _test_ibinop_type_true(self, op):
+        self._test_binop_rhs_true(self._test_ibinop_type, op)
+
+    def _test_ibinop_type_pos_int(self, op):
+        self._test_binop_rhs_pos_int(self._test_ibinop_type, op)
+
+    def _test_ibinop_type_neg_int(self, op):
+        self._test_binop_rhs_neg_int(self._test_ibinop_type, op)
+
+    def _test_ibinop_type_zero_int(self, op):
+        self._test_binop_rhs_zero_int(self._test_ibinop_type, op)
+
+    def _test_ibinop_type_pos_vint(self, op):
+        self._test_binop_rhs_pos_vint(self._test_ibinop_type, op)
+
+    def _test_ibinop_type_neg_vint(self, op):
+        self._test_binop_rhs_neg_vint(self._test_ibinop_type, op)
+
+    def _test_ibinop_type_zero_vint(self, op):
+        self._test_binop_rhs_zero_vint(self._test_ibinop_type, op)
+
+    def _test_ibinop_type_pos_float(self, op):
+        self._test_binop_rhs_pos_float(self._test_ibinop_type, op)
+
+    def _test_ibinop_type_neg_float(self, op):
+        self._test_binop_rhs_neg_float(self._test_ibinop_type, op)
+
+    def _test_ibinop_type_zero_float(self, op):
+        self._test_binop_rhs_zero_float(self._test_ibinop_type, op)
+
+    def _test_ibinop_type_pos_vfloat(self, op):
+        self._test_binop_rhs_pos_vfloat(self._test_ibinop_type, op)
+
+    def _test_ibinop_type_neg_vfloat(self, op):
+        self._test_binop_rhs_neg_vfloat(self._test_ibinop_type, op)
+
+    def _test_ibinop_type_zero_vfloat(self, op):
+        self._test_binop_rhs_zero_vfloat(self._test_ibinop_type, op)
+
+    def _test_ibinop_value_false(self, op):
+        self._test_binop_rhs_false(self._test_ibinop_value, op)
+
+    def _test_ibinop_value_true(self, op):
+        self._test_binop_rhs_true(self._test_ibinop_value, op)
+
+    def _test_ibinop_value_pos_int(self, op):
+        self._test_binop_rhs_pos_int(self._test_ibinop_value, op)
+
+    def _test_ibinop_value_neg_int(self, op):
+        self._test_binop_rhs_neg_int(self._test_ibinop_value, op)
+
+    def _test_ibinop_value_zero_int(self, op):
+        self._test_binop_rhs_zero_int(self._test_ibinop_value, op)
+
+    def _test_ibinop_value_pos_vint(self, op):
+        self._test_binop_rhs_pos_vint(self._test_ibinop_value, op)
+
+    def _test_ibinop_value_neg_vint(self, op):
+        self._test_binop_rhs_neg_vint(self._test_ibinop_value, op)
+
+    def _test_ibinop_value_zero_vint(self, op):
+        self._test_binop_rhs_zero_vint(self._test_ibinop_value, op)
+
+    def _test_ibinop_value_pos_float(self, op):
+        self._test_binop_rhs_pos_float(self._test_ibinop_value, op)
+
+    def _test_ibinop_value_neg_float(self, op):
+        self._test_binop_rhs_neg_float(self._test_ibinop_value, op)
+
+    def _test_ibinop_value_zero_float(self, op):
+        self._test_binop_rhs_zero_float(self._test_ibinop_value, op)
+
+    def _test_ibinop_value_pos_vfloat(self, op):
+        self._test_binop_rhs_pos_vfloat(self._test_ibinop_value, op)
+
+    def _test_ibinop_value_neg_vfloat(self, op):
+        self._test_binop_rhs_neg_vfloat(self._test_ibinop_value, op)
+
+    def _test_ibinop_value_zero_vfloat(self, op):
+        self._test_binop_rhs_zero_vfloat(self._test_ibinop_value, op)
+
+    def test_bool_op(self):
+        self.assertEqual(bool(self._def), bool(self._def_value))
+
+    def test_int_op(self):
+        self.assertEqual(int(self._def), int(self._def_value))
+
+    def test_float_op(self):
+        self.assertEqual(float(self._def), float(self._def_value))
+
+    def test_complex_op(self):
+        self.assertEqual(complex(self._def), complex(self._def_value))
+
+    def test_str_op(self):
+        self.assertEqual(str(self._def), str(self._def_value))
+
+    def test_eq_none(self):
+        self.assertFalse(self._def == None)
+
+    def test_ne_none(self):
+        self.assertTrue(self._def != None)
+
+
+_BINOPS = (
+    ('lt', operator.lt),
+    ('le', operator.le),
+    ('eq', operator.eq),
+    ('ne', operator.ne),
+    ('ge', operator.ge),
+    ('gt', operator.gt),
+    ('add', operator.add),
+    ('radd', lambda a, b: operator.add(b, a)),
+    ('and', operator.and_),
+    ('rand', lambda a, b: operator.and_(b, a)),
+    ('floordiv', operator.floordiv),
+    ('rfloordiv', lambda a, b: operator.floordiv(b, a)),
+    ('lshift', operator.lshift),
+    ('rlshift', lambda a, b: operator.lshift(b, a)),
+    ('mod', operator.mod),
+    ('rmod', lambda a, b: operator.mod(b, a)),
+    ('mul', operator.mul),
+    ('rmul', lambda a, b: operator.mul(b, a)),
+    ('or', operator.or_),
+    ('ror', lambda a, b: operator.or_(b, a)),
+    ('pow', operator.pow),
+    ('rpow', lambda a, b: operator.pow(b, a)),
+    ('rshift', operator.rshift),
+    ('rrshift', lambda a, b: operator.rshift(b, a)),
+    ('sub', operator.sub),
+    ('rsub', lambda a, b: operator.sub(b, a)),
+    ('truediv', operator.truediv),
+    ('rtruediv', lambda a, b: operator.truediv(b, a)),
+    ('xor', operator.xor),
+    ('rxor', lambda a, b: operator.xor(b, a)),
+)
+
+
+_IBINOPS = (
+    ('iadd', operator.iadd),
+    ('iand', operator.iand),
+    ('ifloordiv', operator.ifloordiv),
+    ('ilshift', operator.ilshift),
+    ('imod', operator.imod),
+    ('imul', operator.imul),
+    ('ior', operator.ior),
+    ('ipow', operator.ipow),
+    ('irshift', operator.irshift),
+    ('isub', operator.isub),
+    ('itruediv', operator.itruediv),
+    ('ixor', operator.ixor),
+)
+
+
+_UNARYOPS = (
+    ('neg', operator.neg),
+    ('pos', operator.pos),
+    ('abs', operator.abs),
+    ('invert', operator.invert),
+    ('round', round),
+    ('round_0', partial(round, ndigits=0)),
+    ('round_1', partial(round, ndigits=1)),
+    ('round_2', partial(round, ndigits=2)),
+    ('round_3', partial(round, ndigits=3)),
+    ('ceil', math.ceil),
+    ('floor', math.floor),
+    ('trunc', math.trunc),
+)
+
+
+def _inject_numeric_testing_methods(cls):
+    def test_binop_name(suffix):
+        return 'test_binop_{}_{}'.format(name, suffix)
+
+    def test_ibinop_name(suffix):
+        return 'test_ibinop_{}_{}'.format(name, suffix)
+
+    def test_unaryop_name(suffix):
+        return 'test_unaryop_{}_{}'.format(name, suffix)
+
+    # inject testing methods for each binary operation
+    for name, binop in _BINOPS:
+
+        setattr(cls, test_binop_name('invalid_unknown'), partialmethod(_TestNumericField._test_binop_invalid_unknown, op=binop))
+        setattr(cls, test_binop_name('invalid_none'), partialmethod(_TestNumericField._test_binop_invalid_none, op=binop))
+        setattr(cls, test_binop_name('type_true'), partialmethod(_TestNumericField._test_binop_type_true, op=binop))
+        setattr(cls, test_binop_name('type_pos_int'), partialmethod(_TestNumericField._test_binop_type_pos_int, op=binop))
+        setattr(cls, test_binop_name('type_pos_vint'), partialmethod(_TestNumericField._test_binop_type_pos_vint, op=binop))
+        setattr(cls, test_binop_name('value_true'), partialmethod(_TestNumericField._test_binop_value_true, op=binop))
+        setattr(cls, test_binop_name('value_pos_int'), partialmethod(_TestNumericField._test_binop_value_pos_int, op=binop))
+        setattr(cls, test_binop_name('value_pos_vint'), partialmethod(_TestNumericField._test_binop_value_pos_vint, op=binop))
+        setattr(cls, test_binop_name('lhs_addr_same_true'), partialmethod(_TestNumericField._test_binop_lhs_addr_same_true, op=binop))
+        setattr(cls, test_binop_name('lhs_addr_same_pos_int'), partialmethod(_TestNumericField._test_binop_lhs_addr_same_pos_int, op=binop))
+        setattr(cls, test_binop_name('lhs_addr_same_pos_vint'), partialmethod(_TestNumericField._test_binop_lhs_addr_same_pos_vint, op=binop))
+        setattr(cls, test_binop_name('lhs_value_same_true'), partialmethod(_TestNumericField._test_binop_lhs_value_same_true, op=binop))
+        setattr(cls, test_binop_name('lhs_value_same_pos_int'), partialmethod(_TestNumericField._test_binop_lhs_value_same_pos_int, op=binop))
+        setattr(cls, test_binop_name('lhs_value_same_pos_vint'), partialmethod(_TestNumericField._test_binop_lhs_value_same_pos_vint, op=binop))
+        setattr(cls, test_binop_name('type_neg_int'), partialmethod(_TestNumericField._test_binop_type_neg_int, op=binop))
+        setattr(cls, test_binop_name('type_neg_vint'), partialmethod(_TestNumericField._test_binop_type_neg_vint, op=binop))
+        setattr(cls, test_binop_name('value_neg_int'), partialmethod(_TestNumericField._test_binop_value_neg_int, op=binop))
+        setattr(cls, test_binop_name('value_neg_vint'), partialmethod(_TestNumericField._test_binop_value_neg_vint, op=binop))
+        setattr(cls, test_binop_name('lhs_addr_same_neg_int'), partialmethod(_TestNumericField._test_binop_lhs_addr_same_neg_int, op=binop))
+        setattr(cls, test_binop_name('lhs_addr_same_neg_vint'), partialmethod(_TestNumericField._test_binop_lhs_addr_same_neg_vint, op=binop))
+        setattr(cls, test_binop_name('lhs_value_same_neg_int'), partialmethod(_TestNumericField._test_binop_lhs_value_same_neg_int, op=binop))
+        setattr(cls, test_binop_name('lhs_value_same_neg_vint'), partialmethod(_TestNumericField._test_binop_lhs_value_same_neg_vint, op=binop))
+        setattr(cls, test_binop_name('type_false'), partialmethod(_TestNumericField._test_binop_type_false, op=binop))
+        setattr(cls, test_binop_name('type_zero_int'), partialmethod(_TestNumericField._test_binop_type_zero_int, op=binop))
+        setattr(cls, test_binop_name('type_zero_vint'), partialmethod(_TestNumericField._test_binop_type_zero_vint, op=binop))
+        setattr(cls, test_binop_name('value_false'), partialmethod(_TestNumericField._test_binop_value_false, op=binop))
+        setattr(cls, test_binop_name('value_zero_int'), partialmethod(_TestNumericField._test_binop_value_zero_int, op=binop))
+        setattr(cls, test_binop_name('value_zero_vint'), partialmethod(_TestNumericField._test_binop_value_zero_vint, op=binop))
+        setattr(cls, test_binop_name('lhs_addr_same_false'), partialmethod(_TestNumericField._test_binop_lhs_addr_same_false, op=binop))
+        setattr(cls, test_binop_name('lhs_addr_same_zero_int'), partialmethod(_TestNumericField._test_binop_lhs_addr_same_zero_int, op=binop))
+        setattr(cls, test_binop_name('lhs_addr_same_zero_vint'), partialmethod(_TestNumericField._test_binop_lhs_addr_same_zero_vint, op=binop))
+        setattr(cls, test_binop_name('lhs_value_same_false'), partialmethod(_TestNumericField._test_binop_lhs_value_same_false, op=binop))
+        setattr(cls, test_binop_name('lhs_value_same_zero_int'), partialmethod(_TestNumericField._test_binop_lhs_value_same_zero_int, op=binop))
+        setattr(cls, test_binop_name('lhs_value_same_zero_vint'), partialmethod(_TestNumericField._test_binop_lhs_value_same_zero_vint, op=binop))
+        setattr(cls, test_binop_name('type_pos_float'), partialmethod(_TestNumericField._test_binop_type_pos_float, op=binop))
+        setattr(cls, test_binop_name('type_neg_float'), partialmethod(_TestNumericField._test_binop_type_neg_float, op=binop))
+        setattr(cls, test_binop_name('type_pos_vfloat'), partialmethod(_TestNumericField._test_binop_type_pos_vfloat, op=binop))
+        setattr(cls, test_binop_name('type_neg_vfloat'), partialmethod(_TestNumericField._test_binop_type_neg_vfloat, op=binop))
+        setattr(cls, test_binop_name('value_pos_float'), partialmethod(_TestNumericField._test_binop_value_pos_float, op=binop))
+        setattr(cls, test_binop_name('value_neg_float'), partialmethod(_TestNumericField._test_binop_value_neg_float, op=binop))
+        setattr(cls, test_binop_name('value_pos_vfloat'), partialmethod(_TestNumericField._test_binop_value_pos_vfloat, op=binop))
+        setattr(cls, test_binop_name('value_neg_vfloat'), partialmethod(_TestNumericField._test_binop_value_neg_vfloat, op=binop))
+        setattr(cls, test_binop_name('lhs_addr_same_pos_float'), partialmethod(_TestNumericField._test_binop_lhs_addr_same_pos_float, op=binop))
+        setattr(cls, test_binop_name('lhs_addr_same_neg_float'), partialmethod(_TestNumericField._test_binop_lhs_addr_same_neg_float, op=binop))
+        setattr(cls, test_binop_name('lhs_addr_same_pos_vfloat'), partialmethod(_TestNumericField._test_binop_lhs_addr_same_pos_vfloat, op=binop))
+        setattr(cls, test_binop_name('lhs_addr_same_neg_vfloat'), partialmethod(_TestNumericField._test_binop_lhs_addr_same_neg_vfloat, op=binop))
+        setattr(cls, test_binop_name('lhs_value_same_pos_float'), partialmethod(_TestNumericField._test_binop_lhs_value_same_pos_float, op=binop))
+        setattr(cls, test_binop_name('lhs_value_same_neg_float'), partialmethod(_TestNumericField._test_binop_lhs_value_same_neg_float, op=binop))
+        setattr(cls, test_binop_name('lhs_value_same_pos_vfloat'), partialmethod(_TestNumericField._test_binop_lhs_value_same_pos_vfloat, op=binop))
+        setattr(cls, test_binop_name('lhs_value_same_neg_vfloat'), partialmethod(_TestNumericField._test_binop_lhs_value_same_neg_vfloat, op=binop))
+        setattr(cls, test_binop_name('type_zero_float'), partialmethod(_TestNumericField._test_binop_type_zero_float, op=binop))
+        setattr(cls, test_binop_name('type_zero_vfloat'), partialmethod(_TestNumericField._test_binop_type_zero_vfloat, op=binop))
+        setattr(cls, test_binop_name('value_zero_float'), partialmethod(_TestNumericField._test_binop_value_zero_float, op=binop))
+        setattr(cls, test_binop_name('value_zero_vfloat'), partialmethod(_TestNumericField._test_binop_value_zero_vfloat, op=binop))
+        setattr(cls, test_binop_name('lhs_addr_same_zero_float'), partialmethod(_TestNumericField._test_binop_lhs_addr_same_zero_float, op=binop))
+        setattr(cls, test_binop_name('lhs_addr_same_zero_vfloat'), partialmethod(_TestNumericField._test_binop_lhs_addr_same_zero_vfloat, op=binop))
+        setattr(cls, test_binop_name('lhs_value_same_zero_float'), partialmethod(_TestNumericField._test_binop_lhs_value_same_zero_float, op=binop))
+        setattr(cls, test_binop_name('lhs_value_same_zero_vfloat'), partialmethod(_TestNumericField._test_binop_lhs_value_same_zero_vfloat, op=binop))
+
+    # inject testing methods for each unary operation
+    for name, unaryop in _UNARYOPS:
+        setattr(cls, test_unaryop_name('type'), partialmethod(_TestNumericField._test_unaryop_type, op=unaryop))
+        setattr(cls, test_unaryop_name('value'), partialmethod(_TestNumericField._test_unaryop_value, op=unaryop))
+        setattr(cls, test_unaryop_name('addr_same'), partialmethod(_TestNumericField._test_unaryop_addr_same, op=unaryop))
+        setattr(cls, test_unaryop_name('value_same'), partialmethod(_TestNumericField._test_unaryop_value_same, op=unaryop))
+
+    # inject testing methods for each inplace binary operation
+    for name, ibinop in _IBINOPS:
+        setattr(cls, test_ibinop_name('invalid_unknown'), partialmethod(_TestNumericField._test_ibinop_invalid_unknown, op=ibinop))
+        setattr(cls, test_ibinop_name('invalid_none'), partialmethod(_TestNumericField._test_ibinop_invalid_none, op=ibinop))
+        setattr(cls, test_ibinop_name('type_true'), partialmethod(_TestNumericField._test_ibinop_type_true, op=ibinop))
+        setattr(cls, test_ibinop_name('value_true'), partialmethod(_TestNumericField._test_ibinop_value_true, op=ibinop))
+        setattr(cls, test_ibinop_name('type_pos_int'), partialmethod(_TestNumericField._test_ibinop_type_pos_int, op=ibinop))
+        setattr(cls, test_ibinop_name('type_pos_vint'), partialmethod(_TestNumericField._test_ibinop_type_pos_vint, op=ibinop))
+        setattr(cls, test_ibinop_name('value_pos_int'), partialmethod(_TestNumericField._test_ibinop_value_pos_int, op=ibinop))
+        setattr(cls, test_ibinop_name('value_pos_vint'), partialmethod(_TestNumericField._test_ibinop_value_pos_vint, op=ibinop))
+        setattr(cls, test_ibinop_name('type_neg_int'), partialmethod(_TestNumericField._test_ibinop_type_neg_int, op=ibinop))
+        setattr(cls, test_ibinop_name('type_neg_vint'), partialmethod(_TestNumericField._test_ibinop_type_neg_vint, op=ibinop))
+        setattr(cls, test_ibinop_name('value_neg_int'), partialmethod(_TestNumericField._test_ibinop_value_neg_int, op=ibinop))
+        setattr(cls, test_ibinop_name('value_neg_vint'), partialmethod(_TestNumericField._test_ibinop_value_neg_vint, op=ibinop))
+        setattr(cls, test_ibinop_name('type_false'), partialmethod(_TestNumericField._test_ibinop_type_false, op=ibinop))
+        setattr(cls, test_ibinop_name('value_false'), partialmethod(_TestNumericField._test_ibinop_value_false, op=ibinop))
+        setattr(cls, test_ibinop_name('type_zero_int'), partialmethod(_TestNumericField._test_ibinop_type_zero_int, op=ibinop))
+        setattr(cls, test_ibinop_name('type_zero_vint'), partialmethod(_TestNumericField._test_ibinop_type_zero_vint, op=ibinop))
+        setattr(cls, test_ibinop_name('value_zero_int'), partialmethod(_TestNumericField._test_ibinop_value_zero_int, op=ibinop))
+        setattr(cls, test_ibinop_name('value_zero_vint'), partialmethod(_TestNumericField._test_ibinop_value_zero_vint, op=ibinop))
+        setattr(cls, test_ibinop_name('type_pos_float'), partialmethod(_TestNumericField._test_ibinop_type_pos_float, op=ibinop))
+        setattr(cls, test_ibinop_name('type_neg_float'), partialmethod(_TestNumericField._test_ibinop_type_neg_float, op=ibinop))
+        setattr(cls, test_ibinop_name('type_pos_vfloat'), partialmethod(_TestNumericField._test_ibinop_type_pos_vfloat, op=ibinop))
+        setattr(cls, test_ibinop_name('type_neg_vfloat'), partialmethod(_TestNumericField._test_ibinop_type_neg_vfloat, op=ibinop))
+        setattr(cls, test_ibinop_name('value_pos_float'), partialmethod(_TestNumericField._test_ibinop_value_pos_float, op=ibinop))
+        setattr(cls, test_ibinop_name('value_neg_float'), partialmethod(_TestNumericField._test_ibinop_value_neg_float, op=ibinop))
+        setattr(cls, test_ibinop_name('value_pos_vfloat'), partialmethod(_TestNumericField._test_ibinop_value_pos_vfloat, op=ibinop))
+        setattr(cls, test_ibinop_name('value_neg_vfloat'), partialmethod(_TestNumericField._test_ibinop_value_neg_vfloat, op=ibinop))
+        setattr(cls, test_ibinop_name('type_zero_float'), partialmethod(_TestNumericField._test_ibinop_type_zero_float, op=ibinop))
+        setattr(cls, test_ibinop_name('type_zero_vfloat'), partialmethod(_TestNumericField._test_ibinop_type_zero_vfloat, op=ibinop))
+        setattr(cls, test_ibinop_name('value_zero_float'), partialmethod(_TestNumericField._test_ibinop_value_zero_float, op=ibinop))
+        setattr(cls, test_ibinop_name('value_zero_vfloat'), partialmethod(_TestNumericField._test_ibinop_value_zero_vfloat, op=ibinop))
+
+
+class _TestIntegerFieldCommon(_TestNumericField):
+    def test_assign_true(self):
+        raw = True
+        self._def.value = raw
+        self.assertEqual(self._def, raw)
+        self.assertEqual(self._def.value, raw)
+
+    def test_assign_false(self):
+        raw = False
+        self._def.value = raw
+        self.assertEqual(self._def, raw)
+        self.assertEqual(self._def.value, raw)
+
+    def test_assign_pos_int(self):
+        raw = 477
+        self._def.value = raw
+        self.assertEqual(self._def, raw)
+        self.assertEqual(self._def.value, raw)
+
+    def test_assign_neg_int(self):
+        raw = -13
+        self._def.value = raw
+        self.assertEqual(self._def, raw)
+        self.assertEqual(self._def.value, raw)
+
+    def test_assign_int_field(self):
+        raw = 999
+        field = self._ft()
+        field.value = raw
+        self._def.value = field
+        self.assertEqual(self._def, raw)
+        self.assertEqual(self._def.value, raw)
+
+    def test_assign_float(self):
+        raw = 123.456
+        self._def.value = raw
+        self.assertEqual(self._def, int(raw))
+        self.assertEqual(self._def.value, int(raw))
+
+    def test_assign_invalid_type(self):
+        with self.assertRaises(TypeError):
+            self._def.value = 'yes'
+
+    def test_assign_uint(self):
+        ft = bt2.IntegerFieldType(size=32, is_signed=False)
+        field = ft()
+        raw = 1777
+        field.value = 1777
+        self.assertEqual(field, raw)
+        self.assertEqual(field.value, raw)
+
+    def test_assign_uint_invalid_neg(self):
+        ft = bt2.IntegerFieldType(size=32, is_signed=False)
+        field = ft()
+
+        with self.assertRaises(ValueError):
+            field.value = -23
+
+
+_inject_numeric_testing_methods(_TestIntegerFieldCommon)
+
+
+class IntegerFieldTestCase(_TestIntegerFieldCommon, unittest.TestCase):
+    def setUp(self):
+        self._ft = bt2.IntegerFieldType(25, is_signed=True)
+        self._field = self._ft()
+        self._def = self._ft()
+        self._def.value = 17
+        self._def_value = 17
+        self._def_new_value = -101
+
+
+class EnumerationFieldTestCase(_TestIntegerFieldCommon, unittest.TestCase):
+    def setUp(self):
+        self._ft = bt2.EnumerationFieldType(size=32, is_signed=True)
+        self._ft.append_mapping('whole range', -(2 ** 31), (2 ** 31) - 1)
+        self._def = self._ft()
+        self._def.value = 17
+        self._def_value = 17
+        self._def_new_value = -101
+
+
+class FloatingPointNumberFieldTestCase(_TestNumericField, unittest.TestCase):
+    def setUp(self):
+        self._ft = bt2.FloatingPointNumberFieldType()
+        self._field = self._ft()
+        self._def = self._ft()
+        self._def.value = 52.7
+        self._def_value = 52.7
+        self._def_new_value = -17.164857
+
+    def _test_invalid_op(self, cb):
+        with self.assertRaises(TypeError):
+            cb()
+
+    def test_assign_true(self):
+        self._def.value = True
+        self.assertTrue(self._def)
+        self.assertTrue(self._def.value)
+
+    def test_assign_false(self):
+        self._def.value = False
+        self.assertFalse(self._def)
+        self.assertFalse(self._def.value)
+
+    def test_assign_pos_int(self):
+        raw = 477
+        self._def.value = raw
+        self.assertEqual(self._def, float(raw))
+        self.assertEqual(self._def.value, float(raw))
+
+    def test_assign_neg_int(self):
+        raw = -13
+        self._def.value = raw
+        self.assertEqual(self._def, float(raw))
+        self.assertEqual(self._def.value, float(raw))
+
+    def test_assign_int_field(self):
+        ft = bt2.IntegerFieldType(32)
+        field = ft()
+        raw = 999
+        field.value = raw
+        self._def.value = field
+        self.assertEqual(self._def, float(raw))
+        self.assertEqual(self._def.value, float(raw))
+
+    def test_assign_float(self):
+        raw = -19.23
+        self._def.value = raw
+        self.assertEqual(self._def, raw)
+        self.assertEqual(self._def.value, raw)
+
+    def test_assign_float_field(self):
+        ft = bt2.FloatingPointNumberFieldType(32)
+        field = ft()
+        raw = 101.32
+        field.value = raw
+        self._def.value = field
+        self.assertEqual(self._def, raw)
+        self.assertEqual(self._def.value, raw)
+
+    def test_assign_invalid_type(self):
+        with self.assertRaises(TypeError):
+            self._def.value = 'yes'
+
+    def test_invalid_lshift(self):
+        self._test_invalid_op(lambda: self._def << 23)
+
+    def test_invalid_rshift(self):
+        self._test_invalid_op(lambda: self._def >> 23)
+
+    def test_invalid_and(self):
+        self._test_invalid_op(lambda: self._def & 23)
+
+    def test_invalid_or(self):
+        self._test_invalid_op(lambda: self._def | 23)
+
+    def test_invalid_xor(self):
+        self._test_invalid_op(lambda: self._def ^ 23)
+
+    def test_invalid_invert(self):
+        self._test_invalid_op(lambda: ~self._def)
+
+
+_inject_numeric_testing_methods(FloatingPointNumberFieldTestCase)
+
+
+class StringFieldTestCase(_TestCopySimple, unittest.TestCase):
+    def setUp(self):
+        self._ft = bt2.StringFieldType()
+        self._def_value = 'Hello, World!'
+        self._def = self._ft()
+        self._def.value = self._def_value
+        self._def_new_value = 'Yes!'
+
+    def test_assign_int(self):
+        with self.assertRaises(TypeError):
+            self._def.value = 283
+
+    def test_assign_str(self):
+        raw = 'zorg'
+        self._def = raw
+        self.assertEqual(self._def, raw)
+
+    def test_assign_string_field(self):
+        ft = bt2.StringFieldType()
+        field = ft()
+        raw = 'zorg'
+        field.value = raw
+        self.assertEqual(field, raw)
+
+    def test_eq(self):
+        self.assertEqual(self._def, self._def_value)
+
+    def test_eq(self):
+        self.assertNotEqual(self._def, 23)
+
+    def test_lt_vstring(self):
+        s1 = self._ft()
+        s1.value = 'allo'
+        s2 = self._ft()
+        s2.value = 'bateau'
+        self.assertLess(s1, s2)
+
+    def test_lt_string(self):
+        s1 = self._ft()
+        s1.value = 'allo'
+        self.assertLess(s1, 'bateau')
+
+    def test_le_vstring(self):
+        s1 = self._ft()
+        s1.value = 'allo'
+        s2 = self._ft()
+        s2.value = 'bateau'
+        self.assertLessEqual(s1, s2)
+
+    def test_le_string(self):
+        s1 = self._ft()
+        s1.value = 'allo'
+        self.assertLessEqual(s1, 'bateau')
+
+    def test_gt_vstring(self):
+        s1 = self._ft()
+        s1.value = 'allo'
+        s2 = self._ft()
+        s2.value = 'bateau'
+        self.assertGreater(s2, s1)
+
+    def test_gt_string(self):
+        s1 = self._ft()
+        s1.value = 'allo'
+        self.assertGreater('bateau', s1)
+
+    def test_ge_vstring(self):
+        s1 = self._ft()
+        s1.value = 'allo'
+        s2 = self._ft()
+        s2.value = 'bateau'
+        self.assertGreaterEqual(s2, s1)
+
+    def test_ge_string(self):
+        s1 = self._ft()
+        s1.value = 'allo'
+        self.assertGreaterEqual('bateau', s1)
+
+    def test_bool_op(self):
+        self.assertEqual(bool(self._def), bool(self._def_value))
+
+    def test_str_op(self):
+        self.assertEqual(str(self._def), str(self._def_value))
+
+    def test_len(self):
+        self.assertEqual(len(self._def), len(self._def_value))
+
+    def test_getitem(self):
+        self.assertEqual(self._def[5], self._def_value[5])
+
+    def test_append_str(self):
+        to_append = 'meow meow meow'
+        self._def += to_append
+        self._def_value += to_append
+        self.assertEqual(self._def, self._def_value)
+
+    def test_append_string_field(self):
+        ft = bt2.StringFieldType()
+        field = ft()
+        to_append = 'meow meow meow'
+        field.value = to_append
+        self._def += field
+        self._def_value += to_append
+        self.assertEqual(self._def, self._def_value)
+
+
+class _TestArraySequenceFieldCommon(_TestCopySimple):
+    def _modify_def(self):
+        self._def[2] = 23
+
+    def test_bool_op_true(self):
+        self.assertTrue(self._def)
+
+    def test_len(self):
+        self.assertEqual(len(self._def), 3)
+
+    def test_getitem(self):
+        field = self._def[1]
+        self.assertIs(type(field), bt2.fields._IntegerField)
+        self.assertEqual(field, 1847)
+
+    def test_eq(self):
+        ft = bt2.ArrayFieldType(self._elem_ft, 3)
+        field = ft()
+        field[0] = 45
+        field[1] = 1847
+        field[2] = 1948754
+        self.assertEqual(self._def, field)
+
+    def test_eq_invalid_type(self):
+        self.assertNotEqual(self._def, 23)
+
+    def test_eq_diff_len(self):
+        ft = bt2.ArrayFieldType(self._elem_ft, 2)
+        field = ft()
+        field[0] = 45
+        field[1] = 1847
+        self.assertNotEqual(self._def, field)
+
+    def test_eq_diff_content_same_len(self):
+        ft = bt2.ArrayFieldType(self._elem_ft, 3)
+        field = ft()
+        field[0] = 45
+        field[1] = 1846
+        field[2] = 1948754
+        self.assertNotEqual(self._def, field)
+
+    def test_setitem(self):
+        self._def[2] = 24
+        self.assertEqual(self._def[2], 24)
+
+    def test_setitem_int_field(self):
+        int_field = self._elem_ft()
+        int_field.value = 19487
+        self._def[1] = int_field
+        self.assertEqual(self._def[1], 19487)
+
+    def test_setitem_non_basic_field(self):
+        elem_ft = bt2.StructureFieldType()
+        array_ft = bt2.ArrayFieldType(elem_ft, 3)
+        elem_field = elem_ft()
+        array_field = array_ft()
+
+        with self.assertRaises(TypeError):
+            array_field[1] = 23
+
+    def test_setitem_none(self):
+        with self.assertRaises(TypeError):
+            self._def[1] = None
+
+    def test_setitem_index_wrong_type(self):
+        with self.assertRaises(TypeError):
+            self._def['yes'] = 23
+
+    def test_setitem_index_neg(self):
+        with self.assertRaises(IndexError):
+            self._def[-2] = 23
+
+    def test_setitem_index_out_of_range(self):
+        with self.assertRaises(IndexError):
+            self._def[len(self._def)] = 134679
+
+    def test_iter(self):
+        for field, value in zip(self._def, (45, 1847, 1948754)):
+            self.assertEqual(field, value)
+
+
+class ArrayFieldTestCase(_TestArraySequenceFieldCommon, unittest.TestCase):
+    def setUp(self):
+        self._elem_ft = bt2.IntegerFieldType(32)
+        self._ft = bt2.ArrayFieldType(self._elem_ft, 3)
+        self._def = self._ft()
+        self._def[0] = 45
+        self._def[1] = 1847
+        self._def[2] = 1948754
+
+
+class SequenceFieldTestCase(_TestArraySequenceFieldCommon, unittest.TestCase):
+    def setUp(self):
+        self._elem_ft = bt2.IntegerFieldType(32)
+        self._ft = bt2.SequenceFieldType(self._elem_ft, 'the.length')
+        self._def = self._ft()
+        self._length_field = self._elem_ft(3)
+        self._def.length_field = self._length_field
+        self._def[0] = 45
+        self._def[1] = 1847
+        self._def[2] = 1948754
+
+
+class StructureFieldTestCase(_TestCopySimple, unittest.TestCase):
+    def setUp(self):
+        self._ft0 = bt2.IntegerFieldType(32, is_signed=True)
+        self._ft1 = bt2.StringFieldType()
+        self._ft2 = bt2.FloatingPointNumberFieldType()
+        self._ft3 = bt2.IntegerFieldType(17)
+        self._ft = bt2.StructureFieldType()
+        self._ft.append_field('A', self._ft0)
+        self._ft.append_field('B', self._ft1)
+        self._ft.append_field('C', self._ft2)
+        self._ft.append_field('D', self._ft3)
+        self._def = self._ft()
+        self._def['A'] = -1872
+        self._def['B'] = 'salut'
+        self._def['C'] = 17.5
+        self._def['D'] = 16497
+
+    def _modify_def(self):
+        self._def['B'] = 'hola'
+
+    def test_bool_op_true(self):
+        self.assertTrue(self._def)
+
+    def test_bool_op_false(self):
+        ft = bt2.StructureFieldType()
+        field = ft()
+        self.assertFalse(field)
+
+    def test_len(self):
+        self.assertEqual(len(self._def), 4)
+
+    def test_getitem(self):
+        field = self._def['A']
+        self.assertIs(type(field), bt2.fields._IntegerField)
+        self.assertEqual(field, -1872)
+
+    def test_eq(self):
+        ft = bt2.StructureFieldType()
+        ft.append_field('A', self._ft0)
+        ft.append_field('B', self._ft1)
+        ft.append_field('C', self._ft2)
+        ft.append_field('D', self._ft3)
+        field = ft()
+        field['A'] = -1872
+        field['B'] = 'salut'
+        field['C'] = 17.5
+        field['D'] = 16497
+        self.assertEqual(self._def, field)
+
+    def test_eq_invalid_type(self):
+        self.assertNotEqual(self._def, 23)
+
+    def test_eq_diff_len(self):
+        ft = bt2.StructureFieldType()
+        ft.append_field('A', self._ft0)
+        ft.append_field('B', self._ft1)
+        ft.append_field('C', self._ft2)
+        field = ft()
+        field['A'] = -1872
+        field['B'] = 'salut'
+        field['C'] = 17.5
+        self.assertNotEqual(self._def, field)
+
+    def test_eq_diff_content_same_len(self):
+        ft = bt2.StructureFieldType()
+        ft.append_field('A', self._ft0)
+        ft.append_field('B', self._ft1)
+        ft.append_field('C', self._ft2)
+        ft.append_field('D', self._ft3)
+        field = ft()
+        field['A'] = -1872
+        field['B'] = 'salut'
+        field['C'] = 17.4
+        field['D'] = 16497
+        self.assertNotEqual(self._def, field)
+
+    def test_eq_same_content_diff_keys(self):
+        ft = bt2.StructureFieldType()
+        ft.append_field('A', self._ft0)
+        ft.append_field('B', self._ft1)
+        ft.append_field('E', self._ft2)
+        ft.append_field('D', self._ft3)
+        field = ft()
+        field['A'] = -1872
+        field['B'] = 'salut'
+        field['E'] = 17.4
+        field['D'] = 16497
+        self.assertNotEqual(self._def, field)
+
+    def test_setitem(self):
+        self._def['C'] = -18.47
+        self.assertEqual(self._def['C'], -18.47)
+
+    def test_setitem_int_field(self):
+        int_ft = bt2.IntegerFieldType(16)
+        int_field = int_ft()
+        int_field.value = 19487
+        self._def['D'] = int_field
+        self.assertEqual(self._def['D'], 19487)
+
+    def test_setitem_non_basic_field(self):
+        elem_ft = bt2.StructureFieldType()
+        elem_field = elem_ft()
+        struct_ft = bt2.StructureFieldType()
+        struct_ft.append_field('A', elem_ft)
+        struct_field = struct_ft()
+
+        with self.assertRaises(TypeError):
+            struct_field['A'] = 23
+
+    def test_setitem_none(self):
+        with self.assertRaises(TypeError):
+            self._def['C'] = None
+
+    def test_setitem_key_wrong_type(self):
+        with self.assertRaises(TypeError):
+            self._def[3] = 23
+
+    def test_setitem_wrong_key(self):
+        with self.assertRaises(KeyError):
+            self._def['hi'] = 134679
+
+    def test_at_index(self):
+        self.assertEqual(self._def.at_index(1), 'salut')
+
+    def test_iter(self):
+        orig_values = {
+            'A': -1872,
+            'B': 'salut',
+            'C': 17.5,
+            'D': 16497,
+        }
+
+        for vkey, vval in self._def.items():
+            val = orig_values[vkey]
+            self.assertEqual(vval, val)
diff --git a/tests/bindings/python/bt2/test_packet.py b/tests/bindings/python/bt2/test_packet.py
new file mode 100644 (file)
index 0000000..3ee58a1
--- /dev/null
@@ -0,0 +1,146 @@
+from collections import OrderedDict
+from bt2 import values
+import unittest
+import copy
+import bt2
+
+
+class PacketTestCase(unittest.TestCase):
+    def setUp(self):
+        self._packet = self._create_packet()
+
+    def _create_packet(self, with_ph=True, with_pc=True):
+        # event header
+        eh = bt2.StructureFieldType()
+        eh += OrderedDict((
+            ('id', bt2.IntegerFieldType(8)),
+            ('ts', bt2.IntegerFieldType(32)),
+        ))
+
+        # stream event context
+        sec = bt2.StructureFieldType()
+        sec += OrderedDict((
+            ('cpu_id', bt2.IntegerFieldType(8)),
+            ('stuff', bt2.FloatingPointNumberFieldType()),
+        ))
+
+        # packet context
+        if with_pc:
+            pc = bt2.StructureFieldType()
+            pc += OrderedDict((
+                ('something', bt2.IntegerFieldType(8)),
+                ('something_else', bt2.FloatingPointNumberFieldType()),
+            ))
+        else:
+            pc = None
+
+        # stream class
+        sc = bt2.StreamClass()
+        sc.packet_context_field_type = pc
+        sc.event_header_field_type = eh
+        sc.event_context_field_type = sec
+
+        # event context
+        ec = bt2.StructureFieldType()
+        ec += OrderedDict((
+            ('ant', bt2.IntegerFieldType(16, is_signed=True)),
+            ('msg', bt2.StringFieldType()),
+        ))
+
+        # event payload
+        ep = bt2.StructureFieldType()
+        ep += OrderedDict((
+            ('giraffe', bt2.IntegerFieldType(32)),
+            ('gnu', bt2.IntegerFieldType(8)),
+            ('mosquito', bt2.IntegerFieldType(8)),
+        ))
+
+        # event class
+        event_class = bt2.EventClass('ec')
+        event_class.context_field_type = ec
+        event_class.payload_field_type = ep
+        sc.add_event_class(event_class)
+
+        # packet header
+        if with_ph:
+            ph = bt2.StructureFieldType()
+            ph += OrderedDict((
+                ('magic', bt2.IntegerFieldType(32)),
+                ('stream_id', bt2.IntegerFieldType(16)),
+            ))
+        else:
+            ph = None
+
+        # trace c;ass
+        tc = bt2.Trace()
+        tc.packet_header_field_type = ph
+        tc.add_stream_class(sc)
+
+        # stream
+        stream = sc()
+
+        # packet
+        return stream.create_packet()
+
+    def test_attr_stream(self):
+        self.assertIsNotNone(self._packet.stream)
+
+    def test_get_header_field(self):
+        self.assertIsNotNone(self._packet.header_field)
+
+    def test_no_header_field(self):
+        packet = self._create_packet(with_ph=False)
+        self.assertIsNone(packet.header_field)
+
+    def test_get_context_field(self):
+        self.assertIsNotNone(self._packet.context_field)
+
+    def test_no_context_field(self):
+        packet = self._create_packet(with_pc=False)
+        self.assertIsNone(packet.context_field)
+
+    def _fill_packet(self, packet):
+        packet.header_field['magic'] = 0xc1fc1fc1
+        packet.header_field['stream_id'] = 23
+        packet.context_field['something'] = 17
+        packet.context_field['something_else'] = 188.88
+
+    def test_eq(self):
+        packet1 = self._create_packet()
+        self._fill_packet(packet1)
+        packet2 = self._create_packet()
+        self._fill_packet(packet2)
+        self.assertEqual(packet1, packet2)
+
+    def test_ne_header_field(self):
+        packet1 = self._create_packet()
+        self._fill_packet(packet1)
+        packet2 = self._create_packet()
+        self._fill_packet(packet2)
+        packet2.header_field['stream_id'] = 18
+        self.assertNotEqual(packet1, packet2)
+
+    def test_ne_context_field(self):
+        packet1 = self._create_packet()
+        self._fill_packet(packet1)
+        packet2 = self._create_packet()
+        self._fill_packet(packet2)
+        packet2.context_field['something_else'] = 1948.11
+        self.assertNotEqual(packet1, packet2)
+
+    def test_eq_invalid(self):
+        self.assertFalse(self._packet == 23)
+
+    def _test_copy(self, func):
+        packet = self._create_packet()
+        self._fill_packet(packet)
+        cpy = func(packet)
+        self.assertIsNot(packet, cpy)
+        self.assertNotEqual(packet.addr, cpy.addr)
+        self.assertEqual(packet, cpy)
+
+    def test_copy(self):
+        self._test_copy(copy.copy)
+
+    def test_deepcopy(self):
+        self._test_copy(copy.deepcopy)
diff --git a/tests/bindings/python/bt2/test_stream.py b/tests/bindings/python/bt2/test_stream.py
new file mode 100644 (file)
index 0000000..afeb80f
--- /dev/null
@@ -0,0 +1,106 @@
+from collections import OrderedDict
+from bt2 import values
+import unittest
+import copy
+import bt2
+
+
+class StreamTestCase(unittest.TestCase):
+    def setUp(self):
+        self._stream = self._create_stream()
+
+    def _create_stream(self, name='my_stream'):
+        # event header
+        eh = bt2.StructureFieldType()
+        eh += OrderedDict((
+            ('id', bt2.IntegerFieldType(8)),
+            ('ts', bt2.IntegerFieldType(32)),
+        ))
+
+        # stream event context
+        sec = bt2.StructureFieldType()
+        sec += OrderedDict((
+            ('cpu_id', bt2.IntegerFieldType(8)),
+            ('stuff', bt2.FloatingPointNumberFieldType()),
+        ))
+
+        # packet context
+        pc = bt2.StructureFieldType()
+        pc += OrderedDict((
+            ('something', bt2.IntegerFieldType(8)),
+            ('something_else', bt2.FloatingPointNumberFieldType()),
+        ))
+
+        # stream class
+        sc = bt2.StreamClass()
+        sc.packet_context_field_type = pc
+        sc.event_header_field_type = eh
+        sc.event_context_field_type = sec
+
+        # event context
+        ec = bt2.StructureFieldType()
+        ec += OrderedDict((
+            ('ant', bt2.IntegerFieldType(16, is_signed=True)),
+            ('msg', bt2.StringFieldType()),
+        ))
+
+        # event payload
+        ep = bt2.StructureFieldType()
+        ep += OrderedDict((
+            ('giraffe', bt2.IntegerFieldType(32)),
+            ('gnu', bt2.IntegerFieldType(8)),
+            ('mosquito', bt2.IntegerFieldType(8)),
+        ))
+
+        # event class
+        event_class = bt2.EventClass('ec')
+        event_class.context_field_type = ec
+        event_class.payload_field_type = ep
+        sc.add_event_class(event_class)
+
+        # packet header
+        ph = bt2.StructureFieldType()
+        ph += OrderedDict((
+            ('magic', bt2.IntegerFieldType(32)),
+            ('stream_id', bt2.IntegerFieldType(16)),
+        ))
+
+        # trace c;ass
+        tc = bt2.Trace()
+        tc.packet_header_field_type = ph
+        tc.add_stream_class(sc)
+
+        # stream
+        return sc(name=name)
+
+    def test_attr_stream_class(self):
+        self.assertIsNotNone(self._stream.stream_class)
+
+    def test_attr_name(self):
+        self.assertEqual(self._stream.name, 'my_stream')
+
+    def test_eq(self):
+        stream1 = self._create_stream()
+        stream2 = self._create_stream()
+        self.assertEqual(stream1, stream2)
+
+    def test_ne_name(self):
+        stream1 = self._create_stream()
+        stream2 = self._create_stream('lel')
+        self.assertNotEqual(stream1, stream2)
+
+    def test_eq_invalid(self):
+        self.assertFalse(self._stream == 23)
+
+    def _test_copy(self, func):
+        stream = self._create_stream()
+        cpy = func(stream)
+        self.assertIsNot(stream, cpy)
+        self.assertNotEqual(stream.addr, cpy.addr)
+        self.assertEqual(stream, cpy)
+
+    def test_copy(self):
+        self._test_copy(copy.copy)
+
+    def test_deepcopy(self):
+        self._test_copy(copy.deepcopy)
diff --git a/tests/bindings/python/bt2/test_stream_class.py b/tests/bindings/python/bt2/test_stream_class.py
new file mode 100644 (file)
index 0000000..84bf286
--- /dev/null
@@ -0,0 +1,259 @@
+from bt2 import values
+import unittest
+import copy
+import bt2
+
+
+class StreamClassTestCase(unittest.TestCase):
+    def setUp(self):
+        self._packet_context_ft = bt2.StructureFieldType()
+        self._packet_context_ft.append_field('menu', bt2.FloatingPointNumberFieldType())
+        self._packet_context_ft.append_field('sticker', bt2.StringFieldType())
+        self._event_header_ft = bt2.StructureFieldType()
+        self._event_header_ft.append_field('id', bt2.IntegerFieldType(19))
+        self._event_context_ft = bt2.StructureFieldType()
+        self._event_context_ft.append_field('msg', bt2.StringFieldType())
+        self._ec1, self._ec2 = self._create_event_classes()
+        self._sc = bt2.StreamClass(name='my_stream_class', id=12,
+                                   packet_context_field_type=self._packet_context_ft,
+                                   event_header_field_type=self._event_header_ft,
+                                   event_context_field_type=self._event_context_ft,
+                                   event_classes=(self._ec1, self._ec2))
+
+    def _create_event_classes(self):
+        context_ft = bt2.StructureFieldType()
+        context_ft.append_field('allo', bt2.StringFieldType())
+        context_ft.append_field('zola', bt2.IntegerFieldType(18))
+        payload_ft = bt2.StructureFieldType()
+        payload_ft.append_field('zoom', bt2.StringFieldType())
+        ec1 = bt2.EventClass('event23', id=23, context_field_type=context_ft,
+                             payload_field_type=payload_ft)
+        ec2 = bt2.EventClass('event17', id=17, context_field_type=payload_ft,
+                             payload_field_type=context_ft)
+        return ec1, ec2
+
+    def test_create(self):
+        self.assertEqual(self._sc.name, 'my_stream_class')
+        self.assertEqual(self._sc.id, 12)
+        self.assertEqual(self._sc.packet_context_field_type, self._packet_context_ft)
+        self.assertEqual(self._sc.event_header_field_type, self._event_header_ft)
+        self.assertEqual(self._sc.event_context_field_type, self._event_context_ft)
+        self.assertEqual(self._sc['event23'], self._ec1)
+        self.assertEqual(self._sc['event17'], self._ec2)
+
+    def test_assign_name(self):
+        self._sc.name = 'lel'
+        self.assertEqual(self._sc.name, 'lel')
+
+    def test_assign_invalid_name(self):
+        with self.assertRaises(TypeError):
+            self._sc.name = 17
+
+    def test_assign_id(self):
+        self._sc.id = 1717
+        self.assertEqual(self._sc.id, 1717)
+
+    def test_assign_invalid_id(self):
+        with self.assertRaises(TypeError):
+            self._sc.id = 'lel'
+
+    def test_assign_packet_context_field_type(self):
+        self._sc.packet_context_field_type = self._event_context_ft
+        self.assertEqual(self._sc.packet_context_field_type, self._event_context_ft)
+
+    def test_assign_no_packet_context_field_type(self):
+        self._sc.packet_context_field_type = None
+        self.assertIsNone(self._sc.packet_context_field_type)
+
+    def test_assign_invalid_packet_context_field_type(self):
+        with self.assertRaises(TypeError):
+            self._sc.packet_context_field_type = 'lel'
+
+    def test_assign_event_header_field_type(self):
+        self._sc.event_header_field_type = self._event_header_ft
+        self.assertEqual(self._sc.event_header_field_type, self._event_header_ft)
+
+    def test_assign_no_event_header_field_type(self):
+        self._sc.event_header_field_type = None
+        self.assertIsNone(self._sc.event_header_field_type)
+
+    def test_assign_invalid_event_header_field_type(self):
+        with self.assertRaises(TypeError):
+            self._sc.event_header_field_type = 'lel'
+
+    def test_assign_event_context_field_type(self):
+        self._sc.event_context_field_type = self._packet_context_ft
+        self.assertEqual(self._sc.event_context_field_type, self._packet_context_ft)
+
+    def test_assign_no_event_context_field_type(self):
+        self._sc.event_context_field_type = None
+        self.assertIsNone(self._sc.event_context_field_type)
+
+    def test_assign_invalid_event_context_field_type(self):
+        with self.assertRaises(TypeError):
+            self._sc.event_context_field_type = 'lel'
+
+    def test_trace_prop_no_tc(self):
+        self.assertIsNone(self._sc.trace)
+
+    def test_trace_prop(self):
+        tc = bt2.Trace()
+        tc.add_stream_class(self._sc)
+        self.assertEqual(self._sc.trace.addr, tc.addr)
+
+    def _test_copy(self, cpy):
+        self.assertIsNot(cpy, self._sc)
+        self.assertNotEqual(cpy.addr, self._sc.addr)
+        self.assertEqual(cpy, self._sc)
+
+    def test_copy(self):
+        cpy = copy.copy(self._sc)
+        self._test_copy(cpy)
+        self.assertEqual(self._sc.packet_context_field_type.addr, cpy.packet_context_field_type.addr)
+        self.assertEqual(self._sc.event_header_field_type.addr, cpy.event_header_field_type.addr)
+        self.assertEqual(self._sc.event_context_field_type.addr, cpy.event_context_field_type.addr)
+
+    def test_deepcopy(self):
+        cpy = copy.deepcopy(self._sc)
+        self._test_copy(cpy)
+        self.assertNotEqual(self._sc.packet_context_field_type.addr, cpy.packet_context_field_type.addr)
+        self.assertNotEqual(self._sc.event_header_field_type.addr, cpy.event_header_field_type.addr)
+        self.assertNotEqual(self._sc.event_context_field_type.addr, cpy.event_context_field_type.addr)
+
+    def test_getitem(self):
+        self.assertEqual(self._sc['event23'], self._ec1)
+        self.assertEqual(self._sc['event17'], self._ec2)
+
+    def test_getitem_wrong_key_type(self):
+        with self.assertRaises(TypeError):
+            self._sc[23]
+
+    def test_getitem_wrong_key(self):
+        with self.assertRaises(KeyError):
+            self._sc['hello']
+
+    def test_len(self):
+        self.assertEqual(len(self._sc), 2)
+
+    def test_iter(self):
+        for name, event_class in self._sc.items():
+            self.assertIsInstance(event_class, bt2.EventClass)
+
+            if name == 'event23':
+                self.assertEqual(event_class, self._ec1)
+            elif name == 'event17':
+                self.assertEqual(event_class, self._ec2)
+
+    def test_event_class_with_id(self):
+        self.assertEqual(self._sc.event_class_with_id(23), self._ec1)
+
+    def test_event_class_with_id_wrong_type(self):
+        with self.assertRaises(TypeError):
+            self._sc.event_class_with_id('yes')
+
+    def test_eq(self):
+        ec1, ec2 = self._create_event_classes()
+        sc1 = bt2.StreamClass(name='my_stream_class', id=12,
+                              packet_context_field_type=self._packet_context_ft,
+                              event_header_field_type=self._event_header_ft,
+                              event_context_field_type=self._event_context_ft,
+                              event_classes=(ec1, ec2))
+        ec1, ec2 = self._create_event_classes()
+        sc2 = bt2.StreamClass(name='my_stream_class', id=12,
+                              packet_context_field_type=self._packet_context_ft,
+                              event_header_field_type=self._event_header_ft,
+                              event_context_field_type=self._event_context_ft,
+                              event_classes=(ec1, ec2))
+        self.assertEqual(sc1, sc2)
+
+    def test_ne_name(self):
+        ec1, ec2 = self._create_event_classes()
+        sc1 = bt2.StreamClass(name='my_stream_class1', id=12,
+                              packet_context_field_type=self._packet_context_ft,
+                              event_header_field_type=self._event_header_ft,
+                              event_context_field_type=self._event_context_ft,
+                              event_classes=(ec1, ec2))
+        ec1, ec2 = self._create_event_classes()
+        sc2 = bt2.StreamClass(name='my_stream_class', id=12,
+                              packet_context_field_type=self._packet_context_ft,
+                              event_header_field_type=self._event_header_ft,
+                              event_context_field_type=self._event_context_ft,
+                              event_classes=(ec1, ec2))
+        self.assertNotEqual(sc1, sc2)
+
+    def test_ne_id(self):
+        ec1, ec2 = self._create_event_classes()
+        sc1 = bt2.StreamClass(name='my_stream_class', id=13,
+                              packet_context_field_type=self._packet_context_ft,
+                              event_header_field_type=self._event_header_ft,
+                              event_context_field_type=self._event_context_ft,
+                              event_classes=(ec1, ec2))
+        ec1, ec2 = self._create_event_classes()
+        sc2 = bt2.StreamClass(name='my_stream_class', id=12,
+                              packet_context_field_type=self._packet_context_ft,
+                              event_header_field_type=self._event_header_ft,
+                              event_context_field_type=self._event_context_ft,
+                              event_classes=(ec1, ec2))
+        self.assertNotEqual(sc1, sc2)
+
+    def test_ne_packet_context_field_type(self):
+        ec1, ec2 = self._create_event_classes()
+        sc1 = bt2.StreamClass(name='my_stream_class', id=12,
+                              packet_context_field_type=self._event_context_ft,
+                              event_header_field_type=self._event_header_ft,
+                              event_context_field_type=self._event_context_ft,
+                              event_classes=(ec1, ec2))
+        ec1, ec2 = self._create_event_classes()
+        sc2 = bt2.StreamClass(name='my_stream_class', id=12,
+                              packet_context_field_type=self._packet_context_ft,
+                              event_header_field_type=self._event_header_ft,
+                              event_context_field_type=self._event_context_ft,
+                              event_classes=(ec1, ec2))
+        self.assertNotEqual(sc1, sc2)
+
+    def test_ne_event_header_field_type(self):
+        ec1, ec2 = self._create_event_classes()
+        sc1 = bt2.StreamClass(name='my_stream_class', id=12,
+                              packet_context_field_type=self._packet_context_ft,
+                              event_context_field_type=self._event_context_ft,
+                              event_classes=(ec1, ec2))
+        ec1, ec2 = self._create_event_classes()
+        sc2 = bt2.StreamClass(name='my_stream_class', id=12,
+                              packet_context_field_type=self._packet_context_ft,
+                              event_header_field_type=self._event_header_ft,
+                              event_context_field_type=self._event_context_ft,
+                              event_classes=(ec1, ec2))
+        self.assertNotEqual(sc1, sc2)
+
+    def test_ne_event_context_field_type(self):
+        ec1, ec2 = self._create_event_classes()
+        sc1 = bt2.StreamClass(name='my_stream_class', id=12,
+                              packet_context_field_type=self._packet_context_ft,
+                              event_header_field_type=self._event_header_ft,
+                              event_context_field_type=self._packet_context_ft,
+                              event_classes=(ec1, ec2))
+        ec1, ec2 = self._create_event_classes()
+        sc2 = bt2.StreamClass(name='my_stream_class', id=12,
+                              packet_context_field_type=self._packet_context_ft,
+                              event_header_field_type=self._event_header_ft,
+                              event_context_field_type=self._event_context_ft,
+                              event_classes=(ec1, ec2))
+        self.assertNotEqual(sc1, sc2)
+
+    def test_ne_event_class(self):
+        ec1, ec2 = self._create_event_classes()
+        sc1 = bt2.StreamClass(name='my_stream_class', id=12,
+                              packet_context_field_type=self._packet_context_ft,
+                              event_header_field_type=self._event_header_ft,
+                              event_context_field_type=self._event_context_ft,
+                              event_classes=(ec1,))
+        ec1, ec2 = self._create_event_classes()
+        sc2 = bt2.StreamClass(name='my_stream_class', id=12,
+                              packet_context_field_type=self._packet_context_ft,
+                              event_header_field_type=self._event_header_ft,
+                              event_context_field_type=self._event_context_ft,
+                              event_classes=(ec1, ec2))
+        self.assertNotEqual(sc1, sc2)
+
+    def test_eq_invalid(self):
+        self.assertFalse(self._sc == 23)
diff --git a/tests/bindings/python/bt2/test_trace.py b/tests/bindings/python/bt2/test_trace.py
new file mode 100644 (file)
index 0000000..b6979a4
--- /dev/null
@@ -0,0 +1,307 @@
+from bt2 import values
+import unittest
+import copy
+import uuid
+import bt2
+
+
+class TraceTestCase(unittest.TestCase):
+    def setUp(self):
+        self._sc = self._create_stream_class('sc1', 3)
+        self._tc = bt2.Trace()
+
+    def _create_stream_class(self, name, id):
+        ec1, ec2 = self._create_event_classes()
+        packet_context_ft = bt2.StructureFieldType()
+        packet_context_ft.append_field('menu', bt2.FloatingPointNumberFieldType())
+        packet_context_ft.append_field('sticker', bt2.StringFieldType())
+        event_header_ft = bt2.StructureFieldType()
+        event_header_ft.append_field('id', bt2.IntegerFieldType(19))
+        event_context_ft = bt2.StructureFieldType()
+        event_context_ft.append_field('msg', bt2.StringFieldType())
+        return bt2.StreamClass(name=name, id=id,
+                               packet_context_field_type=packet_context_ft,
+                               event_header_field_type=event_header_ft,
+                               event_context_field_type=event_context_ft,
+                               event_classes=(ec1, ec2))
+
+    def _create_event_classes(self):
+        context_ft = bt2.StructureFieldType()
+        context_ft.append_field('allo', bt2.StringFieldType())
+        context_ft.append_field('zola', bt2.IntegerFieldType(18))
+        payload_ft = bt2.StructureFieldType()
+        payload_ft.append_field('zoom', bt2.StringFieldType())
+        ec1 = bt2.EventClass('event23', id=23, context_field_type=context_ft,
+                             payload_field_type=payload_ft)
+        ec2 = bt2.EventClass('event17', id=17, context_field_type=payload_ft,
+                             payload_field_type=context_ft)
+        return ec1, ec2
+
+    def test_create_default(self):
+        self.assertEqual(len(self._tc), 0)
+
+    def test_create_full(self):
+        header_ft = bt2.StructureFieldType()
+        header_ft.append_field('magic', bt2.IntegerFieldType(32))
+        clock_classes = bt2.ClockClass('cc1'), bt2.ClockClass('cc2')
+        sc = self._create_stream_class('sc1', 3)
+        tc = bt2.Trace(name='my name',
+                       native_byte_order=bt2.ByteOrder.LITTLE_ENDIAN,
+                       env={'the_string': 'value', 'the_int': 23},
+                       packet_header_field_type=header_ft,
+                       clock_classes=clock_classes,
+                       stream_classes=(sc,))
+        self.assertEqual(tc.name, 'my name')
+        self.assertEqual(tc.native_byte_order, bt2.ByteOrder.LITTLE_ENDIAN)
+        self.assertEqual(tc.env['the_string'], 'value')
+        self.assertEqual(tc.env['the_int'], 23)
+        self.assertEqual(tc.packet_header_field_type, header_ft)
+        self.assertEqual(tc.clock_classes['cc1'], clock_classes[0])
+        self.assertEqual(tc.clock_classes['cc2'], clock_classes[1])
+        self.assertEqual(tc[3], sc)
+
+    def test_assign_name(self):
+        self._tc.name = 'lel'
+        self.assertEqual(self._tc.name, 'lel')
+
+    def test_assign_invalid_name(self):
+        with self.assertRaises(TypeError):
+            self._tc.name = 17
+
+    def test_assign_native_byte_order(self):
+        self._tc.native_byte_order = bt2.ByteOrder.BIG_ENDIAN
+        self.assertEqual(self._tc.native_byte_order, bt2.ByteOrder.BIG_ENDIAN)
+
+    def test_assign_invalid_native_byte_order(self):
+        with self.assertRaises(TypeError):
+            self._tc.native_byte_order = 'lel'
+
+    def test_assign_packet_header_field_type(self):
+        header_ft = bt2.StructureFieldType()
+        header_ft.append_field('magic', bt2.IntegerFieldType(32))
+        self._tc.packet_header_field_type = header_ft
+        self.assertEqual(self._tc.packet_header_field_type, header_ft)
+
+    def test_assign_no_packet_header_field_type(self):
+        self._tc.packet_header_field_type = None
+        self.assertIsNone(self._tc.packet_header_field_type)
+
+    def _test_copy(self, cpy):
+        self.assertIsNot(cpy, self._tc)
+        self.assertNotEqual(cpy.addr, self._tc.addr)
+        self.assertEqual(cpy, self._tc)
+        self.assertEqual(len(self._tc), len(cpy))
+
+    def _pre_copy(self):
+        self._tc.name = 'the trace class'
+        sc1 = self._create_stream_class('sc1', 3)
+        sc2 = self._create_stream_class('sc2', 9)
+        sc3 = self._create_stream_class('sc3', 17)
+        self._tc.add_clock_class(bt2.ClockClass('cc1'))
+        self._tc.add_clock_class(bt2.ClockClass('cc2'))
+        self._tc.env['allo'] = 'bateau'
+        self._tc.env['bateau'] = 'cart'
+        self._tc.add_stream_class(sc1)
+        self._tc.add_stream_class(sc2)
+        self._tc.add_stream_class(sc3)
+
+    def test_copy(self):
+        self._pre_copy()
+        cpy = copy.copy(self._tc)
+        self._test_copy(cpy)
+        self.assertEqual(self._tc.packet_header_field_type.addr, cpy.packet_header_field_type.addr)
+        self.assertEqual(self._tc.clock_classes['cc1'].addr, cpy.clock_classes['cc1'].addr)
+        self.assertEqual(self._tc.clock_classes['cc2'].addr, cpy.clock_classes['cc2'].addr)
+        self.assertEqual(self._tc.env['allo'].addr, cpy.env['allo'].addr)
+        self.assertEqual(self._tc.env['bateau'].addr, cpy.env['bateau'].addr)
+
+    def test_deepcopy(self):
+        self._pre_copy()
+        cpy = copy.deepcopy(self._tc)
+        self._test_copy(cpy)
+        self.assertNotEqual(self._tc.packet_header_field_type.addr, cpy.packet_header_field_type.addr)
+        self.assertNotEqual(self._tc.clock_classes['cc1'].addr, cpy.clock_classes['cc1'].addr)
+        self.assertNotEqual(self._tc.clock_classes['cc2'].addr, cpy.clock_classes['cc2'].addr)
+        self.assertNotEqual(self._tc.env['allo'].addr, cpy.env['allo'].addr)
+        self.assertNotEqual(self._tc.env['bateau'].addr, cpy.env['bateau'].addr)
+
+    def test_getitem(self):
+        self._tc.add_stream_class(self._sc)
+        self.assertEqual(self._tc[3].addr, self._sc.addr)
+
+    def test_getitem_wrong_key_type(self):
+        self._tc.add_stream_class(self._sc)
+        with self.assertRaises(TypeError):
+            self._tc['hello']
+
+    def test_getitem_wrong_key(self):
+        self._tc.add_stream_class(self._sc)
+        with self.assertRaises(KeyError):
+            self._tc[4]
+
+    def test_len(self):
+        self.assertEqual(len(self._tc), 0)
+        self._tc.add_stream_class(self._sc)
+        self.assertEqual(len(self._tc), 1)
+
+    def test_iter(self):
+        sc1 = self._create_stream_class('sc1', 3)
+        sc2 = self._create_stream_class('sc2', 9)
+        sc3 = self._create_stream_class('sc3', 17)
+        self._tc.add_stream_class(sc1)
+        self._tc.add_stream_class(sc2)
+        self._tc.add_stream_class(sc3)
+
+        for sid, stream_class in self._tc.items():
+            self.assertIsInstance(stream_class, bt2.StreamClass)
+
+            if sid == 3:
+                self.assertEqual(stream_class.addr, sc1.addr)
+            elif sid == 9:
+                self.assertEqual(stream_class.addr, sc2.addr)
+            elif sid == 17:
+                self.assertEqual(stream_class.addr, sc3.addr)
+
+    def test_env_getitem_wrong_key(self):
+        with self.assertRaises(KeyError):
+            self._tc.env['lel']
+
+    def test_clock_classes_getitem_wrong_key(self):
+        with self.assertRaises(KeyError):
+            self._tc.clock_classes['lel']
+
+    def _test_eq_create_objects(self):
+        cc1_uuid = uuid.UUID('bc7f2f2d-2ee4-4e03-ab1f-2e0e1304e94f')
+        cc1 = bt2.ClockClass('cc1', uuid=cc1_uuid)
+        cc2_uuid = uuid.UUID('da7d6b6f-3108-4706-89bd-ab554732611b')
+        cc2 = bt2.ClockClass('cc2', uuid=cc2_uuid)
+        sc1 = self._create_stream_class('sc1', 3)
+        sc2 = self._create_stream_class('sc2', 9)
+        header_ft = bt2.StructureFieldType()
+        header_ft.append_field('magic', bt2.IntegerFieldType(32))
+        return cc1, cc2, sc1, sc2, header_ft
+
+    def test_eq(self):
+        cc1, cc2, sc1, sc2, header_ft = self._test_eq_create_objects()
+        tc1 = bt2.Trace(name='my name',
+                        native_byte_order=bt2.ByteOrder.LITTLE_ENDIAN,
+                        env={'the_string': 'value', 'the_int': 23},
+                        packet_header_field_type=header_ft,
+                        clock_classes=(cc1, cc2),
+                        stream_classes=(sc1, sc2))
+        cc1, cc2, sc1, sc2, header_ft = self._test_eq_create_objects()
+        tc2 = bt2.Trace(name='my name',
+                        native_byte_order=bt2.ByteOrder.LITTLE_ENDIAN,
+                        env={'the_string': 'value', 'the_int': 23},
+                        packet_header_field_type=header_ft,
+                        clock_classes=(cc1, cc2),
+                        stream_classes=(sc1, sc2))
+        self.assertEqual(tc1, tc2)
+
+    def test_ne_name(self):
+        cc1, cc2, sc1, sc2, header_ft = self._test_eq_create_objects()
+        tc1 = bt2.Trace(name='my name2',
+                        native_byte_order=bt2.ByteOrder.LITTLE_ENDIAN,
+                        env={'the_string': 'value', 'the_int': 23},
+                        packet_header_field_type=header_ft,
+                        clock_classes=(cc1, cc2),
+                        stream_classes=(sc1, sc2))
+        cc1, cc2, sc1, sc2, header_ft = self._test_eq_create_objects()
+        tc2 = bt2.Trace(name='my name',
+                        native_byte_order=bt2.ByteOrder.LITTLE_ENDIAN,
+                        env={'the_string': 'value', 'the_int': 23},
+                        packet_header_field_type=header_ft,
+                        clock_classes=(cc1, cc2),
+                        stream_classes=(sc1, sc2))
+        self.assertNotEqual(tc1, tc2)
+
+    def test_ne_packet_header_field_type(self):
+        cc1, cc2, sc1, sc2, header_ft = self._test_eq_create_objects()
+        tc1 = bt2.Trace(name='my name',
+                        native_byte_order=bt2.ByteOrder.LITTLE_ENDIAN,
+                        env={'the_string': 'value', 'the_int': 23},
+                        packet_header_field_type=header_ft,
+                        clock_classes=(cc1, cc2),
+                        stream_classes=(sc1, sc2))
+        cc1, cc2, sc1, sc2, header_ft = self._test_eq_create_objects()
+        header_ft.append_field('yes', bt2.StringFieldType())
+        tc2 = bt2.Trace(name='my name',
+                        native_byte_order=bt2.ByteOrder.LITTLE_ENDIAN,
+                        env={'the_string': 'value', 'the_int': 23},
+                        packet_header_field_type=header_ft,
+                        clock_classes=(cc1, cc2),
+                        stream_classes=(sc1, sc2))
+        self.assertNotEqual(tc1, tc2)
+
+    def test_ne_native_byte_order(self):
+        cc1, cc2, sc1, sc2, header_ft = self._test_eq_create_objects()
+        tc1 = bt2.Trace(name='my name',
+                        native_byte_order=bt2.ByteOrder.LITTLE_ENDIAN,
+                        env={'the_string': 'value', 'the_int': 23},
+                        packet_header_field_type=header_ft,
+                        clock_classes=(cc1, cc2),
+                        stream_classes=(sc1, sc2))
+        cc1, cc2, sc1, sc2, header_ft = self._test_eq_create_objects()
+        tc2 = bt2.Trace(name='my name',
+                        native_byte_order=bt2.ByteOrder.BIG_ENDIAN,
+                        env={'the_string': 'value', 'the_int': 23},
+                        packet_header_field_type=header_ft,
+                        clock_classes=(cc1, cc2),
+                        stream_classes=(sc1, sc2))
+        self.assertNotEqual(tc1, tc2)
+
+    def test_ne_env(self):
+        cc1, cc2, sc1, sc2, header_ft = self._test_eq_create_objects()
+        tc1 = bt2.Trace(name='my name',
+                        native_byte_order=bt2.ByteOrder.LITTLE_ENDIAN,
+                        env={'the_string': 'value', 'the_int2': 23},
+                        packet_header_field_type=header_ft,
+                        clock_classes=(cc1, cc2),
+                        stream_classes=(sc1, sc2))
+        cc1, cc2, sc1, sc2, header_ft = self._test_eq_create_objects()
+        tc2 = bt2.Trace(name='my name',
+                        native_byte_order=bt2.ByteOrder.LITTLE_ENDIAN,
+                        env={'the_string': 'value', 'the_int': 23},
+                        packet_header_field_type=header_ft,
+                        clock_classes=(cc1, cc2),
+                        stream_classes=(sc1, sc2))
+        self.assertNotEqual(tc1, tc2)
+
+    def test_ne_clock_classes(self):
+        cc1, cc2, sc1, sc2, header_ft = self._test_eq_create_objects()
+        tc1 = bt2.Trace(name='my name',
+                        native_byte_order=bt2.ByteOrder.LITTLE_ENDIAN,
+                        env={'the_string': 'value', 'the_int': 23},
+                        packet_header_field_type=header_ft,
+                        clock_classes=(cc1, cc2),
+                        stream_classes=(sc1, sc2))
+        cc1, cc2, sc1, sc2, header_ft = self._test_eq_create_objects()
+        cc2.frequency = 1234
+        tc2 = bt2.Trace(name='my name',
+                        native_byte_order=bt2.ByteOrder.LITTLE_ENDIAN,
+                        env={'the_string': 'value', 'the_int': 23},
+                        packet_header_field_type=header_ft,
+                        clock_classes=(cc1, cc2),
+                        stream_classes=(sc1, sc2))
+        self.assertNotEqual(tc1, tc2)
+
+    def test_ne_stream_classes(self):
+        cc1, cc2, sc1, sc2, header_ft = self._test_eq_create_objects()
+        tc1 = bt2.Trace(name='my name',
+                        native_byte_order=bt2.ByteOrder.LITTLE_ENDIAN,
+                        env={'the_string': 'value', 'the_int': 23},
+                        packet_header_field_type=header_ft,
+                        clock_classes=(cc1, cc2),
+                        stream_classes=(sc1, sc2))
+        cc1, cc2, sc1, sc2, header_ft = self._test_eq_create_objects()
+        sc2.id = 72632
+        tc2 = bt2.Trace(name='my name',
+                        native_byte_order=bt2.ByteOrder.LITTLE_ENDIAN,
+                        env={'the_string': 'value', 'the_int': 23},
+                        packet_header_field_type=header_ft,
+                        clock_classes=(cc1, cc2),
+                        stream_classes=(sc1, sc2))
+        self.assertNotEqual(tc1, tc2)
+
+    def test_eq_invalid(self):
+        self.assertFalse(self._tc == 23)
diff --git a/tests/bindings/python/bt2/test_values.py b/tests/bindings/python/bt2/test_values.py
new file mode 100644 (file)
index 0000000..f97af21
--- /dev/null
@@ -0,0 +1,1562 @@
+from functools import partial, partialmethod
+import operator
+import unittest
+import numbers
+import math
+import copy
+import bt2
+
+
+class _TestFrozen:
+    def test_is_frozen(self):
+        self._def.freeze()
+        self.assertTrue(self._def.is_frozen)
+
+    def test_frozen(self):
+        self._def.freeze()
+        self.assertTrue(self._def.frozen)
+
+    def test_frozen_exc(self):
+        self._def.freeze()
+
+        with self.assertRaisesRegex(bt2.FrozenError, r'.* value object is frozen$') as cm:
+            self._modify_def()
+
+        self.assertEqual(self._def, self._def_value)
+
+    def test_get_value_when_frozen(self):
+        self._def.freeze()
+        self.assertEqual(self._def, self._def_value)
+
+
+class _TestFrozenSimple(_TestFrozen):
+    def _modify_def(self):
+        self._def.value = self._def_new_value
+
+
+class _TestCopySimple:
+    def test_copy(self):
+        cpy = copy.copy(self._def)
+        self.assertIsNot(cpy, self._def)
+        self.assertNotEqual(cpy.addr, self._def.addr)
+        self.assertEqual(cpy, self._def)
+
+    def test_deepcopy(self):
+        cpy = copy.deepcopy(self._def)
+        self.assertIsNot(cpy, self._def)
+        self.assertNotEqual(cpy.addr, self._def.addr)
+        self.assertEqual(cpy, self._def)
+
+
+_COMP_BINOPS = (
+    operator.eq,
+    operator.ne,
+)
+
+
+class _TestNumericValue(_TestFrozenSimple, _TestCopySimple):
+    def _binop(self, op, rhs):
+        rexc = None
+        rvexc = None
+        comp_value = rhs
+
+        if type(rhs) in (bt2.BoolValue, bt2.IntegerValue, bt2.FloatValue):
+            comp_value = rhs.value
+
+        try:
+            r = op(self._def, rhs)
+        except Exception as e:
+            rexc = e
+
+        try:
+            rv = op(self._def_value, comp_value)
+        except Exception as e:
+            rvexc = e
+
+        if rexc is not None or rvexc is not None:
+            # at least one of the operations raised an exception: in
+            # this case both operations should have raised the same
+            # type of exception (division by zero, bit shift with a
+            # floating point number operand, etc.)
+            self.assertIs(type(rexc), type(rvexc))
+            return None, None
+
+        return r, rv
+
+    def _unaryop(self, op):
+        rexc = None
+        rvexc = None
+
+        try:
+            r = op(self._def)
+        except Exception as e:
+            rexc = e
+
+        try:
+            rv = op(self._def_value)
+        except Exception as e:
+            rvexc = e
+
+        if rexc is not None or rvexc is not None:
+            # at least one of the operations raised an exception: in
+            # this case both operations should have raised the same
+            # type of exception (division by zero, bit shift with a
+            # floating point number operand, etc.)
+            self.assertIs(type(rexc), type(rvexc))
+            return None, None
+
+        return r, rv
+
+    def _test_unaryop_type(self, op):
+        r, rv = self._unaryop(op)
+
+        if r is None:
+            return
+
+        self.assertIsInstance(r, type(rv))
+
+    def _test_unaryop_value(self, op):
+        r, rv = self._unaryop(op)
+
+        if r is None:
+            return
+
+        self.assertEqual(r, rv)
+
+    def _test_unaryop_addr_same(self, op):
+        addr_before = self._def.addr
+        self._unaryop(op)
+        self.assertEqual(self._def.addr, addr_before)
+
+    def _test_unaryop_value_same(self, op):
+        value_before = self._def.value
+        self._unaryop(op)
+        self.assertEqual(self._def.value, value_before)
+
+    def _test_binop_type(self, op, rhs):
+        r, rv = self._binop(op, rhs)
+
+        if r is None:
+            return
+
+        if op in _COMP_BINOPS:
+            # __eq__() and __ne__() always return a 'bool' object
+            self.assertIsInstance(r, bool)
+        else:
+            self.assertIsInstance(r, type(rv))
+
+    def _test_binop_value(self, op, rhs):
+        r, rv = self._binop(op, rhs)
+
+        if r is None:
+            return
+
+        self.assertEqual(r, rv)
+
+    def _test_binop_lhs_addr_same(self, op, rhs):
+        addr_before = self._def.addr
+        r, rv = self._binop(op, rhs)
+        self.assertEqual(self._def.addr, addr_before)
+
+    def _test_binop_lhs_value_same(self, op, rhs):
+        value_before = self._def.value
+        r, rv = self._binop(op, rhs)
+        self.assertEqual(self._def.value, value_before)
+
+    def _test_binop_invalid_unknown(self, op):
+        if op in _COMP_BINOPS:
+            self.skipTest('not testing')
+
+        class A:
+            pass
+
+        with self.assertRaises(TypeError):
+            op(self._def, A())
+
+    def _test_binop_invalid_none(self, op):
+        if op in _COMP_BINOPS:
+            self.skipTest('not testing')
+
+        with self.assertRaises(TypeError):
+            op(self._def, None)
+
+    def _test_ibinop_value(self, op, rhs):
+        r, rv = self._binop(op, rhs)
+
+        if r is None:
+            return
+
+        # The inplace operators are special for value objects because
+        # they do not return a new, immutable object like it's the case
+        # for Python numbers. In Python, `a += 2`, where `a` is a number
+        # object, assigns a new number object reference to `a`, dropping
+        # the old reference. Since BT's value objects are mutable, we
+        # modify their internal value with the inplace operators. This
+        # means however that we can lose data in the process, for
+        # example:
+        #
+        #     int_value_obj += 3.3
+        #
+        # Here, if `int_value_obj` is a Python `int` with the value 2,
+        # it would be a `float` object after this, holding the value
+        # 5.3. In our case, if `int_value_obj` is an integer value
+        # object, 3.3 is converted to an `int` object (3) and added to
+        # the current value of `int_value_obj`, so after this the value
+        # of the object is 5. This does not compare to 5.3, which is
+        # why we also use the `int()` type here.
+        if type(self._def) is bt2.IntegerValue:
+            rv = int(rv)
+
+        self.assertEqual(r, rv)
+
+    def _test_ibinop_type(self, op, rhs):
+        r, rv = self._binop(op, rhs)
+
+        if r is None:
+            return
+
+        self.assertIs(r, self._def)
+
+    def _test_ibinop_invalid_unknown(self, op):
+        class A:
+            pass
+
+        with self.assertRaises(TypeError):
+            op(self._def, A())
+
+    def _test_ibinop_invalid_none(self, op):
+        with self.assertRaises(TypeError):
+            op(self._def, None)
+
+    def _test_binop_rhs_false(self, test_cb, op):
+        test_cb(op, False)
+
+    def _test_binop_rhs_true(self, test_cb, op):
+        test_cb(op, True)
+
+    def _test_binop_rhs_pos_int(self, test_cb, op):
+        test_cb(op, 2)
+
+    def _test_binop_rhs_neg_int(self, test_cb, op):
+        test_cb(op, -23)
+
+    def _test_binop_rhs_zero_int(self, test_cb, op):
+        test_cb(op, 0)
+
+    def _test_binop_rhs_pos_vint(self, test_cb, op):
+        test_cb(op, bt2.create_value(2))
+
+    def _test_binop_rhs_neg_vint(self, test_cb, op):
+        test_cb(op, bt2.create_value(-23))
+
+    def _test_binop_rhs_zero_vint(self, test_cb, op):
+        test_cb(op, bt2.create_value(0))
+
+    def _test_binop_rhs_pos_float(self, test_cb, op):
+        test_cb(op, 2.2)
+
+    def _test_binop_rhs_neg_float(self, test_cb, op):
+        test_cb(op, -23.4)
+
+    def _test_binop_rhs_zero_float(self, test_cb, op):
+        test_cb(op, 0.0)
+
+    def _test_binop_rhs_pos_vfloat(self, test_cb, op):
+        test_cb(op, bt2.create_value(2.2))
+
+    def _test_binop_rhs_neg_vfloat(self, test_cb, op):
+        test_cb(op, bt2.create_value(-23.4))
+
+    def _test_binop_rhs_zero_vfloat(self, test_cb, op):
+        test_cb(op, bt2.create_value(0.0))
+
+    def _test_binop_type_false(self, op):
+        self._test_binop_rhs_false(self._test_binop_type, op)
+
+    def _test_binop_type_true(self, op):
+        self._test_binop_rhs_true(self._test_binop_type, op)
+
+    def _test_binop_type_pos_int(self, op):
+        self._test_binop_rhs_pos_int(self._test_binop_type, op)
+
+    def _test_binop_type_neg_int(self, op):
+        self._test_binop_rhs_neg_int(self._test_binop_type, op)
+
+    def _test_binop_type_zero_int(self, op):
+        self._test_binop_rhs_zero_int(self._test_binop_type, op)
+
+    def _test_binop_type_pos_vint(self, op):
+        self._test_binop_rhs_pos_vint(self._test_binop_type, op)
+
+    def _test_binop_type_neg_vint(self, op):
+        self._test_binop_rhs_neg_vint(self._test_binop_type, op)
+
+    def _test_binop_type_zero_vint(self, op):
+        self._test_binop_rhs_zero_vint(self._test_binop_type, op)
+
+    def _test_binop_type_pos_float(self, op):
+        self._test_binop_rhs_pos_float(self._test_binop_type, op)
+
+    def _test_binop_type_neg_float(self, op):
+        self._test_binop_rhs_neg_float(self._test_binop_type, op)
+
+    def _test_binop_type_zero_float(self, op):
+        self._test_binop_rhs_zero_float(self._test_binop_type, op)
+
+    def _test_binop_type_pos_vfloat(self, op):
+        self._test_binop_rhs_pos_vfloat(self._test_binop_type, op)
+
+    def _test_binop_type_neg_vfloat(self, op):
+        self._test_binop_rhs_neg_vfloat(self._test_binop_type, op)
+
+    def _test_binop_type_zero_vfloat(self, op):
+        self._test_binop_rhs_zero_vfloat(self._test_binop_type, op)
+
+    def _test_binop_value_false(self, op):
+        self._test_binop_rhs_false(self._test_binop_value, op)
+
+    def _test_binop_value_true(self, op):
+        self._test_binop_rhs_true(self._test_binop_value, op)
+
+    def _test_binop_value_pos_int(self, op):
+        self._test_binop_rhs_pos_int(self._test_binop_value, op)
+
+    def _test_binop_value_neg_int(self, op):
+        self._test_binop_rhs_neg_int(self._test_binop_value, op)
+
+    def _test_binop_value_zero_int(self, op):
+        self._test_binop_rhs_zero_int(self._test_binop_value, op)
+
+    def _test_binop_value_pos_vint(self, op):
+        self._test_binop_rhs_pos_vint(self._test_binop_value, op)
+
+    def _test_binop_value_neg_vint(self, op):
+        self._test_binop_rhs_neg_vint(self._test_binop_value, op)
+
+    def _test_binop_value_zero_vint(self, op):
+        self._test_binop_rhs_zero_vint(self._test_binop_value, op)
+
+    def _test_binop_value_pos_float(self, op):
+        self._test_binop_rhs_pos_float(self._test_binop_value, op)
+
+    def _test_binop_value_neg_float(self, op):
+        self._test_binop_rhs_neg_float(self._test_binop_value, op)
+
+    def _test_binop_value_zero_float(self, op):
+        self._test_binop_rhs_zero_float(self._test_binop_value, op)
+
+    def _test_binop_value_pos_vfloat(self, op):
+        self._test_binop_rhs_pos_vfloat(self._test_binop_value, op)
+
+    def _test_binop_value_neg_vfloat(self, op):
+        self._test_binop_rhs_neg_vfloat(self._test_binop_value, op)
+
+    def _test_binop_value_zero_vfloat(self, op):
+        self._test_binop_rhs_zero_vfloat(self._test_binop_value, op)
+
+    def _test_binop_lhs_addr_same_false(self, op):
+        self._test_binop_rhs_false(self._test_binop_lhs_addr_same, op)
+
+    def _test_binop_lhs_addr_same_true(self, op):
+        self._test_binop_rhs_true(self._test_binop_lhs_addr_same, op)
+
+    def _test_binop_lhs_addr_same_pos_int(self, op):
+        self._test_binop_rhs_pos_int(self._test_binop_lhs_addr_same, op)
+
+    def _test_binop_lhs_addr_same_neg_int(self, op):
+        self._test_binop_rhs_neg_int(self._test_binop_lhs_addr_same, op)
+
+    def _test_binop_lhs_addr_same_zero_int(self, op):
+        self._test_binop_rhs_zero_int(self._test_binop_lhs_addr_same, op)
+
+    def _test_binop_lhs_addr_same_pos_vint(self, op):
+        self._test_binop_rhs_pos_vint(self._test_binop_lhs_addr_same, op)
+
+    def _test_binop_lhs_addr_same_neg_vint(self, op):
+        self._test_binop_rhs_neg_vint(self._test_binop_lhs_addr_same, op)
+
+    def _test_binop_lhs_addr_same_zero_vint(self, op):
+        self._test_binop_rhs_zero_vint(self._test_binop_lhs_addr_same, op)
+
+    def _test_binop_lhs_addr_same_pos_float(self, op):
+        self._test_binop_rhs_pos_float(self._test_binop_lhs_addr_same, op)
+
+    def _test_binop_lhs_addr_same_neg_float(self, op):
+        self._test_binop_rhs_neg_float(self._test_binop_lhs_addr_same, op)
+
+    def _test_binop_lhs_addr_same_zero_float(self, op):
+        self._test_binop_rhs_zero_float(self._test_binop_lhs_addr_same, op)
+
+    def _test_binop_lhs_addr_same_pos_vfloat(self, op):
+        self._test_binop_rhs_pos_vfloat(self._test_binop_lhs_addr_same, op)
+
+    def _test_binop_lhs_addr_same_neg_vfloat(self, op):
+        self._test_binop_rhs_neg_vfloat(self._test_binop_lhs_addr_same, op)
+
+    def _test_binop_lhs_addr_same_zero_vfloat(self, op):
+        self._test_binop_rhs_zero_vfloat(self._test_binop_lhs_addr_same, op)
+
+    def _test_binop_lhs_value_same_false(self, op):
+        self._test_binop_rhs_false(self._test_binop_lhs_value_same, op)
+
+    def _test_binop_lhs_value_same_true(self, op):
+        self._test_binop_rhs_true(self._test_binop_lhs_value_same, op)
+
+    def _test_binop_lhs_value_same_pos_int(self, op):
+        self._test_binop_rhs_pos_int(self._test_binop_lhs_value_same, op)
+
+    def _test_binop_lhs_value_same_neg_int(self, op):
+        self._test_binop_rhs_neg_int(self._test_binop_lhs_value_same, op)
+
+    def _test_binop_lhs_value_same_zero_int(self, op):
+        self._test_binop_rhs_zero_int(self._test_binop_lhs_value_same, op)
+
+    def _test_binop_lhs_value_same_pos_vint(self, op):
+        self._test_binop_rhs_pos_vint(self._test_binop_lhs_value_same, op)
+
+    def _test_binop_lhs_value_same_neg_vint(self, op):
+        self._test_binop_rhs_neg_vint(self._test_binop_lhs_value_same, op)
+
+    def _test_binop_lhs_value_same_zero_vint(self, op):
+        self._test_binop_rhs_zero_vint(self._test_binop_lhs_value_same, op)
+
+    def _test_binop_lhs_value_same_pos_float(self, op):
+        self._test_binop_rhs_pos_float(self._test_binop_lhs_value_same, op)
+
+    def _test_binop_lhs_value_same_neg_float(self, op):
+        self._test_binop_rhs_neg_float(self._test_binop_lhs_value_same, op)
+
+    def _test_binop_lhs_value_same_zero_float(self, op):
+        self._test_binop_rhs_zero_float(self._test_binop_lhs_value_same, op)
+
+    def _test_binop_lhs_value_same_pos_vfloat(self, op):
+        self._test_binop_rhs_pos_vfloat(self._test_binop_lhs_value_same, op)
+
+    def _test_binop_lhs_value_same_neg_vfloat(self, op):
+        self._test_binop_rhs_neg_vfloat(self._test_binop_lhs_value_same, op)
+
+    def _test_binop_lhs_value_same_zero_vfloat(self, op):
+        self._test_binop_rhs_zero_vfloat(self._test_binop_lhs_value_same, op)
+
+    def _test_ibinop_type_false(self, op):
+        self._test_binop_rhs_false(self._test_ibinop_type, op)
+
+    def _test_ibinop_type_true(self, op):
+        self._test_binop_rhs_true(self._test_ibinop_type, op)
+
+    def _test_ibinop_type_pos_int(self, op):
+        self._test_binop_rhs_pos_int(self._test_ibinop_type, op)
+
+    def _test_ibinop_type_neg_int(self, op):
+        self._test_binop_rhs_neg_int(self._test_ibinop_type, op)
+
+    def _test_ibinop_type_zero_int(self, op):
+        self._test_binop_rhs_zero_int(self._test_ibinop_type, op)
+
+    def _test_ibinop_type_pos_vint(self, op):
+        self._test_binop_rhs_pos_vint(self._test_ibinop_type, op)
+
+    def _test_ibinop_type_neg_vint(self, op):
+        self._test_binop_rhs_neg_vint(self._test_ibinop_type, op)
+
+    def _test_ibinop_type_zero_vint(self, op):
+        self._test_binop_rhs_zero_vint(self._test_ibinop_type, op)
+
+    def _test_ibinop_type_pos_float(self, op):
+        self._test_binop_rhs_pos_float(self._test_ibinop_type, op)
+
+    def _test_ibinop_type_neg_float(self, op):
+        self._test_binop_rhs_neg_float(self._test_ibinop_type, op)
+
+    def _test_ibinop_type_zero_float(self, op):
+        self._test_binop_rhs_zero_float(self._test_ibinop_type, op)
+
+    def _test_ibinop_type_pos_vfloat(self, op):
+        self._test_binop_rhs_pos_vfloat(self._test_ibinop_type, op)
+
+    def _test_ibinop_type_neg_vfloat(self, op):
+        self._test_binop_rhs_neg_vfloat(self._test_ibinop_type, op)
+
+    def _test_ibinop_type_zero_vfloat(self, op):
+        self._test_binop_rhs_zero_vfloat(self._test_ibinop_type, op)
+
+    def _test_ibinop_value_false(self, op):
+        self._test_binop_rhs_false(self._test_ibinop_value, op)
+
+    def _test_ibinop_value_true(self, op):
+        self._test_binop_rhs_true(self._test_ibinop_value, op)
+
+    def _test_ibinop_value_pos_int(self, op):
+        self._test_binop_rhs_pos_int(self._test_ibinop_value, op)
+
+    def _test_ibinop_value_neg_int(self, op):
+        self._test_binop_rhs_neg_int(self._test_ibinop_value, op)
+
+    def _test_ibinop_value_zero_int(self, op):
+        self._test_binop_rhs_zero_int(self._test_ibinop_value, op)
+
+    def _test_ibinop_value_pos_vint(self, op):
+        self._test_binop_rhs_pos_vint(self._test_ibinop_value, op)
+
+    def _test_ibinop_value_neg_vint(self, op):
+        self._test_binop_rhs_neg_vint(self._test_ibinop_value, op)
+
+    def _test_ibinop_value_zero_vint(self, op):
+        self._test_binop_rhs_zero_vint(self._test_ibinop_value, op)
+
+    def _test_ibinop_value_pos_float(self, op):
+        self._test_binop_rhs_pos_float(self._test_ibinop_value, op)
+
+    def _test_ibinop_value_neg_float(self, op):
+        self._test_binop_rhs_neg_float(self._test_ibinop_value, op)
+
+    def _test_ibinop_value_zero_float(self, op):
+        self._test_binop_rhs_zero_float(self._test_ibinop_value, op)
+
+    def _test_ibinop_value_pos_vfloat(self, op):
+        self._test_binop_rhs_pos_vfloat(self._test_ibinop_value, op)
+
+    def _test_ibinop_value_neg_vfloat(self, op):
+        self._test_binop_rhs_neg_vfloat(self._test_ibinop_value, op)
+
+    def _test_ibinop_value_zero_vfloat(self, op):
+        self._test_binop_rhs_zero_vfloat(self._test_ibinop_value, op)
+
+    def test_bool_op(self):
+        self.assertEqual(bool(self._def), bool(self._def_value))
+
+    def test_int_op(self):
+        self.assertEqual(int(self._def), int(self._def_value))
+
+    def test_float_op(self):
+        self.assertEqual(float(self._def), float(self._def_value))
+
+    def test_complex_op(self):
+        self.assertEqual(complex(self._def), complex(self._def_value))
+
+    def test_str_op(self):
+        self.assertEqual(str(self._def), str(self._def_value))
+
+    def test_eq_none(self):
+        self.assertFalse(self._def == None)
+
+    def test_ne_none(self):
+        self.assertTrue(self._def != None)
+
+
+_BINOPS = (
+    ('lt', operator.lt),
+    ('le', operator.le),
+    ('eq', operator.eq),
+    ('ne', operator.ne),
+    ('ge', operator.ge),
+    ('gt', operator.gt),
+    ('add', operator.add),
+    ('radd', lambda a, b: operator.add(b, a)),
+    ('and', operator.and_),
+    ('rand', lambda a, b: operator.and_(b, a)),
+    ('floordiv', operator.floordiv),
+    ('rfloordiv', lambda a, b: operator.floordiv(b, a)),
+    ('lshift', operator.lshift),
+    ('rlshift', lambda a, b: operator.lshift(b, a)),
+    ('mod', operator.mod),
+    ('rmod', lambda a, b: operator.mod(b, a)),
+    ('mul', operator.mul),
+    ('rmul', lambda a, b: operator.mul(b, a)),
+    ('or', operator.or_),
+    ('ror', lambda a, b: operator.or_(b, a)),
+    ('pow', operator.pow),
+    ('rpow', lambda a, b: operator.pow(b, a)),
+    ('rshift', operator.rshift),
+    ('rrshift', lambda a, b: operator.rshift(b, a)),
+    ('sub', operator.sub),
+    ('rsub', lambda a, b: operator.sub(b, a)),
+    ('truediv', operator.truediv),
+    ('rtruediv', lambda a, b: operator.truediv(b, a)),
+    ('xor', operator.xor),
+    ('rxor', lambda a, b: operator.xor(b, a)),
+)
+
+
+_IBINOPS = (
+    ('iadd', operator.iadd),
+    ('iand', operator.iand),
+    ('ifloordiv', operator.ifloordiv),
+    ('ilshift', operator.ilshift),
+    ('imod', operator.imod),
+    ('imul', operator.imul),
+    ('ior', operator.ior),
+    ('ipow', operator.ipow),
+    ('irshift', operator.irshift),
+    ('isub', operator.isub),
+    ('itruediv', operator.itruediv),
+    ('ixor', operator.ixor),
+)
+
+
+_UNARYOPS = (
+    ('neg', operator.neg),
+    ('pos', operator.pos),
+    ('abs', operator.abs),
+    ('invert', operator.invert),
+    ('round', round),
+    ('round_0', partial(round, ndigits=0)),
+    ('round_1', partial(round, ndigits=1)),
+    ('round_2', partial(round, ndigits=2)),
+    ('round_3', partial(round, ndigits=3)),
+    ('ceil', math.ceil),
+    ('floor', math.floor),
+    ('trunc', math.trunc),
+)
+
+
+def _inject_numeric_testing_methods(cls):
+    def test_binop_name(suffix):
+        return 'test_binop_{}_{}'.format(name, suffix)
+
+    def test_ibinop_name(suffix):
+        return 'test_ibinop_{}_{}'.format(name, suffix)
+
+    def test_unaryop_name(suffix):
+        return 'test_unaryop_{}_{}'.format(name, suffix)
+
+    # inject testing methods for each binary operation
+    for name, binop in _BINOPS:
+
+        setattr(cls, test_binop_name('invalid_unknown'), partialmethod(_TestNumericValue._test_binop_invalid_unknown, op=binop))
+        setattr(cls, test_binop_name('invalid_none'), partialmethod(_TestNumericValue._test_binop_invalid_none, op=binop))
+        setattr(cls, test_binop_name('type_true'), partialmethod(_TestNumericValue._test_binop_type_true, op=binop))
+        setattr(cls, test_binop_name('type_pos_int'), partialmethod(_TestNumericValue._test_binop_type_pos_int, op=binop))
+        setattr(cls, test_binop_name('type_pos_vint'), partialmethod(_TestNumericValue._test_binop_type_pos_vint, op=binop))
+        setattr(cls, test_binop_name('value_true'), partialmethod(_TestNumericValue._test_binop_value_true, op=binop))
+        setattr(cls, test_binop_name('value_pos_int'), partialmethod(_TestNumericValue._test_binop_value_pos_int, op=binop))
+        setattr(cls, test_binop_name('value_pos_vint'), partialmethod(_TestNumericValue._test_binop_value_pos_vint, op=binop))
+        setattr(cls, test_binop_name('lhs_addr_same_true'), partialmethod(_TestNumericValue._test_binop_lhs_addr_same_true, op=binop))
+        setattr(cls, test_binop_name('lhs_addr_same_pos_int'), partialmethod(_TestNumericValue._test_binop_lhs_addr_same_pos_int, op=binop))
+        setattr(cls, test_binop_name('lhs_addr_same_pos_vint'), partialmethod(_TestNumericValue._test_binop_lhs_addr_same_pos_vint, op=binop))
+        setattr(cls, test_binop_name('lhs_value_same_true'), partialmethod(_TestNumericValue._test_binop_lhs_value_same_true, op=binop))
+        setattr(cls, test_binop_name('lhs_value_same_pos_int'), partialmethod(_TestNumericValue._test_binop_lhs_value_same_pos_int, op=binop))
+        setattr(cls, test_binop_name('lhs_value_same_pos_vint'), partialmethod(_TestNumericValue._test_binop_lhs_value_same_pos_vint, op=binop))
+        setattr(cls, test_binop_name('type_neg_int'), partialmethod(_TestNumericValue._test_binop_type_neg_int, op=binop))
+        setattr(cls, test_binop_name('type_neg_vint'), partialmethod(_TestNumericValue._test_binop_type_neg_vint, op=binop))
+        setattr(cls, test_binop_name('value_neg_int'), partialmethod(_TestNumericValue._test_binop_value_neg_int, op=binop))
+        setattr(cls, test_binop_name('value_neg_vint'), partialmethod(_TestNumericValue._test_binop_value_neg_vint, op=binop))
+        setattr(cls, test_binop_name('lhs_addr_same_neg_int'), partialmethod(_TestNumericValue._test_binop_lhs_addr_same_neg_int, op=binop))
+        setattr(cls, test_binop_name('lhs_addr_same_neg_vint'), partialmethod(_TestNumericValue._test_binop_lhs_addr_same_neg_vint, op=binop))
+        setattr(cls, test_binop_name('lhs_value_same_neg_int'), partialmethod(_TestNumericValue._test_binop_lhs_value_same_neg_int, op=binop))
+        setattr(cls, test_binop_name('lhs_value_same_neg_vint'), partialmethod(_TestNumericValue._test_binop_lhs_value_same_neg_vint, op=binop))
+        setattr(cls, test_binop_name('type_false'), partialmethod(_TestNumericValue._test_binop_type_false, op=binop))
+        setattr(cls, test_binop_name('type_zero_int'), partialmethod(_TestNumericValue._test_binop_type_zero_int, op=binop))
+        setattr(cls, test_binop_name('type_zero_vint'), partialmethod(_TestNumericValue._test_binop_type_zero_vint, op=binop))
+        setattr(cls, test_binop_name('value_false'), partialmethod(_TestNumericValue._test_binop_value_false, op=binop))
+        setattr(cls, test_binop_name('value_zero_int'), partialmethod(_TestNumericValue._test_binop_value_zero_int, op=binop))
+        setattr(cls, test_binop_name('value_zero_vint'), partialmethod(_TestNumericValue._test_binop_value_zero_vint, op=binop))
+        setattr(cls, test_binop_name('lhs_addr_same_false'), partialmethod(_TestNumericValue._test_binop_lhs_addr_same_false, op=binop))
+        setattr(cls, test_binop_name('lhs_addr_same_zero_int'), partialmethod(_TestNumericValue._test_binop_lhs_addr_same_zero_int, op=binop))
+        setattr(cls, test_binop_name('lhs_addr_same_zero_vint'), partialmethod(_TestNumericValue._test_binop_lhs_addr_same_zero_vint, op=binop))
+        setattr(cls, test_binop_name('lhs_value_same_false'), partialmethod(_TestNumericValue._test_binop_lhs_value_same_false, op=binop))
+        setattr(cls, test_binop_name('lhs_value_same_zero_int'), partialmethod(_TestNumericValue._test_binop_lhs_value_same_zero_int, op=binop))
+        setattr(cls, test_binop_name('lhs_value_same_zero_vint'), partialmethod(_TestNumericValue._test_binop_lhs_value_same_zero_vint, op=binop))
+        setattr(cls, test_binop_name('type_pos_float'), partialmethod(_TestNumericValue._test_binop_type_pos_float, op=binop))
+        setattr(cls, test_binop_name('type_neg_float'), partialmethod(_TestNumericValue._test_binop_type_neg_float, op=binop))
+        setattr(cls, test_binop_name('type_pos_vfloat'), partialmethod(_TestNumericValue._test_binop_type_pos_vfloat, op=binop))
+        setattr(cls, test_binop_name('type_neg_vfloat'), partialmethod(_TestNumericValue._test_binop_type_neg_vfloat, op=binop))
+        setattr(cls, test_binop_name('value_pos_float'), partialmethod(_TestNumericValue._test_binop_value_pos_float, op=binop))
+        setattr(cls, test_binop_name('value_neg_float'), partialmethod(_TestNumericValue._test_binop_value_neg_float, op=binop))
+        setattr(cls, test_binop_name('value_pos_vfloat'), partialmethod(_TestNumericValue._test_binop_value_pos_vfloat, op=binop))
+        setattr(cls, test_binop_name('value_neg_vfloat'), partialmethod(_TestNumericValue._test_binop_value_neg_vfloat, op=binop))
+        setattr(cls, test_binop_name('lhs_addr_same_pos_float'), partialmethod(_TestNumericValue._test_binop_lhs_addr_same_pos_float, op=binop))
+        setattr(cls, test_binop_name('lhs_addr_same_neg_float'), partialmethod(_TestNumericValue._test_binop_lhs_addr_same_neg_float, op=binop))
+        setattr(cls, test_binop_name('lhs_addr_same_pos_vfloat'), partialmethod(_TestNumericValue._test_binop_lhs_addr_same_pos_vfloat, op=binop))
+        setattr(cls, test_binop_name('lhs_addr_same_neg_vfloat'), partialmethod(_TestNumericValue._test_binop_lhs_addr_same_neg_vfloat, op=binop))
+        setattr(cls, test_binop_name('lhs_value_same_pos_float'), partialmethod(_TestNumericValue._test_binop_lhs_value_same_pos_float, op=binop))
+        setattr(cls, test_binop_name('lhs_value_same_neg_float'), partialmethod(_TestNumericValue._test_binop_lhs_value_same_neg_float, op=binop))
+        setattr(cls, test_binop_name('lhs_value_same_pos_vfloat'), partialmethod(_TestNumericValue._test_binop_lhs_value_same_pos_vfloat, op=binop))
+        setattr(cls, test_binop_name('lhs_value_same_neg_vfloat'), partialmethod(_TestNumericValue._test_binop_lhs_value_same_neg_vfloat, op=binop))
+        setattr(cls, test_binop_name('type_zero_float'), partialmethod(_TestNumericValue._test_binop_type_zero_float, op=binop))
+        setattr(cls, test_binop_name('type_zero_vfloat'), partialmethod(_TestNumericValue._test_binop_type_zero_vfloat, op=binop))
+        setattr(cls, test_binop_name('value_zero_float'), partialmethod(_TestNumericValue._test_binop_value_zero_float, op=binop))
+        setattr(cls, test_binop_name('value_zero_vfloat'), partialmethod(_TestNumericValue._test_binop_value_zero_vfloat, op=binop))
+        setattr(cls, test_binop_name('lhs_addr_same_zero_float'), partialmethod(_TestNumericValue._test_binop_lhs_addr_same_zero_float, op=binop))
+        setattr(cls, test_binop_name('lhs_addr_same_zero_vfloat'), partialmethod(_TestNumericValue._test_binop_lhs_addr_same_zero_vfloat, op=binop))
+        setattr(cls, test_binop_name('lhs_value_same_zero_float'), partialmethod(_TestNumericValue._test_binop_lhs_value_same_zero_float, op=binop))
+        setattr(cls, test_binop_name('lhs_value_same_zero_vfloat'), partialmethod(_TestNumericValue._test_binop_lhs_value_same_zero_vfloat, op=binop))
+
+    # inject testing methods for each unary operation
+    for name, unaryop in _UNARYOPS:
+        setattr(cls, test_unaryop_name('type'), partialmethod(_TestNumericValue._test_unaryop_type, op=unaryop))
+        setattr(cls, test_unaryop_name('value'), partialmethod(_TestNumericValue._test_unaryop_value, op=unaryop))
+        setattr(cls, test_unaryop_name('addr_same'), partialmethod(_TestNumericValue._test_unaryop_addr_same, op=unaryop))
+        setattr(cls, test_unaryop_name('value_same'), partialmethod(_TestNumericValue._test_unaryop_value_same, op=unaryop))
+
+    # inject testing methods for each inplace binary operation
+    for name, ibinop in _IBINOPS:
+        setattr(cls, test_ibinop_name('invalid_unknown'), partialmethod(_TestNumericValue._test_ibinop_invalid_unknown, op=ibinop))
+        setattr(cls, test_ibinop_name('invalid_none'), partialmethod(_TestNumericValue._test_ibinop_invalid_none, op=ibinop))
+        setattr(cls, test_ibinop_name('type_true'), partialmethod(_TestNumericValue._test_ibinop_type_true, op=ibinop))
+        setattr(cls, test_ibinop_name('value_true'), partialmethod(_TestNumericValue._test_ibinop_value_true, op=ibinop))
+        setattr(cls, test_ibinop_name('type_pos_int'), partialmethod(_TestNumericValue._test_ibinop_type_pos_int, op=ibinop))
+        setattr(cls, test_ibinop_name('type_pos_vint'), partialmethod(_TestNumericValue._test_ibinop_type_pos_vint, op=ibinop))
+        setattr(cls, test_ibinop_name('value_pos_int'), partialmethod(_TestNumericValue._test_ibinop_value_pos_int, op=ibinop))
+        setattr(cls, test_ibinop_name('value_pos_vint'), partialmethod(_TestNumericValue._test_ibinop_value_pos_vint, op=ibinop))
+        setattr(cls, test_ibinop_name('type_neg_int'), partialmethod(_TestNumericValue._test_ibinop_type_neg_int, op=ibinop))
+        setattr(cls, test_ibinop_name('type_neg_vint'), partialmethod(_TestNumericValue._test_ibinop_type_neg_vint, op=ibinop))
+        setattr(cls, test_ibinop_name('value_neg_int'), partialmethod(_TestNumericValue._test_ibinop_value_neg_int, op=ibinop))
+        setattr(cls, test_ibinop_name('value_neg_vint'), partialmethod(_TestNumericValue._test_ibinop_value_neg_vint, op=ibinop))
+        setattr(cls, test_ibinop_name('type_false'), partialmethod(_TestNumericValue._test_ibinop_type_false, op=ibinop))
+        setattr(cls, test_ibinop_name('value_false'), partialmethod(_TestNumericValue._test_ibinop_value_false, op=ibinop))
+        setattr(cls, test_ibinop_name('type_zero_int'), partialmethod(_TestNumericValue._test_ibinop_type_zero_int, op=ibinop))
+        setattr(cls, test_ibinop_name('type_zero_vint'), partialmethod(_TestNumericValue._test_ibinop_type_zero_vint, op=ibinop))
+        setattr(cls, test_ibinop_name('value_zero_int'), partialmethod(_TestNumericValue._test_ibinop_value_zero_int, op=ibinop))
+        setattr(cls, test_ibinop_name('value_zero_vint'), partialmethod(_TestNumericValue._test_ibinop_value_zero_vint, op=ibinop))
+        setattr(cls, test_ibinop_name('type_pos_float'), partialmethod(_TestNumericValue._test_ibinop_type_pos_float, op=ibinop))
+        setattr(cls, test_ibinop_name('type_neg_float'), partialmethod(_TestNumericValue._test_ibinop_type_neg_float, op=ibinop))
+        setattr(cls, test_ibinop_name('type_pos_vfloat'), partialmethod(_TestNumericValue._test_ibinop_type_pos_vfloat, op=ibinop))
+        setattr(cls, test_ibinop_name('type_neg_vfloat'), partialmethod(_TestNumericValue._test_ibinop_type_neg_vfloat, op=ibinop))
+        setattr(cls, test_ibinop_name('value_pos_float'), partialmethod(_TestNumericValue._test_ibinop_value_pos_float, op=ibinop))
+        setattr(cls, test_ibinop_name('value_neg_float'), partialmethod(_TestNumericValue._test_ibinop_value_neg_float, op=ibinop))
+        setattr(cls, test_ibinop_name('value_pos_vfloat'), partialmethod(_TestNumericValue._test_ibinop_value_pos_vfloat, op=ibinop))
+        setattr(cls, test_ibinop_name('value_neg_vfloat'), partialmethod(_TestNumericValue._test_ibinop_value_neg_vfloat, op=ibinop))
+        setattr(cls, test_ibinop_name('type_zero_float'), partialmethod(_TestNumericValue._test_ibinop_type_zero_float, op=ibinop))
+        setattr(cls, test_ibinop_name('type_zero_vfloat'), partialmethod(_TestNumericValue._test_ibinop_type_zero_vfloat, op=ibinop))
+        setattr(cls, test_ibinop_name('value_zero_float'), partialmethod(_TestNumericValue._test_ibinop_value_zero_float, op=ibinop))
+        setattr(cls, test_ibinop_name('value_zero_vfloat'), partialmethod(_TestNumericValue._test_ibinop_value_zero_vfloat, op=ibinop))
+
+
+class CreateValueFuncTestCase(unittest.TestCase):
+    def test_create_none(self):
+        v = bt2.create_value(None)
+        self.assertIsNone(v)
+
+    def test_create_bool_false(self):
+        v = bt2.create_value(False)
+        self.assertIsInstance(v, bt2.BoolValue)
+        self.assertFalse(v)
+
+    def test_create_bool_true(self):
+        v = bt2.create_value(True)
+        self.assertIsInstance(v, bt2.BoolValue)
+        self.assertTrue(v)
+
+    def test_create_int_pos(self):
+        raw = 23
+        v = bt2.create_value(raw)
+        self.assertIsInstance(v, bt2.IntegerValue)
+        self.assertEqual(v, raw)
+
+    def test_create_int_neg(self):
+        raw = -23
+        v = bt2.create_value(raw)
+        self.assertIsInstance(v, bt2.IntegerValue)
+        self.assertEqual(v, raw)
+
+    def test_create_float_pos(self):
+        raw = 17.5
+        v = bt2.create_value(raw)
+        self.assertIsInstance(v, bt2.FloatValue)
+        self.assertEqual(v, raw)
+
+    def test_create_float_neg(self):
+        raw = -17.5
+        v = bt2.create_value(raw)
+        self.assertIsInstance(v, bt2.FloatValue)
+        self.assertEqual(v, raw)
+
+    def test_create_string(self):
+        raw = 'salut'
+        v = bt2.create_value(raw)
+        self.assertIsInstance(v, bt2.StringValue)
+        self.assertEqual(v, raw)
+
+    def test_create_string_empty(self):
+        raw = ''
+        v = bt2.create_value(raw)
+        self.assertIsInstance(v, bt2.StringValue)
+        self.assertEqual(v, raw)
+
+    def test_create_array_from_list(self):
+        raw = [1, 2, 3]
+        v = bt2.create_value(raw)
+        self.assertIsInstance(v, bt2.ArrayValue)
+        self.assertEqual(v, raw)
+
+    def test_create_array_from_tuple(self):
+        raw = 4, 5, 6
+        v = bt2.create_value(raw)
+        self.assertIsInstance(v, bt2.ArrayValue)
+        self.assertEqual(v, raw)
+
+    def test_create_array_from_empty_list(self):
+        raw = []
+        v = bt2.create_value(raw)
+        self.assertIsInstance(v, bt2.ArrayValue)
+        self.assertEqual(v, raw)
+
+    def test_create_array_from_empty_tuple(self):
+        raw = ()
+        v = bt2.create_value(raw)
+        self.assertIsInstance(v, bt2.ArrayValue)
+        self.assertEqual(v, raw)
+
+    def test_create_map(self):
+        raw = {'salut': 23}
+        v = bt2.create_value(raw)
+        self.assertIsInstance(v, bt2.MapValue)
+        self.assertEqual(v, raw)
+
+    def test_create_map_empty(self):
+        raw = {}
+        v = bt2.create_value(raw)
+        self.assertIsInstance(v, bt2.MapValue)
+        self.assertEqual(v, raw)
+
+    def test_create_vfalse(self):
+        v = bt2.create_value(bt2.create_value(False))
+        self.assertIsInstance(v, bt2.BoolValue)
+        self.assertFalse(v)
+
+    def test_create_invalid(self):
+        class A:
+            pass
+
+        a = A()
+
+        with self.assertRaisesRegex(TypeError, "cannot create value object from 'A' object") as cm:
+            v = bt2.create_value(a)
+
+
+class BoolValueTestCase(_TestFrozenSimple, _TestCopySimple, unittest.TestCase):
+    def setUp(self):
+        self._f = bt2.BoolValue(False)
+        self._t = bt2.BoolValue(True)
+        self._def = self._f
+        self._def_value = False
+        self._def_new_value = True
+
+    def _assert_expecting_bool(self):
+        return self.assertRaisesRegex(TypeError, r"expecting a 'bool' object")
+
+    def test_create_default(self):
+        b = bt2.BoolValue()
+        self.assertFalse(b)
+
+    def test_create_false(self):
+        self.assertFalse(self._f.value)
+        self.assertFalse(self._f)
+
+    def test_create_true(self):
+        self.assertTrue(self._t.value)
+        self.assertTrue(self._t)
+
+    def test_create_from_vfalse(self):
+        b = bt2.BoolValue(self._f)
+        self.assertFalse(b.value)
+        self.assertFalse(b)
+
+    def test_create_from_vtrue(self):
+        b = bt2.BoolValue(self._t)
+        self.assertTrue(b.value)
+        self.assertTrue(b)
+
+    def test_create_from_int_non_zero(self):
+        with self.assertRaises(TypeError):
+            b = bt2.BoolValue(23)
+
+    def test_create_from_int_zero(self):
+        with self.assertRaises(TypeError):
+            b = bt2.BoolValue(0)
+
+    def test_assign_true(self):
+        b = bt2.BoolValue()
+        b.value = True
+        self.assertTrue(b)
+
+    def test_assign_false(self):
+        b = bt2.BoolValue()
+        b.value = False
+        self.assertFalse(b)
+
+    def test_assign_vtrue(self):
+        b = bt2.BoolValue()
+        b.value = self._t
+        self.assertTrue(b)
+
+    def test_assign_vfalse(self):
+        b = bt2.BoolValue()
+        b.value = False
+        self.assertFalse(b)
+
+    def test_assign_int(self):
+        with self.assertRaises(TypeError):
+            b = bt2.BoolValue()
+            b.value = 23
+
+    def test_bool_op(self):
+        self.assertEqual(bool(self._def), bool(self._def_value))
+
+    def test_str_op(self):
+        self.assertEqual(str(self._def), str(self._def_value))
+
+    def test_eq_none(self):
+        self.assertFalse(self._def == None)
+
+    def test_ne_none(self):
+        self.assertTrue(self._def != None)
+
+    def test_vfalse_eq_false(self):
+        self.assertEqual(self._f, False)
+
+    def test_vfalse_ne_true(self):
+        self.assertNotEqual(self._f, True)
+
+    def test_vtrue_eq_true(self):
+        self.assertEqual(self._t, True)
+
+    def test_vtrue_ne_false(self):
+        self.assertNotEqual(self._t, False)
+
+
+class IntegerValueTestCase(_TestNumericValue, unittest.TestCase):
+    def setUp(self):
+        self._pv = 23
+        self._nv = -52
+        self._ip = bt2.IntegerValue(self._pv)
+        self._in = bt2.IntegerValue(self._nv)
+        self._def = self._ip
+        self._def_value = self._pv
+        self._def_new_value = -101
+
+    def _assert_expecting_int(self):
+        return self.assertRaisesRegex(TypeError, r'expecting a number object')
+
+    def _assert_expecting_int64(self):
+        return self.assertRaisesRegex(ValueError, r"expecting a signed 64-bit integral value")
+
+    def _assert_expecting_uint64(self):
+        return self.assertRaisesRegex(ValueError, r"expecting an unsigned 64-bit integral value")
+
+    def test_create_default(self):
+        i = bt2.IntegerValue()
+        self.assertEqual(i.value, 0)
+
+    def test_create_pos(self):
+        self.assertEqual(self._ip.value, self._pv)
+        self.assertEqual(self._ip, self._pv)
+
+    def test_create_neg(self):
+        self.assertEqual(self._in.value, self._nv)
+        self.assertEqual(self._in, self._nv)
+
+    def test_create_pos_too_big(self):
+        with self._assert_expecting_int64():
+            i = bt2.IntegerValue(2 ** 63)
+
+    def test_create_neg_too_big(self):
+        with self._assert_expecting_int64():
+            i = bt2.IntegerValue(-(2 ** 63) - 1)
+
+    def test_create_from_vint(self):
+        i = bt2.IntegerValue(self._ip)
+        self.assertEqual(i, self._pv)
+
+    def test_create_from_false(self):
+        i = bt2.IntegerValue(False)
+        self.assertFalse(i.value)
+        self.assertFalse(i)
+
+    def test_create_from_true(self):
+        i = bt2.IntegerValue(True)
+        self.assertTrue(i.value)
+        self.assertTrue(i)
+
+    def test_create_from_float(self):
+        i = bt2.IntegerValue(99.6)
+        self.assertEqual(i, 99)
+
+    def test_create_from_vfloat(self):
+        f = bt2.create_value(17.5)
+        i = bt2.IntegerValue(f)
+        self.assertEqual(i, 17)
+
+    def test_create_from_unknown(self):
+        class A:
+            pass
+
+        with self._assert_expecting_int():
+            i = bt2.IntegerValue(A())
+
+    def test_create_from_varray(self):
+        with self._assert_expecting_int():
+            i = bt2.IntegerValue(bt2.ArrayValue())
+
+    def test_assign_true(self):
+        raw = True
+        self._def.value = raw
+        self.assertEqual(self._def, raw)
+        self.assertEqual(self._def.value, raw)
+
+    def test_assign_false(self):
+        raw = False
+        self._def.value = raw
+        self.assertEqual(self._def, raw)
+        self.assertEqual(self._def.value, raw)
+
+    def test_assign_pos_int(self):
+        raw = 477
+        self._def.value = raw
+        self.assertEqual(self._def, raw)
+        self.assertEqual(self._def.value, raw)
+
+    def test_assign_neg_int(self):
+        raw = -13
+        self._def.value = raw
+        self.assertEqual(self._def, raw)
+        self.assertEqual(self._def.value, raw)
+
+    def test_assign_vint(self):
+        raw = 999
+        self._def.value = bt2.create_value(raw)
+        self.assertEqual(self._def, raw)
+        self.assertEqual(self._def.value, raw)
+
+    def test_assign_vfloat(self):
+        raw = 123.456
+        self._def.value = bt2.create_value(raw)
+        self.assertEqual(self._def, int(raw))
+        self.assertEqual(self._def.value, int(raw))
+
+
+_inject_numeric_testing_methods(IntegerValueTestCase)
+
+
+class FloatValueTestCase(_TestNumericValue, unittest.TestCase):
+    def setUp(self):
+        self._pv = 23.4
+        self._nv = -52.7
+        self._fp = bt2.FloatValue(self._pv)
+        self._fn = bt2.FloatValue(self._nv)
+        self._def = self._fp
+        self._def_value = self._pv
+        self._def_new_value = -101.88
+
+    def _assert_expecting_float(self):
+        return self.assertRaisesRegex(TypeError, r"expecting a real number object")
+
+    def _test_invalid_op(self, cb):
+        with self.assertRaises(TypeError):
+            cb()
+
+    def test_create_default(self):
+        f = bt2.FloatValue()
+        self.assertEqual(f.value, 0.0)
+
+    def test_create_pos(self):
+        self.assertEqual(self._fp.value, self._pv)
+        self.assertEqual(self._fp, self._pv)
+
+    def test_create_neg(self):
+        self.assertEqual(self._fn.value, self._nv)
+        self.assertEqual(self._fn, self._nv)
+
+    def test_create_from_vint(self):
+        f = bt2.FloatValue(self._fp)
+        self.assertEqual(f, self._pv)
+
+    def test_create_from_false(self):
+        f = bt2.FloatValue(False)
+        self.assertFalse(f.value)
+        self.assertFalse(f)
+
+    def test_create_from_true(self):
+        f = bt2.FloatValue(True)
+        self.assertTrue(f.value)
+        self.assertTrue(f)
+
+    def test_create_from_int(self):
+        raw = 17
+        f = bt2.FloatValue(raw)
+        self.assertEqual(f.value, float(raw))
+
+    def test_create_from_vint(self):
+        raw = 17
+        f = bt2.FloatValue(bt2.create_value(raw))
+        self.assertEqual(f.value, float(raw))
+
+    def test_create_from_vfloat(self):
+        raw = 17.17
+        f = bt2.FloatValue(bt2.create_value(raw))
+        self.assertEqual(f.value, raw)
+
+    def test_create_from_unknown(self):
+        class A:
+            pass
+
+        with self._assert_expecting_float():
+            f = bt2.FloatValue(A())
+
+    def test_create_from_varray(self):
+        with self._assert_expecting_float():
+            f = bt2.FloatValue(bt2.ArrayValue())
+
+    def test_assign_true(self):
+        self._def.value = True
+        self.assertTrue(self._def)
+        self.assertTrue(self._def.value)
+
+    def test_assign_false(self):
+        self._def.value = False
+        self.assertFalse(self._def)
+        self.assertFalse(self._def.value)
+
+    def test_assign_pos_int(self):
+        raw = 477
+        self._def.value = raw
+        self.assertEqual(self._def, float(raw))
+        self.assertEqual(self._def.value, float(raw))
+
+    def test_assign_neg_int(self):
+        raw = -13
+        self._def.value = raw
+        self.assertEqual(self._def, float(raw))
+        self.assertEqual(self._def.value, float(raw))
+
+    def test_assign_vint(self):
+        raw = 999
+        self._def.value = bt2.create_value(raw)
+        self.assertEqual(self._def, float(raw))
+        self.assertEqual(self._def.value, float(raw))
+
+    def test_assign_float(self):
+        raw = -19.23
+        self._def.value = raw
+        self.assertEqual(self._def, raw)
+        self.assertEqual(self._def.value, raw)
+
+    def test_assign_vfloat(self):
+        raw = 101.32
+        self._def.value = bt2.create_value(raw)
+        self.assertEqual(self._def, raw)
+        self.assertEqual(self._def.value, raw)
+
+    def test_invalid_lshift(self):
+        self._test_invalid_op(lambda: self._def << 23)
+
+    def test_invalid_rshift(self):
+        self._test_invalid_op(lambda: self._def >> 23)
+
+    def test_invalid_and(self):
+        self._test_invalid_op(lambda: self._def & 23)
+
+    def test_invalid_or(self):
+        self._test_invalid_op(lambda: self._def | 23)
+
+    def test_invalid_xor(self):
+        self._test_invalid_op(lambda: self._def ^ 23)
+
+    def test_invalid_invert(self):
+        self._test_invalid_op(lambda: ~self._def)
+
+
+_inject_numeric_testing_methods(FloatValueTestCase)
+
+
+class StringValueTestCase(_TestCopySimple, _TestFrozenSimple, unittest.TestCase):
+    def setUp(self):
+        self._def_value = 'Hello, World!'
+        self._def = bt2.StringValue(self._def_value)
+        self._def_new_value = 'Yes!'
+
+    def _assert_expecting_str(self):
+        return self.assertRaises(TypeError)
+
+    def test_create_default(self):
+        s = bt2.StringValue()
+        self.assertEqual(s.value, '')
+
+    def test_create_from_str(self):
+        raw = 'liberté'
+        s = bt2.StringValue(raw)
+        self.assertEqual(s.value, raw)
+
+    def test_create_from_vstr(self):
+        raw = 'liberté'
+        s = bt2.StringValue(bt2.create_value(raw))
+        self.assertEqual(s.value, raw)
+
+    def test_create_from_unknown(self):
+        class A:
+            pass
+
+        with self._assert_expecting_str():
+            i = bt2.StringValue(A())
+
+    def test_create_from_varray(self):
+        with self._assert_expecting_str():
+            i = bt2.StringValue(bt2.ArrayValue())
+
+    def test_assign_int(self):
+        with self._assert_expecting_str():
+            self._def.value = 283
+
+    def test_assign_str(self):
+        raw = 'zorg'
+        self._def = raw
+        self.assertEqual(self._def, raw)
+
+    def test_assign_vstr(self):
+        raw = 'zorg'
+        self._def = bt2.create_value(raw)
+        self.assertEqual(self._def, raw)
+
+    def test_eq(self):
+        self.assertEqual(self._def, self._def_value)
+
+    def test_eq(self):
+        self.assertNotEqual(self._def, 23)
+
+    def test_lt_vstring(self):
+        s1 = bt2.StringValue('allo')
+        s2 = bt2.StringValue('bateau')
+        self.assertLess(s1, s2)
+
+    def test_lt_string(self):
+        s1 = bt2.StringValue('allo')
+        self.assertLess(s1, 'bateau')
+
+    def test_le_vstring(self):
+        s1 = bt2.StringValue('allo')
+        s2 = bt2.StringValue('bateau')
+        self.assertLessEqual(s1, s2)
+
+    def test_le_string(self):
+        s1 = bt2.StringValue('allo')
+        self.assertLessEqual(s1, 'bateau')
+
+    def test_gt_vstring(self):
+        s1 = bt2.StringValue('allo')
+        s2 = bt2.StringValue('bateau')
+        self.assertGreater(s2, s1)
+
+    def test_gt_string(self):
+        s1 = bt2.StringValue('allo')
+        self.assertGreater('bateau', s1)
+
+    def test_ge_vstring(self):
+        s1 = bt2.StringValue('allo')
+        s2 = bt2.StringValue('bateau')
+        self.assertGreaterEqual(s2, s1)
+
+    def test_ge_string(self):
+        s1 = bt2.StringValue('allo')
+        self.assertGreaterEqual('bateau', s1)
+
+    def test_bool_op(self):
+        self.assertEqual(bool(self._def), bool(self._def_value))
+
+    def test_str_op(self):
+        self.assertEqual(str(self._def), str(self._def_value))
+
+    def test_len(self):
+        self.assertEqual(len(self._def), len(self._def_value))
+
+    def test_getitem(self):
+        self.assertEqual(self._def[5], self._def_value[5])
+
+    def test_append_str(self):
+        to_append = 'meow meow meow'
+        self._def += to_append
+        self._def_value += to_append
+        self.assertEqual(self._def, self._def_value)
+
+    def test_append_vstr(self):
+        to_append = 'meow meow meow'
+        self._def += bt2.create_value(to_append)
+        self._def_value += to_append
+        self.assertEqual(self._def, self._def_value)
+
+
+class ArrayValueTestCase(_TestFrozen, unittest.TestCase):
+    def setUp(self):
+        self._def_value = [None, False, True, -23, 0, 42, -42.4, 23.17, 'yes']
+        self._def = bt2.ArrayValue(copy.deepcopy(self._def_value))
+
+    def _modify_def(self):
+        self._def[2] = 'xyz'
+
+    def _assert_type_error(self):
+        return self.assertRaises(TypeError)
+
+    def test_create_default(self):
+        a = bt2.ArrayValue()
+        self.assertEqual(len(a), 0)
+
+    def test_create_from_array(self):
+        self.assertEqual(self._def, self._def_value)
+
+    def test_create_from_tuple(self):
+        t = 1, 2, False, None
+        a = bt2.ArrayValue(t)
+        self.assertEqual(a, t)
+
+    def test_create_from_varray(self):
+        va = bt2.ArrayValue(copy.deepcopy(self._def_value))
+        a = bt2.ArrayValue(va)
+        self.assertEqual(va, a)
+
+    def test_create_from_unknown(self):
+        class A:
+            pass
+
+        with self._assert_type_error():
+            a = bt2.ArrayValue(A())
+
+    def test_bool_op_true(self):
+        self.assertTrue(bool(self._def))
+
+    def test_bool_op_false(self):
+        self.assertFalse(bool(bt2.ArrayValue()))
+
+    def test_len(self):
+        self.assertEqual(len(self._def), len(self._def_value))
+
+    def test_copy(self):
+        to_copy = (1, 2, 'hello', (4, 5.2))
+        a = bt2.ArrayValue(to_copy)
+        cpy = copy.copy(a)
+        self.assertEqual(a, cpy)
+        self.assertNotEqual(a.addr, cpy.addr)
+        self.assertEqual(a[3].addr, cpy[3].addr)
+
+    def test_deepcopy(self):
+        to_copy = (1, 2, 'hello', (4, 5.2))
+        a = bt2.ArrayValue(to_copy)
+        cpy = copy.deepcopy(a)
+        self.assertEqual(a, cpy)
+        self.assertNotEqual(a.addr, cpy.addr)
+        self.assertNotEqual(a[3].addr, cpy[3].addr)
+
+    def test_eq_int(self):
+        self.assertNotEqual(self._def, 23)
+
+    def test_eq_diff_len(self):
+        a1 = bt2.create_value([1, 2, 3])
+        a2 = bt2.create_value([1, 2])
+        self.assertNotEqual(a1, a2)
+
+    def test_eq_diff_content_same_len(self):
+        a1 = bt2.create_value([1, 2, 3])
+        a2 = bt2.create_value([4, 5, 6])
+        self.assertNotEqual(a1, a2)
+
+    def test_eq_same_content_same_len(self):
+        raw = (3, True, [1, 2.5, None, {'a': 17.6, 'b': None}])
+        a1 = bt2.ArrayValue(raw)
+        a2 = bt2.ArrayValue(copy.deepcopy(raw))
+        self.assertEqual(a1, a2)
+
+    def test_setitem_int(self):
+        raw = 19
+        self._def[2] = raw
+        self.assertEqual(self._def[2], raw)
+
+    def test_setitem_vint(self):
+        raw = 19
+        self._def[2] = bt2.create_value(raw)
+        self.assertEqual(self._def[2], raw)
+
+    def test_setitem_none(self):
+        self._def[2] = None
+        self.assertIsNone(self._def[2])
+
+    def test_setitem_index_wrong_type(self):
+        with self._assert_type_error():
+            self._def['yes'] = 23
+
+    def test_setitem_index_neg(self):
+        with self.assertRaises(IndexError):
+            self._def[-2] = 23
+
+    def test_setitem_index_out_of_range(self):
+        with self.assertRaises(IndexError):
+            self._def[len(self._def)] = 23
+
+    def test_append_none(self):
+        self._def.append(None)
+        self.assertIsNone(self._def[len(self._def) - 1])
+
+    def test_append_int(self):
+        raw = 145
+        self._def.append(raw)
+        self.assertEqual(self._def[len(self._def) - 1], raw)
+
+    def test_append_vint(self):
+        raw = 145
+        self._def.append(bt2.create_value(raw))
+        self.assertEqual(self._def[len(self._def) - 1], raw)
+
+    def test_append_unknown(self):
+        class A:
+            pass
+
+        with self._assert_type_error():
+            self._def.append(A())
+
+    def test_iadd(self):
+        raw = 4, 5, True
+        self._def += raw
+        self.assertEqual(self._def[len(self._def) - 3], raw[0])
+        self.assertEqual(self._def[len(self._def) - 2], raw[1])
+        self.assertEqual(self._def[len(self._def) - 1], raw[2])
+
+    def test_iadd_unknown(self):
+        class A:
+            pass
+
+        with self._assert_type_error():
+            self._def += A()
+
+    def test_iadd_list_unknown(self):
+        class A:
+            pass
+
+        with self._assert_type_error():
+            self._def += [A()]
+
+    def test_iter(self):
+        for velem, elem in zip(self._def, self._def_value):
+            self.assertEqual(velem, elem)
+
+
+class MapValueTestCase(_TestFrozen, unittest.TestCase):
+    def setUp(self):
+        self._def_value = {
+            'none': None,
+            'false': False,
+            'true': True,
+            'neg-int': -23,
+            'zero': 0,
+            'pos-int': 42,
+            'neg-float': -42.4,
+            'pos-float': 23.17,
+            'str': 'yes'
+        }
+        self._def = bt2.MapValue(copy.deepcopy(self._def_value))
+
+    def _modify_def(self):
+        self._def['zero'] = 1
+
+    def test_create_default(self):
+        m = bt2.MapValue()
+        self.assertEqual(len(m), 0)
+
+    def test_create_from_dict(self):
+        self.assertEqual(self._def, self._def_value)
+
+    def test_create_from_vmap(self):
+        vm = bt2.MapValue(copy.deepcopy(self._def_value))
+        m = bt2.MapValue(vm)
+        self.assertEqual(vm, m)
+
+    def test_create_from_unknown(self):
+        class A:
+            pass
+
+        with self.assertRaises(AttributeError):
+            m = bt2.MapValue(A())
+
+    def test_bool_op_true(self):
+        self.assertTrue(bool(self._def))
+
+    def test_bool_op_false(self):
+        self.assertFalse(bool(bt2.MapValue()))
+
+    def test_len(self):
+        self.assertEqual(len(self._def), len(self._def_value))
+
+    def test_copy(self):
+        to_copy = {
+            'yes': 1,
+            'no': 2,
+            's': 'hello',
+            'inner': (4, 5.2)
+        }
+        m = bt2.MapValue(to_copy)
+        cpy = copy.copy(m)
+        self.assertEqual(m, cpy)
+        self.assertNotEqual(m.addr, cpy.addr)
+        self.assertEqual(m['inner'].addr, cpy['inner'].addr)
+
+    def test_deepcopy(self):
+        to_copy = {
+            'yes': 1,
+            'no': 2,
+            's': 'hello',
+            'inner': (4, 5.2)
+        }
+        m = bt2.MapValue(to_copy)
+        cpy = copy.deepcopy(m)
+        self.assertEqual(m, cpy)
+        self.assertNotEqual(m.addr, cpy.addr)
+        self.assertNotEqual(m['inner'].addr, cpy['inner'].addr)
+
+    def test_eq_int(self):
+        self.assertNotEqual(self._def, 23)
+
+    def test_eq_diff_len(self):
+        a1 = bt2.create_value({'a': 1, 'b': 2, 'c': 3})
+        a2 = bt2.create_value({'a': 1, 'b': 2})
+        self.assertNotEqual(a1, a2)
+
+    def test_eq_diff_content_same_len(self):
+        a1 = bt2.create_value({'a': 1, 'b': 2, 'c': 3})
+        a2 = bt2.create_value({'a': 4, 'b': 2, 'c': 3})
+        self.assertNotEqual(a1, a2)
+
+    def test_eq_same_content_diff_keys(self):
+        a1 = bt2.create_value({'a': 1, 'b': 2, 'c': 3})
+        a2 = bt2.create_value({'a': 1, 'k': 2, 'c': 3})
+        self.assertNotEqual(a1, a2)
+
+    def test_eq_same_content_same_len(self):
+        raw = {
+            '3': 3,
+            'True': True,
+            'array': [1, 2.5, None, {'a': 17.6, 'b': None}]
+        }
+        a1 = bt2.MapValue(raw)
+        a2 = bt2.MapValue(copy.deepcopy(raw))
+        self.assertEqual(a1, a2)
+        self.assertEqual(a1, raw)
+
+    def test_setitem_int(self):
+        raw = 19
+        self._def['pos-int'] = raw
+        self.assertEqual(self._def['pos-int'], raw)
+
+    def test_setitem_vint(self):
+        raw = 19
+        self._def['pos-int'] = bt2.create_value(raw)
+        self.assertEqual(self._def['pos-int'], raw)
+
+    def test_setitem_none(self):
+        self._def['none'] = None
+        self.assertIsNone(self._def['none'])
+
+    def test_setitem_new_int(self):
+        old_len = len(self._def)
+        self._def['new-int'] = 23
+        self.assertEqual(self._def['new-int'], 23)
+        self.assertEqual(len(self._def), old_len + 1)
+
+    def test_setitem_index_wrong_type(self):
+        with self.assertRaises(TypeError):
+            self._def[18] = 23
+
+    def test_iter(self):
+        for vkey, vval in self._def.items():
+            val = self._def_value[vkey]
+            self.assertEqual(vval, val)
+
+    def test_getitem_wrong_key(self):
+        with self.assertRaises(KeyError):
+            self._def['kilojoule']
diff --git a/tests/bindings/python/bt2/testall.sh.in b/tests/bindings/python/bt2/testall.sh.in
new file mode 100644 (file)
index 0000000..9065753
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+check_coverage() {
+       coverage run $@
+}
+
+PYTHON_BUILD_DIR="@abs_top_builddir@/bindings/python"
+BT2_NATIVE_LIBS_DIR="@abs_top_builddir@/bindings/python/bt2/.libs"
+TESTS_UTILS_PYTHON_DIR="@abs_top_srcdir@/tests/utils/python"
+TESTRUNNER_PY="@abs_top_srcdir@/tests/utils/python/testrunner.py"
+THIS_DIR="@abs_top_srcdir@/tests/bindings/python/bt2"
+
+if test "x$TESTALL_COVERAGE" = "x1"; then
+       EXEC=check_coverage
+else
+       EXEC="@PYTHON@"
+
+fi
+
+PYTHONPATH="$PYTHON_BUILD_DIR:$BT2_NATIVE_LIBS_DIR:$TESTS_UTILS_PYTHON_DIR" \
+       "$EXEC" "$TESTRUNNER_PY" "$THIS_DIR"
+res=$?
+
+if test "x$TESTALL_COVERAGE_REPORT" = "x1"; then
+       coverage report -m
+fi
+
+if test "x$TESTALL_COVERAGE_HTML" = "x1"; then
+       coverage html
+fi
+
+exit $res
diff --git a/tests/utils/python/testrunner.py b/tests/utils/python/testrunner.py
new file mode 100644 (file)
index 0000000..ef1872f
--- /dev/null
@@ -0,0 +1,34 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2016 Philippe Proulx <pproulx@efficios.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+from tap import TAPTestRunner
+import unittest
+import sys
+
+
+if __name__ == '__main__':
+    loader = unittest.TestLoader()
+    tests = loader.discover(sys.argv[1])
+    runner = TAPTestRunner()
+    runner.set_stream(True)
+    runner.set_format('{method_name}')
+    sys.exit(0 if runner.run(tests).wasSuccessful() else 1)
This page took 0.127487 seconds and 4 git commands to generate.