addOption( 'test', 'Test Option' ); $this->assertTrue( $params->supportsOption( 'test' ) ); $this->assertFalse( $params->supportsOption( 'xyzzy' ) ); $this->assertFalse( $params->hasOption( 'test' ) ); $this->assertNull( $params->getOption( 'test' ) ); $this->assertSame( 'default', $params->getOption( 'test', 'default' ) ); $this->assertSame( [], $params->getOptions() ); $this->assertSame( [], $params->getOptionsSequence() ); $this->assertSame( [ 'test' ], $params->getOptionNames() ); // Provide option value $params->setOptionsAndArgs( [ 'test' => 'foo' ], [] ); $this->assertTrue( $params->hasOption( 'test' ) ); $this->assertSame( 'foo', $params->getOption( 'test' ) ); $this->assertSame( 'foo', $params->getOption( 'test', 'default' ) ); $this->assertSame( [ 'test' => 'foo' ], $params->getOptions() ); $this->assertSame( [ [ 'test', 'foo' ] ], $params->getOptionsSequence() ); $this->assertSame( [ 'test' ], $params->getOptionNames() ); // Delete option $params->deleteOption( 'test' ); $this->assertFalse( $params->hasOption( 'test' ) ); $this->assertFalse( $params->supportsOption( 'test' ) ); $this->assertNull( $params->getOption( 'test' ) ); $this->assertSame( [], $params->getOptions() ); $this->assertSame( [], $params->getOptionsSequence() ); $this->assertSame( [], $params->getOptionNames() ); } public function testOptionSequence() { $params = new MaintenanceParameters(); $params->addOption( 'foo', 'Foo', false, false ); $params->addOption( 'bar', 'Bar', false, true ); $params->loadWithArgv( [ '--bar', 'A', '--bar=B', '--foo', '--bar', 'C' ] ); $this->assertSame( [ [ 'bar', 'A' ], [ 'bar', 'B' ], [ 'foo', 1 ], [ 'bar', 'C' ], ], $params->getOptionsSequence() ); $params->setOptionsAndArgs( [ 'bar' => [ 'x', 'y' ], 'foo' => 1 ], [] ); $this->assertSame( [ [ 'bar', 'x' ], [ 'bar', 'y' ], [ 'foo', 1 ], ], $params->getOptionsSequence() ); } public function testArg() { $params = new MaintenanceParameters(); $this->assertSame( 0, $params->addArg( 'test', 'Test arg', true ) ); $this->assertSame( 1, $params->addArg( 'best', 'Best arg' ) ); $this->assertSame( 'test', $params->getArgName( 0 ) ); $this->assertSame( 'best', $params->getArgName( 1 ) ); $this->assertNull( $params->getArgName( 2 ) ); $this->assertFalse( $params->hasArg( 0 ) ); $this->assertNull( $params->getArg( 0 ) ); $this->assertSame( [], $params->getArgs() ); // Provide argument values $params->setOptionsAndArgs( [], [ 'foo', 'bar', 'cuzz' ] ); $this->assertTrue( $params->hasArg( 0 ) ); $this->assertTrue( $params->hasArg( 'test' ) ); $this->assertTrue( $params->hasArg( 1 ) ); $this->assertTrue( $params->hasArg( 'best' ) ); $this->assertTrue( $params->hasArg( 2 ) ); $this->assertFalse( $params->hasArg( 3 ) ); $this->assertSame( 'foo', $params->getArg( 0 ) ); $this->assertSame( 'foo', $params->getArg( 'test' ) ); $this->assertSame( 'bar', $params->getArg( 1 ) ); $this->assertSame( 'bar', $params->getArg( 'best' ) ); $this->assertSame( 'cuzz', $params->getArg( 2 ) ); $this->assertNull( $params->getArg( 3 ) ); $this->assertSame( [ 'foo', 'bar', 'cuzz' ], $params->getArgs() ); } public function testClear() { $params = new MaintenanceParameters(); $params->addOption( 'test', 'Test Option' ); $params->addArg( 'jest', 'Jest arg' ); $params->setOptionsAndArgs( [ 'test' => 'foo' ], [ 'x', 'y' ] ); $this->assertTrue( $params->hasOption( 'test' ) ); $this->assertTrue( $params->hasArg( 'jest' ) ); $params->clear(); $this->assertFalse( $params->hasOption( 'test' ) ); $this->assertFalse( $params->hasArg( 'jest' ) ); $this->assertSame( 'jest', $params->getArgName( 0 ) ); $this->assertTrue( $params->supportsOption( 'test' ) ); } public static function provideArgv() { yield 'nothing' => [ [], [], [] ]; yield 'simple' => [ [ '--simple', 'test' ], [ 'simple' => 1 ], [ 'test' ] ]; yield 'trailing option' => [ [ 'test', '--simple' ], [ 'simple' => 1 ], [ 'test' ] ]; yield 'not a trailing option' => [ [ 'test', '--', '--simple' ], [], [ 'test', '--simple' ] ]; yield 'option value' => [ [ '--value', 'foo' ], [ 'value' => 'foo' ], [] ]; yield 'option value swallows option' => [ [ '--value', '--simple' ], [ 'value' => '--simple' ], [] ]; yield 'option value swallows delimiter' => [ [ '--value', '--', '--simple' ], [ 'value' => '--', 'simple' => 1 ], [] ]; yield 'option value assigned' => [ [ '--value=foo', 'test' ], [ 'value' => 'foo' ], [ 'test' ] ]; yield 'option value short' => [ [ '-sv', 'foo' ], [ 'simple' => 1, 'value' => 'foo' ], [] ]; yield 'short option and lonely dash' => [ [ '-s', '-', 'foo' ], [ 'simple' => 1 ], [ '-', 'foo' ] ]; yield 'multi value' => [ [ '--multi', 'foo', 'test', '--multi', 'bar' ], [ 'multi' => [ 'foo', 'bar' ] ], [ 'test' ] ]; yield 'multi value short' => [ [ '-mm', 'foo', 'bar', 'test' ], [ 'multi' => [ 'foo', 'bar' ] ], [ 'test' ] ]; yield 'extra option' => [ [ '--extra', 'test' ], [ 'extra' => 1 ], [ 'test' ] ]; yield 'extra with assignment' => [ [ '--extra=foo', 'test' ], [ 'extra' => 'foo' ], [ 'test' ] ]; } /** * @dataProvider provideArgv */ public function testLoad( $argv, $expectedOptions, $expectedArgs ) { $params = new MaintenanceParameters(); $params->setAllowUnregisteredOptions( true ); $params->addOption( 'simple', 'simple option', false, false, 's' ); $params->addOption( 'value', 'value option', false, true, 'v' ); $params->addOption( 'multi', 'multi option', false, true, 'm', true ); $params->addArg( 'foo', 'Foozels', false ); $params->loadWithArgv( $argv ); $params->validate(); $this->assertFalse( $params->hasErrors() ); $this->assertSame( [], $params->getErrors() ); $this->assertSame( $expectedOptions, $params->getOptions() ); $this->assertSame( $expectedArgs, $params->getArgs() ); } public function testMultiArg() { $params = new MaintenanceParameters(); $params->setAllowUnregisteredOptions( true ); $params->addArg( 'foo', 'Foozels', true ); $params->addArg( 'bar', 'Bazels', false, true ); $params->loadWithArgv( [ 'a', 'b', 'c', 'd' ] ); $this->assertSame( 'a', $params->getArg( 0 ) ); $this->assertSame( 'a', $params->getArg( 'foo' ) ); $this->assertSame( 'b', $params->getArg( 1 ) ); $this->assertSame( 'b', $params->getArg( 'bar' ) ); $this->assertSame( [ 'a', 'b', 'c', 'd' ], $params->getArgs() ); $this->assertSame( [ 'c', 'd' ], $params->getArgs( 2 ) ); $this->assertSame( [ 'a', 'b', 'c', 'd' ], $params->getArgs( 'foo' ) ); $this->assertSame( [ 'b', 'c', 'd' ], $params->getArgs( 'bar' ) ); } public static function provideAddArgFailure() { yield 'Already defined' => [ [ 'foo', 'testin 1' ], [ 'foo', 'testin 2' ], ]; yield 'Required after optional' => [ [ 'foo', 'Foo test', false ], [ 'bar', 'Bar test', true ], ]; yield 'after multi' => [ [ 'foo', 'Foo test', false, true ], [ 'bar', 'Bar test' ], ]; } /** * @dataProvider provideAddArgFailure */ public function testAddArgFailure( $arg1, $arg2 ) { $params = new MaintenanceParameters(); $params->addArg( ...$arg1 ); $this->expectException( UnexpectedValueException::class ); $params->addArg( ...$arg2 ); } public function testLoadSkip() { $params = new MaintenanceParameters(); $params->addOption( 'value', 'value option', false, true, 'v' ); $argv = [ 'A', '--value', 'V', 'B' ]; $params->loadWithArgv( $argv ); $this->assertSame( 'A', $params->getArg( 0 ) ); $params->loadWithArgv( $argv, 1 ); $this->assertSame( 'B', $params->getArg( 0 ) ); $params->loadWithArgv( $argv, 2 ); $this->assertSame( 'V', $params->getArg( 0 ) ); } public static function provideBadArgv() { yield 'nothing' => [ [], [ 'Option --simple is required!', 'Argument is required!' ] ]; yield 'missing option' => [ [ 'arg' ], [ 'Option --simple is required!' ] ]; yield 'missing arg' => [ [ '--simple' ], [ 'Argument is required!' ] ]; yield 'option given twice' => [ [ '--simple', 'arg', '-s' ], [ 'Option --simple given twice' ] ]; yield 'Unexpected option' => [ [ '--extra', 'arg', '-s' ], [ 'Unexpected option --extra!' ] ]; yield 'Missing value' => [ [ '-s', 'arg', '--value' ], [ 'Option --value needs a value after it!' ] ]; yield 'Missing value short' => [ [ '-s', 'arg', '-v' ], [ 'Option -v needs a value after it!' ] ]; } /** * @dataProvider provideBadArgv */ public function testValidationErrors( $argv, $errors ) { $params = new MaintenanceParameters(); $params->addOption( 'simple', 'simple option', true, false, 's' ); $params->addOption( 'value', 'value option', false, true, 'v' ); $params->addArg( 'foo', 'Foozels', true ); $params->loadWithArgv( $argv ); $params->validate(); $this->assertTrue( $params->hasErrors() ); $this->assertSame( $errors, $params->getErrors() ); } private function findInLines( array $lines, $regex, $start = 0 ) { for ( $i = $start; $i < count( $lines ); $i++ ) { if ( str_contains( $lines[ $i ], $regex ) ) { return $i; } } return false; } public function testHelp() { $params = new MaintenanceParameters(); $params->setName( 'Foo' ); $params->setDescription( 'Frobs the foo' ); // Optional, no value $params->addOption( 'flag', 'First flag', false, false, 'f' ); // Required, with value $params->addOption( 'value', 'Some value', true, true ); // Required, with value, and with short name. $params->addOption( 'thing', 'Words here', false, true, 't' ); $params->assignGroup( 'Test flags', [ 'flag' ] ); $help = $params->getHelp(); $lines = preg_split( '/\s*[\r\n]\s*/', $help ); $lines = array_values( array_filter( $lines ) ); $synopsisIndex = $this->findInLines( $lines, 'Usage: php maintenance/run.php Foo' ); $this->assertNotFalse( $synopsisIndex ); $synopsis = $lines[$synopsisIndex]; $this->assertStringContainsString( '[OPTION]... --value ', $synopsis ); $this->assertStringContainsString( 'Frobs the foo', $help ); $this->assertStringContainsString( '--flag (-f): First flag', $help ); $this->assertStringContainsString( '--value : Some value', $help ); $this->assertStringContainsString( '--thing (-t) : Words here', $help ); $this->assertNotFalse( $this->findInLines( $lines, 'Test flags' ) ); $this->assertNotFalse( $this->findInLines( $lines, 'Script specific options' ) ); $sectionOffset = $this->findInLines( $lines, 'Script specific parameters' ); $this->assertGreaterThan( $sectionOffset, $this->findInLines( $lines, '--value', $sectionOffset + 1 ) ); $sectionOffset = $this->findInLines( $lines, 'Test flags' ); $this->assertGreaterThan( $sectionOffset, $this->findInLines( $lines, '--flag', $sectionOffset + 1 ) ); } public static function provideArgHelp() { yield [ [ 'foo', 'Foo arg' ], '', ': Foo arg' ]; yield [ [ 'foo', 'Foo arg', false ], '[foo]', '[foo]: Foo arg' ]; yield [ [ 'foo', 'Foo arg', true, true ], '...', '...: Foo arg' ]; yield [ [ 'foo', 'Foo arg', false, true ], '[foo]...', '[foo]...: Foo arg' ]; } /** * @dataProvider provideArgHelp */ public function testArgHelp( $arg, $expectedSynopsis, $expectedLine ) { $params = new MaintenanceParameters(); $params->setName( 'Foo' ); $params->addArg( ...$arg ); $help = $params->getHelp(); $lines = preg_split( '/\s*[\r\n]\s*/', $help ); $lines = array_values( array_filter( $lines ) ); $synopsisIndex = $this->findInLines( $lines, 'Usage: php maintenance/run.php Foo' ); $this->assertNotFalse( $synopsisIndex ); $synopsis = $lines[$synopsisIndex]; $this->assertStringContainsString( $expectedSynopsis, $synopsis ); $this->assertNotFalse( $this->findInLines( $lines, $expectedLine ) ); } public function testMergeOptions() { $params = new MaintenanceParameters(); $params->setName( 'Foo' ); $params->setDescription( 'Frobs the foo' ); $params->addOption( 'flag', 'First flag', false, false, 'f' ); $params->setOptionsAndArgs( [ 'flag' => 1 ], [] ); $other = new MaintenanceParameters(); $other->setName( 'Bar' ); $other->setDescription( 'Mars the bar' ); $other->addOption( 'value', 'Some value', true, true ); $params->mergeOptions( $other ); // declarations from both objects are presetn $this->assertTrue( $params->supportsOption( 'flag' ) ); $this->assertTrue( $params->supportsOption( 'value' ) ); // values are reset $this->assertFalse( $params->hasOption( 'flag' ) ); } public function testSetOptionsAndArgs() { $params = new MaintenanceParameters(); $params->setOptionsAndArgs( [ 'foo' => 'X' ], [ 'one' ] ); $this->assertTrue( $params->hasOption( 'foo' ) ); $this->assertTrue( $params->hasArg( 0 ) ); $this->assertSame( [ 'one' ], $params->getArgs() ); $this->assertSame( [ 'foo' => 'X' ], $params->getOptions() ); $params->setOptionsAndArgs( [ 'bar' => 'Y' ], [] ); $this->assertTrue( $params->hasOption( 'bar' ) ); $this->assertFalse( $params->hasOption( 'foo' ) ); $this->assertFalse( $params->hasArg( 0 ) ); $this->assertSame( [], $params->getArgs() ); $this->assertSame( [ 'bar' => 'Y' ], $params->getOptions() ); } }