Skip to content

Commit

Permalink
WIP: tests which involve getopt() are hard and I'm belligerent
Browse files Browse the repository at this point in the history
  • Loading branch information
ben-xo committed Oct 17, 2022
1 parent d953f64 commit fbf5464
Show file tree
Hide file tree
Showing 5 changed files with 246 additions and 10 deletions.
55 changes: 49 additions & 6 deletions dir2cast.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ function __autoloader($class_name)

/* CLASSES **********************************************/

class ExitException extends Exception {}

abstract class GetterSetter {

protected $parameters = array();
Expand Down Expand Up @@ -1573,7 +1575,7 @@ public static function get_primed_error($type)
return 'dir2cast requires getID3. You should download this from <a href="' . DIR2CAST_HOMEPAGE . '">' . DIR2CAST_HOMEPAGE .'</a> and install it with dir2cast.';
}
}

public static function display($message, $errfile, $errline)
{
if(self::$errors)
Expand Down Expand Up @@ -1629,16 +1631,41 @@ public static function display($message, $errfile, $errline)
echo strip_tags(self::get_primed_error(ErrorHandler::$primer)) . "\n";
}

exit(-1);
throw new ExitException("", -1);
}
}


public static function display404($message)
{
if(defined('CLI_ONLY'))
{
header("HTTP/1.0 404 Not Found");
header("Content-type: text/plain");
}
echo "Not Found: $message\n";
throw new ExitException("", -2);
}
}

class SettingsHandler
{
private static $settings_cache = array();

/**
* getopt() uses argv directly and is a pain to mock. It's nicer to pass argv in,
* but mocking it a pain.
*/
public static function getopt($argv_in, $short_options, $long_options)
{
if($argv_in != $GLOBALS['argv'])
{
return fake_getopt($argv_in, $short_options, $long_options);
}
return getopt($short_options, $long_options);
}



/**
* This method sets up all app-wide settings that are required at initialization time.
*
Expand Down Expand Up @@ -1670,7 +1697,7 @@ public static function bootstrap(array $SERVER, array $GET, array $argv)
define('INI_FILE', $ini_file_name);
}

$cli_options = getopt('', array('help', 'media-dir::', 'media-url::', 'output::', 'dont-uncache', 'min-file-age::', 'debug', 'ignore-dir2cast-mtime', 'clock-offset::'));
$cli_options = self::getopt($argv, '', array('help', 'media-dir::', 'media-url::', 'output::', 'dont-uncache', 'min-file-age::', 'debug', 'ignore-dir2cast-mtime', 'clock-offset::'));
if($cli_options) {
if(isset($cli_options['help'])) {
print "Usage: php dir2cast.php [--help] [--media-dir=MP3_DIR] [--media-url=MP3_URL] [--output=OUTPUT_FILE]\n";
Expand All @@ -1687,6 +1714,10 @@ public static function bootstrap(array $SERVER, array $GET, array $argv)
if(!defined('MP3_DIR') && !empty($cli_options['media-dir']))
{
define('MP3_DIR', realpath($cli_options['media-dir']));
if(!is_dir(MP3_DIR) or !is_readable(MP3_DIR))
{
ErrorHandler::display404($cli_options['media-dir']);
}
}
if(!defined('MP3_URL') && !empty($cli_options['media-url']))
{
Expand Down Expand Up @@ -1739,11 +1770,16 @@ public static function bootstrap(array $SERVER, array $GET, array $argv)
if(!defined('MP3_DIR'))
{
if(!empty($GET['dir']))
{
define('MP3_DIR', MP3_BASE . '/' . safe_path(magic_stripslashes($GET['dir'])));
if(!is_dir(MP3_DIR) or !is_readable(MP3_DIR))
{
ErrorHandler::display404($GET['dir']);
}
}
else
define('MP3_DIR', MP3_BASE);
}

}

/**
Expand Down Expand Up @@ -2181,7 +2217,14 @@ function main($args)
if(isset($GLOBALS['argv'])) {
$args = $argv;
}
exit(main($args));
try
{
exit(main($args));
}
catch(ExitException $e)
{
exit($e->getCode());
}
}

/* THE END *********************************************/
75 changes: 75 additions & 0 deletions test/FakeGetoptTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php declare(strict_types=1);

use PHPUnit\Framework\TestCase;

final class FakeGetoptTest extends TestCase
{
public function test_fake_getopt_no_args()
{
$this->assertEquals(
fake_getopt(array('php', '--halp'), '', array()),
array()
);
$this->assertEquals(
fake_getopt(array('php'), '', array()),
array()
);
}
public function test_fake_getopt_no_match()
{
$this->assertEquals(
fake_getopt(array('php', '--halp'), '', array('help')),
array()
);
$this->assertEquals(
fake_getopt(array('php'), '', array('help')),
array()
);
}

public function test_fake_getopt_bool_arg()
{
$this->assertEquals(
fake_getopt(array('php', '--help'), '', array('help')),
array('help' => false)
);
}
public function test_fake_getopt_string_arg()
{
$this->assertEquals(
fake_getopt(array('php', '--media-dir'), '', array('media-dir::')),
array('media-dir' => '')
);
$this->assertEquals(
fake_getopt(array('php', '--media-dir='), '', array('media-dir::')),
array() // XXX: seems to be a bug in getopt
);
$this->assertEquals(
fake_getopt(array('php', '--media-dir=test'), '', array('media-dir::')),
array('media-dir' => 'test')
);
}
public function test_fake_getopt_escaping()
{
$this->assertEquals(
fake_getopt(array('php', "--media-dir= "), '', array('media-dir::')),
array('media-dir' => ' ')
);
print(fake_getopt_command(array('php', '--media-dir=""'), '', array('media-dir::')));
$this->assertEquals(
fake_getopt(array('php', '--media-dir=""'), '', array('media-dir::')),
array('media-dir' => '""')
);
$this->assertEquals(
fake_getopt(array('php', "--media-dir=''"), '', array('media-dir::')),
array('media-dir' => "''")
);
}
public function test_fake_getopt_both_arg_types()
{
$this->assertEquals(
fake_getopt(array('php', '--help', '--media-dir'), '', array('help', 'media-dir::')),
array('help' => false, 'media-dir' => '')
);
}
}
23 changes: 23 additions & 0 deletions test/FourOhFourTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php declare(strict_types=1);

use PHPUnit\Framework\TestCase;

final class FourOhFourTest extends TestCase
{
public static function setUpBeforeClass(): void
{
prepare_testing_dir();
}

public function test_non_existent_dir_prints_bare_error_CLI_case(): void
{
exec('php dir2cast.php --output=out.xml --media-dir=imaginary-dir', $output, $returncode);
$this->assertEquals("Not Found: imaginary-dir", implode("\n", $output));
$this->assertEquals(254, $returncode); // 254 is -2
}

public static function tearDownAfterClass(): void
{
chdir('..');
}
}
73 changes: 69 additions & 4 deletions test/SettingsHandlerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,30 @@ class SettingsHandlerTest extends TestCase
'MIN_FILE_AGE',
);

public function test_getopt_hook()
{
$argv_copy = $GLOBALS['argv'];
$argc_copy = $GLOBALS['argc'];

$short_options = '';
$long_options = array('help', 'media-dir::', 'bootstrap');

$cli_options = SettingsHandler::getopt(array(), $short_options, $long_options);
$this->assertEquals($cli_options, array());

$cli_options = SettingsHandler::getopt(array('--help'), $short_options, $long_options);
$this->assertEquals($cli_options, array('help' => true));

$cli_options = SettingsHandler::getopt(array('--media-dir='), $short_options, $long_options);
$this->assertEquals($cli_options, array('media-dir' => ''));

$cli_options = SettingsHandler::getopt(array('--media-dir=test'), $short_options, $long_options);
$this->assertEquals($cli_options, array('media-dir' => 'test'));

$this->assertEquals($argv_copy, $GLOBALS['argv']);
$this->assertEquals($argc_copy, $GLOBALS['argc']);
}

/**
* @runInSeparateProcess
* @preserveGlobalState disabled
Expand Down Expand Up @@ -121,12 +145,13 @@ public function test_defines_CLI_ONLY_if_argv0()
/**
* @runInSeparateProcess
* @preserveGlobalState disabled
* @testWith [null]
* ["dir2cast.php"]
* @testWith [null, null]
* ["dir2cast.php", null]
* ["dir2cast.php", "--media-dir="]
*/
public function test_bootstrap_sets_sensible_global_defaults_for_entire_installation($argv0)
public function test_bootstrap_sets_sensible_global_defaults_for_entire_installation($argv0, $argv1)
{
SettingsHandler::bootstrap(array(), array(), array($argv0));
SettingsHandler::bootstrap(array(), array(), array($argv0, $argv1));
$this->assertEquals(MIN_CACHE_TIME, 5);
$this->assertEquals(FORCE_PASSWORD, '');
$this->assertEquals(TMP_DIR, DIR2CAST_BASE . '/temp');
Expand All @@ -153,6 +178,46 @@ public function test_when_SERVER_HTTP_HOST_then_MP3_BASE_defaults_to_same_dir()
$this->assertEquals(MP3_DIR, '/var/www');
}

/**
* @runInSeparateProcess
* @preserveGlobalState disabled
*/
public function test_cli_media_404()
{
$temp = tempnam('./', 'test_cli_media_404');
try
{
$this->expectException("ExitException");
$this->expectExceptionCode(-2);
SettingsHandler::bootstrap(array(), array(), array("dir2cast.php", "--media-dir=$temp"));
}
catch(Exception $e)
{
throw $e;
}
finally
{
unlink($temp);
}
}

/**
* @runInSeparateProcess
* @preserveGlobalState disabled
*/
public function test_cli_media_dir_404()
{

}

/**
* @runInSeparateProcess
* @preserveGlobalState disabled
*/
public function test_cli_arg_parsing()
{

}
// TODO: test HTTP_HOST + GET dir

/**
Expand Down
30 changes: 30 additions & 0 deletions test/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,36 @@ function temp_xml_glob()
return '.' . DIRECTORY_SEPARATOR . 'temp' . DIRECTORY_SEPARATOR . '*.xml';
}

function escape_single_quoted_string($string)
{
return str_replace(array("'", '\\'), array("\\'", '\\\\'), $string);
}

function fake_getopt_command($argv_in, $short_options, $long_options)
{
$argv_string = "'" . implode("', '", array_map('escape_single_quoted_string', $argv_in) ). "'";
$argv_count = count($argv_in);
$short_options_string = addslashes($short_options);
$long_options_string = "'" . implode("', '", array_map('escape_single_quoted_string', $long_options) ). "'";

$command_parts = array(
'php', '-d', 'register_argc_argv=false', '-r', <<<EOSCRIPT
\$GLOBALS["argv"]=array($argv_string);
\$GLOBALS["argc"]=$argv_count;
print(serialize((getopt('$short_options_string', array($long_options_string)))));
EOSCRIPT
);
return implode(" ", array_map('escapeshellarg', $command_parts));
}

function fake_getopt($argv_in, $short_options, $long_options)
{
// $command = php -d 'register_argc_argv=false' -r '$GLOBALS["argv"]=array("php", "--stuff");$GLOBALS["argc"]=1;print(serialize((getopt("", array("stuff")))));'

$command = fake_getopt_command($argv_in, $short_options, $long_options);
exec($command, $output, $result_code);
return unserialize($output[0]);
}

define('NO_DISPATCHER', true);

Expand Down

0 comments on commit fbf5464

Please sign in to comment.