# Copyright (C) 2010, 2011 Sebastian Thiel (byronimo@gmail.com) and contributors
#
# This module is part of async and is released under
# the New BSD License: http://www.opensource.org/licenses/bsd-license.php
"""Channel testing"""
from .lib import TestBase
from async.channel import (
    CallbackChannelWriter, 
    CallbackChannelReader, 
    ChannelWriter, 
    ChannelReader,
    IteratorReader, 
    mkchannel, 
    ReadOnly
)

import time

class TestChannels(TestBase):

    def test_base(self):
        # creating channel yields a write and a read channal
        wc, rc = mkchannel()
        assert isinstance(wc, ChannelWriter)        # default args
        assert isinstance(rc, ChannelReader)


        # TEST UNLIMITED SIZE CHANNEL - writing+reading is FIFO
        item = 1
        item2 = 2
        wc.write(item)
        wc.write(item2)

        # read all - it blocks as its still open for writing
        to = 0.2
        st = time.time()
        assert rc.read(timeout=to) == [item, item2]
        assert time.time() - st >= to

        # next read blocks. it waits a second
        st = time.time()
        assert len(rc.read(1, True, to)) == 0
        assert time.time() - st >= to

        # writing to a closed channel raises
        assert not wc.closed()
        wc.close()
        assert wc.closed()
        wc.close()  # fine
        assert wc.closed()

        self.failUnlessRaises(ReadOnly, wc.write, 1)

        # reading from a closed channel never blocks
        assert len(rc.read()) == 0
        assert len(rc.read(5)) == 0
        assert len(rc.read(1)) == 0


        # test callback channels
        wc, rc = mkchannel(wtype = CallbackChannelWriter, rtype = CallbackChannelReader)

        cb = [0, 0, 0]      # set slots to one if called
        def pre_write(item):
            cb[0] = 1
            return item + 1
        def pre_read(count):
            cb[1] = 1
        def post_read(items):
            assert isinstance(items, list)
            cb[2] = 1
            return [ i+1 for i in items]


        # set, verify it returns previous one
        assert wc.set_pre_cb(pre_write) is None
        assert rc.set_pre_cb(pre_read) is None
        assert rc.set_post_cb(post_read) is None
        assert wc.set_pre_cb(pre_write) is pre_write
        assert rc.set_pre_cb(pre_read) is pre_read
        assert rc.set_post_cb(post_read) is post_read

        # writer transforms input
        val = 5
        wc.write(val)
        assert cb[0] == 1 and cb[1] == 0

        rval = rc.read(1)[0]        # read one item, must not block
        assert cb[0] == 1 and cb[1] == 1 and cb[2] == 1
        assert rval == val + 1 + 1


        # ITERATOR READER
        reader = IteratorReader(iter(list(range(10))))
        assert len(reader.read(2)) == 2
        assert len(reader.read(0)) == 8
        # its empty now
        assert len(reader.read(0)) == 0
        assert len(reader.read(5)) == 0

        # doesn't work if item is not an iterator
        self.failUnlessRaises(ValueError, IteratorReader, list())


        # test general read-iteration - its supported by all readers
        reader = IteratorReader(iter(list(range(10))))

        assert len(list(reader)) == 10

        # NOTE: its thread-safety is tested by the pool
