Add tests

This commit is contained in:
R. Eric Wheeler 2021-01-25 12:35:32 -08:00
parent 95c5b66fdc
commit ac0de0f92d
12 changed files with 2301 additions and 107 deletions

1
.gitignore vendored
View File

@ -2,3 +2,4 @@
.idea/ .idea/
.php_cs.cache .php_cs.cache
vendor/ vendor/
.phpunit.cache/

View File

@ -1,6 +1,6 @@
# getch # getch
This simply uses the FFI extension to enable _getwch in Windows and an implimentation of _getwch on linux. This simply uses the FFI extension to enable _getch and _ungetch in Windows and linux.
```shell script ```shell script
$ composer require sikofitt/getch:dev-master $ composer require sikofitt/getch:dev-master
@ -11,17 +11,46 @@ $ composer require sikofitt/getch:dev-master
$g = new Getch($linuxLibrary = null); // can also be a library that implements a function called _getch; $g = new Getch($linuxLibrary = null); // can also be a library that implements a function called _getch;
// by default uses the bundled Resources/libgetch.so // by default uses the bundled Resources/libgetch.so
// on windows uses the built in _getch function. // on windows uses the built in _getch function.
$char = $g->getch(); $ord = $g->getch();
print $char; print \chr($ord);
$ord = $g->ungetch('A');
$res = $g->getch();
$ord === $res // 65
``` ```
There is also a helper function called getch(); Note that if you want to put a word into the STDIN stack, you need to do it in reverse.
```php
foreach(\str_split(\strrev('Hello World!')) as $char) {
ungetch($char);
}
$result = '';
do {
$ord = getch();
$result .= \chr($ord);
} while($ord !== ord('!'));
print $result; // Hello World!
```
There are also helper functions called getch() and ungetch();
```php ```php
use function Sikofitt\Console\getch; use function Sikofitt\Console\getch;
$char = getch($linuxLibrary = null); $ord = getch($linuxLibrary = null);
print $char; print \chr($ord);
$ord = ungetch('B');
$res = getch();
$ord === $res // 66
``` ```
## Tests ## Tests
No tests yet. Just written.
vendor/bin/phpunit

View File

@ -1,23 +1,29 @@
{ {
"name": "sikofitt/getch", "name": "sikofitt/getch",
"description": "Implements getch and getwch for windows and linux using ffi", "description": "Implements _getch and _ungetch for windows and linux using ffi",
"type": "library", "type": "library",
"require": { "require": {
"php": ">=7.4", "php": ">=7.4",
"ext-ffi": "*" "ext-ffi": "*"
}, },
"require-dev": { "require-dev": {
"friendsofphp/php-cs-fixer": "^2.17", "friendsofphp/php-cs-fixer": "^2.18",
"jetbrains/phpstorm-stubs": "dev-master" "jetbrains/phpstorm-stubs": "dev-master",
"phpunit/phpunit": "^9.5"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"Sikofitt\\Console\\": "src/Console/" "Sikofitt\\Console\\": "src/Console/"
}, },
"files": [ "files": [
"src/Console/function.php" "functions.php"
] ]
}, },
"autoload-dev": {
"psr-4": {
"Sikofitt\\Tests\\Console\\": "tests/"
}
},
"license": "MPL-2.0", "license": "MPL-2.0",
"authors": [ "authors": [
{ {

2135
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,6 @@
<?php declare(strict_types=1); <?php
declare(strict_types=1);
/* /*
* Copyright (c) 2020 https://rewiv.com sikofitt@gmail.com * Copyright (c) 2020 https://rewiv.com sikofitt@gmail.com
@ -10,26 +12,27 @@
* You can obtain one at https://mozilla.org/MPL/2.0/. * You can obtain one at https://mozilla.org/MPL/2.0/.
*/ */
namespace Sikofitt\Console; use Sikofitt\Console\Getch;
if (!function_exists('getch')) { if (!function_exists('getch')) {
function getch(string $linuxLibrary = null): string function getch(string $linuxLibrary = null): int
{ {
$g = new Getch($linuxLibrary); $g = new Getch($linuxLibrary);
return $g->getch(); return $g->getch();
} }
} }
if(!function_exists('ungetch')) { if (!function_exists('ungetch')) {
function ungetch($char, string $linuxLibrary = null): int
function ungetch(string $char, string $linuxLibrary = null): int
{ {
$g = new Getch($linuxLibrary); $g = new Getch($linuxLibrary);
return $g->ungetch($char); return $g->ungetch($char);
} }
} }
if(!function_exists('ungetchString')) { if (!function_exists('ungetchString')) {
function ungetchString(string $string, string $linuxLibrary = null): bool function ungetchString(string $string, string $linuxLibrary = null): bool
{ {
$g = new Getch($linuxLibrary); $g = new Getch($linuxLibrary);

30
phpunit.xml Normal file
View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.5/phpunit.xsd"
bootstrap="vendor/autoload.php"
cacheResultFile=".phpunit.cache/test-results"
executionOrder="depends,defects"
forceCoversAnnotation="false"
beStrictAboutCoversAnnotation="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
failOnRisky="true"
failOnWarning="true"
colors="true"
verbose="true">
<testsuites>
<testsuite name="getch">
<directory suffix="Test.php">tests/Getch</directory>
</testsuite>
<testsuite name="ungetch">
<directory suffix="Test.php">tests/Ungetch</directory>
</testsuite>
</testsuites>
<coverage cacheDirectory=".phpunit.cache/code-coverage"
processUncoveredFiles="true">
<include>
<directory suffix=".php">src</directory>
</include>
</coverage>
</phpunit>

View File

@ -1,4 +1,6 @@
<?php declare(strict_types=1); <?php
declare(strict_types=1);
/* /*
* Copyright (c) 2020 https://rewiv.com sikofitt@gmail.com * Copyright (c) 2020 https://rewiv.com sikofitt@gmail.com
@ -17,8 +19,12 @@ use RuntimeException;
final class Getch final class Getch
{ {
private const LINUX_LIBRARY = __DIR__ . '/Resources/libgetch.so'; private const LINUX_LIBRARY = __DIR__.'/Resources/libgetch.so';
private const WINDOWS_LIBRARY = 'ucrtbase.dll'; private const WINDOWS_LIBRARY = 'ucrtbase.dll';
private const DECLARATIONS = <<<DECLARATIONS
int _getch();
int _ungetch(int c);
DECLARATIONS;
private static ?FFI $ffi = null; private static ?FFI $ffi = null;
@ -28,44 +34,37 @@ final class Getch
$linuxLibrary = self::LINUX_LIBRARY; $linuxLibrary = self::LINUX_LIBRARY;
} }
if (self::$ffi === null) { if (null === self::$ffi) {
$osFamily = PHP_OS_FAMILY; $osFamily = PHP_OS_FAMILY;
if ($osFamily === 'Windows') { if ('Windows' === $osFamily) {
self::$ffi = FFI::cdef('char _getch(); int _ungetch(char c);', self::WINDOWS_LIBRARY); self::$ffi = FFI::cdef(self::DECLARATIONS, self::WINDOWS_LIBRARY);
} elseif ($osFamily === 'Linux') { } elseif ('Linux' === $osFamily) {
if (!file_exists($linuxLibrary)) { if (!file_exists($linuxLibrary)) {
throw new RuntimeException(sprintf('Could not find library file %s.', $linuxLibrary)); throw new RuntimeException(sprintf('Could not find library file %s.', $linuxLibrary));
} }
self::$ffi = FFI::cdef('char _getch(); int _ungetch(char ch);', $linuxLibrary); self::$ffi = FFI::cdef(self::DECLARATIONS, $linuxLibrary);
} else { } else {
throw new RuntimeException(sprintf('Sorry, %s is not supported yet.', $osFamily)); throw new RuntimeException(sprintf('Sorry, %s is not supported yet.', $osFamily));
} }
} }
} }
public function getch(): string public function getch(): int
{ {
return self::$ffi->_getch(); return self::$ffi->_getch();
} }
public function ungetch(string $char) public function ungetch($char): int
{ {
return self::$ffi->_ungetch($char[0]); if (!is_string($char) && !is_int($char)) {
throw new \TypeError('ungetch takes a parameter of int or string.');
} }
public function ungetchString(string $string): bool if (is_string($char)) {
{ $char = ord($char[0]);
$stringReverse = \strrev($string);
$result = false;
foreach(\str_split($stringReverse) as $char) {
$result = self::$ffi->_ungetch($char) > 0;
if(!$result) {
return false;
}
} }
return $result; return self::$ffi->_ungetch($char);
} }
} }

View File

@ -2,6 +2,7 @@
#include <termios.h> #include <termios.h>
#include <unistd.h> #include <unistd.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
static struct termios oldattr; static struct termios oldattr;
static char *strrev(char *str) static char *strrev(char *str)
@ -10,7 +11,7 @@ static char *strrev(char *str)
if (! str || ! *str) if (! str || ! *str)
return str; return str;
for (p1 = str, p2 = str + wcslen(str) - 1; p2 > p1; ++p1, --p2) for (p1 = str, p2 = str + strlen(str) - 1; p2 > p1; ++p1, --p2)
{ {
*p1 ^= *p2; *p1 ^= *p2;
*p2 ^= *p1; *p2 ^= *p1;

Binary file not shown.

56
tests/Getch/GetchTest.php Normal file
View File

@ -0,0 +1,56 @@
<?php
namespace Sikofitt\Tests\Console\Getch;
use PHPUnit\Framework\TestCase;
use Sikofitt\Console\Getch;
class GetchTest extends TestCase
{
private \FFI $ffi;
public function setUp(): void
{
$this->ffi = \FFI::load(__DIR__.'/../test.h');
$stdin = $this->ffi->stdin;
foreach (range('D', 'A') as $character) {
$this->ffi->ungetc(ord($character), $stdin);
}
parent::setUp(); // TODO: Change the autogenerated stub
}
/**
* @preserveGlobalState disabled
*/
public function testFailureOnInvalidLibrary()
{
$this->expectException(\RuntimeException::class);
\getch(__DIR__.'/library.so');
}
public function testGetchClass()
{
$getch = new Getch();
foreach (range('A', 'D') as $character) {
self::assertSame(\ord($character), $getch->getch());
}
}
public function testGetchFunction()
{
foreach (range('A', 'D') as $character) {
self::assertSame(\ord($character), getch());
}
}
public function testUnsupportedOS()
{
if (PHP_OS_FAMILY !== 'Linux' || PHP_OS_FAMILY !== 'Windows') {
self::markTestSkipped('This test only applies to non Linux or Windows systems.');
}
$this->expectException(\RuntimeException::class);
\getch();
}
}

View File

@ -0,0 +1,47 @@
<?php
namespace Sikofitt\Tests\Console\Ungetch;
use PHPUnit\Framework\TestCase;
use Sikofitt\Console\Getch;
class UngetchTest extends TestCase
{
public function setUp(): void
{
parent::setUp(); // TODO: Change the autogenerated stub
}
public function testUngetch()
{
$g = new Getch();
$res = $g->ungetch('A');
self::assertSame(65, $res);
$res = $g->getch();
self::assertSame(65, $res);
$res = ungetch(65);
self::assertSame(65, $res);
$res = $g->getch();
self::assertSame(65, $res);
}
public function testTypeError()
{
$g = new Getch();
$this->expectException(\TypeError::class);
$g->ungetch(new \stdClass());
}
public function testForFun()
{
foreach(\str_split(\strrev('Hello World!')) as $char) {
ungetch($char);
}
$result = '';
do {
$ord = getch();
$result .= \chr($ord);
} while($ord !== ord('!'));
self::assertSame('Hello World!', $result);
}
}

17
tests/test.h Normal file
View File

@ -0,0 +1,17 @@
typedef struct _iobuf
{
char* _ptr;
int _cnt;
char* _base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char* _tmpfname;
} FILE;
FILE *stdin;
int ungetc(int ch, FILE *stream);