[Avocado-devel] RFC: avocado.utils.process (or, how to handle the split stdout/stderr recording)
Jeff Nelson
jen at redhat.com
Thu Nov 9 15:30:27 UTC 2017
On Wed, 8 Nov 2017 10:01:22 -0500
Cleber Rosa <crosa at redhat.com> wrote:
> Hi everyone,
>
> This is kind of brainstorm about the really annoying issue we've trying
> to deal with in Avocado. The root problem we're trying to fix is that
> Avocado's "output check" feature can not be used when the test's
> generated content combines both stdout and stderr in a single file. One
> example is QEMU's iotests[1].
>
> The root cause of this limitation is that avocado.utils.process.run()[2]
> (because of avocado.utils.process.SubProcess[3]) decided a long time ago
> to split the content of a process STDOUT and STDERR to separate files
> (and different logging streams). This is reflected on Avocado's "output
> check" feature[4]. Users can choose to record the test's generated
> STDOUT, STDERR, both (all) or none of them. This is how the command
> line options look like:
>
> --output-check-record {none,all,stdout,stderr}
> Record output streams of your tests to reference files
> (valid options: none (do not record output streams),
> all (record both stdout and stderr), stdout (record
> only stderr), stderr (record only stderr). Current:
> none
>
> Which map to the following recorded files:
>
> +----------+----------------------------+--------------------+
> | OPTION | RECORDED FILES | CONTENT |
> +----------+----------------------------+--------------------+
> | none | | |
> +----------+----------------------------+--------------------+
> | | stdout | process FD 1 |
> + all +----------------------------+--------------------+
> | | stderr | process FD 2 |
> +----------+----------------------------+--------------------+
> | stdout | stdout | process FD 1 |
> +----------+----------------------------+--------------------+
> | stderr | stderr | process FD 2 |
> +----------+----------------------------+--------------------+
>
> Notice how the "all" option still creates two separate files, with
> content coming from individual file descriptors. Adding another record
> option would actually be pretty simple, that is, something like:
>
> +----------+----------------------------+--------------------+
> | OPTION | RECORDED FILES | CONTENT |
> +----------+----------------------------+--------------------+
> | combined | combined_stdout_stderr | process FD 1 and 2 |
> +----------+----------------------------+--------------------+
>
> What is *not* easily possible, is to have two options such as "all" and
> "combined" at the same time. The reason is that it the order of the
> content written to "combined_stdout_stderr" can not be guaranteed to be
> correct.
>
> I've attempted to allow for both options to be used at once, by using a
> pair of file-like objects with specialized writes that would either
> share single list of records or just share a secondary file with a lock
> (pseudo/prototype code):
>
> class TeeFile(file):
> def __init__(self, path, tee_path, tee_lock, mode='a'):
> super(TeeFile, self).__init__(path, mode)
> self._tee_path = tee_path
> self._tee_lock = tee_lock
>
> def write(self, record):
> self.tee_lock.acquire()
> super(TeeFile, self).write(record)
> with open(self._tee_path, 'a') as tee_file:
> tee_file.write(record)
> tee_file.flush()
> self.tee_lock.release()
>
> But the fact that the Python standard library "subprocess.Popen"
> implementation works with file descriptors doesn't make it possible to
> implement a shared/synchronized write strategy. Rewriting parts of
> "subprocess.Popen" or "avocado.utils.process.SubProcess" is, IMO, a
> clear sign to not attempt something way more complicated and error prone
> for the sake of this feature.
Agreed!
> Accepting the fact that either combined (stdout+stderr) or a split
> (stdout, stderr) will be available, the point of option names comes into
> play. Both "stdout", "stderr" and "none" are aptly named. But, with
> the introduction of a third *real* output record option/file, "all"
> becomes a rather bad option name.
>
> To keep things simple, I'd add an alias from "all" to "both",
> deprecating the former. The new option would be named either "combined"
> and the generated file named "combined_stdout_stderr" by default. It's
> a rather verbose name, but I'd go with explicit option names rather than
> confusing ones.
>
> Comments?
I think this is a reasonable approach; drop "all" and use "both" to
indicate stdout and stderr. The generated file name looks fine.
Someone looking at these options (without knowing the history) will
see: none, both, combined, stdout and stderr. This looks OK, once they
understand the difference between both and combined.
-Jeff
> --
>
> [1] -
> https://git.qemu.org/?p=qemu.git;a=blob;f=tests/qemu-iotests/check;h=e6b6ff7a047562099c2fb5f0fd35652780c9c006;hb=HEAD#l757
> [2] -
> http://avocado-framework.readthedocs.io/en/55.0/api/utils/avocado.utils.html#avocado.utils.process.run
> [3] -
> http://avocado-framework.readthedocs.io/en/55.0/api/utils/avocado.utils.html#avocado.utils.process.SubProcess
> [4] -
> http://avocado-framework.readthedocs.io/en/55.0/WritingTests.html#test-output-check-and-output-record-mode
>
More information about the Avocado-devel
mailing list