From 44b5cc5dc503967e5813e80ea54a33d5fbe9149e Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Wed, 20 Apr 2016 13:44:03 +0900 Subject: [PATCH] Bug 1266343 - Extend the ConfigureTestSandbox to hook subprocess.Popen. r=chmanchester --- .../mozbuild/test/configure/common.py | 91 ++++++++++++++++++- .../test/configure/test_checks_configure.py | 10 +- 2 files changed, 94 insertions(+), 7 deletions(-) diff --git a/python/mozbuild/mozbuild/test/configure/common.py b/python/mozbuild/mozbuild/test/configure/common.py index f2e30d5f3982..8986e177ede1 100644 --- a/python/mozbuild/mozbuild/test/configure/common.py +++ b/python/mozbuild/mozbuild/test/configure/common.py @@ -4,7 +4,9 @@ from __future__ import absolute_import, print_function, unicode_literals +import errno import os +import subprocess from mozbuild.configure import ConfigureSandbox from mozbuild.util import ReadOnlyNamespace @@ -12,22 +14,64 @@ from mozpack import path as mozpath from which import WhichError +from buildconfig import ( + topobjdir, + topsrcdir, +) + class ConfigureTestVFS(object): def __init__(self, paths): self._paths = set(mozpath.abspath(p) for p in paths) def exists(self, path): - return mozpath.abspath(path) in self._paths + path = mozpath.abspath(path) + if path in self._paths: + return True + if mozpath.basedir(path, [topsrcdir, topobjdir]): + return os.path.exists(path) + return False def isfile(self, path): - return mozpath.abspath(path) in self._paths + path = mozpath.abspath(path) + if path in self._paths: + return True + if mozpath.basedir(path, [topsrcdir, topobjdir]): + return os.path.isfile(path) + return False class ConfigureTestSandbox(ConfigureSandbox): + '''Wrapper around the ConfigureSandbox for testing purposes. + + Its arguments are the same as ConfigureSandbox, except for the additional + `paths` argument, which is a dict where the keys are file paths and the + values are either None or a function that will be called when the sandbox + calls an implemented function from subprocess with the key as command. + When the command is CONFIG_SHELL, the function for the path of the script + that follows will be called. + + The API for those functions is: + retcode, stdout, stderr = func(stdin, args) + + This class is only meant to implement the minimal things to make + moz.configure testing possible. As such, it takes shortcuts. + ''' def __init__(self, paths, config, environ, *args, **kwargs): self._search_path = environ.get('PATH', '').split(os.pathsep) + self._subprocess_paths = { + mozpath.abspath(k): v for k, v in paths.iteritems() if v + } + + paths = paths.keys() + + environ = dict(environ) + if 'CONFIG_SHELL' not in environ: + environ['CONFIG_SHELL'] = mozpath.abspath('/bin/sh') + self._subprocess_paths[environ['CONFIG_SHELL']] = self.shell + paths.append(environ['CONFIG_SHELL']) + vfs = ConfigureTestVFS(paths) self.OS = ReadOnlyNamespace(path=ReadOnlyNamespace(**{ @@ -49,6 +93,17 @@ class ConfigureTestSandbox(ConfigureSandbox): WhichError=WhichError, ) + if what == 'subprocess.Popen': + return self.Popen + + if what == 'subprocess': + return ReadOnlyNamespace( + CalledProcessError=subprocess.CalledProcessError, + check_output=self.check_output, + PIPE=subprocess.PIPE, + Popen=self.Popen, + ) + return super(ConfigureTestSandbox, self)._get_one_import(what) def which(self, command): @@ -57,3 +112,35 @@ class ConfigureTestSandbox(ConfigureSandbox): if self.OS.path.exists(path): return path raise WhichError() + + def Popen(self, args, stdin=None, stdout=None, stderr=None, **kargs): + try: + program = self.which(args[0]) + except WhichError: + raise OSError(errno.ENOENT, 'File not found') + + func = self._subprocess_paths.get(program) + retcode, stdout, stderr = func(stdin, args[1:]) + + class Process(object): + def communicate(self, stdin=None): + return stdout, stderr + + def wait(self): + return retcode + + return Process() + + def check_output(self, args): + proc = self.Popen(args) + stdout, stderr = proc.communicate() + retcode = proc.wait() + if retcode: + raise subprocess.CalledProcessError(retcode, args, stdout) + return stdout + + def shell(self, stdin, args): + script = mozpath.abspath(args[0]) + if script in self._subprocess_paths: + return self._subprocess_paths[script](stdin, args[1:]) + return 127, '', 'File not found' diff --git a/python/mozbuild/mozbuild/test/configure/test_checks_configure.py b/python/mozbuild/mozbuild/test/configure/test_checks_configure.py index 70b4fca5659e..0e0dfe39c20d 100644 --- a/python/mozbuild/mozbuild/test/configure/test_checks_configure.py +++ b/python/mozbuild/mozbuild/test/configure/test_checks_configure.py @@ -131,11 +131,11 @@ class TestChecksConfigure(unittest.TestCase): prog='/bin/configure'): config = {} out = StringIO() - paths = ( - self.KNOWN_A, - self.KNOWN_B, - self.KNOWN_C, - ) + paths = { + self.KNOWN_A: None, + self.KNOWN_B: None, + self.KNOWN_C: None, + } environ = dict(environ) environ['PATH'] = os.pathsep.join(os.path.dirname(p) for p in paths) sandbox = ConfigureTestSandbox(paths, config, environ, [prog] + args,