From 3ecb63aaf5fb8cece4aa543ee17c63986b06447e Mon Sep 17 00:00:00 2001 From: Peter Hamilton <peter.hamilton@jhuapl.edu> Date: Wed, 17 Jun 2015 14:10:31 -0400 Subject: [PATCH] Finishing Boolean primitive implementation This change finishes the Boolean primitive implementation, including a complete test suite for the Boolean class. --- kmip/core/primitives.py | 118 +++++-- .../unit/core/primitives/test_boolean.py | 303 ++++++++++++++++++ .../unit/core/primitives/test_primitives.py | 37 --- 3 files changed, 395 insertions(+), 63 deletions(-) create mode 100644 kmip/tests/unit/core/primitives/test_boolean.py diff --git a/kmip/core/primitives.py b/kmip/core/primitives.py index 8779364..6c0ddc6 100644 --- a/kmip/core/primitives.py +++ b/kmip/core/primitives.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +import logging import six import sys @@ -428,60 +429,125 @@ class Enumeration(Integer): class Boolean(Base): + """ + An encodeable object representing a boolean value. - def __init__(self, value=None, tag=Tags.DEFAULT): + A Boolean is one of the KMIP primitive object types. It is encoded as an + unsigned, big-endian, 8-byte value, capable of taking the values True (1) + or False (0). For more information, see Section 9.1 of the KMIP 1.1 + specification. + """ + LENGTH = 8 + + def __init__(self, value=True, tag=Tags.DEFAULT): + """ + Create a Boolean object. + + Args: + value (bool): The value of the Boolean. Optional, defaults to True. + tag (Tags): An enumeration defining the tag of the Boolean object. + Optional, defaults to Tags.DEFAULT. + """ super(Boolean, self).__init__(tag, type=Types.BOOLEAN) + self.logger = logging.getLogger(__name__) self.value = value - self.length = 8 + self.length = self.LENGTH + + self.validate() def read_value(self, istream): - value = unpack('!Q', str(istream[0:self.length]))[0] + """ + Read the value of the Boolean object from the input stream. + + Args: + istream (Stream): A buffer containing the encoded bytes of the + value of a Boolean object. Usually a BytearrayStream object. + Required. + + Raises: + ValueError: if the read boolean value is not a 0 or 1. + """ + try: + value = unpack('!Q', istream.read(self.LENGTH))[0] + except: + self.logger.error("Error reading boolean value from buffer") + raise if value == 1: self.value = True elif value == 0: self.value = False else: - raise errors.ReadValueError(Boolean.__name__, 'value', - value) + raise ValueError("expected: 0 or 1, observed: {0}".format(value)) - for _ in range(self.length): - istream.pop(0) + self.validate() def read(self, istream): + """ + Read the encoding of the Boolean object from the input stream. + + Args: + istream (Stream): A buffer containing the encoded bytes of a + Boolean object. Usually a BytearrayStream object. Required. + """ super(Boolean, self).read(istream) self.read_value(istream) def write_value(self, ostream): - if self.value is None: - raise errors.WriteValueError(Boolean.__name__, 'value', - self.value) + """ + Write the value of the Boolean object to the output stream. - data_buffer = bytearray() - - if isinstance(self.value, type(True)): - if self.value: - data_buffer.extend(pack('!Q', 1)) - else: - data_buffer.extend(pack('!Q', 0)) - else: - raise errors.WriteTypeError(Boolean.__name__, 'value', - type(self.value)) - - ostream.extend(data_buffer) + Args: + ostream (Stream): A buffer to contain the encoded bytes of the + value of a Boolean object. Usually a BytearrayStream object. + Required. + """ + try: + ostream.write(pack('!Q', self.value)) + except: + self.logger.error("Error writing boolean value to buffer") + raise def write(self, ostream): + """ + Write the encoding of the Boolean object to the output stream. + + Args: + ostream (Stream): A buffer to contain the encoded bytes of a + Boolean object. Usually a BytearrayStream object. Required. + """ super(Boolean, self).write(ostream) self.write_value(ostream) def validate(self): - self.__validate() + """ + Verify that the value of the Boolean object is valid. - def __validate(self): - pass + Raises: + TypeError: if the value is not of type bool. + """ + if self.value: + if not isinstance(self.value, bool): + raise TypeError("expected: {0}, observed: {1}".format( + bool, type(self.value))) def __repr__(self): - return '<Boolean, %s>' % (self.value) + return "{0}(value={1})".format(type(self).__name__, repr(self.value)) + + def __str__(self): + return "{0}".format(repr(self.value)) + + def __eq__(self, other): + if isinstance(other, Boolean): + return self.value == other.value + else: + return NotImplemented + + def __ne__(self, other): + if isinstance(other, Boolean): + return not self.__eq__(other) + else: + return NotImplemented class TextString(Base): diff --git a/kmip/tests/unit/core/primitives/test_boolean.py b/kmip/tests/unit/core/primitives/test_boolean.py new file mode 100644 index 0000000..0074fe1 --- /dev/null +++ b/kmip/tests/unit/core/primitives/test_boolean.py @@ -0,0 +1,303 @@ +# Copyright (c) 2015 The Johns Hopkins University/Applied Physics Laboratory +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from testtools import TestCase + +from kmip.core.primitives import Boolean +from kmip.core.utils import BytearrayStream + + +class TestBoolean(TestCase): + + def setUp(self): + super(TestBoolean, self).setUp() + self.stream = BytearrayStream() + + def tearDown(self): + super(TestBoolean, self).tearDown() + + def test_init(self): + """ + Test that a Boolean object can be instantiated. + """ + boolean = Boolean(False) + self.assertEqual(False, boolean.value) + + def test_init_unset(self): + """ + Test that a Boolean object can be instantiated with no input. + """ + boolean = Boolean() + self.assertEqual(True, boolean.value) + + def test_validate_on_valid(self): + """ + Test that a Boolean object can be validated on good input. + """ + boolean = Boolean(True) + boolean.validate() + + def test_validate_on_valid_unset(self): + """ + Test that a Boolean object with no preset value can be validated. + """ + boolean = Boolean() + boolean.validate() + + def test_validate_on_invalid_type(self): + """ + Test that a TypeError is raised when a Boolean object is built with an + invalid value. + """ + self.assertRaises(TypeError, Boolean, 'invalid') + + def test_read_true(self): + """ + Test that a Boolean object representing the value True can be read + from a byte stream. + """ + encoding = (b'\x42\x00\x00\x06\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00' + b'\x00\x01') + stream = BytearrayStream(encoding) + boolean = Boolean() + + boolean.read(stream) + + self.assertTrue(boolean.value) + + def test_read_false(self): + """ + Test that a Boolean object representing the value False can be read + from a byte stream. + """ + encoding = (b'\x42\x00\x00\x06\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00' + b'\x00\x00') + stream = BytearrayStream(encoding) + boolean = Boolean() + + boolean.read(stream) + + self.assertFalse(boolean.value) + + def test_read_bad_encoding(self): + """ + Test that an Exception is raised when the Boolean read operation fails + on a bad encoding. + """ + encoding = (b'\x42\x00\x00\x06\x00\x00\x00\x08') + stream = BytearrayStream(encoding) + boolean = Boolean() + + self.assertRaises(Exception, boolean.read, stream) + + def test_read_bad_value(self): + """ + Test that a ValueError is raised when the Boolean read operations + reads a valid integer but invalid boolean. + """ + encoding = (b'\x42\x00\x00\x06\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00' + b'\x00\x02') + stream = BytearrayStream(encoding) + boolean = Boolean() + + self.assertRaises(ValueError, boolean.read, stream) + + def test_write_true(self): + """ + Test that a Boolean object representing the value True can be written + to a byte stream. + """ + encoding = (b'\x42\x00\x00\x06\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00' + b'\x00\x01') + stream = BytearrayStream() + boolean = Boolean(True) + + boolean.write(stream) + + self.assertEqual(encoding, stream.read()) + + def test_write_false(self): + """ + Test that a Boolean object representing the value False can be written + to a byte stream. + """ + encoding = (b'\x42\x00\x00\x06\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00' + b'\x00\x00') + stream = BytearrayStream() + boolean = Boolean(False) + + boolean.write(stream) + + self.assertEqual(encoding, stream.read()) + + def test_write_bad_value(self): + """ + Test that an Exception is raised when the Boolean write operation fails + on a bad boolean value. + """ + stream = BytearrayStream() + boolean = Boolean() + boolean.value = 'invalid' + + self.assertRaises(Exception, boolean.write, stream) + + def test_repr_default(self): + """ + Test that the representation of a Boolean object is formatted properly + and can be used by eval to create a new Boolean object. + """ + boolean = Boolean() + + self.assertEqual("Boolean(value=True)", repr(boolean)) + self.assertEqual(boolean, eval(repr(boolean))) + + def test_repr_true(self): + """ + Test that the representation of a Boolean object representing the + value True is formatted properly and can be used by eval to create a + new Boolean object. + """ + boolean = Boolean(True) + + self.assertEqual("Boolean(value=True)", repr(boolean)) + self.assertEqual(boolean, eval(repr(boolean))) + self.assertTrue(eval(repr(boolean)).value) + + def test_repr_false(self): + """ + Test that the representation of a Boolean object representing the + value False is formatted properly and can be used by eval to create a + new Boolean object. + """ + boolean = Boolean(False) + + self.assertEqual("Boolean(value=False)", repr(boolean)) + self.assertEqual(boolean, eval(repr(boolean))) + self.assertFalse(eval(repr(boolean)).value) + + def test_str_default(self): + """ + Test that the string representation of a Boolean object is formatted + properly. + """ + boolean = Boolean() + + self.assertEqual("True", str(boolean)) + + def test_str_true(self): + """ + Test that the string representation of a Boolean object representing + the value True is formatted properly. + """ + boolean = Boolean(True) + + self.assertEqual("True", str(boolean)) + + def test_str_false(self): + """ + Test that the string representation of a Boolean object representing + the value False is formatted properly. + """ + boolean = Boolean(False) + + self.assertEqual("False", str(boolean)) + + def test_equal_on_equal(self): + """ + Test that the equality operator returns True when comparing two + Boolean objects. + """ + a = Boolean(False) + b = Boolean(False) + + self.assertTrue(a == b) + self.assertTrue(b == a) + + def test_equal_on_equal_and_empty(self): + """ + Test that the equality operator returns True when comparing two + Boolean objects. + """ + a = Boolean() + b = Boolean() + + self.assertTrue(a == b) + self.assertTrue(b == a) + + def test_equal_on_not_equal(self): + """ + Test that the equality operator returns False when comparing two + Boolean objects with different values. + """ + a = Boolean(True) + b = Boolean(False) + + self.assertFalse(a == b) + self.assertFalse(b == a) + + def test_equal_on_type_mismatch(self): + """ + Test that the equality operator returns False when comparing a + Boolean object to a non-Boolean object. + """ + a = Boolean() + b = 'invalid' + + self.assertFalse(a == b) + self.assertFalse(b == a) + + def test_not_equal_on_equal(self): + """ + Test that the inequality operator returns False when comparing + two Boolean objects with the same values. + """ + a = Boolean(False) + b = Boolean(False) + + self.assertFalse(a != b) + self.assertFalse(b != a) + + def test_not_equal_on_equal_and_empty(self): + """ + Test that the inequality operator returns False when comparing + two Boolean objects. + """ + a = Boolean() + b = Boolean() + + self.assertFalse(a != b) + self.assertFalse(b != a) + + def test_not_equal_on_not_equal(self): + """ + Test that the inequality operator returns True when comparing two + Boolean objects with different values. + """ + a = Boolean(True) + b = Boolean(False) + + self.assertTrue(a != b) + self.assertTrue(b != a) + + def test_not_equal_on_type_mismatch(self): + """ + Test that the inequality operator returns True when comparing a + Boolean object to a non-Boolean object. + """ + a = Boolean() + b = 'invalid' + + self.assertTrue(a != b) + self.assertTrue(b != a) diff --git a/kmip/tests/unit/core/primitives/test_primitives.py b/kmip/tests/unit/core/primitives/test_primitives.py index 6c0086e..f0b5335 100644 --- a/kmip/tests/unit/core/primitives/test_primitives.py +++ b/kmip/tests/unit/core/primitives/test_primitives.py @@ -906,43 +906,6 @@ class TestEnumeration(TestCase): self.assertEqual(encoding, result, self.bad_encoding) -class TestBoolean(TestCase): - - def setUp(self): - super(TestBoolean, self).setUp() - self.stream = BytearrayStream() - - def tearDown(self): - super(TestBoolean, self).tearDown() - - def test_init(self): - self.skip('') - - def test_init_unset(self): - self.skip('') - - def test_validate_on_valid(self): - self.skip('') - - def test_validate_on_valid_unset(self): - self.skip('') - - def test_validate_on_invalid_type(self): - self.skip('') - - def test_read_value(self): - self.skip('') - - def test_read(self): - self.skip('') - - def test_write_value(self): - self.skip('') - - def test_write(self): - self.skip('') - - class TestTextString(TestCase): def setUp(self):