基础代码

This commit is contained in:
2021-02-26 22:23:13 +08:00
parent 7884df52f0
commit a719feebba
2585 changed files with 328263 additions and 0 deletions
+30
View File
@@ -0,0 +1,30 @@
<?php
require dirname( dirname( __FILE__ ) ) . '/lib/cli/cli.php';
/**
* Compatibility with PHPUnit 6+
*/
if ( class_exists( 'PHPUnit\Runner\Version' ) ) {
require_once dirname( __FILE__ ) . '/phpunit6-compat.php';
}
function cli_autoload( $className ) {
$className = ltrim($className, '\\');
$fileName = '';
$namespace = '';
if ($lastNsPos = strrpos($className, '\\')) {
$namespace = substr($className, 0, $lastNsPos);
$className = substr($className, $lastNsPos + 1);
$fileName = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
}
$fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
if ( 'cli' !== substr( $fileName, 0, 3 ) ) {
return;
}
require dirname( dirname( __FILE__ ) ) . '/lib/' . $fileName;
}
spl_autoload_register( 'cli_autoload' );
+19
View File
@@ -0,0 +1,19 @@
<?php
// From core "tests/phpunit/includes/phpunit6-compat.php" without `getTickets()` (see https://core.trac.wordpress.org/ticket/39822).
if ( class_exists( 'PHPUnit\Runner\Version' ) && version_compare( PHPUnit\Runner\Version::id(), '6.0', '>=' ) ) {
class_alias( 'PHPUnit\Framework\TestCase', 'PHPUnit_Framework_TestCase' );
class_alias( 'PHPUnit\Framework\Exception', 'PHPUnit_Framework_Exception' );
class_alias( 'PHPUnit\Framework\ExpectationFailedException', 'PHPUnit_Framework_ExpectationFailedException' );
class_alias( 'PHPUnit\Framework\Error\Notice', 'PHPUnit_Framework_Error_Notice' );
class_alias( 'PHPUnit\Framework\Error\Warning', 'PHPUnit_Framework_Error_Warning' );
class_alias( 'PHPUnit\Framework\Test', 'PHPUnit_Framework_Test' );
class_alias( 'PHPUnit\Framework\Warning', 'PHPUnit_Framework_Warning' );
class_alias( 'PHPUnit\Framework\AssertionFailedError', 'PHPUnit_Framework_AssertionFailedError' );
class_alias( 'PHPUnit\Framework\TestSuite', 'PHPUnit_Framework_TestSuite' );
class_alias( 'PHPUnit\Framework\TestListener', 'PHPUnit_Framework_TestListener' );
class_alias( 'PHPUnit\Util\GlobalState', 'PHPUnit_Util_GlobalState' );
class_alias( 'PHPUnit\Util\Getopt', 'PHPUnit_Util_Getopt' );
}
+284
View File
@@ -0,0 +1,284 @@
<?php
use cli\Arguments;
/**
* Class TestArguments
* @todo add more tests to increase coverage
*
* @backupGlobals enabled
*/
class TestArguments extends PHPUnit_Framework_TestCase
{
/**
* Array of expected settings
* @var array
*/
protected $settings = null;
/**
* Array of flags
* @var array
*/
protected $flags = null;
/**
* Array of expected options
* @var array
*/
protected $options = null;
/**
* Clear the $_SERVER['argv'] array
*/
public static function clearArgv()
{
$_SERVER['argv'] = array();
$_SERVER['argc'] = 0;
}
/**
* Add one or more element(s) at the end of the $_SERVER['argv'] array
*
* @param array $args: value(s) to add to the argv array
*/
public static function pushToArgv($args)
{
if (is_string($args)) {
$args = explode(' ', $args);
}
foreach ($args as $arg) {
array_push($_SERVER['argv'], $arg);
}
$_SERVER['argc'] += count($args);
}
/**
* Set up valid flags and options
*/
public function setUp()
{
self::clearArgv();
self::pushToArgv('my_script.php');
$this->flags = array(
'flag1' => array(
'aliases' => 'f',
'description' => 'Test flag 1'
),
'flag2' => array(
'description' => 'Test flag 2'
)
);
$this->options = array(
'option1' => array(
'aliases' => 'o',
'description' => 'Test option 1'
),
'option2' => array(
'aliases' => array('x', 'y'),
'description' => 'Test option 2 with default',
'default' => 'some default value'
)
);
$this->settings = array(
'strict' => true,
'flags' => $this->flags,
'options' => $this->options
);
}
/**
* Tear down fixtures
*/
public function tearDown()
{
$this->flags = null;
$this->options = null;
$this->settings = null;
self::clearArgv();
}
/**
* Test adding a flag, getting a flag and getting all flags
*/
public function testAddFlags()
{
$args = new cli\Arguments($this->settings);
$expectedFlags = $this->flags;
$expectedFlags['flag1']['default'] = false;
$expectedFlags['flag1']['stackable'] = false;
$expectedFlags['flag2']['default'] = false;
$expectedFlags['flag2']['stackable'] = false;
$expectedFlags['flag2']['aliases'] = array();
$this->assertSame($expectedFlags, $args->getFlags());
$this->assertSame($expectedFlags['flag1'], $args->getFlag('flag1'));
$this->assertSame($expectedFlags['flag1'], $args->getFlag('f'));
$expectedFlag1Argument = new cli\arguments\Argument('-f');
$this->assertSame($expectedFlags['flag1'], $args->getFlag($expectedFlag1Argument));
}
/**
* Test adding a option, getting a option and getting all options
*/
public function testAddOptions()
{
$args = new cli\Arguments($this->settings);
$expectedOptions = $this->options;
$expectedOptions['option1']['default'] = null;
$this->assertSame($expectedOptions, $args->getOptions());
$this->assertSame($expectedOptions['option1'], $args->getOption('option1'));
$this->assertSame($expectedOptions['option1'], $args->getOption('o'));
$expectedOption1Argument = new cli\arguments\Argument('-o');
$this->assertSame($expectedOptions['option1'], $args->getOption($expectedOption1Argument));
}
/**
* Data provider with valid args and options
*
* @return array set of args and expected parsed values
*/
public function settingsWithValidOptions()
{
return array(
array(
array('-o', 'option_value', '-f'),
array('option1' => 'option_value', 'flag1' => true)
),
array(
array('--option1', 'option_value', '--flag1'),
array('option1' => 'option_value', 'flag1' => true)
),
array(
array('-f', '--option1', 'option_value'),
array('flag1' => true, 'option1' => 'option_value')
)
);
}
/**
* Data provider with missing options
*
* @return array set of args and expected parsed values
*/
public function settingsWithMissingOptions()
{
return array(
array(
array('-f', '--option1'),
array('flag1' => true, 'option1' => 'Error should be triggered')
),
array(
array('--option1', '-f'),
array('option1' => 'Error should be triggered', 'flag1' => true)
)
);
}
/**
* Data provider with missing options. The default value should be populated
*
* @return array set of args and expected parsed values
*/
public function settingsWithMissingOptionsWithDefault()
{
return array(
array(
array('-f', '--option2'),
array('flag1' => true, 'option2' => 'some default value')
),
array(
array('--option2', '-f'),
array('option2' => 'some default value', 'flag1' => true)
)
);
}
public function settingsWithNoOptionsWithDefault()
{
return array(
array(
array(),
array('flag1' => false, 'flag2' => false, 'option2' => 'some default value')
)
);
}
/**
* Generic private testParse method.
*
* @param array $args arguments as they appear in the cli
* @param array $expectedValues expected values after parsing
*/
private function _testParse($cliParams, $expectedValues)
{
self::pushToArgv($cliParams);
$args = new cli\Arguments($this->settings);
$args->parse();
foreach ($expectedValues as $name => $value) {
if ($args->isFlag($name)) {
$this->assertEquals($value, $args[$name]);
}
if ($args->isOption($name)) {
$this->assertEquals($value, $args[$name]);
}
}
}
/**
* @param array $args arguments as they appear in the cli
* @param array $expectedValues expected values after parsing
*
* @dataProvider settingsWithValidOptions
*/
public function testParseWithValidOptions($cliParams, $expectedValues)
{
$this->_testParse($cliParams, $expectedValues);
}
/**
* @param array $args arguments as they appear in the cli
* @param array $expectedValues expected values after parsing
* @dataProvider settingsWithMissingOptions
* @expectedException PHPUnit_Framework_Error_Warning
* @expectedExceptionMessage no value given for --option1
*/
public function testParseWithMissingOptions($cliParams, $expectedValues)
{
$this->_testParse($cliParams, $expectedValues);
}
/**
* @param array $args arguments as they appear in the cli
* @param array $expectedValues expected values after parsing
* @dataProvider settingsWithMissingOptionsWithDefault
*/
public function testParseWithMissingOptionsWithDefault($cliParams, $expectedValues)
{
$this->_testParse($cliParams, $expectedValues);
}
/**
* @param array $args arguments as they appear in the cli
* @param array $expectedValues expected values after parsing
* @dataProvider settingsWithNoOptionsWithDefault
*/
public function testParseWithNoOptionsWithDefault($cliParams, $expectedValues) {
$this->_testParse($cliParams, $expectedValues);
}
}
+553
View File
@@ -0,0 +1,553 @@
<?php
use cli\Colors;
class testsCli extends PHPUnit_Framework_TestCase {
function setUp() {
// Reset enable state
\cli\Colors::enable( null );
// Empty the cache
\cli\Colors::clearStringCache();
}
function test_string_length() {
$this->assertEquals( \cli\Colors::length( 'x' ), 1 );
$this->assertEquals( \cli\Colors::length( '日' ), 1 );
}
function test_string_width() {
$this->assertEquals( \cli\Colors::width( 'x' ), 1 );
$this->assertEquals( \cli\Colors::width( '日' ), 2 ); // Double-width char.
}
function test_encoded_string_length() {
$this->assertEquals( \cli\Colors::length( 'hello' ), 5 );
$this->assertEquals( \cli\Colors::length( 'óra' ), 3 );
$this->assertEquals( \cli\Colors::length( '日本語' ), 3 );
}
function test_encoded_string_width() {
$this->assertEquals( \cli\Colors::width( 'hello' ), 5 );
$this->assertEquals( \cli\Colors::width( 'óra' ), 3 );
$this->assertEquals( \cli\Colors::width( '日本語' ), 6 ); // 3 double-width chars.
}
function test_encoded_string_pad() {
$this->assertEquals( 6, strlen( \cli\Colors::pad( 'hello', 6 ) ) );
$this->assertEquals( 7, strlen( \cli\Colors::pad( 'óra', 6 ) ) ); // special characters take one byte
$this->assertEquals( 9, strlen( \cli\Colors::pad( '日本語', 6 ) ) ); // each character takes two bytes
$this->assertEquals( 17, strlen( \cli\Colors::pad( 'עִבְרִית', 6 ) ) ); // process Hebrew vowels
$this->assertEquals( 6, strlen( \cli\Colors::pad( 'hello', 6, false, false, STR_PAD_RIGHT ) ) );
$this->assertEquals( 7, strlen( \cli\Colors::pad( 'óra', 6, false, false, STR_PAD_LEFT ) ) ); // special characters take one byte
$this->assertEquals( 9, strlen( \cli\Colors::pad( '日本語', 6, false, false, STR_PAD_BOTH ) ) ); // each character takes two bytes
$this->assertSame( 4, strpos( \cli\Colors::pad( 'hello', 10, false, false, STR_PAD_RIGHT ), 'o' ) );
$this->assertSame( 9, strpos( \cli\Colors::pad( 'hello', 10, false, false, STR_PAD_LEFT ), 'o' ) );
$this->assertSame( 6, strpos( \cli\Colors::pad( 'hello', 10, false, false, STR_PAD_BOTH ), 'o' ) );
$this->assertSame( 1, strpos( \cli\Colors::pad( 'hello', 10, false, false, STR_PAD_RIGHT ), 'e' ) );
$this->assertSame( 6, strpos( \cli\Colors::pad( 'hello', 10, false, false, STR_PAD_LEFT ), 'e' ) );
$this->assertSame( 3, strpos( \cli\Colors::pad( 'hello', 10, false, false, STR_PAD_BOTH ), 'e' ) );
}
function test_colorized_string_pad() {
// Colors enabled.
Colors::enable( true );
$x = Colors::colorize( '%Gx%n', true ); // colorized `x` string
$ora = Colors::colorize( "%Góra%n", true ); // colorized `óra` string
$this->assertSame( 22, strlen( Colors::pad( $x, 11 ) ) );
$this->assertSame( 22, strlen( Colors::pad( $x, 11, false /*pre_colorized*/ ) ) );
$this->assertSame( 22, strlen( Colors::pad( $x, 11, true /*pre_colorized*/ ) ) );
$this->assertSame( 23, strlen( Colors::pad( $ora, 11 ) ) ); // +1 for two-byte "ó".
$this->assertSame( 23, strlen( Colors::pad( $ora, 11, false /*pre_colorized*/ ) ) );
$this->assertSame( 23, strlen( Colors::pad( $ora, 11, true /*pre_colorized*/ ) ) );
// Colors disabled.
Colors::disable( true );
$this->assertFalse( Colors::shouldColorize() );
$this->assertSame( 20, strlen( Colors::pad( $x, 20 ) ) );
$this->assertSame( 20, strlen( Colors::pad( $x, 20, false /*pre_colorized*/ ) ) );
$this->assertSame( 31, strlen( Colors::pad( $x, 20, true /*pre_colorized*/ ) ) );
$this->assertSame( 21, strlen( Colors::pad( $ora, 20 ) ) ); // +1 for two-byte "ó".
$this->assertSame( 21, strlen( Colors::pad( $ora, 20, false /*pre_colorized*/ ) ) );
$this->assertSame( 32, strlen( Colors::pad( $ora, 20, true /*pre_colorized*/ ) ) );
}
function test_encoded_substr() {
$this->assertEquals( \cli\safe_substr( \cli\Colors::pad( 'hello', 6), 0, 2 ), 'he' );
$this->assertEquals( \cli\safe_substr( \cli\Colors::pad( 'óra', 6), 0, 2 ), 'ór' );
$this->assertEquals( \cli\safe_substr( \cli\Colors::pad( '日本語', 6), 0, 2 ), '日本' );
$this->assertSame( 'el', \cli\safe_substr( Colors::pad( 'hello', 6 ), 1, 2 ) );
$this->assertSame( 'a ', \cli\safe_substr( Colors::pad( 'óra', 6 ), 2, 2 ) );
$this->assertSame( ' ', \cli\safe_substr( Colors::pad( 'óra', 6 ), 5, 2 ) );
$this->assertSame( '本語', \cli\safe_substr( Colors::pad( '日本語', 8 ), 1, 2 ) );
$this->assertSame( '語 ', \cli\safe_substr( Colors::pad( '日本語', 8 ), 2, 2 ) );
$this->assertSame( ' ', \cli\safe_substr( Colors::pad( '日本語', 8 ), -1 ) );
$this->assertSame( ' ', \cli\safe_substr( Colors::pad( '日本語', 8 ), -1, 2 ) );
$this->assertSame( '語 ', \cli\safe_substr( Colors::pad( '日本語', 8 ), -3, 3 ) );
}
function test_various_substr() {
// Save.
$test_safe_substr = getenv( 'PHP_CLI_TOOLS_TEST_SAFE_SUBSTR' );
if ( function_exists( 'mb_detect_order' ) ) {
$mb_detect_order = mb_detect_order();
}
putenv( 'PHP_CLI_TOOLS_TEST_SAFE_SUBSTR' );
// Latin, kana, Latin, Latin combining, Thai combining, Hangul.
$str = 'lムnöม้p를'; // 18 bytes.
// Large string.
$large_str_str_start = 65536 * 2;
$large_str = str_repeat( 'a', $large_str_str_start ) . $str;
$large_str_len = strlen( $large_str ); // 128K + 18 bytes.
if ( \cli\can_use_icu() ) {
putenv( 'PHP_CLI_TOOLS_TEST_SAFE_SUBSTR=1' ); // Tests grapheme_substr().
$this->assertSame( '', \cli\safe_substr( $str, 0, 0 ) );
$this->assertSame( 'l', \cli\safe_substr( $str, 0, 1 ) );
$this->assertSame( 'lム', \cli\safe_substr( $str, 0, 2 ) );
$this->assertSame( 'lムn', \cli\safe_substr( $str, 0, 3 ) );
$this->assertSame( 'lムnö', \cli\safe_substr( $str, 0, 4 ) );
$this->assertSame( 'lムnöม้', \cli\safe_substr( $str, 0, 5 ) );
$this->assertSame( 'lムnöม้p', \cli\safe_substr( $str, 0, 6 ) );
$this->assertSame( 'lムnöม้p를', \cli\safe_substr( $str, 0, 7 ) );
$this->assertSame( 'lムnöม้p를', \cli\safe_substr( $str, 0, 8 ) );
$this->assertSame( '', \cli\safe_substr( $str, 19 ) ); // Start too large.
$this->assertSame( '', \cli\safe_substr( $str, 19, 7 ) ); // Start too large, with length.
$this->assertSame( '', \cli\safe_substr( $str, 8 ) ); // Start same as length.
$this->assertSame( '', \cli\safe_substr( $str, 8, 0 ) ); // Start same as length, with zero length.
$this->assertSame( '를', \cli\safe_substr( $str, -1 ) );
$this->assertSame( 'p를', \cli\safe_substr( $str, -2 ) );
$this->assertSame( 'ม้p를', \cli\safe_substr( $str, -3 ) );
$this->assertSame( 'öม้p를', \cli\safe_substr( $str, -4 ) );
$this->assertSame( 'öม้p', \cli\safe_substr( $str, -4, 3 ) );
$this->assertSame( 'nö', \cli\safe_substr( $str, -5, 2 ) );
$this->assertSame( 'ム', \cli\safe_substr( $str, -6, 1 ) );
$this->assertSame( 'ムnöม้p를', \cli\safe_substr( $str, -6 ) );
$this->assertSame( 'lムnöม้p를', \cli\safe_substr( $str, -7 ) );
$this->assertSame( 'lムnö', \cli\safe_substr( $str, -7, 4 ) );
$this->assertSame( 'lムnöม้p를', \cli\safe_substr( $str, -8 ) );
$this->assertSame( 'lムnöม้p를', \cli\safe_substr( $str, -9 ) ); // Negative start too large.
$this->assertSame( $large_str, \cli\safe_substr( $large_str, 0 ) );
$this->assertSame( '', \cli\safe_substr( $large_str, $large_str_str_start, 0 ) );
$this->assertSame( 'l', \cli\safe_substr( $large_str, $large_str_str_start, 1 ) );
$this->assertSame( 'lム', \cli\safe_substr( $large_str, $large_str_str_start, 2 ) );
$this->assertSame( 'p를', \cli\safe_substr( $large_str, -2 ) );
}
if ( \cli\can_use_pcre_x() ) {
putenv( 'PHP_CLI_TOOLS_TEST_SAFE_SUBSTR=2' ); // Tests preg_split( '/\X/u' ).
$this->assertSame( '', \cli\safe_substr( $str, 0, 0 ) );
$this->assertSame( 'l', \cli\safe_substr( $str, 0, 1 ) );
$this->assertSame( 'lム', \cli\safe_substr( $str, 0, 2 ) );
$this->assertSame( 'lムn', \cli\safe_substr( $str, 0, 3 ) );
$this->assertSame( 'lムnö', \cli\safe_substr( $str, 0, 4 ) );
$this->assertSame( 'lムnöม้', \cli\safe_substr( $str, 0, 5 ) );
$this->assertSame( 'lムnöม้p', \cli\safe_substr( $str, 0, 6 ) );
$this->assertSame( 'lムnöม้p를', \cli\safe_substr( $str, 0, 7 ) );
$this->assertSame( 'lムnöม้p를', \cli\safe_substr( $str, 0, 8 ) );
$this->assertSame( '', \cli\safe_substr( $str, 19 ) ); // Start too large.
$this->assertSame( '', \cli\safe_substr( $str, 19, 7 ) ); // Start too large, with length.
$this->assertSame( '', \cli\safe_substr( $str, 8 ) ); // Start same as length.
$this->assertSame( '', \cli\safe_substr( $str, 8, 0 ) ); // Start same as length, with zero length.
$this->assertSame( '를', \cli\safe_substr( $str, -1 ) );
$this->assertSame( 'p를', \cli\safe_substr( $str, -2 ) );
$this->assertSame( 'ม้p를', \cli\safe_substr( $str, -3 ) );
$this->assertSame( 'öม้p를', \cli\safe_substr( $str, -4 ) );
$this->assertSame( 'öม้p', \cli\safe_substr( $str, -4, 3 ) );
$this->assertSame( 'nö', \cli\safe_substr( $str, -5, 2 ) );
$this->assertSame( 'ム', \cli\safe_substr( $str, -6, 1 ) );
$this->assertSame( 'ムnöม้p를', \cli\safe_substr( $str, -6 ) );
$this->assertSame( 'lムnöม้p를', \cli\safe_substr( $str, -7 ) );
$this->assertSame( 'lムnö', \cli\safe_substr( $str, -7, 4 ) );
$this->assertSame( 'lムnöม้p를', \cli\safe_substr( $str, -8 ) );
$this->assertSame( 'lムnöม้p를', \cli\safe_substr( $str, -9 ) ); // Negative start too large.
$this->assertSame( $large_str, \cli\safe_substr( $large_str, 0 ) );
$this->assertSame( '', \cli\safe_substr( $large_str, $large_str_str_start, 0 ) );
$this->assertSame( 'l', \cli\safe_substr( $large_str, $large_str_str_start, 1 ) );
$this->assertSame( 'lム', \cli\safe_substr( $large_str, $large_str_str_start, 2 ) );
$this->assertSame( 'p를', \cli\safe_substr( $large_str, -2 ) );
}
if ( function_exists( 'mb_substr' ) ) {
putenv( 'PHP_CLI_TOOLS_TEST_SAFE_SUBSTR=4' ); // Tests mb_substr().
$this->assertSame( '', \cli\safe_substr( $str, 0, 0 ) );
$this->assertSame( 'l', \cli\safe_substr( $str, 0, 1 ) );
$this->assertSame( 'lム', \cli\safe_substr( $str, 0, 2 ) );
$this->assertSame( 'lムn', \cli\safe_substr( $str, 0, 3 ) );
$this->assertSame( 'lムno', \cli\safe_substr( $str, 0, 4 ) ); // Wrong.
}
putenv( 'PHP_CLI_TOOLS_TEST_SAFE_SUBSTR=8' ); // Tests substr().
$this->assertSame( '', \cli\safe_substr( $str, 0, 0 ) );
$this->assertSame( 'l', \cli\safe_substr( $str, 0, 1 ) );
$this->assertSame( "l\xe3", \cli\safe_substr( $str, 0, 2 ) ); // Corrupt.
$this->assertSame( '', \cli\safe_substr( $str, strlen( $str ) + 1 ) ); // Return '' not false to match behavior of other methods when `$start` > strlen.
// Non-UTF-8 - both grapheme_substr() and preg_split( '/\X/u' ) will fail.
putenv( 'PHP_CLI_TOOLS_TEST_SAFE_SUBSTR' );
if ( function_exists( 'mb_substr' ) && function_exists( 'mb_detect_order' ) ) {
// Latin-1
mb_detect_order( array( 'UTF-8', 'ISO-8859-1' ) );
$str = "\xe0b\xe7"; // "àbç" in ISO-8859-1
$this->assertSame( "\xe0b", \cli\safe_substr( $str, 0, 2 ) );
$this->assertSame( "\xe0b", mb_substr( $str, 0, 2, 'ISO-8859-1' ) );
}
// Restore.
putenv( false == $test_safe_substr ? 'PHP_CLI_TOOLS_TEST_SAFE_SUBSTR' : "PHP_CLI_TOOLS_TEST_SAFE_SUBSTR=$test_safe_substr" );
if ( function_exists( 'mb_detect_order' ) ) {
mb_detect_order( $mb_detect_order );
}
}
function test_is_width_encoded_substr() {
$this->assertSame( 'he', \cli\safe_substr( Colors::pad( 'hello', 6 ), 0, 2, true /*is_width*/ ) );
$this->assertSame( 'ór', \cli\safe_substr( Colors::pad( 'óra', 6 ), 0, 2, true /*is_width*/ ) );
$this->assertSame( '日', \cli\safe_substr( Colors::pad( '日本語', 8 ), 0, 2, true /*is_width*/ ) );
$this->assertSame( '日', \cli\safe_substr( Colors::pad( '日本語', 8 ), 0, 3, true /*is_width*/ ) );
$this->assertSame( '日本', \cli\safe_substr( Colors::pad( '日本語', 8 ), 0, 4, true /*is_width*/ ) );
$this->assertSame( '日本語', \cli\safe_substr( Colors::pad( '日本語', 8 ), 0, 6, true /*is_width*/ ) );
$this->assertSame( '日本語 ', \cli\safe_substr( Colors::pad( '日本語', 8 ), 0, 7, true /*is_width*/ ) );
$this->assertSame( 'el', \cli\safe_substr( Colors::pad( 'hello', 6 ), 1, 2, true /*is_width*/ ) );
$this->assertSame( 'a ', \cli\safe_substr( Colors::pad( 'óra', 6 ), 2, 2, true /*is_width*/ ) );
$this->assertSame( ' ', \cli\safe_substr( Colors::pad( 'óra', 6 ), 5, 2, true /*is_width*/ ) );
$this->assertSame( '', \cli\safe_substr( '1日4本語90', 0, 0, true /*is_width*/ ) );
$this->assertSame( '1', \cli\safe_substr( '1日4本語90', 0, 1, true /*is_width*/ ) );
$this->assertSame( '1', \cli\safe_substr( '1日4本語90', 0, 2, true /*is_width*/ ) );
$this->assertSame( '1日', \cli\safe_substr( '1日4本語90', 0, 3, true /*is_width*/ ) );
$this->assertSame( '1日4', \cli\safe_substr( '1日4本語90', 0, 4, true /*is_width*/ ) );
$this->assertSame( '1日4', \cli\safe_substr( '1日4本語90', 0, 5, true /*is_width*/ ) );
$this->assertSame( '1日4本', \cli\safe_substr( '1日4本語90', 0, 6, true /*is_width*/ ) );
$this->assertSame( '1日4本', \cli\safe_substr( '1日4本語90', 0, 7, true /*is_width*/ ) );
$this->assertSame( '1日4本語', \cli\safe_substr( '1日4本語90', 0, 8, true /*is_width*/ ) );
$this->assertSame( '1日4本語9', \cli\safe_substr( '1日4本語90', 0, 9, true /*is_width*/ ) );
$this->assertSame( '1日4本語90', \cli\safe_substr( '1日4本語90', 0, 10, true /*is_width*/ ) );
$this->assertSame( '1日4本語90', \cli\safe_substr( '1日4本語90', 0, 11, true /*is_width*/ ) );
$this->assertSame( '日', \cli\safe_substr( '1日4本語90', 1, 2, true /*is_width*/ ) );
$this->assertSame( '日4', \cli\safe_substr( '1日4本語90', 1, 3, true /*is_width*/ ) );
$this->assertSame( '4本語9', \cli\safe_substr( '1日4本語90', 2, 6, true /*is_width*/ ) );
$this->assertSame( '本', \cli\safe_substr( '1日4本語90', 3, 1, true /*is_width*/ ) );
$this->assertSame( '本', \cli\safe_substr( '1日4本語90', 3, 2, true /*is_width*/ ) );
$this->assertSame( '本', \cli\safe_substr( '1日4本語90', 3, 3, true /*is_width*/ ) );
$this->assertSame( '本語', \cli\safe_substr( '1日4本語90', 3, 4, true /*is_width*/ ) );
$this->assertSame( '本語9', \cli\safe_substr( '1日4本語90', 3, 5, true /*is_width*/ ) );
$this->assertSame( '0', \cli\safe_substr( '1日4本語90', 6, 1, true /*is_width*/ ) );
$this->assertSame( '', \cli\safe_substr( '1日4本語90', 7, 1, true /*is_width*/ ) );
$this->assertSame( '', \cli\safe_substr( '1日4本語90', 6, 0, true /*is_width*/ ) );
$this->assertSame( '0', \cli\safe_substr( '1日4本語90', -1, 3, true /*is_width*/ ) );
$this->assertSame( '90', \cli\safe_substr( '1日4本語90', -2, 3, true /*is_width*/ ) );
$this->assertSame( '語9', \cli\safe_substr( '1日4本語90', -3, 3, true /*is_width*/ ) );
$this->assertSame( '本語9', \cli\safe_substr( '1日4本語90', -4, 5, true /*is_width*/ ) );
}
function test_colorized_string_length() {
$this->assertEquals( \cli\Colors::length( \cli\Colors::colorize( '%Gx%n', true ) ), 1 );
$this->assertEquals( \cli\Colors::length( \cli\Colors::colorize( '%G日%n', true ) ), 1 );
}
function test_colorized_string_width() {
// Colors enabled.
Colors::enable( true );
$x = Colors::colorize( '%Gx%n', true );
$dw = Colors::colorize( '%G日%n', true ); // Double-width char.
$this->assertSame( 1, Colors::width( $x ) );
$this->assertSame( 1, Colors::width( $x, false /*pre_colorized*/ ) );
$this->assertSame( 1, Colors::width( $x, true /*pre_colorized*/ ) );
$this->assertSame( 2, Colors::width( $dw ) );
$this->assertSame( 2, Colors::width( $dw, false /*pre_colorized*/ ) );
$this->assertSame( 2, Colors::width( $dw, true /*pre_colorized*/ ) );
// Colors disabled.
Colors::disable( true );
$this->assertFalse( Colors::shouldColorize() );
$this->assertSame( 12, Colors::width( $x ) );
$this->assertSame( 12, Colors::width( $x, false /*pre_colorized*/ ) );
$this->assertSame( 1, Colors::width( $x, true /*pre_colorized*/ ) );
$this->assertSame( 13, Colors::width( $dw ) );
$this->assertSame( 13, Colors::width( $dw, false /*pre_colorized*/ ) );
$this->assertSame( 2, Colors::width( $dw, true /*pre_colorized*/ ) );
}
function test_colorize_string_is_colored() {
$original = '%Gx';
$colorized = "\033[32;1mx";
$this->assertEquals( \cli\Colors::colorize( $original, true ), $colorized );
}
function test_colorize_when_colorize_is_forced() {
$original = '%gx%n';
$this->assertEquals( \cli\Colors::colorize( $original, false ), 'x' );
}
function test_binary_string_is_converted_back_to_original_string() {
$string = 'x';
$string_with_color = '%b' . $string;
$colorized_string = "\033[34m$string";
// Ensure colorization is applied correctly
$this->assertEquals( \cli\Colors::colorize( $string_with_color, true ), $colorized_string );
// Ensure that the colorization is reverted
$this->assertEquals( \cli\Colors::decolorize( $colorized_string ), $string );
}
function test_string_cache() {
$string = 'x';
$string_with_color = '%k' . $string;
$colorized_string = "\033[30m$string";
// Ensure colorization works
$this->assertEquals( \cli\Colors::colorize( $string_with_color, true ), $colorized_string );
// Test that the value was cached appropriately
$test_cache = array(
'passed' => $string_with_color,
'colorized' => $colorized_string,
'decolorized' => $string,
);
$real_cache = \cli\Colors::getStringCache();
// Test that the cache value exists
$this->assertTrue( isset( $real_cache[ md5( $string_with_color ) ] ) );
// Test that the cache value is correctly set
$this->assertEquals( $test_cache, $real_cache[ md5( $string_with_color ) ] );
}
function test_string_cache_colorize() {
$string = 'x';
$string_with_color = '%k' . $string;
$colorized_string = "\033[30m$string";
// Colors enabled.
Colors::enable( true );
// Ensure colorization works
$this->assertSame( $colorized_string, Colors::colorize( $string_with_color ) );
$this->assertSame( $colorized_string, Colors::colorize( $string_with_color ) );
// Colors disabled.
Colors::disable( true );
$this->assertFalse( Colors::shouldColorize() );
// Ensure it doesn't come from the cache.
$this->assertSame( $string, Colors::colorize( $string_with_color ) );
$this->assertSame( $string, Colors::colorize( $string_with_color ) );
// Check that escaped % isn't stripped on putting into cache.
$string = 'x%%n';
$string_with_color = '%k' . $string;
$this->assertSame( 'x%n', Colors::colorize( $string_with_color ) );
$this->assertSame( 'x%n', Colors::colorize( $string_with_color ) );
}
function test_decolorize() {
// Colors enabled.
Colors::enable( true );
$string = '%kx%%n%n';
$colorized_string = Colors::colorize( $string );
$both_string = '%gfoo' . $colorized_string . 'bar%%%n';
$this->assertSame( 'x%n', Colors::decolorize( $string ) );
$this->assertSame( 'x', Colors::decolorize( $colorized_string ) );
$this->assertSame( 'fooxbar%', Colors::decolorize( $both_string ) );
$this->assertSame( $string, Colors::decolorize( $string, 1 /*keep_tokens*/ ) );
$this->assertSame( 'x%n', Colors::decolorize( $colorized_string, 1 /*keep_tokens*/ ) );
$this->assertSame( '%gfoox%nbar%%%n', Colors::decolorize( $both_string, 1 /*keep_tokens*/ ) );
$this->assertSame( 'x%n', Colors::decolorize( $string, 2 /*keep_encodings*/ ) );
$this->assertSame( 'x', Colors::decolorize( $colorized_string, 2 /*keep_encodings*/ ) );
$this->assertSame( 'fooxbar%', Colors::decolorize( $both_string, 2 /*keep_encodings*/ ) );
$this->assertSame( $string, Colors::decolorize( $string, 3 /*noop*/ ) );
$this->assertSame( $colorized_string, Colors::decolorize( $colorized_string, 3 /*noop*/ ) );
$this->assertSame( $both_string, Colors::decolorize( $both_string, 3 /*noop*/ ) );
}
function test_strwidth() {
// Save.
$test_strwidth = getenv( 'PHP_CLI_TOOLS_TEST_STRWIDTH' );
if ( function_exists( 'mb_detect_order' ) ) {
$mb_detect_order = mb_detect_order();
}
putenv( 'PHP_CLI_TOOLS_TEST_STRWIDTH' );
// UTF-8.
// 4 characters, one a double-width Han = 5 spacing chars, with 2 combining chars. Adapted from http://unicode.org/faq/char_combmark.html#7 (combining acute accent added after "a").
$str = "a\xCC\x81\xE0\xA4\xA8\xE0\xA4\xBF\xE4\xBA\x9C\xF0\x90\x82\x83";
if ( \cli\can_use_icu() ) {
$this->assertSame( 5, \cli\strwidth( $str ) ); // Tests grapheme_strlen().
putenv( 'PHP_CLI_TOOLS_TEST_STRWIDTH=2' ); // Test preg_split( '/\X/u' ).
$this->assertSame( 5, \cli\strwidth( $str ) );
} else {
$this->assertSame( 5, \cli\strwidth( $str ) ); // Tests preg_split( '/\X/u' ).
}
if ( function_exists( 'mb_strwidth' ) && function_exists( 'mb_detect_order' ) ) {
putenv( 'PHP_CLI_TOOLS_TEST_STRWIDTH=4' ); // Test mb_strwidth().
mb_detect_order( array( 'UTF-8', 'ISO-8859-1' ) );
$this->assertSame( 5, \cli\strwidth( $str ) );
}
putenv( 'PHP_CLI_TOOLS_TEST_STRWIDTH=8' ); // Test safe_strlen().
if ( \cli\can_use_icu() || \cli\can_use_pcre_x() ) {
$this->assertSame( 4, \cli\strwidth( $str ) ); // safe_strlen() (correctly) does not account for double-width Han so out by 1.
} elseif ( function_exists( 'mb_strlen' ) && function_exists( 'mb_detect_order' ) ) {
$this->assertSame( 4, \cli\strwidth( $str ) ); // safe_strlen() (correctly) does not account for double-width Han so out by 1.
$this->assertSame( 6, mb_strlen( $str, 'UTF-8' ) );
} else {
$this->assertSame( 16, \cli\strwidth( $str ) ); // strlen() - no. of bytes.
$this->assertSame( 16, strlen( $str ) );
}
// Nepali जस्ट ट॓स्ट गर्दै - 1st word: 3 spacing + 1 combining, 2nd word: 3 spacing + 2 combining, 3rd word: 3 spacing + 2 combining = 9 spacing chars + 2 spaces = 11 chars.
$str = "\xe0\xa4\x9c\xe0\xa4\xb8\xe0\xa5\x8d\xe0\xa4\x9f \xe0\xa4\x9f\xe0\xa5\x93\xe0\xa4\xb8\xe0\xa5\x8d\xe0\xa4\x9f \xe0\xa4\x97\xe0\xa4\xb0\xe0\xa5\x8d\xe0\xa4\xa6\xe0\xa5\x88";
putenv( 'PHP_CLI_TOOLS_TEST_STRWIDTH' );
if ( \cli\can_use_icu() ) {
$this->assertSame( 11, \cli\strwidth( $str ) ); // Tests grapheme_strlen().
putenv( 'PHP_CLI_TOOLS_TEST_STRWIDTH=2' ); // Test preg_split( '/\X/u' ).
$this->assertSame( 11, \cli\strwidth( $str ) );
} else {
$this->assertSame( 11, \cli\strwidth( $str ) ); // Tests preg_split( '/\X/u' ).
}
if ( function_exists( 'mb_strwidth' ) && function_exists( 'mb_detect_order' ) ) {
putenv( 'PHP_CLI_TOOLS_TEST_STRWIDTH=4' ); // Test mb_strwidth().
mb_detect_order( array( 'UTF-8' ) );
$this->assertSame( 11, \cli\strwidth( $str ) );
}
// Non-UTF-8 - both grapheme_strlen() and preg_split( '/\X/u' ) will fail.
putenv( 'PHP_CLI_TOOLS_TEST_STRWIDTH' );
if ( function_exists( 'mb_strwidth' ) && function_exists( 'mb_detect_order' ) ) {
// Latin-1
mb_detect_order( array( 'UTF-8', 'ISO-8859-1' ) );
$str = "\xe0b\xe7"; // "àbç" in ISO-8859-1
$this->assertSame( 3, \cli\strwidth( $str ) ); // Test mb_strwidth().
$this->assertSame( 3, mb_strwidth( $str, 'ISO-8859-1' ) );
// Shift JIS.
mb_detect_order( array( 'UTF-8', 'SJIS' ) );
$str = "\x82\xb1\x82\xf1\x82\xc9\x82\xbf\x82\xcd\x90\xa2\x8a\x45!"; // "こャにちは世界!" ("Hello world!") in Shift JIS - 7 double-width chars plus Latin exclamation mark.
$this->assertSame( 15, \cli\strwidth( $str ) ); // Test mb_strwidth().
$this->assertSame( 15, mb_strwidth( $str, 'SJIS' ) );
putenv( 'PHP_CLI_TOOLS_TEST_STRWIDTH=8' ); // Test safe_strlen().
if ( function_exists( 'mb_strlen' ) && function_exists( 'mb_detect_order' ) ) {
$this->assertSame( 8, \cli\strwidth( $str ) ); // mb_strlen() - doesn't allow for double-width.
$this->assertSame( 8, mb_strlen( $str, 'SJIS' ) );
} else {
$this->assertSame( 15, \cli\strwidth( $str ) ); // strlen() - no. of bytes.
$this->assertSame( 15, strlen( $str ) );
}
}
// Restore.
putenv( false == $test_strwidth ? 'PHP_CLI_TOOLS_TEST_STRWIDTH' : "PHP_CLI_TOOLS_TEST_STRWIDTH=$test_strwidth" );
if ( function_exists( 'mb_detect_order' ) ) {
mb_detect_order( $mb_detect_order );
}
}
function test_safe_strlen() {
// Save.
$test_safe_strlen = getenv( 'PHP_CLI_TOOLS_TEST_SAFE_STRLEN' );
if ( function_exists( 'mb_detect_order' ) ) {
$mb_detect_order = mb_detect_order();
}
putenv( 'PHP_CLI_TOOLS_TEST_SAFE_STRLEN' );
// UTF-8.
// ASCII l, 3-byte kana, ASCII n, ASCII o + 2-byte combining umlaut, 6-byte Thai combining, ASCII, 3-byte Hangul. grapheme length 7, bytes 18.
$str = 'lムnöม้p를';
if ( \cli\can_use_icu() ) {
putenv( 'PHP_CLI_TOOLS_TEST_SAFE_STRLEN' ); // Test grapheme_strlen().
$this->assertSame( 7, \cli\safe_strlen( $str ) );
if ( \cli\can_use_pcre_x() ) {
putenv( 'PHP_CLI_TOOLS_TEST_SAFE_STRLEN=2' ); // Test preg_split( '/\X/u' ).
$this->assertSame( 7, \cli\safe_strlen( $str ) );
}
} elseif ( \cli\can_use_pcre_x() ) {
$this->assertSame( 7, \cli\safe_strlen( $str ) ); // Tests preg_split( '/\X/u' ).
} else {
putenv( 'PHP_CLI_TOOLS_TEST_SAFE_STRLEN=8' ); // Test strlen().
$this->assertSame( 18, \cli\safe_strlen( $str ) ); // strlen() - no. of bytes.
$this->assertSame( 18, strlen( $str ) );
}
if ( function_exists( 'mb_strlen' ) && function_exists( 'mb_detect_order' ) ) {
putenv( 'PHP_CLI_TOOLS_TEST_SAFE_STRLEN=4' ); // Test mb_strlen().
mb_detect_order( array( 'UTF-8', 'ISO-8859-1' ) );
$this->assertSame( 7, \cli\safe_strlen( $str ) );
$this->assertSame( 9, mb_strlen( $str, 'UTF-8' ) ); // mb_strlen() - counts the 2 combining chars.
}
// Non-UTF-8 - both grapheme_strlen() and preg_split( '/\X/u' ) will fail.
putenv( 'PHP_CLI_TOOLS_TEST_SAFE_STRLEN' );
if ( function_exists( 'mb_strlen' ) && function_exists( 'mb_detect_order' ) ) {
// Latin-1
mb_detect_order( array( 'UTF-8', 'ISO-8859-1' ) );
$str = "\xe0b\xe7"; // "àbç" in ISO-8859-1
$this->assertSame( 3, \cli\safe_strlen( $str ) ); // Test mb_strlen().
$this->assertSame( 3, mb_strlen( $str, 'ISO-8859-1' ) );
}
// Restore.
putenv( false == $test_safe_strlen ? 'PHP_CLI_TOOLS_TEST_SAFE_STRLEN' : "PHP_CLI_TOOLS_TEST_SAFE_STRLEN=$test_safe_strlen" );
if ( function_exists( 'mb_detect_order' ) ) {
mb_detect_order( $mb_detect_order );
}
}
}
+30
View File
@@ -0,0 +1,30 @@
<?php
use cli\Colors;
class testsColors extends PHPUnit_Framework_TestCase {
/**
* @dataProvider dataColors
*/
function testColors( $str, $color ) {
// Colors enabled.
Colors::enable( true );
$colored = Colors::color( $color );
$this->assertSame( Colors::colorize( $str ), Colors::color( $color ) );
if ( in_array( 'reset', $color ) ) {
$this->assertTrue( false !== strpos( $colored, '[0m' ) );
} else {
$this->assertTrue( false === strpos( $colored, '[0m' ) );
}
}
function dataColors() {
$ret = array();
foreach ( Colors::getColors() as $str => $color ) {
$ret[] = array( $str, $color );
}
return $ret;
}
}
+54
View File
@@ -0,0 +1,54 @@
<?php
use cli\Shell;
/**
* Class TestShell
*/
class TestShell extends PHPUnit_Framework_TestCase {
/**
* Test getting TERM columns.
*/
function testColumns() {
// Save.
$env_term = getenv( 'TERM' );
$env_columns = getenv( 'COLUMNS' );
$env_is_windows = getenv( 'WP_CLI_TEST_IS_WINDOWS' );
$env_shell_columns_reset = getenv( 'PHP_CLI_TOOLS_TEST_SHELL_COLUMNS_RESET' );
putenv( 'PHP_CLI_TOOLS_TEST_SHELL_COLUMNS_RESET=1' );
// No TERM should result in default 80.
putenv( 'TERM' );
putenv( 'COLUMNS=80' );
putenv( 'WP_CLI_TEST_IS_WINDOWS=0' );
$columns = cli\Shell::columns();
$this->assertSame( 80, $columns );
putenv( 'WP_CLI_TEST_IS_WINDOWS=1' );
$columns = cli\Shell::columns();
$this->assertSame( 80, $columns );
// TERM and COLUMNS should result in whatever COLUMNS is.
putenv( 'TERM=vt100' );
putenv( 'COLUMNS=100' );
putenv( 'WP_CLI_TEST_IS_WINDOWS=0' );
$columns = cli\Shell::columns();
$this->assertSame( 100, $columns );
putenv( 'WP_CLI_TEST_IS_WINDOWS=1' );
$columns = cli\Shell::columns();
$this->assertSame( 100, $columns );
// Restore.
putenv( false === $env_term ? 'TERM' : "TERM=$env_term" );
putenv( false === $env_columns ? 'COLUMNS' : "COLUMNS=$env_columns" );
putenv( false === $env_is_windows ? 'WP_CLI_TEST_IS_WINDOWS' : "WP_CLI_TEST_IS_WINDOWS=$env_is_windows" );
putenv( false === $env_shell_columns_reset ? 'PHP_CLI_TOOLS_TEST_SHELL_COLUMNS_RESET' : "PHP_CLI_TOOLS_TEST_SHELL_COLUMNS_RESET=$env_shell_columns_reset" );
}
}
+279
View File
@@ -0,0 +1,279 @@
<?php
use cli\Streams;
use cli\Table;
use cli\table\Ascii;
use cli\Colors;
/**
* Class Test_Table_Ascii
*
* Acceptance tests for ASCII table drawing.
* It will redirect STDOUT to temporary file and check that output matches with expected
*/
class Test_Table_Ascii extends PHPUnit_Framework_TestCase {
/**
* @var string Path to temporary file, where STDOUT output will be redirected during tests
*/
private $_mockFile;
/**
* @var \cli\Table Instance
*/
private $_instance;
/**
* Creates instance and redirects STDOUT to temporary file
*/
public function setUp() {
$this->_mockFile = tempnam(sys_get_temp_dir(), 'temp');
$resource = fopen($this->_mockFile, 'wb');
Streams::setStream('out', $resource);
$this->_instance = new Table();
$this->_instance->setRenderer(new Ascii());
}
/**
* Cleans temporary file
*/
public function tearDown() {
if (file_exists($this->_mockFile)) {
unlink($this->_mockFile);
}
}
/**
* Draw simple One column table
*/
public function testDrawOneColumnTable() {
$headers = array('Test Header');
$rows = array(
array('x'),
);
$output = <<<'OUT'
+-------------+
| Test Header |
+-------------+
| x |
+-------------+
OUT;
$this->assertInOutEquals(array($headers, $rows), $output);
}
/**
* Draw simple One column table with colored string
* Output should look like:
* +-------------+
* | Test Header |
* +-------------+
* | x |
* +-------------+
*
* where `x` character has green color.
* At the same time it checks that `green` defined in `cli\Colors` really looks as `green`.
*/
public function testDrawOneColumnColoredTable() {
Colors::enable( true );
$headers = array('Test Header');
$rows = array(
array(Colors::colorize('%Gx%n', true)),
);
// green `x`
$x = "\x1B\x5B\x33\x32\x3B\x31\x6Dx\x1B\x5B\x30\x6D";
$output = <<<OUT
+-------------+
| Test Header |
+-------------+
| $x |
+-------------+
OUT;
$this->assertInOutEquals(array($headers, $rows), $output);
}
/**
* Check it works with colors disabled.
*/
public function testDrawOneColumnColorDisabledTable() {
Colors::disable( true );
$this->assertFalse( Colors::shouldColorize() );
$headers = array('Test Header');
$rows = array(
array('%Gx%n'),
);
$output = <<<OUT
+-------------+
| Test Header |
+-------------+
| %Gx%n |
+-------------+
OUT;
$this->assertInOutEquals(array($headers, $rows), $output);
}
/**
* Checks that spacing and borders are handled correctly in table
*/
public function testSpacingInTable() {
$headers = array('A', ' ', 'C', '');
$rows = array(
array(' ', 'B1', '', 'D1'),
array('A2', '', ' C2', null),
);
$output = <<<'OUT'
+-------+------+-----+----+
| A | | C | |
+-------+------+-----+----+
| | B1 | | D1 |
| A2 | | C2 | |
+-------+------+-----+----+
OUT;
$this->assertInOutEquals(array($headers, $rows), $output);
}
/**
* Test correct table indentation and border positions for multibyte strings
*/
public function testTableWithMultibyteStrings() {
$headers = array('German', 'French', 'Russian', 'Chinese');
$rows = array(
array('Schätzen', 'Apprécier', 'Оценить', '欣賞'),
);
$output = <<<'OUT'
+----------+-----------+---------+---------+
| German | French | Russian | Chinese |
+----------+-----------+---------+---------+
| Schätzen | Apprécier | Оценить | 欣賞 |
+----------+-----------+---------+---------+
OUT;
$this->assertInOutEquals(array($headers, $rows), $output);
}
/**
* Test that % gets escaped correctly.
*/
public function testTableWithPercentCharacters() {
$headers = array( 'Heading', 'Heading2', 'Heading3' );
$rows = array(
array( '% at start', 'at end %', 'in % middle' )
);
$output = <<<'OUT'
+------------+----------+-------------+
| Heading | Heading2 | Heading3 |
+------------+----------+-------------+
| % at start | at end % | in % middle |
+------------+----------+-------------+
OUT;
$this->assertInOutEquals(array($headers, $rows), $output);
}
/**
* Test that a % is appropriately padded in the table
*/
public function testTablePaddingWithPercentCharacters() {
$headers = array( 'ID', 'post_title', 'post_name' );
$rows = array(
array(
3,
'10%',
''
),
array(
1,
'Hello world!',
'hello-world'
),
);
$output = <<<'OUT'
+----+--------------+-------------+
| ID | post_title | post_name |
+----+--------------+-------------+
| 3 | 10% | |
| 1 | Hello world! | hello-world |
+----+--------------+-------------+
OUT;
$this->assertInOutEquals(array($headers, $rows), $output);
}
/**
* Draw wide multiplication Table.
* Example with many columns, many rows
*/
public function testDrawMultiplicationTable() {
$maxFactor = 16;
$headers = array_merge(array('x'), range(1, $maxFactor));
for ($i = 1, $rows = array(); $i <= $maxFactor; ++$i) {
$rows[] = array_merge(array($i), range($i, $i * $maxFactor, $i));
}
$output = <<<'OUT'
+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| x | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| 1 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
| 2 | 2 | 4 | 6 | 8 | 10 | 12 | 14 | 16 | 18 | 20 | 22 | 24 | 26 | 28 | 30 | 32 |
| 3 | 3 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 30 | 33 | 36 | 39 | 42 | 45 | 48 |
| 4 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | 64 |
| 5 | 5 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 |
| 6 | 6 | 12 | 18 | 24 | 30 | 36 | 42 | 48 | 54 | 60 | 66 | 72 | 78 | 84 | 90 | 96 |
| 7 | 7 | 14 | 21 | 28 | 35 | 42 | 49 | 56 | 63 | 70 | 77 | 84 | 91 | 98 | 105 | 112 |
| 8 | 8 | 16 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 88 | 96 | 104 | 112 | 120 | 128 |
| 9 | 9 | 18 | 27 | 36 | 45 | 54 | 63 | 72 | 81 | 90 | 99 | 108 | 117 | 126 | 135 | 144 |
| 10 | 10 | 20 | 30 | 40 | 50 | 60 | 70 | 80 | 90 | 100 | 110 | 120 | 130 | 140 | 150 | 160 |
| 11 | 11 | 22 | 33 | 44 | 55 | 66 | 77 | 88 | 99 | 110 | 121 | 132 | 143 | 154 | 165 | 176 |
| 12 | 12 | 24 | 36 | 48 | 60 | 72 | 84 | 96 | 108 | 120 | 132 | 144 | 156 | 168 | 180 | 192 |
| 13 | 13 | 26 | 39 | 52 | 65 | 78 | 91 | 104 | 117 | 130 | 143 | 156 | 169 | 182 | 195 | 208 |
| 14 | 14 | 28 | 42 | 56 | 70 | 84 | 98 | 112 | 126 | 140 | 154 | 168 | 182 | 196 | 210 | 224 |
| 15 | 15 | 30 | 45 | 60 | 75 | 90 | 105 | 120 | 135 | 150 | 165 | 180 | 195 | 210 | 225 | 240 |
| 16 | 16 | 32 | 48 | 64 | 80 | 96 | 112 | 128 | 144 | 160 | 176 | 192 | 208 | 224 | 240 | 256 |
+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
OUT;
$this->assertInOutEquals(array($headers, $rows), $output);
}
/**
* Draw a table with headers but no data
*/
public function testDrawWithHeadersNoData() {
$headers = array('header 1', 'header 2');
$rows = array();
$output = <<<'OUT'
+----------+----------+
| header 1 | header 2 |
+----------+----------+
+----------+----------+
OUT;
$this->assertInOutEquals(array($headers, $rows), $output);
}
/**
* Verifies that Input and Output equals,
* Sugar method for fast access from tests
*
* @param array $input First element is header array, second element is rows array
* @param mixed $output Expected output
*/
private function assertInOutEquals(array $input, $output) {
$this->_instance->setHeaders($input[0]);
$this->_instance->setRows($input[1]);
$this->_instance->display();
$this->assertOutFileEqualsWith($output);
}
/**
* Checks that contents of input string and temporary file match
*
* @param mixed $expected Expected output
*/
private function assertOutFileEqualsWith($expected) {
$this->assertEquals($expected, file_get_contents($this->_mockFile));
}
}
+245
View File
@@ -0,0 +1,245 @@
<?php
use cli\Colors, cli\Table, cli\Table\Ascii;
/**
* Tests for cli\Table
*/
class Test_Table extends PHPUnit_Framework_TestCase {
public function test_column_value_too_long_ascii() {
$constraint_width = 80;
$table = new cli\Table;
$renderer = new cli\Table\Ascii;
$renderer->setConstraintWidth( $constraint_width );
$table->setRenderer( $renderer );
$table->setHeaders( array( 'Field', 'Value' ) );
$table->addRow( array( 'description', 'The 2012 theme for WordPress is a fully responsive theme that looks great on any device. Features include a front page template with its own widgets, an optional display font, styling for post formats on both index and single views, and an optional no-sidebar page template. Make it yours with a custom menu, header image, and background.' ) );
$table->addRow( array( 'author', '<a href="http://wordpress.org/" title="Visit author homepage">the WordPress team</a>' ) );
$out = $table->getDisplayLines();
$this->assertCount( 12, $out );
$this->assertEquals( $constraint_width, strlen( $out[0] ) );
$this->assertEquals( $constraint_width, strlen( $out[1] ) );
$this->assertEquals( $constraint_width, strlen( $out[2] ) );
$this->assertEquals( $constraint_width, strlen( $out[3] ) );
$this->assertEquals( $constraint_width, strlen( $out[4] ) );
$this->assertEquals( $constraint_width, strlen( $out[5] ) );
$this->assertEquals( $constraint_width, strlen( $out[6] ) );
$this->assertEquals( $constraint_width, strlen( $out[7] ) );
$this->assertEquals( $constraint_width, strlen( $out[8] ) );
$this->assertEquals( $constraint_width, strlen( $out[9] ) );
$this->assertEquals( $constraint_width, strlen( $out[10] ) );
$this->assertEquals( $constraint_width, strlen( $out[11] ) );
$constraint_width = 81;
$renderer = new cli\Table\Ascii;
$renderer->setConstraintWidth( $constraint_width );
$table->setRenderer( $renderer );
$out = $table->getDisplayLines();
for ( $i = 0; $i < count( $out ); $i++ ) {
$this->assertEquals( $constraint_width, strlen( $out[ $i ] ) );
}
}
public function test_column_value_too_long_with_multibytes() {
$constraint_width = 80;
$table = new cli\Table;
$renderer = new cli\Table\Ascii;
$renderer->setConstraintWidth( $constraint_width );
$table->setRenderer( $renderer );
$table->setHeaders( array( 'Field', 'Value' ) );
$table->addRow( array( '1この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。2この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。', 'こんにちは' ) );
$table->addRow( array( 'Lorem Ipsum is simply dummy text of the printing and typesetting industry.', 'Hello' ) );
$out = $table->getDisplayLines();
for ( $i = 0; $i < count( $out ); $i++ ) {
$this->assertEquals( $constraint_width, \cli\strwidth( $out[$i] ) );
}
$constraint_width = 81;
$renderer = new cli\Table\Ascii;
$renderer->setConstraintWidth( $constraint_width );
$table->setRenderer( $renderer );
$out = $table->getDisplayLines();
for ( $i = 0; $i < count( $out ); $i++ ) {
$this->assertEquals( $constraint_width, \cli\strwidth( $out[$i] ) );
}
}
public function test_column_odd_single_width_with_double_width() {
$dummy = new cli\Table;
$renderer = new cli\Table\Ascii;
$strip_borders = function ( $a ) {
return array_map( function ( $v ) {
return substr( $v, 2, -2 );
}, $a );
};
$renderer->setWidths( array( 10 ) );
// 1 single-width, 6 double-width, 1 single-width, 2 double-width, 1 half-width, 2 double-width.
$out = $renderer->row( array( '1あいうえおか2きくカけこ' ) );
$result = $strip_borders( explode( "\n", $out ) );
$this->assertSame( 3, count( $result ) );
$this->assertSame( '1あいうえ ', $result[0] ); // 1 single width, 4 double-width, space = 10.
$this->assertSame( 'おか2きくカ', $result[1] ); // 2 double-width, 1 single-width, 2 double-width, 1 half-width = 10.
$this->assertSame( 'けこ ', $result[2] ); // 2 double-width, 8 spaces = 10.
// Minimum width 1.
$renderer->setWidths( array( 1 ) );
$out = $renderer->row( array( '1あいうえおか2きくカけこ' ) );
$result = $strip_borders( explode( "\n", $out ) );
$this->assertSame( 13, count( $result ) );
// Uneven rows.
$this->assertSame( '1', $result[0] );
$this->assertSame( 'あ', $result[1] );
// Zero width does no wrapping.
$renderer->setWidths( array( 0 ) );
$out = $renderer->row( array( '1あいうえおか2きくカけこ' ) );
$result = $strip_borders( explode( "\n", $out ) );
$this->assertSame( 1, count( $result ) );
}
public function test_column_fullwidth_and_combining() {
$constraint_width = 80;
$table = new cli\Table;
$renderer = new cli\Table\Ascii;
$renderer->setConstraintWidth( $constraint_width );
$table->setRenderer( $renderer );
$table->setHeaders( array( 'Field', 'Value' ) );
$table->addRow( array( 'ID', 2151 ) );
$table->addRow( array( 'post_author', 1 ) );
$table->addRow( array( 'post_title', 'only-english-lorem-ipsum-dolor-sit-amet-consectetur-adipisicing-elit-sed-do-eiusmod-tempor-incididunt-ut-labore' ) );
$table->addRow( array( 'post_content',
//'ให้รู้จัก ให้หาหนทางใหม่' .
'♫ มีอีกหลายต่อหลายคน เขาอดทนก็เพื่อรัก' . "\n" .
'รักผลักดันให้รู้จัก ให้หาหนทางใหม่' . "\r\n" .
'ฉันจะล้มตั้งหลายที ดีที่รักมาฉุดไว้' . "\r\n" .
'รักสร้างสรรค์สิ่งมากมาย และหลอมละลายทุกหัวใจ' . "\r\n" .
'จะมาร้ายดียังไง แต่ใจก็ยังต้องการ' . "\r\n" .
'ในทุกๆ วัน โลกหมุนด้วยความรัก ♫' . "\n" .
'ขอแสดงความยินดี งานแต่งพี่ Earn & Menn' ."\r\n" .
'เที่ยวปายหน้าร้อน ก็เที่ยวได้เหมือนกันน่ะ' . "\r\n" .
' ジョバンニはまっ赤になってうなずきました。けれどもいつかジョバンニの眼のなかには涙がいっぱいになりました。そうだ僕は知っていたのだ、もちろんカムパネルラも知っている。' ."\r\n" .
'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore' . "\n" .
''
) );
$out = $table->getDisplayLines();
for ( $i = 0; $i < count( $out ); $i++ ) {
$this->assertEquals( $constraint_width, \cli\strwidth( $out[$i] ) );
}
$constraint_width = 81;
$renderer = new cli\Table\Ascii;
$renderer->setConstraintWidth( $constraint_width );
$table->setRenderer( $renderer );
$out = $table->getDisplayLines();
for ( $i = 0; $i < count( $out ); $i++ ) {
$this->assertEquals( $constraint_width, \cli\strwidth( $out[$i] ) );
}
$constraint_width = 200;
$renderer = new cli\Table\Ascii;
$renderer->setConstraintWidth( $constraint_width );
$table->setRenderer( $renderer );
$out = $table->getDisplayLines();
for ( $i = 0; $i < count( $out ); $i++ ) {
$this->assertEquals( $constraint_width, \cli\strwidth( $out[$i] ) );
}
}
public function test_ascii_pre_colorized_widths() {
Colors::enable( true );
$headers = array( 'package', 'version', 'result' );
$items = array(
array( Colors::colorize( '%ygaa/gaa-kabes%n' ), 'dev-master', Colors::colorize( "%rx%n" ) ),
array( Colors::colorize( '%ygaa/gaa-log%n' ), '*', Colors::colorize( "%gok%n" ) ),
array( Colors::colorize( '%ygaa/gaa-nonsense%n' ), 'v3.0.11', Colors::colorize( "%rx%n" ) ),
array( Colors::colorize( '%ygaa/gaa-100%%new%n' ), 'v100%new', Colors::colorize( "%gok%n" ) ),
);
// Disable colorization, as `\WP_CLI\Formatter::show_table()` does for Ascii tables.
Colors::disable( true );
$this->assertFalse( Colors::shouldColorize() );
// Account for colorization of columns 0 & 2.
$table = new Table;
$renderer = new Ascii;
$table->setRenderer( $renderer );
$table->setAsciiPreColorized( array( true, false, true ) );
$table->setHeaders( $headers );
$table->setRows( $items );
$out = $table->getDisplayLines();
// "+ 4" accommodates 3 borders and header.
$this->assertSame( 4 + 4, count( $out ) );
// Borders & header.
$this->assertSame( 42, strlen( $out[0] ) );
$this->assertSame( 42, strlen( $out[1] ) );
$this->assertSame( 42, strlen( $out[2] ) );
$this->assertSame( 42, strlen( $out[7] ) );
// Data.
$this->assertSame( 60, strlen( $out[3] ) );
$this->assertSame( 60, strlen( $out[4] ) );
$this->assertSame( 60, strlen( $out[5] ) );
$this->assertSame( 60, strlen( $out[6] ) );
// Don't account for colorization of columns 0 & 2.
$table = new Table;
$renderer = new Ascii;
$table->setRenderer( $renderer );
$table->setHeaders( $headers );
$table->setRows( $items );
$out = $table->getDisplayLines();
// "+ 4" accommodates 3 borders and header.
$this->assertSame( 4 + 4, count( $out ) );
// Borders & header.
$this->assertSame( 56, strlen( $out[0] ) );
$this->assertSame( 56, strlen( $out[1] ) );
$this->assertSame( 56, strlen( $out[2] ) );
$this->assertSame( 56, strlen( $out[7] ) );
// Data.
$this->assertSame( 56, strlen( $out[3] ) );
$this->assertSame( 56, strlen( $out[4] ) );
$this->assertSame( 56, strlen( $out[5] ) );
$this->assertSame( 56, strlen( $out[6] ) );
}
}