Changeset - 9defd2c75048
[Not reviewed]
0 1 2
Branko Majic (branko) - 10 years ago 2013-12-29 21:09:05
branko@majic.rs
PYD-3: Implemented the initial set of tests that cover most of the functionality.
3 files changed with 340 insertions and 1 deletions:
0 comments (0 inline, 0 general)
setup.py
Show inline comments
 
@@ -3,7 +3,7 @@ from setuptools import setup
 

	
 
README = open(os.path.join(os.path.dirname(__file__), 'README.rst')).read()
 
INSTALL_REQUIREMENTS = ["Pillow"]
 
TEST_REQUIREMENTS = []
 
TEST_REQUIREMENTS = ["mock"]
 

	
 
# allow setup.py to be run from any path
 
os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir)))
 
@@ -21,6 +21,7 @@ setup(
 
    author_email='branko@majic.rs',
 
    install_requires=INSTALL_REQUIREMENTS,
 
    tests_require=TEST_REQUIREMENTS,
 
    test_suite="tests",
 
    classifiers=[
 
        'Environment :: Other Environment',
 
        'Environment :: Web Environment',
tests/__init__.py
Show inline comments
 
new file 100644
tests/test_pydenticon.py
Show inline comments
 
new file 100644
 
# Standard library imports.
 
import hashlib
 
import unittest
 
from io import BytesIO
 

	
 
# Third-party Python library imports.
 
import mock
 
import PIL
 

	
 
# Library imports.
 
from pydenticon import Generator
 

	
 

	
 
class GeneratorTest(unittest.TestCase):
 
    """
 
    Implements tests for pydenticon.Generator class.
 
    """
 

	
 
    def test_init_entropy(self):
 
        """
 
        Tests if the constructor properly checks for entropy provided by a
 
        digest algorithm.
 
        """
 

	
 
        # Set-up the mock instance.
 
        hexdigest_method = mock.MagicMock(return_value="aabb")
 
        digest_instance = mock.MagicMock()
 
        digest_instance.hexdigest = hexdigest_method
 

	
 
        # Set-up digest function that will always return the same digest
 
        # instance.
 
        digest_method = mock.MagicMock(return_value=digest_instance)
 

	
 
        # This should require 23 bits of entropy, while the digest we defined
 
        # provided 2*8 bits of entropy (2 bytes).
 
        self.assertRaises(ValueError, Generator, 5, 5, digest=digest_method)
 

	
 
    def test_init_parameters(self):
 
        """
 
        Verifies that the constructor sets-up the instance properties correctly.
 
        """
 

	
 
        generator = Generator(5, 5, digest=hashlib.sha1, foreground=["#111111", "#222222"], background="#aabbcc")
 

	
 
        # sha1 provides 160 bits of entropy - 20 bytes.
 
        self.assertEqual(generator.digest_entropy, 20 * 8)
 
        self.assertEqual(generator.digest, hashlib.sha1)
 
        self.assertEqual(generator.rows, 5)
 
        self.assertEqual(generator.columns, 5)
 
        self.assertEqual(generator.foreground, ["#111111", "#222222"])
 
        self.assertEqual(generator.background, "#aabbcc")
 

	
 
    def test_get_bit(self):
 
        """
 
        Tests if the check whether bit is 1 or 0 is performed correctly.
 
        """
 

	
 
        generator = Generator(5, 5)
 
        hash_bytes = [0b10010001, 0b10001000, 0b00111001]
 

	
 
        # Check a couple of bits from the above hash bytes.
 
        self.assertEqual(True, generator._get_bit(0, hash_bytes))
 
        self.assertEqual(True, generator._get_bit(7, hash_bytes))
 
        self.assertEqual(False, generator._get_bit(22, hash_bytes))
 
        self.assertEqual(True, generator._get_bit(23, hash_bytes))
 

	
 
    def test_generate_matrix(self):
 
        """
 
        Verifies that the matrix is generated correctly based on passed hashed
 
        bytes.
 
        """
 

	
 
        # The resulting half-matrix should be as follows (first byte is for
 
        # ignored in matrix generation):
 
        #
 
        # 100
 
        # 011
 
        # 100
 
        # 001
 
        # 110
 
        hash_bytes = [0b11111111, 0b10101010, 0b01010101]
 

	
 
        expected_matrix = [
 
            [True, False, False, False, True],
 
            [False, True, True, True, False],
 
            [True, False, False, False, True],
 
            [False, False, True, False, False],
 
            [True, True, False, True, True],
 
            ]
 

	
 
        generator = Generator(5, 5)
 

	
 
        matrix = generator._generate_matrix(hash_bytes)
 

	
 
        self.assertEqual(matrix, expected_matrix)
 

	
 
    def test_data_to_digest_byte_list_raw(self):
 
        """
 
        Test if correct digest byte list is returned for raw (non-hex-digest)
 
        data passed to the method.
 
        """
 

	
 
        # Set-up some raw data, and set-up the expected result.
 
        data = "this is a test\n"
 
        expected_digest_byte_list = [225, 156, 18, 131, 201, 37, 179, 32, 102, 133, 255, 82, 42, 207, 227, 230]
 

	
 
        # Instantiate a generator.
 
        generator = Generator(5, 5, digest=hashlib.md5)
 

	
 
        # Call the method and get the results.
 
        digest_byte_list = generator._data_to_digest_byte_list(data)
 

	
 
        # Verify the expected and actual result are identical.
 
        self.assertEqual(expected_digest_byte_list, digest_byte_list)
 

	
 
    def test_data_to_digest_byte_list_hex(self):
 
        """
 
        Test if correct digest byte list is returned for passed hex digest
 
        string.
 
        """
 

	
 
        # Set-up some test hex digest (md5), and expected result.
 
        hex_digest = "e19c1283c925b3206685ff522acfe3e6"
 
        expected_digest_byte_list = [225, 156, 18, 131, 201, 37, 179, 32, 102, 133, 255, 82, 42, 207, 227, 230]
 

	
 
        # Instantiate a generator.
 
        generator = Generator(5, 5, digest=hashlib.md5)
 

	
 
        # Call the method and get the results.
 
        digest_byte_list = generator._data_to_digest_byte_list(hex_digest)
 

	
 
        # Verify the expected and actual result are identical.
 
        self.assertEqual(expected_digest_byte_list, digest_byte_list)
 

	
 
    def test_data_to_digest_byte_list_hex_lookalike(self):
 
        """
 
        Test if correct digest byte list is returned for passed raw data that
 
        has same length as hex digest string.
 
        """
 

	
 
        # Set-up some test hex digest (md5), and expected result.
 
        data = "qqwweerrttyyuuiiooppaassddffgghh"
 
        expected_digest_byte_list = [25, 182, 52, 218, 118, 220, 26, 145, 164, 222, 33, 221, 183, 140, 98, 246]
 

	
 
        # Instantiate a generator.
 
        generator = Generator(5, 5, digest=hashlib.md5)
 

	
 
        # Call the method and get the results.
 
        digest_byte_list = generator._data_to_digest_byte_list(data)
 

	
 
        # Verify the expected and actual result are identical.
 
        self.assertEqual(expected_digest_byte_list, digest_byte_list)
 

	
 
    def test_generate_png_basics(self):
 
        """
 
        Tests some basics about generated PNG identicon image. This includes:
 

	
 
        - Dimensions of generated image.
 
        - Format of generated image.
 
        - Mode of generated image.
 
        """
 

	
 
        # Set-up parameters that will be used for generating the image.
 
        width = 200
 
        height = 200
 
        padding = [20, 20, 20, 20]
 
        foreground = "#ffffff"
 
        background = "#000000"
 
        matrix = [
 
            [0, 0, 1, 0, 0],
 
            [0, 0, 1, 0, 0],
 
            [0, 0, 1, 0, 0],
 
            [0, 1, 1, 1, 0],
 
            [0, 1, 1, 1, 0],
 
            ]
 

	
 
        # Set-up a generator.
 
        generator = Generator(5, 5)
 

	
 
        # Generate the raw image.
 
        raw_image = generator._generate_png(matrix, width, height, padding, foreground, background)
 

	
 
        # Try to load the raw image.
 
        image_stream = BytesIO(raw_image)
 
        image = PIL.Image.open(image_stream)
 

	
 
        # Verify image size, format, and mode.
 
        self.assertEqual(image.size[0], 240)
 
        self.assertEqual(image.size[1], 240)
 
        self.assertEqual(image.format, "PNG")
 
        self.assertEqual(image.mode, "RGB")
 

	
 
    def test_generate_ascii(self):
 
        """
 
        Tests the generated identicon in ASCII format.
 
        """
 

	
 
        # Set-up parameters that will be used for generating the image.
 
        foreground = "1"
 
        background = "0"
 
        matrix = [
 
            [0, 0, 1, 0, 0],
 
            [0, 0, 1, 0, 0],
 
            [0, 0, 1, 0, 0],
 
            [0, 1, 1, 1, 0],
 
            [0, 1, 1, 1, 0],
 
            ]
 

	
 
        # Set-up a generator.
 
        generator = Generator(5, 5)
 

	
 
        # Generate the ASCII image.
 
        ascii_image = generator._generate_ascii(matrix, foreground, background)
 

	
 
        # Verify that the result is as expected.
 
        expected_result = """00100
 
00100
 
00100
 
01110
 
01110"""
 
        self.assertEqual(ascii_image, expected_result)
 

	
 
    def test_generate_format(self):
 
        """
 
        Tests if identicons are generated in requested format.
 
        """
 

	
 
        # Set-up a generator.
 
        generator = Generator(5, 5)
 

	
 
        # Set-up some test data.
 
        data = "some test data"
 

	
 
        # Verify that PNG image is returned when requested.
 
        raw_image = generator.generate(data, 200, 200, output_format="png")
 
        image_stream = BytesIO(raw_image)
 
        image = PIL.Image.open(image_stream)
 
        self.assertEqual(image.format, "PNG")
 

	
 
        # Verify that ASCII "image" is returned when requested.
 
        raw_image = generator.generate(data, 200, 200, output_format="ascii")
 
        self.assertIsInstance(raw_image, str)
 

	
 
    def test_generate_format_invalid(self):
 
        """
 
        Tests if an exception is raised in case an unsupported format is
 
        requested when generating the identicon.
 
        """
 

	
 
        # Set-up a generator.
 
        generator = Generator(5, 5)
 

	
 
        # Set-up some test data.
 
        data = "some test data"
 

	
 
        # Verify that an exception is raised in case of unsupported format.
 
        self.assertRaises(ValueError, generator.generate, data, 200, 200, output_format="invalid")
 

	
 
    @mock.patch.object(Generator, '_generate_png')
 
    def test_generate_inverted_png(self, generate_png_mock):
 
        """
 
        Tests if the foreground and background are properly inverted when
 
        generating PNG images.
 
        """
 

	
 
        # Set-up some test data.
 
        data = "Some test data"
 

	
 
        # Set-up one foreground and background colour.
 
        foreground = "#ffffff"
 
        background = "#000000"
 

	
 
        # Set-up the generator.
 
        generator = Generator(5, 5, foreground=[foreground], background=background)
 

	
 
        # Verify that colours are picked correctly when no inverstion is requsted.
 
        generator.generate(data, 200, 200, inverted=False, output_format="png")
 
        generate_png_mock.assert_called_with(mock.ANY, mock.ANY, mock.ANY, mock.ANY, foreground, background)
 

	
 
        # Verify that colours are picked correctly when inversion is requsted.
 
        generator.generate(data, 200, 200, inverted=True, output_format="png")
 
        generate_png_mock.assert_called_with(mock.ANY, mock.ANY, mock.ANY, mock.ANY, background, foreground)
 

	
 
    @mock.patch.object(Generator, '_generate_ascii')
 
    def test_generate_inverted_ascii(self, generate_ascii_mock):
 
        """
 
        Tests if the foreground and background are properly inverted when
 
        generating ASCII "images".
 
        """
 

	
 
        # Set-up some test data.
 
        data = "Some test data"
 

	
 
        # Set-up one foreground and background colour. These are not used for
 
        # ASCII itself (instead a plus/minus sign is used).
 
        foreground = "#ffffff"
 
        background = "#000000"
 

	
 
        # Set-up the generator.
 
        generator = Generator(5, 5, foreground=[foreground], background=background)
 

	
 
        # Verify that foreground/background is picked correctly when no
 
        # inverstion is requsted.
 
        generator.generate(data, 200, 200, inverted=False, output_format="ascii")
 
        generate_ascii_mock.assert_called_with(mock.ANY, "+", "-")
 

	
 
        # Verify that foreground/background is picked correctly when inversion
 
        # is requsted.
 
        generator.generate(data, 200, 200, inverted=True, output_format="ascii")
 
        generate_ascii_mock.assert_called_with(mock.ANY, "-", "+")
 

	
 
    @mock.patch.object(Generator, '_generate_png')
 
    def test_generate_foreground(self, generate_png_mock):
 
        """
 
        Tests if the foreground colour is picked correctly.
 
        """
 

	
 
        # Set-up some foreground colours and a single background colour.
 
        foreground = ["#000000", "#111111", "#222222", "#333333", "#444444", "#555555"]
 
        background = "#ffffff"
 

	
 
        # Set-up the generator.
 
        generator = Generator(5, 5, foreground=foreground, background=background)
 

	
 
        # The first byte of hex digest should be 121 for this data, which should
 
        # result in foreground colour of index '1'.
 
        data = "some test data"
 
        generator.generate(data, 200, 200)
 
        generate_png_mock.assert_called_with(mock.ANY, mock.ANY, mock.ANY, mock.ANY, foreground[1], background)
 

	
 
        # The first byte of hex digest should be 149 for this data, which should
 
        # result in foreground colour of index '5'.
 
        data = "some other test data"
 
        generator.generate(data, 200, 200)
 
        generate_png_mock.assert_called_with(mock.ANY, mock.ANY, mock.ANY, mock.ANY, foreground[5], background)
 

	
 
if __name__ == '__main__':
 
    unittest.main()
0 comments (0 inline, 0 general)