--- /dev/null
+### -*- mode: python, coding: utf-8 -*-
+###
+### Test `ByteString'
+###
+### (c) 2019 Straylight/Edgeware
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the Python interface to Catacomb.
+###
+### Catacomb/Python is free software: you can redistribute it and/or
+### modify it under the terms of the GNU General Public License as
+### published by the Free Software Foundation; either version 2 of the
+### License, or (at your option) any later version.
+###
+### Catacomb/Python is distributed in the hope that it will be useful, but
+### WITHOUT ANY WARRANTY; without even the implied warranty of
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+### General Public License for more details.
+###
+### You should have received a copy of the GNU General Public License
+### along with Catacomb/Python. If not, write to the Free Software
+### Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+### USA.
+
+###--------------------------------------------------------------------------
+### Imported modules.
+
+import sys as SYS
+import catacomb as C
+import unittest as U
+import testutils as T
+
+###--------------------------------------------------------------------------
+class TestByteString (U.TestCase):
+
+ def test_create(me):
+
+ ## Create a string and make sure it looks right.
+ x = C.ByteString(T.bin("abcde"))
+ me.assertEqual(x, T.bin("abcde"))
+ me.assertEqual(x, C.bytes("6162636465"))
+ me.assertEqual(len(x), 5)
+
+ def test_index(me):
+
+ x = C.ByteString(T.bin("once upon a time there was a string"))
+
+ ## Check that simple indexing works.
+ me.assertEqual(x[3], 'e')
+ me.assertEqual(x[-5], 't')
+
+ ## Check out-of-range detection.
+ x[34]; me.assertRaises(IndexError, lambda: x[35])
+ x[-35]; me.assertRaises(IndexError, lambda: x[-36])
+
+ ## Check slicing.
+ me.assertEqual(x[7:17], T.bin("on a time "))
+
+ ## Complex slicing is also supported.
+ me.assertEqual(x[5:23:3], C.bytes("756e206d7472"))
+
+ def test_compare(me):
+ """
+ Test byte string comparison.
+
+ This is rather important, since we override it and many of the other
+ tests assume that comparison works.
+ """
+
+ def check(big, small):
+ """Check comparisons between BIG and SMALL strings."""
+
+ ## Equality.
+ me.assertTrue(big == big)
+ me.assertFalse(big == small)
+
+ ## Inequality.
+ me.assertFalse(big != big)
+ me.assertTrue(big != small)
+
+ ## Strict less-than.
+ me.assertFalse(big < big)
+ me.assertFalse(big < small)
+ me.assertTrue(small < big)
+
+ ## Non-strict less-than.
+ me.assertTrue(big <= big)
+ me.assertFalse(big <= small)
+ me.assertTrue(small <= big)
+
+ ## Non-strict greater-than.
+ me.assertTrue(big >= big)
+ me.assertTrue(big >= small)
+ me.assertFalse(small >= big)
+
+ ## Strict greater-than.
+ me.assertFalse(big > big)
+ me.assertTrue(big > small)
+ me.assertFalse(small > big)
+
+ ## Strings with equal length.
+ check(C.ByteString(T.bin("a string which is unlike the second")),
+ C.ByteString(T.bin("a string that is not like the first")))
+
+ ## A string and a prefix of it.
+ check(C.ByteString(T.bin("short strings order before longer ones")),
+ C.ByteString(T.bin("short string")))
+
+ ## The `ctstreq' function.
+ x = T.bin("special test string")
+ y = T.bin("my different string")
+ me.assertTrue(C.ctstreq(x, x))
+ me.assertFalse(C.ctstreq(x, y))
+
+ def test_operators(me):
+
+ ## Some example strings.
+ x = C.bytes("03a5fc")
+ y = C.bytes("5fac30")
+ z = C.bytes("00000000")
+
+ ## Operands of a binary operator must have equal lengths.
+ me.assertRaises(ValueError, lambda: x&z)
+ me.assertRaises(ValueError, lambda: x|z)
+ me.assertRaises(ValueError, lambda: x^z)
+
+ ## Bitwise AND.
+ me.assertEqual(type(x&y), C.ByteString)
+ me.assertEqual(x&y, C.bytes("03a430"))
+
+ ## Bitwise OR.
+ me.assertEqual(type(x | y), C.ByteString)
+ me.assertEqual(x | y, C.bytes("5fadfc"))
+
+ # Bitwise XOR.
+ me.assertEqual(type(x ^ y), C.ByteString)
+ me.assertEqual(x ^ y, C.bytes("5c09cc"))
+
+ # Bitwise NOT.
+ me.assertEqual(type(~x), C.ByteString)
+ me.assertEqual(~x, C.bytes("fc5a03"))
+
+ ## Concatenation.
+ me.assertEqual(x + y, C.bytes("03a5fc5fac30"))
+
+ ## Replication (asymmetric but commutative).
+ me.assertEqual(3*x, C.bytes("03a5fc03a5fc03a5fc"))
+ me.assertEqual(x*3, C.bytes("03a5fc03a5fc03a5fc"))
+
+ ## Replication by zero (regression test).
+ me.assertEqual(0*x, C.ByteString(T.bin("")))
+ me.assertEqual(x*0, C.ByteString(T.bin("")))
+
+ def test_zero(me):
+ me.assertEqual(C.ByteString.zero(7), T.bin(7*"\0"))
+ me.assertEqual(C.ByteString.zero(0), T.bin(""))
+
+###----- That's all, folks --------------------------------------------------
+
+if __name__ == "__main__": U.main()