diff --git a/tests/conftest.py b/tests/conftest.py index 37054626..8724ead5 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -97,10 +97,48 @@ assert ast.parse(WINDOWS_SHIM_TEMPLATE.format(spoofer=repr(r"C:\\Temp\\file.py") def spoof(tmp_path_factory, **kwargs): """Modify PATH to override subprocess executables - spoof(program1='replacement', ...) + spoof(tmp_path_factory, program1='replacement', ...) - Creates temporary directory with symlinks to targets. + For the test suite we need a way override executables, so that we can + substitute desired results such as errors or just speed up OCR. + On POSIXish platforms we create a temporary folder with overrides that + are symlinks to the executables we want to run. We do not actually override + PATH. We also set an environment variable _OCRMYPDF_TEST_PATH, which + OCRmyPDF's subprocess wrapper will check before they use regular PATH. The + output is a folder full of executables we are overriding. We can override + multiple executables. The end result is a folder we can use in a PATH-style + lookup to override some executables: + + /tmp/abcxyz/tesseract -> ocrmypdf/tests/resources/spoof/tesseract_crash.py + /tmp/abcxyz/gs -> ocrmypdf/tests/resources/spoof/gs_backflip.py + + Windows needs extra help from us because usually, only the Administrator + can create symlinks. Instead we create small Python scripts that call + the programs we want, implementing the effect of a symlink. This is cleaner + than creating Windows executables or trying to use non-Python scripts. + The temporary folder generated for Windows could like: + + %TEMP%\abcxyz\tesseract.py: + (script that runs ocrmypdf/tests/resources/spoof/tesseract_crash.py) + %TEMP%\abcxyz\gswin32c.py: + (script that runs ocrmypdf/tests/resources/spoof/gs_backflip.py) + %TEMP%\abcxyz\gswin64c.py: + (script that runs ocrmypdf/tests/resources/spoof/gs_backflip.py) + + We also address one quirk here, that Ghostscript may be known as gswin32c + or gswin64c, depending on what the user installed (regardless of Windows + itself). On POSIX, Ghostscript is just 'gs'. We handle the special case here + too. + + All of this is intimately dependent on the machinery in ocrmypdf.exec.run(). + In particular, for Windows, that code has to know that if there is a .py + file, it needs to run it with Python, since Windows does not like being + asked to execute files. + + We don't overload PATH directly because we have some tests where we call + ocrmypdf as a subprocess (to exercise the command line interface) and some + tests where we call it as an API. """ env = os.environ.copy() slug = '-'.join(v.replace('.py', '') for v in sorted(kwargs.values()))