diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index b216a35b..b2fd1bce 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1,229 +1,232 @@ array( 'AASTNode' => 'parser/aast/api/node', 'AASTNodeList' => 'parser/aast/api/list', 'AASTToken' => 'parser/aast/api/token', 'AASTTree' => 'parser/aast/api/tree', 'AbstractDirectedGraph' => 'utils/abstractgraph', 'AbstractDirectedGraphTestCase' => 'utils/abstractgraph/__tests__', 'BaseHTTPFuture' => 'future/http/base', 'CommandException' => 'future/exec', 'ConduitClient' => 'conduit/client', 'ConduitClientException' => 'conduit/client', 'ConduitFuture' => 'conduit/client', 'ExecFuture' => 'future/exec', 'FileFinder' => 'filesystem/filefinder', 'FileList' => 'filesystem/filelist', 'Filesystem' => 'filesystem', 'FilesystemException' => 'filesystem', 'Future' => 'future', 'FutureIterator' => 'future', 'FutureProxy' => 'future/proxy', 'HTTPFuture' => 'future/http/http', 'HTTPFutureResponseStatus' => 'future/http/status/base', 'HTTPFutureResponseStatusCURL' => 'future/http/status/curl', 'HTTPFutureResponseStatusHTTP' => 'future/http/status/http', 'HTTPFutureResponseStatusParse' => 'future/http/status/parse', 'HTTPFutureResponseStatusTransport' => 'future/http/status/transport', 'HTTPSFuture' => 'future/http/https', 'ImmediateFuture' => 'future/immediate', 'JSONReadableSerializerTestCase' => 'parser/json/__tests__', 'LinesOfALargeFile' => 'filesystem/linesofalargefile', 'MFilterTestHelper' => 'utils/__tests__', 'PhutilConsoleFormatter' => 'console', 'PhutilConsoleStdinNotInteractiveException' => 'console/exception', 'PhutilDaemon' => 'daemon/base', 'PhutilDaemonOverseer' => 'daemon/overseer', 'PhutilDefaultSyntaxHighlighter' => 'markup/syntax/highlighter/default', 'PhutilDefaultSyntaxHighlighterEngine' => 'markup/syntax/engine/default', 'PhutilDefaultSyntaxHighlighterEnginePygmentsFuture' => 'markup/syntax/highlighter/pygments/future', 'PhutilDefaultSyntaxHighlighterEngineTestCase' => 'markup/syntax/engine/default/__tests__', 'PhutilDivinerSyntaxHighlighter' => 'markup/syntax/highlighter/diviner', 'PhutilDocblockParser' => 'parser/docblock', 'PhutilDocblockParserTestCase' => 'parser/docblock/__tests__', 'PhutilEmailAddress' => 'parser/emailaddress', 'PhutilEmailAddressTestCase' => 'parser/emailaddress/__tests__', 'PhutilErrorHandler' => 'error', 'PhutilEvent' => 'events/event', 'PhutilEventConstants' => 'events/constant/base', 'PhutilEventEngine' => 'events/engine', 'PhutilEventListener' => 'events/listener', 'PhutilEventType' => 'events/constant/type', 'PhutilExcessiveServiceCallsDaemon' => 'daemon/torture/excessiveservicecalls', 'PhutilFatalDaemon' => 'daemon/torture/fatal', 'PhutilHangForeverDaemon' => 'daemon/torture/hangforever', 'PhutilInteractiveEditor' => 'console/editor', 'PhutilJSON' => 'parser/json', 'PhutilMarkupEngine' => 'markup/engine', 'PhutilMarkupTestCase' => 'markup/__tests__', 'PhutilMissingSymbolException' => 'symbols/exception/missing', 'PhutilNiceDaemon' => 'daemon/torture/nice', 'PhutilProcessGroupDaemon' => 'daemon/torture/processgroup', 'PhutilPygmentsSyntaxHighlighter' => 'markup/syntax/highlighter/pygments', 'PhutilRainbowSyntaxHighlighter' => 'markup/syntax/highlighter/rainbow', 'PhutilReadableSerializer' => 'readableserializer', 'PhutilRemarkupBlockStorage' => 'markup/engine/remarkup/blockstorage', 'PhutilRemarkupEngine' => 'markup/engine/remarkup', 'PhutilRemarkupEngineBlockRule' => 'markup/engine/remarkup/blockrule/base', 'PhutilRemarkupEngineRemarkupCodeBlockRule' => 'markup/engine/remarkup/blockrule/remarkupcode', 'PhutilRemarkupEngineRemarkupDefaultBlockRule' => 'markup/engine/remarkup/blockrule/remarkupdefault', 'PhutilRemarkupEngineRemarkupHeaderBlockRule' => 'markup/engine/remarkup/blockrule/remarkupheader', 'PhutilRemarkupEngineRemarkupInlineBlockRule' => 'markup/engine/remarkup/blockrule/remarkupinline', 'PhutilRemarkupEngineRemarkupListBlockRule' => 'markup/engine/remarkup/blockrule/remarkuplist', 'PhutilRemarkupEngineRemarkupLiteralBlockRule' => 'markup/engine/remarkup/blockrule/remarkupliteral', 'PhutilRemarkupEngineRemarkupNoteBlockRule' => 'markup/engine/remarkup/blockrule/remarkupnote', 'PhutilRemarkupEngineRemarkupQuotesBlockRule' => 'markup/engine/remarkup/blockrule/remarkupquotes', 'PhutilRemarkupEngineTestCase' => 'markup/engine/remarkup/__tests__', 'PhutilRemarkupRule' => 'markup/engine/remarkup/markuprule/base', 'PhutilRemarkupRuleBold' => 'markup/engine/remarkup/markuprule/bold', 'PhutilRemarkupRuleEscapeHTML' => 'markup/engine/remarkup/markuprule/escapehtml', 'PhutilRemarkupRuleEscapeRemarkup' => 'markup/engine/remarkup/markuprule/escaperemarkup', 'PhutilRemarkupRuleHyperlink' => 'markup/engine/remarkup/markuprule/hyperlink', 'PhutilRemarkupRuleItalic' => 'markup/engine/remarkup/markuprule/italics', 'PhutilRemarkupRuleLinebreaks' => 'markup/engine/remarkup/markuprule/linebreaks', 'PhutilRemarkupRuleMonospace' => 'markup/engine/remarkup/markuprule/monospace', 'PhutilSaturateStdoutDaemon' => 'daemon/torture/saturatestdout', 'PhutilServiceProfiler' => 'serviceprofiler', + 'PhutilSimpleOptions' => 'parser/simpleoptions', + 'PhutilSimpleOptionsTestCase' => 'parser/simpleoptions/__tests__', 'PhutilSymbolLoader' => 'symbols', 'PhutilSyntaxHighlighter' => 'markup/syntax/highlighter/base', 'PhutilSyntaxHighlighterEngine' => 'markup/syntax/engine/base', 'PhutilTortureTestDaemon' => 'daemon/torture/base', 'PhutilURI' => 'parser/uri', 'PhutilURITestCase' => 'parser/uri/__tests__', 'PhutilUTF8TestCase' => 'utils/__tests__', 'PhutilUtilsTestCase' => 'utils/__tests__', 'PhutilXHPASTSyntaxHighlighter' => 'markup/syntax/highlighter/xhpast', 'PhutilXHPASTSyntaxHighlighterTestCase' => 'markup/syntax/highlighter/xhpast/__tests__', 'TempFile' => 'filesystem/tempfile', 'TestAbstractDirectedGraph' => 'utils/abstractgraph/__tests__', 'XHPASTNode' => 'parser/xhpast/api/node', 'XHPASTSyntaxErrorException' => 'parser/xhpast/api/exception', 'XHPASTToken' => 'parser/xhpast/api/token', 'XHPASTTree' => 'parser/xhpast/api/tree', 'XHPASTTreeTestCase' => 'parser/xhpast/api/tree/__tests__', ), 'function' => array( 'Futures' => 'future', 'array_mergev' => 'utils', 'array_select_keys' => 'utils', 'coalesce' => 'utils', 'csprintf' => 'xsprintf/csprintf', 'exec_manual' => 'future/exec', 'execx' => 'future/exec', 'head' => 'utils', 'id' => 'utils', 'idx' => 'utils', 'ifilter' => 'utils', 'igroup' => 'utils', 'ipull' => 'utils', 'isort' => 'utils', 'jsprintf' => 'xsprintf/jsprintf', 'last' => 'utils', 'mfilter' => 'utils', 'mgroup' => 'utils', 'mpull' => 'utils', 'msort' => 'utils', 'newv' => 'utils', 'nonempty' => 'utils', 'phlog' => 'error', 'phutil_console_confirm' => 'console', 'phutil_console_format' => 'console', 'phutil_console_prompt' => 'console', 'phutil_console_require_tty' => 'console', 'phutil_console_wrap' => 'console', 'phutil_deprecated' => 'moduleutils', 'phutil_error_listener_example' => 'error', 'phutil_escape_html' => 'markup', 'phutil_escape_uri' => 'markup', 'phutil_get_library_name_for_root' => 'moduleutils', 'phutil_get_library_root' => 'moduleutils', 'phutil_get_library_root_for_path' => 'moduleutils', 'phutil_is_utf8' => 'utils', 'phutil_passthru' => 'future/exec', 'phutil_render_tag' => 'markup', 'phutil_utf8_shorten' => 'utils', 'phutil_utf8_strlen' => 'utils', 'phutil_utf8ize' => 'utils', 'phutil_utf8v' => 'utils', 'vcsprintf' => 'xsprintf/csprintf', 'vjsprintf' => 'xsprintf/jsprintf', 'xhp_parser_node_constants' => 'parser/xhpast/constants', 'xhpast_get_binary_path' => 'parser/xhpast/bin', 'xhpast_get_build_instructions' => 'parser/xhpast/bin', 'xhpast_get_parser_future' => 'parser/xhpast/bin', 'xhpast_is_available' => 'parser/xhpast/bin', 'xhpast_parser_token_constants' => 'parser/xhpast/constants', 'xsprintf' => 'xsprintf', 'xsprintf_callback_example' => 'xsprintf', 'xsprintf_command' => 'xsprintf/csprintf', 'xsprintf_javascript' => 'xsprintf/jsprintf', ), 'requires_class' => array( 'AbstractDirectedGraphTestCase' => 'ArcanistPhutilTestCase', 'BaseHTTPFuture' => 'Future', 'ConduitFuture' => 'FutureProxy', 'ExecFuture' => 'Future', 'FutureProxy' => 'Future', 'HTTPFuture' => 'BaseHTTPFuture', 'HTTPFutureResponseStatusCURL' => 'HTTPFutureResponseStatus', 'HTTPFutureResponseStatusHTTP' => 'HTTPFutureResponseStatus', 'HTTPFutureResponseStatusParse' => 'HTTPFutureResponseStatus', 'HTTPFutureResponseStatusTransport' => 'HTTPFutureResponseStatus', 'HTTPSFuture' => 'BaseHTTPFuture', 'ImmediateFuture' => 'Future', 'JSONReadableSerializerTestCase' => 'ArcanistPhutilTestCase', 'PhutilDefaultSyntaxHighlighterEngine' => 'PhutilSyntaxHighlighterEngine', 'PhutilDefaultSyntaxHighlighterEnginePygmentsFuture' => 'FutureProxy', 'PhutilDefaultSyntaxHighlighterEngineTestCase' => 'ArcanistPhutilTestCase', 'PhutilDocblockParserTestCase' => 'ArcanistPhutilTestCase', 'PhutilEmailAddressTestCase' => 'ArcanistPhutilTestCase', 'PhutilEventType' => 'PhutilEventConstants', 'PhutilExcessiveServiceCallsDaemon' => 'PhutilTortureTestDaemon', 'PhutilFatalDaemon' => 'PhutilTortureTestDaemon', 'PhutilHangForeverDaemon' => 'PhutilTortureTestDaemon', 'PhutilMarkupTestCase' => 'ArcanistPhutilTestCase', 'PhutilNiceDaemon' => 'PhutilTortureTestDaemon', 'PhutilProcessGroupDaemon' => 'PhutilTortureTestDaemon', 'PhutilRemarkupEngine' => 'PhutilMarkupEngine', 'PhutilRemarkupEngineRemarkupCodeBlockRule' => 'PhutilRemarkupEngineBlockRule', 'PhutilRemarkupEngineRemarkupDefaultBlockRule' => 'PhutilRemarkupEngineBlockRule', 'PhutilRemarkupEngineRemarkupHeaderBlockRule' => 'PhutilRemarkupEngineBlockRule', 'PhutilRemarkupEngineRemarkupInlineBlockRule' => 'PhutilRemarkupEngineBlockRule', 'PhutilRemarkupEngineRemarkupListBlockRule' => 'PhutilRemarkupEngineBlockRule', 'PhutilRemarkupEngineRemarkupLiteralBlockRule' => 'PhutilRemarkupEngineBlockRule', 'PhutilRemarkupEngineRemarkupNoteBlockRule' => 'PhutilRemarkupEngineBlockRule', 'PhutilRemarkupEngineRemarkupQuotesBlockRule' => 'PhutilRemarkupEngineBlockRule', 'PhutilRemarkupEngineTestCase' => 'ArcanistPhutilTestCase', 'PhutilRemarkupRuleBold' => 'PhutilRemarkupRule', 'PhutilRemarkupRuleEscapeHTML' => 'PhutilRemarkupRule', 'PhutilRemarkupRuleEscapeRemarkup' => 'PhutilRemarkupRule', 'PhutilRemarkupRuleHyperlink' => 'PhutilRemarkupRule', 'PhutilRemarkupRuleItalic' => 'PhutilRemarkupRule', 'PhutilRemarkupRuleLinebreaks' => 'PhutilRemarkupRule', 'PhutilRemarkupRuleMonospace' => 'PhutilRemarkupRule', 'PhutilSaturateStdoutDaemon' => 'PhutilTortureTestDaemon', + 'PhutilSimpleOptionsTestCase' => 'ArcanistPhutilTestCase', 'PhutilTortureTestDaemon' => 'PhutilDaemon', 'PhutilURITestCase' => 'ArcanistPhutilTestCase', 'PhutilUTF8TestCase' => 'ArcanistPhutilTestCase', 'PhutilUtilsTestCase' => 'ArcanistPhutilTestCase', 'PhutilXHPASTSyntaxHighlighterTestCase' => 'ArcanistPhutilTestCase', 'TestAbstractDirectedGraph' => 'AbstractDirectedGraph', 'XHPASTNode' => 'AASTNode', 'XHPASTToken' => 'AASTToken', 'XHPASTTree' => 'AASTTree', 'XHPASTTreeTestCase' => 'ArcanistPhutilTestCase', ), 'requires_interface' => array( ), )); diff --git a/src/markup/engine/remarkup/__tests__/data/block-then-list.txt b/src/markup/engine/remarkup/__tests__/data/block-then-list.txt index ea300d1d..d6bacae1 100644 --- a/src/markup/engine/remarkup/__tests__/data/block-then-list.txt +++ b/src/markup/engine/remarkup/__tests__/data/block-then-list.txt @@ -1,7 +1,7 @@ code block - still a code block ~~~~~~~~~~ -code block +
code block -- still a code block \ No newline at end of file +- still a code block
\ No newline at end of file diff --git a/src/markup/engine/remarkup/__tests__/data/diff.txt b/src/markup/engine/remarkup/__tests__/data/diff.txt index 680d636e..9f0f86ad 100644 --- a/src/markup/engine/remarkup/__tests__/data/diff.txt +++ b/src/markup/engine/remarkup/__tests__/data/diff.txt @@ -1,23 +1,23 @@ here is a diff @@ derp derp @@ x y - + - x - y + z derp derp ~~~~~~~~~~

here is a diff

-@@ derp derp @@ +
@@ derp derp @@ x y - x - y -+ z ++ z

derp derp

\ No newline at end of file diff --git a/src/markup/engine/remarkup/__tests__/data/tick-block-multi.txt b/src/markup/engine/remarkup/__tests__/data/tick-block-multi.txt index 14ee4375..5152d490 100644 --- a/src/markup/engine/remarkup/__tests__/data/tick-block-multi.txt +++ b/src/markup/engine/remarkup/__tests__/data/tick-block-multi.txt @@ -1,12 +1,12 @@ ```code more code more code``` ~~~~~~~~~~ -code +
code more code -more code \ No newline at end of file +more code
\ No newline at end of file diff --git a/src/markup/engine/remarkup/__tests__/data/tick-block.txt b/src/markup/engine/remarkup/__tests__/data/tick-block.txt index 1363c749..31372c14 100644 --- a/src/markup/engine/remarkup/__tests__/data/tick-block.txt +++ b/src/markup/engine/remarkup/__tests__/data/tick-block.txt @@ -1,3 +1,3 @@ ```code``` ~~~~~~~~~~ -code \ No newline at end of file +
code
\ No newline at end of file diff --git a/src/markup/engine/remarkup/blockrule/remarkupcode/PhutilRemarkupEngineRemarkupCodeBlockRule.php b/src/markup/engine/remarkup/blockrule/remarkupcode/PhutilRemarkupEngineRemarkupCodeBlockRule.php index 2e9c8194..1b7ae129 100644 --- a/src/markup/engine/remarkup/blockrule/remarkupcode/PhutilRemarkupEngineRemarkupCodeBlockRule.php +++ b/src/markup/engine/remarkup/blockrule/remarkupcode/PhutilRemarkupEngineRemarkupCodeBlockRule.php @@ -1,125 +1,166 @@ getBlockPattern(), $block)) { return false; } if (preg_match('@^[a-z]+://\S+$@', trim($block))) { return false; } return true; } public function shouldContinueWithBlock($block, $last_block) { // If the first code block begins with ```, we keep matching blocks until // we hit a terminating ```, regardless of their content. if (preg_match('/^```/', $last_block)) { if (preg_match('/```$/', $last_block)) { return false; } return true; } // If we just matched a code block based on indentation, always match the // next block if it is indented, too. This basically means that we'll treat // lists after code blocks as more code, but usually the "-" is from a diff // or from objective C or something; it is rare to intentionally follow a // code block with a list. if (preg_match('/^\s{2,}/', $block)) { return true; } return false; } public function shouldMergeBlocks() { return true; } public function markupText($text) { if (preg_match('/^```/', $text)) { // If this is a ```-style block, trim off the backticks. $text = preg_replace('/```\s*$/', '', substr($text, 3)); } $lines = explode("\n", $text); $lang = nonempty( $this->getEngine()->getConfig('phutil.codeblock.language-default'), 'php'); - $aux_class = ''; - do { - $first_line = reset($lines); - - $matches = null; - if (preg_match('/^\s{2,}lang\s*=\s*(.*)$/i', $first_line, $matches)) { - $lang = $matches[1]; - array_shift($lines); - continue; + $options = array( + 'counterexample' => false, + 'lang' => $lang, + 'name' => null, + 'lines' => null, + ); + + $custom = PhutilSimpleOptions::parse(head($lines)); + if ($custom) { + $valid = true; + foreach ($custom as $key => $value) { + if (!array_key_exists($key, $options)) { + $valid = false; + break; + } } - - if (preg_match('/^\s{2,}COUNTEREXAMPLE$/i', $first_line, $matches)) { - $aux_class = ' remarkup-counterexample'; + if ($valid) { array_shift($lines); - continue; + $options = $custom + $options; } + } - } while (false); + if ($options['counterexample']) { + $aux_class = ' remarkup-counterexample'; + } else { + $aux_class = null; + } // Normalize the text back to a 0-level indent. $min_indent = 80; foreach ($lines as $line) { for ($ii = 0; $ii < strlen($line); $ii++) { if ($line[$ii] != ' ') { $min_indent = min($ii, $min_indent); break; } } } + if ($min_indent) { $indent_string = str_repeat(' ', $min_indent); $text = preg_replace( '/^'.$indent_string.'/m', '', implode("\n", $lines)); + } else { + $text = implode("\n", $lines); + } + + $name_header = null; + if ($options['name']) { + $name_header = phutil_render_tag( + 'div', + array( + 'class' => 'remarkup-code-header', + ), + phutil_escape_html($options['name'])); + } + + $aux_style = null; + if ($options['lines']) { + // Put a minimum size on this because the scrollbar is otherwise + // unusable. + $height = max(6, (int)$options['lines']); + $aux_style = 'max-height: '.(2 * $height).'em;'; } $engine = new PhutilDefaultSyntaxHighlighterEngine(); $engine->setConfig( 'pygments.enabled', $this->getEngine()->getConfig('pygments.enabled')); - return - ''. - $engine->highlightSource($lang, $text). - ''; + $code_body = phutil_render_tag( + 'code', + array( + 'class' => 'remarkup-code'.$aux_class, + 'style' => $aux_style, + ), + $engine->highlightSource($options['lang'], $text)); + + return phutil_render_tag( + 'div', + array( + 'class' => 'remarkup-code-block', + ), + $name_header.$code_body); + } } diff --git a/src/markup/engine/remarkup/blockrule/remarkupcode/__init__.php b/src/markup/engine/remarkup/blockrule/remarkupcode/__init__.php index bebc0745..b35222e2 100644 --- a/src/markup/engine/remarkup/blockrule/remarkupcode/__init__.php +++ b/src/markup/engine/remarkup/blockrule/remarkupcode/__init__.php @@ -1,14 +1,16 @@ '4', + * 'eyes' => '2', + * ); + * + * @param string Input option list. + * @return dict Parsed dictionary. + * @task parse + */ + public static function parse($input) { + $result = array(); + + $vars = explode(',', $input); + foreach ($vars as $var) { + if (strpos($var, '=') !== false) { + list($key, $value) = explode('=', $var, 2); + $value = trim($value); + } else { + list($key, $value) = array($var, true); + } + $key = trim($key); + $key = strtolower($key); + if (!self::isValidKey($key)) { + continue; + } + if (!strlen($value)) { + unset($result[$key]); + continue; + } + $result[$key] = $value; + } + + return $result; + } + + +/* -( Unparsing Simple Options )------------------------------------------- */ + + + /** + * Convert a dictionary into a simple option list. For example: + * + * array( + * 'legs' => '4', + * 'eyes' => '2', + * ); + * + * ...becomes: + * + * legs=4, eyes=2 + * + * @param dict Input dictionary. + * @return string Unparsed option list. + */ + public static function unparse(array $options) { + $result = array(); + foreach ($options as $name => $value) { + if (!self::isValidKey($name)) { + throw new Exception( + "SimpleOptions: keys must contain only lowercase letters."); + } + if (!strlen($value)) { + continue; + } + if ($value === true) { + $result[] = $name; + } else { + $result[] = $name.'='.$value; + } + } + return implode(', ', $result); + } + + +/* -( Internals )---------------------------------------------------------- */ + + + private static function isValidKey($key) { + return (bool)preg_match('/^[a-z]+$/', $key); + } + +} diff --git a/src/parser/simpleoptions/__init__.php b/src/parser/simpleoptions/__init__.php new file mode 100644 index 00000000..80cd4e4f --- /dev/null +++ b/src/parser/simpleoptions/__init__.php @@ -0,0 +1,10 @@ + array(), + + // Basic parsing. + 'legs=4' => array('legs' => '4'), + 'legs=4,eyes=2' => array('legs' => '4', 'eyes' => '2'), + + // Repeated keys mean last specification wins. + 'legs=4,legs=3' => array('legs' => '3'), + + // Keys with no value should map to true. + 'flag' => array('flag' => true), + 'legs=4,flag' => array('legs' => '4', 'flag' => true), + + // Spaces should be ignored. + ' flag ' => array('flag' => true), + ' legs = 4 , eyes = 2' => array('legs' => '4', 'eyes' => '2'), + + // Case should be ignored. + 'LEGS=4' => array('legs' => '4'), + 'legs=4, LEGS=4' => array('legs' => '4'), + + // Empty values should be absent. + 'legs=' => array(), + 'legs=4,legs=,eyes=2' => array('eyes' => '2'), + ); + + foreach ($map as $string => $expect) { + $this->assertEqual( + $expect, + PhutilSimpleOptions::parse($string), + "Correct parse of '{$string}'"); + } + } + + public function testSimpleOptionsUnparse() { + $map = array( + '' => array(), + 'legs=4' => array('legs' => '4'), + 'legs=4, eyes=2' => array('legs' => '4', 'eyes' => '2'), + 'eyes=2, legs=4' => array('eyes' => '2', 'legs' => '4'), + 'legs=4, head' => array('legs' => '4', 'head' => true), + 'eyes=2' => array('legs' => '', 'eyes' => '2'), + ); + + foreach ($map as $expect => $dict) { + $this->assertEqual( + $expect, + PhutilSimpleOptions::unparse($dict), + "Correct unparse of ".print_r($dict, true)); + } + + $bogus = array( + array('LEGS' => true), + array('LEGS' => 4), + array('!' => '!'), + array('' => '2'), + ); + + foreach ($bogus as $bad_input) { + $caught = null; + try { + PhutilSimpleOptions::unparse($bad_input); + } catch (Exception $ex) { + $caught = $ex; + } + $this->assertEqual( + true, + $caught instanceof Exception, + "Correct throw on unparse of '{$bad_input}'"); + } + } + +} diff --git a/src/parser/simpleoptions/__tests__/__init__.php b/src/parser/simpleoptions/__tests__/__init__.php new file mode 100644 index 00000000..536586cc --- /dev/null +++ b/src/parser/simpleoptions/__tests__/__init__.php @@ -0,0 +1,14 @@ +