Files
graphene/Scripts/regression.py
Paweł Marczewski 1d25612006 [CI] Enable pylint unconditionally, fix violations
Pylint output was filtered so that many files with existing pylint
violations were allowed to stay broken.

I made sure all files pass pylint, but whitelisted some rules that
we commonly disable:

* missing docstrings: most of the code is tests/internal anyway
* invalid-name: too many violations, and we commonly use one- or
  two-character names (like "a, b" or "t1, t2") which is
  disallowed by this rule; we could tweak it and then fix
  remaining violations such as camel-case or lowercase constants
* fixme: we leave TODOs as a matter of practice, same as in C
* high-level style rules like too-few-* and too-many-*,
  no-self-use

Hopefully that will make using pylint less annoying, while also
catching serious issues (such as unused variables or imports).
2020-11-17 13:45:09 -08:00

140 lines
4.9 KiB
Python

import contextlib
import os
import pathlib
import signal
import subprocess
import sys
import unittest
# pylint: disable=subprocess-popen-preexec-fn,subprocess-run-check
HAS_SGX = os.environ.get('SGX') == '1'
ON_X86 = os.uname().machine in ['x86_64']
def expectedFailureIf(predicate):
if predicate:
return unittest.expectedFailure
return lambda func: func
class RegressionTestCase(unittest.TestCase):
LOADER_ENV = 'PAL_LOADER'
LIBPAL_PATH_ENV = 'LIBPAL_PATH'
HOST_PAL_PATH_ENV = 'HOST_PAL_PATH'
DEFAULT_TIMEOUT = (20 if HAS_SGX else 10)
def get_manifest(self, filename):
return filename + '.manifest' + ('.sgx' if HAS_SGX else '')
def get_env(self, name):
try:
return os.environ[name]
except KeyError:
self.fail('environment variable {} not set'.format(name))
def has_debug(self):
libpal = self.get_env(self.LIBPAL_PATH_ENV)
p = subprocess.run(['objdump', '-x', libpal], check=True, stdout=subprocess.PIPE)
dump = p.stdout.decode()
return '.debug_info' in dump
def run_gdb(self, args, gdb_script, **kwds):
host_pal_path = self.get_env(self.HOST_PAL_PATH_ENV)
# See also pal_loader.
prefix = ['gdb', '-q']
env = os.environ.copy()
if HAS_SGX:
prefix += ['-x', os.path.join(host_pal_path, 'gdb_integration/graphene_sgx_gdb.py')]
sgx_gdb = os.path.join(host_pal_path, 'gdb_integration/sgx_gdb.so')
env['LD_PRELOAD'] = sgx_gdb + ':' + env.get('LD_PRELOAD', '')
else:
prefix += ['-x', os.path.join(host_pal_path, 'gdb_integration/graphene_gdb.py')]
# Override TTY, as apparently os.setpgrp() confuses GDB and causes it to hang.
prefix += ['-x', gdb_script, '-batch', '-tty=/dev/null']
prefix += ['--args']
return self.run_binary(args, prefix=prefix, env=env, **kwds)
def run_binary(self, args, *, timeout=None, prefix=None, **kwds):
timeout = (max(self.DEFAULT_TIMEOUT, timeout) if timeout is not None
else self.DEFAULT_TIMEOUT)
loader = self.get_env(self.LOADER_ENV)
if not pathlib.Path(loader).exists():
self.skipTest('loader ({}) not found'.format(loader))
libpal = self.get_env(self.LIBPAL_PATH_ENV)
if not pathlib.Path(libpal).exists():
self.skipTest('libpal ({}) not found'.format(libpal))
if prefix is None:
prefix = []
with subprocess.Popen([*prefix, loader, libpal, 'init', *args],
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
preexec_fn=os.setpgrp,
**kwds) as process:
try:
stdout, stderr = process.communicate(timeout=timeout)
except subprocess.TimeoutExpired:
os.killpg(process.pid, signal.SIGKILL)
self.fail('timeout ({} s) expired'.format(timeout))
self.print_output(stdout, stderr)
if process.returncode:
raise subprocess.CalledProcessError(
process.returncode, args, stdout, stderr)
return stdout.decode(), stderr.decode()
@classmethod
def run_native_binary(cls, args, timeout=None, libpath=None, **kwds):
timeout = (max(cls.DEFAULT_TIMEOUT, timeout) if timeout is not None
else cls.DEFAULT_TIMEOUT)
my_env = os.environ.copy()
if not libpath is None:
my_env["LD_LIBRARY_PATH"] = libpath
with subprocess.Popen(args,
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
env=my_env,
preexec_fn=os.setpgrp,
**kwds) as process:
try:
stdout, stderr = process.communicate(timeout=timeout)
except subprocess.TimeoutExpired:
os.killpg(process.pid, signal.SIGKILL)
raise AssertionError('timeout ({} s) expired'.format(timeout))
cls.print_output(stdout, stderr)
if process.returncode:
raise subprocess.CalledProcessError(
process.returncode, args, stdout, stderr)
return stdout.decode(), stderr.decode()
@staticmethod
def print_output(stdout: bytes, stderr: bytes):
'''
Print command output (stdout, stderr) so that pytest can capture it.
'''
sys.stdout.write(stdout.decode(errors='surrogateescape'))
sys.stderr.write(stderr.decode(errors='surrogateescape'))
@contextlib.contextmanager
def expect_returncode(self, returncode):
if returncode == 0:
raise ValueError('expected returncode should be nonzero')
try:
yield
self.fail('did not fail (expected {})'.format(returncode))
except subprocess.CalledProcessError as e:
self.assertEqual(e.returncode, returncode,
'failed with returncode {} (expected {})'.format(
e.returncode, returncode))