Skip to content

Commit

Permalink
Stream readers and writers for NdJson input
Browse files Browse the repository at this point in the history
  • Loading branch information
ciaranmcnulty committed Jan 23, 2022
1 parent f307b4f commit cc87eee
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 5 deletions.
21 changes: 21 additions & 0 deletions messages/php/src/Streams/NdJsonStreamReader.php
@@ -0,0 +1,21 @@
<?php declare(strict_types=1);

namespace Cucumber\Messages\Streams;

use Cucumber\Messages\Envelope;
use Generator;

final class NdJsonStreamReader implements StreamReader
{
use WithFileHandleTrait;

/**
* @return Generator<int, Envelope>
*/
public function envelopes() : Generator
{
while (!feof($this->fileHandle) && ($line = fgets($this->fileHandle))) {
yield Envelope::fromJson($line);
}
}
}
20 changes: 20 additions & 0 deletions messages/php/src/Streams/NdJsonStreamWriter.php
@@ -0,0 +1,20 @@
<?php declare(strict_types=1);

namespace Cucumber\Messages\Streams;

use Cucumber\Messages\Envelope;

final class NdJsonStreamWriter implements StreamWriter
{
use WithFileHandleTrait;

/**
* @param iterable<Envelope> $envelopes
*/
public function writeEnvelopes(iterable $envelopes) : void
{
foreach($envelopes as $envelope) {
fputs($this->fileHandle, $envelope->asJson() . "\n");
}
}
}
13 changes: 13 additions & 0 deletions messages/php/src/Streams/StreamReader.php
@@ -0,0 +1,13 @@
<?php declare(strict_types=1);

namespace Cucumber\Messages\Streams;

use Cucumber\Messages\Envelope;

interface StreamReader
{
/**
* @return iterable<Envelope>
*/
public function envelopes(): iterable;
}
13 changes: 13 additions & 0 deletions messages/php/src/Streams/StreamWriter.php
@@ -0,0 +1,13 @@
<?php declare(strict_types=1);

namespace Cucumber\Messages\Streams;

use Cucumber\Messages\Envelope;

interface StreamWriter
{
/**
* @param iterable<Envelope> $envelopes
*/
public function writeEnvelopes(iterable $envelopes): void;
}
24 changes: 24 additions & 0 deletions messages/php/src/Streams/WithFileHandleTrait.php
@@ -0,0 +1,24 @@
<?php declare(strict_types=1);

namespace Cucumber\Messages\Streams;

/**
* @internal
*/
trait WithFileHandleTrait
{
/**
* @param resource $fileHandle
*/
private function __construct(
private mixed $fileHandle,
){}

/**
* @param resource $fileHandle
*/
public static function fromFileHandle($fileHandle): self
{
return new self($fileHandle);
}
}
59 changes: 54 additions & 5 deletions messages/php/tests/AcceptanceTest.php
@@ -1,11 +1,13 @@
<?php

use Cucumber\Messages\Envelope;
use Cucumber\Messages\Streams\NdJsonStreamReader;
use Cucumber\Messages\Streams\NdJsonStreamWriter;
use PHPUnit\Framework\TestCase;

class AcceptanceTest extends TestCase
{
/** @dataProvider provideJson */
/** @dataProvider provideJsonLines */
public function testAllNdJsonSurvivesDecodingThenEncoding(string $json) : void
{
$envelope = Envelope::fromJson($json);
Expand All @@ -14,18 +16,65 @@ public function testAllNdJsonSurvivesDecodingThenEncoding(string $json) : void
self::assertJsonStringEqualsJsonString($json, $newJson);
}

/** @dataProvider provideNdJsonFilenames */
public function testAllFileStreamsSurviveDecodingThenEncoding(string $filename) : void
{
$sourceHandle = fopen($filename, 'r');
$destHandle = fopen('php://memory', 'w');

$reader = NdJsonStreamReader::fromFileHandle($sourceHandle);
$writer = NdJsonStreamWriter::fromFileHandle($destHandle);

$writer->writeEnvelopes($reader->envelopes());

rewind($sourceHandle);
rewind($destHandle);

while (!feof($sourceHandle)) {
$sourceLine = fgets($sourceHandle);
$destLine = fgets($destHandle);

if (!$sourceLine && !$destLine) {
break;
}

self::assertJsonStringEqualsJsonString($sourceLine, $destLine);
}

// we exhausted source so dest should also be at end
self::assertTrue(feof($destHandle));
}

/**
* @return Generator<string, list<string>>
* @return Generator<string, array{0: string}>
*/
public function provideJson() : Generator
public function provideJsonLines() : Generator
{
$filePattern = __DIR__ . '/../../../compatibility-kit/javascript/features/**/*.ndjson';
foreach (glob($filePattern) as $filename) {
foreach ($this->getSampleFiles() as $filename) {
foreach(file($filename) ?: [] as $lineNumber => $line) {
// key is provided for better error messages
$key = realpath($filename) . ':' . $lineNumber;
yield $key => [$line];
}
}
}

/**
* @return Generator<string, array{0: string}>
*/
public function provideNdJsonFilenames() : Generator
{
foreach($this->getSampleFiles() as $filename)
{
yield $filename => [$filename];
}
}

/**
* @return list<string>
*/
private function getSampleFiles(): array
{
return glob(__DIR__ . '/../../../compatibility-kit/javascript/features/**/*.ndjson') ?: [];
}
}

0 comments on commit cc87eee

Please sign in to comment.