Move RunCommandExtractHashedFiles() to test_runner.py
Moves the logic for RunCommandExtractHashedFiles() to test_runner.py, as
_TestCaseRunner.TestPixel() is the only client. The new implementation
also leverages _TestCaseRunner.RunCommand() for better error handling.
Reusing RunCommand() required adding support for redirecting standard
output to an in-memory io.BytesIO.
Also makes pixel test output less noisy.
Fixed: pdfium:1964
Change-Id: I597ff6a54570885e89f444c4b283449cfed92c48
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/102910
Commit-Queue: K. Moon <kmoon@chromium.org>
Reviewed-by: Nigi <nigi@chromium.org>
diff --git a/testing/tools/common.py b/testing/tools/common.py
index 37f2ed3..80b7815 100755
--- a/testing/tools/common.py
+++ b/testing/tools/common.py
@@ -23,14 +23,6 @@
raise Exception('Confused, can not determine OS, aborting.')
-def RunCommand(cmd):
- try:
- subprocess.check_call(cmd)
- return None
- except subprocess.CalledProcessError as e:
- return e
-
-
def RunCommandPropagateErr(cmd,
stdout_has_errors=False,
exit_status_on_error=None):
@@ -63,25 +55,6 @@
return output
-# RunCommandExtractHashedFiles returns a tuple: (raised_exception, hashed_files)
-# It runs the given command. If it fails it will return an exception and None.
-# If it succeeds it will return None and the list of processed files extracted
-# from the output of the command. It expects lines in this format:
-# MD5:<path_to_image_file>:<md5_hash_in_hex>
-# The returned hashed_files is a list of (file_path, MD5-hash) pairs.
-def RunCommandExtractHashedFiles(cmd):
- try:
- output = subprocess.check_output(cmd, universal_newlines=True)
- ret = []
- for line in output.split('\n'):
- line = line.strip()
- if line.startswith("MD5:"):
- ret.append([x.strip() for x in line.lstrip("MD5:").rsplit(":", 1)])
- return None, ret
- except subprocess.CalledProcessError as e:
- return e, None
-
-
class DirectoryFinder:
'''A class for finding directories and paths under either a standalone
checkout or a chromium checkout of PDFium.'''
diff --git a/testing/tools/pngdiffer.py b/testing/tools/pngdiffer.py
index 4c371ea..2ab2a3f 100755
--- a/testing/tools/pngdiffer.py
+++ b/testing/tools/pngdiffer.py
@@ -8,10 +8,9 @@
import itertools
import os
import shutil
+import subprocess
import sys
-import common
-
class PathMode:
"""PathMode indicates the available expected results' paths.
@@ -80,16 +79,23 @@
break
return actual_paths
+ def _RunCommand(self, cmd):
+ try:
+ subprocess.run(cmd, capture_output=True, check=True)
+ return None
+ except subprocess.CalledProcessError as e:
+ return e
+
def _RunImageCompareCommand(self, image_diff):
cmd = [self.pdfium_diff_path]
if self.reverse_byte_order:
cmd.append('--reverse-byte-order')
cmd.extend([image_diff.actual_path, image_diff.expected_path])
- return common.RunCommand(cmd)
+ return self._RunCommand(cmd)
def _RunImageDiffCommand(self, image_diff):
# TODO(crbug.com/pdfium/1925): Diff mode ignores --reverse-byte-order.
- return common.RunCommand([
+ return self._RunCommand([
self.pdfium_diff_path, '--subtract', image_diff.actual_path,
image_diff.expected_path, image_diff.diff_path
])
diff --git a/testing/tools/test_runner.py b/testing/tools/test_runner.py
index 8835981..a065820 100644
--- a/testing/tools/test_runner.py
+++ b/testing/tools/test_runner.py
@@ -6,6 +6,7 @@
import argparse
from dataclasses import dataclass, field
from datetime import timedelta
+from io import BytesIO
import multiprocessing
import os
import re
@@ -555,7 +556,18 @@
Returns:
The test result.
"""
+
+ # Standard output and error are directed to the test log. If `stdout` was
+ # provided, redirect standard output to it instead.
if stdout:
+ assert stdout != subprocess.PIPE
+ try:
+ stdout.fileno()
+ except OSError:
+ # `stdout` doesn't have a file descriptor, so it can't be passed to
+ # `subprocess.run()` directly.
+ original_stdout = stdout
+ stdout = subprocess.PIPE
stderr = subprocess.PIPE
else:
stdout = subprocess.PIPE
@@ -578,8 +590,13 @@
test_result.status = result_types.TIMEOUT
test_result.reason = 'Command {} timed out'.format(run_result.cmd)
+ if stdout == subprocess.PIPE and stderr == subprocess.PIPE:
+ # Copy captured standard output to the original `stdout`.
+ original_stdout.write(run_result.stdout)
+
if not test_result.IsPass():
- if stdout == subprocess.PIPE:
+ # On failure, report captured output to the test log.
+ if stderr == subprocess.STDOUT:
test_result.log = run_result.stdout
else:
test_result.log = run_result.stderr
@@ -703,17 +720,20 @@
cmd_to_run.append(self.pdf_path)
- raised_exception, results = common.RunCommandExtractHashedFiles(cmd_to_run)
- if raised_exception:
- return self.test_case.NewResult(
- result_types.FAIL, reason=str(raised_exception))
+ with BytesIO() as command_output:
+ test_result = self.RunCommand(cmd_to_run, stdout=command_output)
+ if not test_result.IsPass():
+ return test_result
- test_result = self.test_case.NewResult(
- result_types.PASS,
- image_artifacts=[
- self._NewImageArtifact(image_path=image_path, md5_hash=md5_hash)
- for image_path, md5_hash in results
- ])
+ test_result.image_artifacts = []
+ for line in command_output.getvalue().splitlines():
+ # Expect this format: MD5:<path to image file>:<hexadecimal MD5 hash>
+ line = bytes.decode(line).strip()
+ if line.startswith('MD5:'):
+ image_path, md5_hash = line[4:].rsplit(':', 1)
+ test_result.image_artifacts.append(
+ self._NewImageArtifact(
+ image_path=image_path.strip(), md5_hash=md5_hash.strip()))
if self.actual_images:
image_diffs = _per_process_state.image_differ.ComputeDifferences(