Skip to content

Fix failures on long CRAM test scripts with internal commands that can consume stdin - #48

Open
nmerinov wants to merge 1 commit into
aiiie:mainfrom
nmerinov:stdin-consumers-in-tests
Open

Fix failures on long CRAM test scripts with internal commands that can consume stdin#48
nmerinov wants to merge 1 commit into
aiiie:mainfrom
nmerinov:stdin-consumers-in-tests

Conversation

@nmerinov

@nmerinov nmerinov commented Jul 28, 2026

Copy link
Copy Markdown

During work on CRAM-based testsuite in prplFoundation we observed the issue that CRAM tests where text for the commands longer than 8KB can fail with error messages like the following:

+  /bin/sh: 72: Syntax error: Unterminated quoted string (no-eol)

After investigation we found that failures related to the commands that can try to read from stdin if there are any data available in stdin. You can find minimal example in the provided tests/stdin-consumers-in-test.t.

With current CRAM implementation this test fails with the following error:

--- tests/stdin-consumers-in-test.t
+++ tests/stdin-consumers-in-test.t.err
@@ -71,30 +71,5 @@
   $ consume ; printf '%s\n' "chunk-0034-alpha-bravo-charlie-delta-echo-foxtrot-golf-hotel-india-juliet-kilo-lima-mike-november-oscar-papa-quebec-romeo-sierra-tango-uniform-victor-whiskey-xray-yankee-zulu"
   chunk-0034-alpha-bravo-charlie-delta-echo-foxtrot-golf-hotel-india-juliet-kilo-lima-mike-november-oscar-papa-quebec-romeo-sierra-tango-uniform-victor-whiskey-xray-yankee-zulu
   $ consume ; printf '%s\n' "chunk-0035-alpha-bravo-charlie-delta-echo-foxtrot-golf-hotel-india-juliet-kilo-lima-mike-november-oscar-papa-quebec-romeo-sierra-tango-uniform-victor-whiskey-xray-yankee-zulu"
-  chunk-0035-alpha-bravo-charlie-delta-echo-foxtrot-golf-hotel-india-juliet-kilo-lima-mike-november-oscar-papa-quebec-romeo-sierra-tango-uniform-victor-whiskey-xray-yankee-zulu
+  /bin/sh: 72: Syntax error: Unterminated quoted string (no-eol)
   $ consume ; printf '%s\n' "chunk-0036-alpha-bravo-charlie-delta-echo-foxtrot-golf-hotel-india-juliet-kilo-lima-mike-november-oscar-papa-quebec-romeo-sierra-tango-uniform-victor-whiskey-xray-yankee-zulu"
-  chunk-0036-alpha-bravo-charlie-delta-echo-foxtrot-golf-hotel-india-juliet-kilo-lima-mike-november-oscar-papa-quebec-romeo-sierra-tango-uniform-victor-whiskey-xray-yankee-zulu
-  $ consume ; printf '%s\n' "chunk-0037-alpha-bravo-charlie-delta-echo-foxtrot-golf-hotel-india-juliet-kilo-lima-mike-november-oscar-papa-quebec-romeo-sierra-tango-uniform-victor-whiskey-xray-yankee-zulu"
-  chunk-0037-alpha-bravo-charlie-delta-echo-foxtrot-golf-hotel-india-juliet-kilo-lima-mike-november-oscar-papa-quebec-romeo-sierra-tango-uniform-victor-whiskey-xray-yankee-zulu
-  $ consume ; printf '%s\n' "chunk-0038-alpha-bravo-charlie-delta-echo-foxtrot-golf-hotel-india-juliet-kilo-lima-mike-november-oscar-papa-quebec-romeo-sierra-tango-uniform-victor-whiskey-xray-yankee-zulu"
-  chunk-0038-alpha-bravo-charlie-delta-echo-foxtrot-golf-hotel-india-juliet-kilo-lima-mike-november-oscar-papa-quebec-romeo-sierra-tango-uniform-victor-whiskey-xray-yankee-zulu
-  $ consume ; printf '%s\n' "chunk-0039-alpha-bravo-charlie-delta-echo-foxtrot-golf-hotel-india-juliet-kilo-lima-mike-november-oscar-papa-quebec-romeo-sierra-tango-uniform-victor-whiskey-xray-yankee-zulu"
-  chunk-0039-alpha-bravo-charlie-delta-echo-foxtrot-golf-hotel-india-juliet-kilo-lima-mike-november-oscar-papa-quebec-romeo-sierra-tango-uniform-victor-whiskey-xray-yankee-zulu
-  $ consume ; printf '%s\n' "chunk-0040-alpha-bravo-charlie-delta-echo-foxtrot-golf-hotel-india-juliet-kilo-lima-mike-november-oscar-papa-quebec-romeo-sierra-tango-uniform-victor-whiskey-xray-yankee-zulu"
-  chunk-0040-alpha-bravo-charlie-delta-echo-foxtrot-golf-hotel-india-juliet-kilo-lima-mike-november-oscar-papa-quebec-romeo-sierra-tango-uniform-victor-whiskey-xray-yankee-zulu
-  $ consume ; printf '%s\n' "chunk-0041-alpha-bravo-charlie-delta-echo-foxtrot-golf-hotel-india-juliet-kilo-lima-mike-november-oscar-papa-quebec-romeo-sierra-tango-uniform-victor-whiskey-xray-yankee-zulu"
-  chunk-0041-alpha-bravo-charlie-delta-echo-foxtrot-golf-hotel-india-juliet-kilo-lima-mike-november-oscar-papa-quebec-romeo-sierra-tango-uniform-victor-whiskey-xray-yankee-zulu
-  $ consume ; printf '%s\n' "chunk-0042-alpha-bravo-charlie-delta-echo-foxtrot-golf-hotel-india-juliet-kilo-lima-mike-november-oscar-papa-quebec-romeo-sierra-tango-uniform-victor-whiskey-xray-yankee-zulu"
-  chunk-0042-alpha-bravo-charlie-delta-echo-foxtrot-golf-hotel-india-juliet-kilo-lima-mike-november-oscar-papa-quebec-romeo-sierra-tango-uniform-victor-whiskey-xray-yankee-zulu
-  $ consume ; printf '%s\n' "chunk-0043-alpha-bravo-charlie-delta-echo-foxtrot-golf-hotel-india-juliet-kilo-lima-mike-november-oscar-papa-quebec-romeo-sierra-tango-uniform-victor-whiskey-xray-yankee-zulu"
-  chunk-0043-alpha-bravo-charlie-delta-echo-foxtrot-golf-hotel-india-juliet-kilo-lima-mike-november-oscar-papa-quebec-romeo-sierra-tango-uniform-victor-whiskey-xray-yankee-zulu
-  $ consume ; printf '%s\n' "chunk-0044-alpha-bravo-charlie-delta-echo-foxtrot-golf-hotel-india-juliet-kilo-lima-mike-november-oscar-papa-quebec-romeo-sierra-tango-uniform-victor-whiskey-xray-yankee-zulu"
-  chunk-0044-alpha-bravo-charlie-delta-echo-foxtrot-golf-hotel-india-juliet-kilo-lima-mike-november-oscar-papa-quebec-romeo-sierra-tango-uniform-victor-whiskey-xray-yankee-zulu
-  $ consume ; printf '%s\n' "chunk-0045-alpha-bravo-charlie-delta-echo-foxtrot-golf-hotel-india-juliet-kilo-lima-mike-november-oscar-papa-quebec-romeo-sierra-tango-uniform-victor-whiskey-xray-yankee-zulu"
-  chunk-0045-alpha-bravo-charlie-delta-echo-foxtrot-golf-hotel-india-juliet-kilo-lima-mike-november-oscar-papa-quebec-romeo-sierra-tango-uniform-victor-whiskey-xray-yankee-zulu
-  $ consume ; printf '%s\n' "chunk-0046-alpha-bravo-charlie-delta-echo-foxtrot-golf-hotel-india-juliet-kilo-lima-mike-november-oscar-papa-quebec-romeo-sierra-tango-uniform-victor-whiskey-xray-yankee-zulu"
-  chunk-0046-alpha-bravo-charlie-delta-echo-foxtrot-golf-hotel-india-juliet-kilo-lima-mike-november-oscar-papa-quebec-romeo-sierra-tango-uniform-victor-whiskey-xray-yankee-zulu
-  $ consume ; printf '%s\n' "chunk-0047-alpha-bravo-charlie-delta-echo-foxtrot-golf-hotel-india-juliet-kilo-lima-mike-november-oscar-papa-quebec-romeo-sierra-tango-uniform-victor-whiskey-xray-yankee-zulu"
-  chunk-0047-alpha-bravo-charlie-delta-echo-foxtrot-golf-hotel-india-juliet-kilo-lima-mike-november-oscar-papa-quebec-romeo-sierra-tango-uniform-victor-whiskey-xray-yankee-zulu
-  $ consume ; printf '%s\n' "chunk-0048-alpha-bravo-charlie-delta-echo-foxtrot-golf-hotel-india-juliet-kilo-lima-mike-november-oscar-papa-quebec-romeo-sierra-tango-uniform-victor-whiskey-xray-yankee-zulu"
-  chunk-0048-alpha-bravo-charlie-delta-echo-foxtrot-golf-hotel-india-juliet-kilo-lima-mike-november-oscar-papa-quebec-romeo-sierra-tango-uniform-victor-whiskey-xray-yankee-zulu

Root cause of the issue

Currently CRAM execute all tests in the following manner:

  1. Open /bin/sh process and send all commands to it's stdin.
  2. Due to the internal implementation shell does not read all input at once, but instead it read only several KB of input from stdin.
  3. When shell evaluates initial part of input it will read rest data from stdin.
  4. Unfortunately each command, evaluated by shell, could "steal" part of the remaining data from stdin for their own needs. This situation can be easily found for commands that awaits user input, but part of shell applications can read stdin only if data in stdin available immediately.

Suggested solution

In order to solve the issue with such commands we suggest to send commands to shell through shell script instead of piping it to stdin.

This made with new arguments cram._process.execute() function. New execute() implementation can get either script=b'commands' argument for test from cram/_test.py, or stdin=b'patch text' argument for usage from cram/_cli.py.

Some of applications could consume data from stdin, when stdin is
not empty. As the result, when we trying to pass shell script through
stdin of shell process some data can be processed by such consumers
instead of desired shell process.

In order to solve this provide the following changes:
* cram/_process.py: Add new named "script" argument for the "execute()"
  function. Users can pass arguments either as "script" or as "stdin".
  The "stdin" capability preserved to run "patch" utility from "cram/_cli.py".
* cram/_test.py: Run tests through "script" option instead of "stdin".
  This change required to make sure that internal test commands can't
  steal data targeted for shell process.
* tests/debug.t: Replace test option from "-s" to "-x", because now
  test commands passed through file, not through stdin.
* tests/consume-stdin.py: Test utility that can steal data from stdin
  if it available.
* tests/stdin-consumers-in-test.t: Create trivial test which contains
  more than 8KB of commands, to be sure that shell will not read all of
  them from stdin at once. On each command run the "consume-stdin.py"
  script in order to steal remainging data from stdin before shell
  will read them for own execution.

Signed-off-by: Nikolai Merinov <n.merinov@inango-systems.com>
Reported-by: Petr Štetiar <petr.stetiar@prplfoundation.org>
Comment thread tests/debug.t
Comment on lines +1 to +2

et up cram alias and example tests:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why change this?

@@ -0,0 +1,100 @@
Exercise cram script with determenistic output that consumes extra stdin if any after each command

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Exercise cram script with determenistic output that consumes extra stdin if any after each command
Exercise cram script with deterministic output that consumes extra stdin if any after each command

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why so many test case? What's the benefit?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants