Source code for anybox.recipe.odoo.testing

"""Utilities for unit tests."""
import os
import unittest
import sys
import shutil
import subprocess
import logging
from tempfile import mkdtemp
from UserDict import UserDict

from zc.buildout.easy_install import Installer
from pip.vcs import vcs as pip_vcs

from . import vcs
from .base import BaseRecipe

logger = logging.getLogger(__name__)

COMMIT_USER_NAME = 'Test'
COMMIT_USER_EMAIL = 'test@example.org'
COMMIT_USER_FULL = '%s %s' % (COMMIT_USER_NAME, COMMIT_USER_EMAIL)


[docs]class TestingRecipe(BaseRecipe): """A subclass with just enough few defaults for unit testing.""" release_filenames = {'8.0': 'blob-%s.tgz'} nightly_filenames = {'8.0': '8-0-nightly-%s.tbz'} release_dl_url = {'8.0': 'http://release.odoo.test/src/'} def __init__(self, buildout, name, options): # we need to make buildout a regular object, because some subsystems # will set extra attributes on it if isinstance(buildout, dict): buildout = UserDict(buildout) super(TestingRecipe, self).__init__(buildout, name, options)
[docs]class FakeRepo(vcs.base.BaseRepo): log = [] log_std_options = True vcs_control_dir = '.fake' revision = 'fakerev' name = 'fakevcs' # for pip.vcs.VersionSupport registration
[docs] def get_update(self, revision): self.revision = revision if not os.path.isdir(self.target_dir): os.mkdir(self.target_dir) control = os.path.join(self.target_dir, self.vcs_control_dir) if not os.path.isdir(control): os.mkdir(control) options = self.options.copy() if self.log_std_options: options['offline'] = self.offline options['clear_locks'] = self.clear_locks self.log.append((self.target_dir, self.url, revision, options),)
[docs] def revert(self, revision): self.revision = revision self.log.append(('revert', revision, self.target_dir))
[docs] def parents(self, pip_compatible=False): return [self.revision]
[docs] def archive(self, target): if not os.path.isdir(target): os.makedirs(target) with open(os.path.join(target, '.fake_archival.txt'), 'w') as f: f.write(str(self.revision))
[docs] def is_local_fixed_revision(self, revspec): return revspec in getattr(self, 'fixed_revs', ())
vcs.SUPPORTED['fakevcs'] = FakeRepo pip_vcs.register(FakeRepo) # for tests around gp.vcsdevelop
[docs]def get_vcs_log(): return FakeRepo.log
[docs]def clear_vcs_log(): FakeRepo.log = []
[docs]class PersistentRevFakeRepo(FakeRepo): """A variant of FakeRepo that still needs the directory structure around. Makes for a more realistic test of some conditions. In particular, reproduced launchpad #TODO """ current_revisions = {} @property def revision(self): return self.__class__.current_revisions.get(self.target_dir, 'fakerev') @revision.setter
[docs] def revision(self, v): self.__class__.current_revisions[self.target_dir] = v
[docs] def uncommitted_changes(self): """This needs the directory to really exist and is controllable.""" files = set(os.listdir(self.target_dir)) files.discard('.fake') return bool(files)
vcs.SUPPORTED['pr_fakevcs'] = PersistentRevFakeRepo
[docs]class RecipeTestCase(unittest.TestCase): """A base setup for tests of recipe classes""" fictive_dist_name = 'FictiveDist' fictive_name = 'fictivedist' fictive_version = None
[docs] def setUp(self): b_dir = self.buildout_dir = mkdtemp('test_oerp_base_recipe') eggs_dir = os.path.join(b_dir, 'eggs') os.mkdir(eggs_dir) develop_dir = os.path.join(b_dir, 'develop-eggs') os.mkdir(develop_dir) self.buildout = {} self.buildout['buildout'] = { 'directory': b_dir, 'offline': False, 'parts-directory': os.path.join(b_dir, 'parts'), 'bin-directory': os.path.join(b_dir, 'bin'), 'find-links': '', 'allow-hosts': '', 'eggs-directory': eggs_dir, 'develop-eggs-directory': develop_dir, 'python': 'main_python', } self.buildout['main_python'] = dict(executable=sys.executable) # temporary monkey patch of easy_install to avoid actual requests to # PyPI (offline mode currently does not protect against that, even # though I checked that it is recognized by zc.recipe.egg # TODO this does not seem to really work in some context # (Debian wheezy buildslave in a virtualenv, with zc.recipe.egg 2.0.0a3 # we see nose being downloaded several times) self.unreachable_distributions = set() self.exc_distributions = {} # distrib name -> exc to raise Installer._orig_obtain = Installer._obtain def _obtain(inst, requirement, source=None): if requirement.project_name in self.unreachable_distributions: return None exc = self.exc_distributions.get(requirement.project_name) if exc is not None: raise exc return inst._orig_obtain(requirement, source=source) Installer._obtain = _obtain
[docs] def make_recipe(self, name='openerp', **options): self.recipe = TestingRecipe(self.buildout, name, options)
[docs] def tearDown(self): clear_vcs_log() shutil.rmtree(self.buildout_dir) Installer._obtain = Installer._orig_obtain # leftover egg-info at root of the source dir (frequent cwd) # impairs use of this very same source dir for real-life testing # with a 'develop' option. for egg_info in (self.fictive_dist_name + '.egg-info', 'Babel.egg-info'): if os.path.isdir(egg_info): shutil.rmtree(egg_info)
[docs] def build_babel_egg(self): """build an egg for fake babel in buildout's eggs directory. Require the test case to already have a ``test_dir`` attribute (typically set on class with the dirname of the test) """ subprocess.check_call( [sys.executable, 'setup.py', 'bdist_egg', '-d', self.recipe.b_options['eggs-directory'], '-b', os.path.join(self.buildout_dir, 'build')], cwd=os.path.join(self.test_dir, 'fake_babel'), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
[docs] def build_fictive_egg(self): """build an egg of a fictive distribution for testing purposes. Require the test case to already have a ``test_dir`` attribute (typically set on class with the dirname of the test) """ subprocess.check_call( [sys.executable, 'setup.py', 'bdist_egg', '-d', self.recipe.b_options['eggs-directory'], '-b', os.path.join(self.buildout_dir, 'build')], cwd=os.path.join(self.test_dir, 'fictive_dist'), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
[docs] def fill_working_set(self, fictive=False, babel=False): self.assertTrue(fictive or babel) if babel: self.build_babel_egg() self.recipe.options['eggs'] = 'Babel' if fictive: self.build_fictive_egg() self.recipe.options['eggs'] = self.fictive_dist_name self.recipe.install_requirements() # to get 'ws' attribute if fictive: egg = self.recipe.ws.by_key.get(self.fictive_name) if egg is None: self.fail("Our crafted testing egg has not been installed") # precise version depends on the setuptools version # from setuptools 8.0, normalization to 0.123.dev0 # according to PEP440 occurs self.assertTrue(egg.version.startswith('0.123'), msg="Our crafted testing egg " "is being superseded by %r" % egg) self.fictive_version = egg.version
[docs] def silence_buildout_develop(self): """Silence easy_install develop operations performed by zc.buildout. """ try: # grabbing it with getLogger, even after the import is not # effective: the level gets overwritten afterwards from zc.buildout.easy_install import logger as zc_logger except: logger.warn("Could not grab zc.buildout.easy_install logger") else: zc_logger.setLevel(logging.ERROR)
[docs] def develop_fictive(self, require_install=False): """Develop fictive distribution in buildout's directory. Require the test case to already have a ``test_dir`` attribute (typically set on class with the dirname of the test) :param bool require_install: if ``True``, will be required from ``eggs`` option, and installed. """ self.silence_buildout_develop() res = self.recipe.develop(os.path.join(self.test_dir, 'fictive_dist')) if require_install: self.recipe.options['eggs'] = self.fictive_dist_name self.recipe.install_requirements() return res