#!/usr/bin/env python3

from __future__ import division
from __future__ import print_function

import unittest
import time

from circularbuffer import CircularBuffer


class CircularBufferTests(unittest.TestCase):
    """
    Tests for CircularBuffer.
    """

    def test_1_write(self):
        obj = CircularBuffer(5)
        self.assertEqual(obj.write(1), 1)
        self.assertEqual(obj.write((2, 3)), 2)

    def test_2_write_then_read(self):
        obj = CircularBuffer(3)
        self.assertEqual(obj.write(1), 1)
        self.assertEqual(obj.read(1), 1)

        self.assertEqual(obj.write((2, 3)), 2)
        self.assertEqual(obj.read(2), (2, 3))

    def test_3_write_cycle(self):
        obj = CircularBuffer(5)
        self.assertEqual(obj.write((1, 2, 3, 4)), 4)
        self.assertEqual(obj.read(4), (1, 2, 3, 4))
        self.assertEqual(obj.write((5, 6)), 2)

    def test_4_timings(self):
        obj = CircularBuffer(5)
        start_time = time.time()
        for i in range(100):
            obj.write((0, 1, 2, 3, 4))
        time_for_hundred = time.time() - start_time

        start_time = time.time()
        for i in range(10000):
            obj.write((0, 1, 2, 3, 4))
        time_for_ten_thousand = time.time() - start_time
        self.assertLess(time_for_ten_thousand, time_for_hundred * 110)

    # Comment the following line to force the check the bonus code
    @unittest.expectedFailure
    def test_bonus_1_content_function(self):
        obj = CircularBuffer(3)
        self.assertTrue(obj.is_empty())
        self.assertFalse(obj.is_full())

        obj.write(1)
        self.assertFalse(obj.is_empty())
        self.assertFalse(obj.is_full())

        obj.write(2)
        self.assertFalse(obj.is_empty())
        self.assertFalse(obj.is_full())

        obj.write(3)
        self.assertFalse(obj.is_empty())
        self.assertTrue(obj.is_full())

    # Comment the following line to force the check the bonus code
    @unittest.expectedFailure
    def test_bonus_1_string_representation(self):
        obj = CircularBuffer(5)
        self.assertEqual(repr(obj), "CircularBuffer(size=5, available=0)")

        obj.write(1)
        self.assertEqual(repr(obj), "CircularBuffer(size=5, available=1)")

        obj.write((2, 3, 4, 5))
        self.assertEqual(repr(obj), "CircularBuffer(size=5, available=5)")

    # Comment the following line to force the check the bonus code
    @unittest.expectedFailure
    def test_bonus_2_iterate(self):
        obj = CircularBuffer(5)
        obj.write((0, 1, 2, 3, 4))
        for i, j in zip(obj, range(5)):
            self.assertEqual(i, j)

    # Comment the following line to force the check the bonus code
    @unittest.expectedFailure
    def test_bonus_3_write_more_erase_oldest_value(self):
        obj = CircularBuffer(3)
        obj.write((0, 1, 2))
        obj.write((3, 4))
        self.assertEqual(obj.read(), 2)
        self.assertEqual(obj.read(), 3)
        self.assertEqual(obj.read(), 4)

    # Comment the following line to force the check the bonus code
    @unittest.expectedFailure
    def test_bonus_3_exception_raised_at_read(self):
        obj = CircularBuffer(3)
        obj.write((0, 1, 2))
        obj.read(3)
        with self.assertRaises(IndexError):
            obj.read()


if __name__ == "__main__":
    unittest.main(verbosity=2)
