Commit 762b0c401a5 for php.net
commit 762b0c401a53935c1a9333f97e310e3eef3e0063
Author: Weilin Du <weilindu@php.net>
Date: Wed Aug 5 00:38:56 2026 +0800
ext/readline: write test for readline CLI (#22994)
This commit offers basic behavior tests for ext/readline/readline_cli.c
diff --git a/ext/readline/tests/readline_cli_auto_prepend.phpt b/ext/readline/tests/readline_cli_auto_prepend.phpt
new file mode 100644
index 00000000000..71d4477a7cd
--- /dev/null
+++ b/ext/readline/tests/readline_cli_auto_prepend.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Interactive shell: auto_prepend_file is executed before input
+--EXTENSIONS--
+readline
+--SKIPIF--
+<?php
+if (!function_exists('proc_open')) die('skip proc_open() not available');
+?>
+--FILE--
+<?php
+$prepend = tempnam(sys_get_temp_dir(), 'readline_cli_prepend');
+file_put_contents($prepend, <<<'PHP'
+<?php
+echo "prepended\n";
+define('READLINE_CLI_PREPENDED', 'ok');
+readline_completion_function(static fn() => []);
+PHP);
+
+$php = getenv('TEST_PHP_EXECUTABLE_ESCAPED');
+$ini = getenv('TEST_PHP_EXTRA_ARGS');
+$descriptorspec = [['pipe', 'r'], STDOUT, STDERR];
+$proc = proc_open("$php $ini -d auto_prepend_file=" . escapeshellarg($prepend) . " -a", $descriptorspec, $pipes);
+fwrite($pipes[0], "echo READLINE_CLI_PREPENDED . \"\n\";\n");
+fwrite($pipes[0], "exit\n");
+fclose($pipes[0]);
+proc_close($proc);
+unlink($prepend);
+?>
+--EXPECTF--
+%AInteractive shell%Aprepended%Aok%A
diff --git a/ext/readline/tests/readline_cli_completion_readline.phpt b/ext/readline/tests/readline_cli_completion_readline.phpt
new file mode 100644
index 00000000000..cd4cbe14834
--- /dev/null
+++ b/ext/readline/tests/readline_cli_completion_readline.phpt
@@ -0,0 +1,37 @@
+--TEST--
+Interactive shell: default completion function
+--EXTENSIONS--
+readline
+--SKIPIF--
+<?php
+if (READLINE_LIB !== "readline") die('skip readline only');
+if (!function_exists('shell_exec')) die('skip shell_exec() not available');
+?>
+--FILE--
+<?php
+$php = getenv('TEST_PHP_EXECUTABLE_ESCAPED');
+$ini = getenv('TEST_PHP_EXTRA_ARGS');
+
+putenv('TERM=VT100');
+
+$code = <<<'PHP'
+$readline_cli_completion_variable = strtolower("VARIABLE_OK\n");
+echo $readline_cli_completion_var ;
+#prec 3
+echo "precision_result=" . ini_get("precision") . "\n";
+function readline_cli_completion_function() { echo strtolower("FUNCTION_OK\n"); }
+readline_cli_completion_fun );
+define('READLINE_CLI_COMPLETION_CONSTANT', strtolower("CONSTANT_OK\n"));
+echo READLINE_CLI_COMPLETION_CON ;
+class ReadlineCliCompletionClass {
+ public static function completionMethod() { echo strtolower("METHOD_OK\n"); }
+}
+echo "class_ok:" . ReadlineCliCompletionCla ::class . "\n";
+ReadlineCliCompletionClass::completionM );
+exit
+PHP;
+
+echo shell_exec("echo " . escapeshellarg($code) . " | $php $ini -a");
+?>
+--EXPECTF--
+%AInteractive shell%Avariable_ok%Aprecision_result=3%Afunction_ok%Aconstant_ok%Aclass_ok:ReadlineCliCompletionClass%Amethod_ok%A
diff --git a/ext/readline/tests/readline_cli_long_input.phpt b/ext/readline/tests/readline_cli_long_input.phpt
new file mode 100644
index 00000000000..a74850aef2d
--- /dev/null
+++ b/ext/readline/tests/readline_cli_long_input.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Interactive shell: input buffer grows for long lines
+--EXTENSIONS--
+readline
+--SKIPIF--
+<?php
+if (!function_exists('proc_open')) die('skip proc_open() not available');
+?>
+--FILE--
+<?php
+$php = getenv('TEST_PHP_EXECUTABLE_ESCAPED');
+$ini = getenv('TEST_PHP_EXTRA_ARGS');
+$descriptorspec = [['pipe', 'r'], STDOUT, STDERR];
+$proc = proc_open("$php $ini -a", $descriptorspec, $pipes);
+$long = str_repeat('x', 5000);
+fwrite($pipes[0], 'echo strlen("' . $long . '") . "\n";' . "\n");
+fwrite($pipes[0], "exit\n");
+fclose($pipes[0]);
+proc_close($proc);
+?>
+--EXPECTF--
+%AInteractive shell%A5000%A
diff --git a/ext/readline/tests/readline_cli_multiline_states.phpt b/ext/readline/tests/readline_cli_multiline_states.phpt
new file mode 100644
index 00000000000..1aaea49ac96
--- /dev/null
+++ b/ext/readline/tests/readline_cli_multiline_states.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Interactive shell: multiline input states
+--EXTENSIONS--
+readline
+--SKIPIF--
+<?php
+if (!function_exists('proc_open')) die('skip proc_open() not available');
+?>
+--FILE--
+<?php
+$php = getenv('TEST_PHP_EXECUTABLE_ESCAPED');
+$ini = getenv('TEST_PHP_EXTRA_ARGS');
+$descriptorspec = [['pipe', 'r'], STDOUT, STDERR];
+$proc = proc_open("$php $ini -a", $descriptorspec, $pipes);
+
+fwrite($pipes[0], "\n");
+fwrite($pipes[0], "echo strtoupper('single \\' semi ; brace }\nend');\n");
+fwrite($pipes[0], "echo strtoupper(\"double \\\" semi ; brace }\nend\");\n");
+fwrite($pipes[0], "echo strtoupper(\n\"paren\"\n);\n");
+fwrite($pipes[0], "echo \"arithmetic=\", 6 /\n2 + 100;\n");
+fwrite($pipes[0], "if (true) {\necho strtoupper(\"block_body\n\");\n}\n");
+fwrite($pipes[0], "echo strtoupper(\n\"hash_comment\\n\"\n# ) ;\n);\n");
+fwrite($pipes[0], "echo strtoupper(\n\"slash_comment\\n\"\n// ) ;\n);\n");
+fwrite($pipes[0], "echo strtoupper(\n\"block_comment\\n\" /*\n) ;\n*/\n);\n");
+fwrite($pipes[0], "#[AllowDynamicProperties]\nclass ReadlineCliCoverageClass {}\n");
+fwrite($pipes[0], "echo strtoupper((new ReflectionClass(ReadlineCliCoverageClass::class))->getAttributes()[0]->getName()), \"\\n\";\n");
+fwrite($pipes[0], "if (true) ?>outside } );\n<?php\necho strtoupper(\"outside_ok\\n\");\n");
+fwrite($pipes[0], "quit\n");
+fclose($pipes[0]);
+proc_close($proc);
+?>
+--EXPECTF--
+%AInteractive shell%ASINGLE%ADOUBLE%APAREN%Aarithmetic=103%ABLOCK_BODY%AHASH_COMMENT%ASLASH_COMMENT%ABLOCK_COMMENT%AALLOWDYNAMICPROPERTIES%AOUTSIDE_OK%A
diff --git a/ext/readline/tests/readline_cli_pager.phpt b/ext/readline/tests/readline_cli_pager.phpt
new file mode 100644
index 00000000000..2d530947232
--- /dev/null
+++ b/ext/readline/tests/readline_cli_pager.phpt
@@ -0,0 +1,29 @@
+--TEST--
+Interactive shell: output through cli.pager
+--EXTENSIONS--
+readline
+--SKIPIF--
+<?php
+if (!function_exists('proc_open')) die('skip proc_open() not available');
+if (READLINE_LIB !== "readline") die('skip readline only');
+if (PHP_OS_FAMILY === 'Windows') die('skip tr pager is not portable on Windows');
+?>
+--FILE--
+<?php
+$php = getenv('TEST_PHP_EXECUTABLE_ESCAPED');
+$ini = getenv('TEST_PHP_EXTRA_ARGS');
+$descriptorspec = [['pipe', 'r'], STDOUT, STDERR];
+$proc = proc_open("$php $ini -d cli.pager='tr a-z A-Z' -a", $descriptorspec, $pipes);
+fwrite($pipes[0], "echo \"pager output\n\";\n");
+fwrite($pipes[0], "quit\n");
+fclose($pipes[0]);
+proc_close($proc);
+?>
+--EXPECT--
+Interactive shell
+
+php > echo "pager output
+php " ";
+pager output
+PAGER OUTPUT
+php > quit
diff --git a/ext/readline/tests/readline_cli_prompt.phpt b/ext/readline/tests/readline_cli_prompt.phpt
new file mode 100644
index 00000000000..ddb5296cf94
--- /dev/null
+++ b/ext/readline/tests/readline_cli_prompt.phpt
@@ -0,0 +1,43 @@
+--TEST--
+Interactive shell: custom prompt escape sequences
+--EXTENSIONS--
+readline
+--SKIPIF--
+<?php
+if (READLINE_LIB !== "readline") die('skip readline only');
+if (!function_exists('proc_open')) die('skip proc_open() not available');
+?>
+--FILE--
+<?php
+$php = getenv('TEST_PHP_EXECUTABLE_ESCAPED');
+$ini = getenv('TEST_PHP_EXTRA_ARGS');
+$prompt = 'pre\\\\-\n-\t-\e-\v-\b-\>-\`-\q-' . "\xC3\xA9\xC3\xA9" . '-`echo \'dyn\';`-`-x ';
+$descriptorspec = [['pipe', 'r'], STDOUT, STDERR];
+$proc = proc_open("$php $ini -d " . escapeshellarg("cli.prompt=\"$prompt\"") . " -a", $descriptorspec, $pipes);
+fwrite($pipes[0], "if (true) {\n");
+fwrite($pipes[0], "echo strtoupper(\"prompt_body\\n\");\n");
+fwrite($pipes[0], "}\n");
+fwrite($pipes[0], "quit\n");
+fclose($pipes[0]);
+proc_close($proc);
+?>
+--EXPECTF--
+Interactive shell
+
+
+Warning: prompt contains unsupported unicode characters in Unknown on line 0
+dynpre\-
+- -%c-%d.%d.%s-php->-`-\q-????-dyn--x if (true) {
+
+Warning: prompt contains unsupported unicode characters in Unknown on line 0
+dynpre\-
+- -%c-%d.%d.%s-php-{-`-\q-????-dyn--x echo strtoupper("prompt_body\n");
+
+Warning: prompt contains unsupported unicode characters in Unknown on line 0
+dynpre\-
+- -%c-%d.%d.%s-php-{-`-\q-????-dyn--x }
+
+Warning: prompt contains unsupported unicode characters in Unknown on line 0
+dynPROMPT_BODY
+pre\-
+- -%c-%d.%d.%s-php->-`-\q-????-dyn--x quit