amphp / byte-stream

A non-blocking stream abstraction for PHP based on Amp.

Home Page:https://amphp.org/byte-stream

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Increase test coverage

kelunik opened this issue · comments

We're currently at only 36%, that's way too low. This is an issue that should stay open as long as we're not on an acceptable level.

We're now at 64%, progress!

I'd be happy to work on this one!

Just submit a PR. :-)

I'm having trouble while testing the ZlibOutputStream.

Here are my steps:

  1. I create a IteratorStream with the fixtures/foobar.txt contents, like in the ZlibInputStreamTest.

  2. In the ZlibOutputStream instance I don't know what to pass as to the first parameter. What kind of OutputStream should I use? I tried to use a new ResourceOutputStream(fopen("php://memory", "wb+")). I also tried to create a ResourceInputStream with the same resource stream, but I can't read it back.

  3. I'm writing the contents of the IteratorStream to the ZlibOutputStream.

  4. Now I need to assert that the contents of the OutputStream instance match fixtures/foobar.txt.gz. I know I can't read from that interface, but I'm not sure how to proceed. Maybe a stream_socket_pair like in the ResourceStreamTest?

I guess we could add an OutputBuffer that implements Promise and OutputStream and resolves the promise once the stream has ended or fails it if there's an error.

Would you be interested in working on that?

Another solution is to use a stream pair, as you suggested.

Yes absolutely. In my mind this would complement the InMemoryStream.

I don't have a really good grasp of the Amp framework, so I'm not sure I can make this test pass.
Here's what I've tried so far.

I will try to increase coverage on easier parts.
If you can point me to some direction I will give it another shot, otherwise I can leave that part.
So far I've achieved a 90% coverage!

https://gist.github.com/sebdesign/b2ea5b9dc1bedb3f9614fa8f44031ab8

@sebdesign The issue is your output buffer implementation, it needs to be something like this:

<?php

namespace Amp\ByteStream;

use Amp\ByteStream\ClosedException;
use Amp\Deferred;
use Amp\Promise;
use Amp\Success;

class OutputBuffer implements OutputStream, Promise {
    private $deferred;
    private $contents;
    private $closed = false;
    
    public function __construct() {
        $this->deferred = new Deferred;
    }

    public function write(string $data): Promise {
        if ($this->closed) {
            throw new ClosedException("The stream has already been closed.");
        }
    
        $this->contents .= $data;
        
        return new Success(\strlen($data));
    }
    
    public function end(string $finalData = ""): Promise {
        if ($this->closed) {
            throw new ClosedException("The stream has already been closed.");
        }
        
        $this->contents .= $finalData;
        $this->closed = true;
        
        $this->deferred->resolve($this->contents);
        $this->contents = "";
        
        return new Success(\strlen($finalData));
    }
    
    public function onResolve(callable $onResolve) {
        $this->deferred->promise()->onResolve($onResolve);
    }
}

It needs to collect the contents and resolve the promise (async placeholder) once the stream is complete.

Thanks for your feedback!

But I'm still getting the same failure when comparing the compressed/uncompressed files.

Sorry, didn't have a closer look. The issue is that the stream is written in single byte chunks, so there are other compression blocks generated as when generating the complete file in one chunk. I guess it's best to test the input stream and then test the output stream by writing the output and then reading the contents again and see whether it's the same content (uncompressed).

Oh now I get it. That means we won't do any assertions against the compressed file?

That's probably best. Another option is to compress the full file in one chunk with the same compression level as the pre-generated file.

I got it passing by writing a $fileStream = new ResourceInputStream(fopen($file1, 'r')); to the OutputBuffer through the ZlibOutputStream.

Then I used an InMemoryStream with the contents of the OutputBuffer through the ZlibInputStream to decompress the data and assert against the uncompressed file.

I changed the code in the end method of the ZlibOutputStream to call the end method on the destination.
Then in the test I'm calling yield $gzStream->end() otherwise nothing works.

One thing I noticed is that the close() method on the ZlipOutputStream is not being called.

Indeed, the if ($error) looks wrong in the onResolve call.

We're at 94% now, think I can close this issue. 🎉