First this brings a change to the paper API: the terminology of bibdata and bibentry is clarified: now bibentry is a dictionary of the form {citekey: bibdata} where bibdata corresponds to the actual dictionary of bibliographic fields and values {author: ..., year: ...}. Now bibentry is an attribute of the paper object that is generated from citekey and bibdata. This commit fixes all tests in particular an issue with citekey in bibentry not updated. Also removes prints in tests and deprecated assertEquals. Usecase tests now fail if the command ask for unexpected inputs. Removes queries for user input in attach and add commands (for deletion of a copied document file). The input was not coherent with tests and is annoying.
240 lines
6.4 KiB
Python
240 lines
6.4 KiB
Python
import sys
|
|
import io
|
|
import os
|
|
import shutil
|
|
import glob
|
|
import unittest
|
|
|
|
import dotdot
|
|
import fake_filesystem
|
|
import fake_filesystem_shutil
|
|
import fake_filesystem_glob
|
|
|
|
from pubs.p3 import input, _fake_stdio, _get_fake_stdio_ucontent
|
|
from pubs import content, filebroker
|
|
|
|
# code for fake fs
|
|
|
|
real_os = os
|
|
real_open = open
|
|
real_shutil = shutil
|
|
real_glob = glob
|
|
real_io = io
|
|
|
|
|
|
|
|
# def _mod_list():
|
|
# ml = []
|
|
# import pubs
|
|
# for importer, modname, ispkg in pkgutil.walk_packages(
|
|
# path=pubs.__path__,
|
|
# prefix=pubs.__name__ + '.',
|
|
# onerror=lambda x: None):
|
|
# # HACK to not load textnote
|
|
# if not modname.startswith('pubs.plugs.texnote'):
|
|
# ml.append((modname, __import__(modname, fromlist='dummy')))
|
|
# return ml
|
|
|
|
|
|
ENCODING = 'utf8'
|
|
|
|
|
|
class UnicodeStringIOWrapper(object):
|
|
"""This is a hack because fake_filesystem does not provide mock of io.
|
|
"""
|
|
|
|
override = ['read', 'readline', 'readlines', 'write', 'writelines']
|
|
|
|
def __init__(self, strio):
|
|
self._strio = strio # The real StringIO
|
|
|
|
def __getattr__(self, name):
|
|
if name in UnicodeStringIOWrapper.override:
|
|
return object.__getattribute__(self, name)
|
|
else:
|
|
return self._strio.__getattribute__(name)
|
|
|
|
def __iter__(self):
|
|
for l in self.readlines():
|
|
yield l
|
|
|
|
def read(self, *args):
|
|
return self._strio.read(*args).decode(ENCODING)
|
|
|
|
def readline(self, *args):
|
|
return self._strio.readline(*args).decode(ENCODING)
|
|
|
|
def readlines(self, *args):
|
|
return [l.decode(ENCODING) for l in self._strio.readlines(*args)]
|
|
|
|
def write(self, data):
|
|
self._strio.write(data.encode(ENCODING))
|
|
|
|
def writelines(self, data):
|
|
self._strio.write([l.encode(ENCODING) for l in data])
|
|
|
|
def __enter__(self):
|
|
self._strio.__enter__()
|
|
return self
|
|
|
|
def __exit__(self, *args):
|
|
return self._strio.__exit__(*args)
|
|
|
|
|
|
def _force_binary_mode(mode):
|
|
if 'b' in mode:
|
|
raise ValueError('Open should not happen in binary mode.')
|
|
return mode + 'b'
|
|
|
|
|
|
class FakeIO(object):
|
|
|
|
def __init__(self, fake_open):
|
|
self.fake_open = fake_open
|
|
|
|
def open(self, *args, **kwargs):
|
|
# Forces binary mode for FakeFileOpen
|
|
args = list(args)
|
|
if len(args) > 1:
|
|
args[1] = _force_binary_mode(args[1])
|
|
else:
|
|
kwargs['mode'] = _force_binary_mode(kwargs.get('mode', 'r'))
|
|
fakefs_stringio = self.fake_open.Call(*args, **kwargs)
|
|
return UnicodeStringIOWrapper(fakefs_stringio)
|
|
|
|
|
|
def create_fake_fs(module_list):
|
|
|
|
fake_fs = fake_filesystem.FakeFilesystem()
|
|
fake_os = fake_filesystem.FakeOsModule(fake_fs)
|
|
fake_open = fake_filesystem.FakeFileOpen(fake_fs)
|
|
fake_shutil = fake_filesystem_shutil.FakeShutilModule(fake_fs)
|
|
fake_glob = fake_filesystem_glob.FakeGlobModule(fake_fs)
|
|
fake_io = FakeIO(fake_open)
|
|
|
|
fake_fs.CreateDirectory(fake_os.path.expanduser('~'))
|
|
|
|
sys.modules['os'] = fake_os
|
|
sys.modules['shutil'] = fake_shutil
|
|
sys.modules['glob'] = fake_glob
|
|
sys.modules['io'] = fake_io
|
|
|
|
for md in module_list:
|
|
md.os = fake_os
|
|
md.shutil = fake_shutil
|
|
md.open = fake_open
|
|
md.file = None
|
|
md.io = fake_io
|
|
|
|
return {'fs': fake_fs,
|
|
'os': fake_os,
|
|
'open': fake_open,
|
|
'io': fake_io,
|
|
'shutil': fake_shutil,
|
|
'glob': fake_glob}
|
|
|
|
|
|
def unset_fake_fs(module_list):
|
|
try:
|
|
__builtins__.open = real_open
|
|
except AttributeError:
|
|
__builtins__['open'] = real_open
|
|
|
|
sys.modules['os'] = real_os
|
|
sys.modules['shutil'] = real_shutil
|
|
sys.modules['glob'] = real_glob
|
|
sys.modules['io'] = real_io
|
|
|
|
for md in module_list:
|
|
md.os = real_os
|
|
md.shutil = real_shutil
|
|
md.open = real_open
|
|
md.io = real_io
|
|
|
|
|
|
def copy_dir(fs, real_dir, fake_dir = None):
|
|
"""Copy all the data directory into the fake fs"""
|
|
if fake_dir is None:
|
|
fake_dir = real_dir
|
|
for filename in real_os.listdir(real_dir):
|
|
real_path = os.path.abspath(real_os.path.join(real_dir, filename))
|
|
fake_path = fs['os'].path.join(fake_dir, filename)
|
|
if real_os.path.isfile(real_path):
|
|
with real_open(real_path, 'rb') as f:
|
|
fs['fs'].CreateFile(fake_path, contents=f.read())
|
|
if real_os.path.isdir(real_path):
|
|
fs['fs'].CreateDirectory(fake_path)
|
|
copy_dir(fs, real_path, fake_path)
|
|
|
|
|
|
# redirecting output
|
|
|
|
def redirect(f):
|
|
def newf(*args, **kwargs):
|
|
old_stderr, old_stdout = sys.stderr, sys.stdout
|
|
stdout = _fake_stdio()
|
|
stderr = _fake_stdio()
|
|
sys.stdout, sys.stderr = stdout, stderr
|
|
try:
|
|
return f(*args, **kwargs), _get_fake_stdio_ucontent(stdout), _get_fake_stdio_ucontent(stderr)
|
|
finally:
|
|
sys.stderr, sys.stdout = old_stderr, old_stdout
|
|
return newf
|
|
|
|
|
|
# Test helpers
|
|
|
|
# automating input
|
|
|
|
real_input = input
|
|
|
|
|
|
class FakeInput():
|
|
""" Replace the input() command, and mock user input during tests
|
|
|
|
Instanciate as :
|
|
input = FakeInput(['yes', 'no'])
|
|
then replace the input command in every module of the package :
|
|
input.as_global()
|
|
Then :
|
|
input() returns 'yes'
|
|
input() returns 'no'
|
|
input() raises IndexError
|
|
"""
|
|
|
|
class UnexpectedInput(Exception):
|
|
pass
|
|
|
|
def __init__(self, inputs, module_list=tuple()):
|
|
self.inputs = list(inputs) or []
|
|
self.module_list = module_list
|
|
self._cursor = 0
|
|
|
|
def as_global(self):
|
|
for md in self.module_list:
|
|
md.input = self
|
|
md.editor_input = self
|
|
# if mdname.endswith('files'):
|
|
# md.editor_input = self
|
|
|
|
def add_input(self, inp):
|
|
self.inputs.append(inp)
|
|
|
|
def __call__(self, *args, **kwargs):
|
|
try:
|
|
inp = self.inputs[self._cursor]
|
|
self._cursor += 1
|
|
return inp
|
|
except IndexError:
|
|
raise self.UnexpectedInput('Unexpected user input in test.')
|
|
|
|
|
|
class TestFakeFs(unittest.TestCase):
|
|
"""Abstract TestCase intializing the fake filesystem."""
|
|
|
|
def setUp(self):
|
|
self.fs = create_fake_fs([content, filebroker])
|
|
|
|
def tearDown(self):
|
|
unset_fake_fs([content, filebroker])
|