1# -*- coding: utf-8 -*-
2
3# Copyright(C) 2010-2011 Romain Bignon, Laurent Bachelier
4#
5# This file is part of weboob.
6#
7# weboob is free software: you can redistribute it and/or modify
8# it under the terms of the GNU Lesser General Public License as published by
9# the Free Software Foundation, either version 3 of the License, or
10# (at your option) any later version.
11#
12# weboob is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU Lesser General Public License for more details.
16#
17# You should have received a copy of the GNU Lesser General Public License
18# along with weboob. If not, see <http://www.gnu.org/licenses/>.
19
20from __future__ import print_function
21import sys
22from functools import wraps
23from unittest import TestCase
24
25from weboob.capabilities.base import empty
26from weboob.core import Weboob
27
28# This is what nose does for Python 2.6 and lower compatibility
29# We do the same so nose becomes optional
30try:
31    from unittest.case import SkipTest
32except:
33    from nose.plugins.skip import SkipTest
34
35
36__all__ = ['BackendTest', 'SkipTest', 'skip_without_config']
37
38
39class BackendTest(TestCase):
40    MODULE = None
41
42    def __init__(self, *args, **kwargs):
43        super(BackendTest, self).__init__(*args, **kwargs)
44
45        self.backends = {}
46        self.backend_instance = None
47        self.backend = None
48        self.weboob = Weboob()
49
50        # Skip tests when passwords are missing
51        self.weboob.requests.register('login', self.login_cb)
52
53        if self.weboob.load_backends(modules=[self.MODULE]):
54            # provide the tests with all available backends
55            self.backends = self.weboob.backend_instances
56
57    def login_cb(self, backend_name, value):
58        raise SkipTest('missing config \'%s\' is required for this test' % value.label)
59
60    def run(self, result):
61        """
62        Call the parent run() for each backend instance.
63        Skip the test if we have no backends.
64        """
65        # This is a hack to fix an issue with nosetests running
66        # with many tests. The default is 1000.
67        sys.setrecursionlimit(10000)
68        try:
69            if not len(self.backends):
70                self.backend = self.weboob.build_backend(self.MODULE, nofail=True)
71                TestCase.run(self, result)
72            else:
73                # Run for all backend
74                for backend_instance in self.backends.keys():
75                    print(backend_instance)
76                    self.backend = self.backends[backend_instance]
77                    TestCase.run(self, result)
78        finally:
79            self.weboob.deinit()
80
81    def shortDescription(self):
82        """
83        Generate a description with the backend instance name.
84        """
85        # do not use TestCase.shortDescription as it returns None
86        return '%s [%s]' % (str(self), self.backend_instance)
87
88    def is_backend_configured(self):
89        """
90        Check if the backend is in the user configuration file
91        """
92        return self.weboob.backends_config.backend_exists(self.backend.config.instname)
93
94    def assertNotEmpty(self, obj, *args):
95        """
96        Assert an object is neither `empty` in the BaseObject parlance.
97
98        `obj` should not be `None`, `NotLoaded`, or `NotAvailable`.
99        """
100        self.assertFalse(empty(obj), *args)
101
102
103def skip_without_config(*keys):
104    """Decorator to skip a test if backend config is missing
105
106    :param keys: if any of these keys is missing in backend config, skip test. Can be empty.
107    """
108
109    for key in keys:
110        if callable(key):
111            raise TypeError('skip_without_config() must be called with arguments')
112
113    def decorator(func):
114        @wraps(func)
115        def wrapper(self, *args, **kwargs):
116            config = self.backend.config
117            if not self.is_backend_configured():
118                raise SkipTest('a backend must be declared in configuration for this test')
119            for key in keys:
120                if not config[key].get():
121                    raise SkipTest('config key %r is required for this test' %
122                                   key)
123
124            return func(self, *args, **kwargs)
125        return wrapper
126    return decorator
127