<?php

/*
 * This file is part of KoolKode Async.
 *
 * (c) Martin Schröder <m.schroeder2007@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace KoolKode\Async\Stream;

use KoolKode\Async\Context;
use KoolKode\Async\Socket\Socket;
use KoolKode\Async\Test\AsyncTestCase;

/**
 * @covers \KoolKode\Async\Stream\SyncWriter
 * @covers \KoolKode\Async\Stream\SyncWrite
 */
class SyncWriterTest extends AsyncTestCase
{
    public function testMultipleWrites(Context $context)
    {
        list ($a, $b) = Socket::createPair();
        
        $message = str_repeat('a', 100000);
        
        $sender = function (Context $context) use ($a, $message) {
            $writer = new SyncWriter(new Writer($context->getLoop(), $a));
            $writes = [];
            
            try {
                foreach (str_split($message, 7000) as $i => $chunk) {
                    $ctx = ($i % 5) ? $context : $context->unreference();
                    
                    $writes[] = $writer->write($ctx, $chunk);
                }
                
                $len = array_sum(yield $context->all($writes));
            } finally {
                $writer->close();
            }
            
            return $len;
        };
        
        $receiver = function (Context $context) use ($b) {
            $reader = new Reader($context->getLoop(), $b);
            
            return yield $reader->buffer($context);
        };
        
        $this->assertEquals([
            strlen($message),
            $message
        ], yield $context->all([
            $sender,
            $receiver
        ]));
    }
    
    public function testKeepsLoopBusy(Context $context)
    {
        $stream = $this->createMock(WritableStream::class);
        $stream->expects($this->any())->method('write')->willReturnCallback(function (Context $context, string $chunk) {
            return $context->delay(10, strlen($chunk));
        });
        
        $writer = new SyncWriter($stream);
        
        $this->assertEquals([
            3,
            3
        ], yield $context->all([
            $writer->write($context->unreference(), 'foo'),
            $writer->write($context, 'bar')
        ]));
    }
    
    public function testFailureDelegation(Context $context)
    {
        list ($a, $b) = Socket::createPair();
        
        $writer = new SyncWriter(new Writer($context->getLoop(), $a));
        $writer->close();
        
        $this->expectException(StreamClosedException::class);
        
        yield $writer->write($context, 'foo');
    }
}
