Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / test.py @ 9c6c3d69

History | View | Annotate | Download (10.5 kB)

1
# Copyright 2013 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
from unittest import makeSuite, TestSuite, TextTestRunner, TestCase
35
from time import sleep
36
from inspect import getmembers, isclass
37
from json import loads
38
from random import randint
39

    
40
from kamaki.clients.connection.test import (
41
    KamakiConnection,
42
    KamakiHTTPConnection,
43
    KamakiResponse,
44
    KamakiHTTPResponse)
45
from kamaki.clients.utils.test import Utils
46
from kamaki.clients.astakos.test import Astakos
47
from kamaki.clients.compute.test import Compute, ComputeRest
48
from kamaki.clients.cyclades.test import Cyclades, CycladesRest
49
from kamaki.clients.image.test import Image
50
from kamaki.clients.storage.test import Storage
51
from kamaki.clients.pithos.test import Pithos, PithosRest
52

    
53

    
54
class ClientError(TestCase):
55

    
56
    def test___init__(self):
57
        from kamaki.clients import ClientError
58
        for msg, status, details, exp_msg, exp_status, exp_details in (
59
                ('some msg', 42, 0.28, 0, 0, 0),
60
                ('some msg', 'fail', [], 0, 0, 0),
61
                ('some msg', 42, 'details on error', 0, 0, 0),
62
                (
63
                    '404 {"ExampleError":'
64
                    ' {"message": "a msg", "code": 42, "details": "dets"}}',
65
                    404,
66
                    0,
67
                    '404 ExampleError (a msg)\n',
68
                    42,
69
                    ['dets']),
70
                (
71
                    '404 {"ExampleError":'
72
                    ' {"message": "a msg", "code": 42}}',
73
                    404,
74
                    'details on error',
75
                    '404 ExampleError (a msg)\n',
76
                    42,
77
                    0),
78
                (
79
                    '404 {"ExampleError":'
80
                    ' {"details": "Explain your error"}}',
81
                    404,
82
                    'details on error',
83
                    '404 ExampleError',
84
                    0,
85
                    ['details on error', 'Explain your error']),
86
                ('some msg\n', -10, ['details', 'on', 'error'], 0, 0, 0)):
87
            ce = ClientError(msg, status, details)
88
            exp_msg = exp_msg or (msg if msg.endswith('\n') else msg + '\n')
89
            exp_status = exp_status or status
90
            exp_details = exp_details or details
91
            self.assertEqual('%s' % ce, exp_msg)
92
            self.assertEqual(
93
                exp_status if isinstance(exp_status, int) else 0,
94
                ce.status)
95
            self.assertEqual(exp_details, ce.details)
96

    
97

    
98
class SilentEvent(TestCase):
99

    
100
    def thread_content(self, methodid, raiseException=0):
101
        wait = 0.1
102
        self.can_finish = -1
103
        while self.can_finish < methodid and wait < 4:
104
            sleep(wait)
105
            wait = 2 * wait
106
        if raiseException and raiseException == methodid:
107
            raise Exception('Some exception')
108
        self._value = methodid
109
        self.assertTrue(wait < 4)
110

    
111
    def setUp(self):
112
        from kamaki.clients import SilentEvent
113
        self.SE = SilentEvent
114

    
115
    def test_run(self):
116
        threads = [self.SE(self.thread_content, i) for i in range(4)]
117
        for t in threads:
118
            t.start()
119

    
120
        for i in range(4):
121
            self.assertTrue(threads[i].is_alive())
122
            self.can_finish = i
123
            threads[i].join()
124
            self.assertFalse(threads[i].is_alive())
125

    
126
    def test_value(self):
127
        threads = [self.SE(self.thread_content, i) for i in range(4)]
128
        for t in threads:
129
            t.start()
130

    
131
        for mid, t in enumerate(threads):
132
            if t.is_alive():
133
                self.can_finish = mid
134
                continue
135
            self.assertTrue(mid, t.value)
136

    
137
    def test_exception(self):
138
        threads = [self.SE(self.thread_content, i, (i % 2)) for i in range(4)]
139
        for t in threads:
140
            t.start()
141

    
142
        for i, t in enumerate(threads):
143
            if t.is_alive():
144
                self.can_finish = i
145
                continue
146
            if i % 2:
147
                self.assertTrue(isinstance(t.exception, Exception))
148
            else:
149
                self.assertFalse(t.exception)
150

    
151

    
152
class FakeConnection(object):
153
    """A fake Connection class"""
154

    
155
    def __init__(self):
156
        pass
157

    
158

    
159
class Client(TestCase):
160

    
161
    def assert_dicts_are_equal(self, d1, d2):
162
        for k, v in d1.items():
163
            self.assertTrue(k in d2)
164
            if isinstance(v, dict):
165
                self.assert_dicts_are_equal(v, d2[k])
166
            else:
167
                self.assertEqual(unicode(v), unicode(d2[k]))
168

    
169
    def setUp(self):
170
        from kamaki.clients import Client
171
        self.base_url = 'http://example.com'
172
        self.token = 's0m370k3n=='
173
        self.client = Client(self.base_url, self.token, FakeConnection())
174

    
175
    def test___init__(self):
176
        self.assertEqual(self.client.base_url, self.base_url)
177
        self.assertEqual(self.client.token, self.token)
178
        self.assert_dicts_are_equal(self.client.headers, {})
179
        DATE_FORMATS = [
180
            '%a %b %d %H:%M:%S %Y',
181
            '%A, %d-%b-%y %H:%M:%S GMT',
182
            '%a, %d %b %Y %H:%M:%S GMT']
183
        self.assertEqual(self.client.DATE_FORMATS, DATE_FORMATS)
184
        self.assertTrue(isinstance(self.client.http_client, FakeConnection))
185

    
186
    def test__init_thread_limit(self):
187
        exp = 'Nothing set here'
188
        for faulty in (-1, 0.5, 'a string', {}):
189
            self.assertRaises(
190
                AssertionError,
191
                self.client._init_thread_limit,
192
                faulty)
193
            self.assertEqual(exp, getattr(self.client, '_thread_limit', exp))
194
            self.assertEqual(exp, getattr(self.client, '_elapsed_old', exp))
195
            self.assertEqual(exp, getattr(self.client, '_elapsed_new', exp))
196
        self.client._init_thread_limit(42)
197
        self.assertEqual(42, self.client._thread_limit)
198
        self.assertEqual(0.0, self.client._elapsed_old)
199
        self.assertEqual(0.0, self.client._elapsed_new)
200

    
201
    def test__watch_thread_limit(self):
202
        waits = (
203
            dict(args=((0.1, 1), (0.1, 2), (0.2, 1), (0.7, 1), (0.3, 2))),
204
            dict(args=((1.0 - (i / 10.0), (i + 1)) for i in range(7))),
205
            dict(max=1, args=tuple([(randint(1, 10) / 3.0, 1), ] * 10)),
206
            dict(
207
                limit=5,
208
                args=tuple([
209
                    (1.0 + (i / 10.0), (5 - i - 1)) for i in range(4)] + [
210
                    (2.0, 1), (1.9, 2), (2.0, 1), (2.0, 2)])),
211
            dict(args=tuple(
212
                [(1.0 - (i / 10.0), (i + 1)) for i in range(7)] + [
213
                (0.1, 7), (0.2, 6), (0.4, 5), (0.3, 6), (0.2, 7), (0.1, 7)])),)
214
        for wait_dict in waits:
215
            if 'max' in wait_dict:
216
                self.client.MAX_THREADS = wait_dict['max']
217
            else:
218
                self.client.MAX_THREADS = 7
219
            if 'limit' in wait_dict:
220
                self.client._init_thread_limit(wait_dict['limit'])
221
            else:
222
                self.client._init_thread_limit()
223
                self.client._watch_thread_limit(list())
224
                self.assertEqual(1, self.client._thread_limit)
225
            for wait, exp_limit in wait_dict['args']:
226
                self.client._elapsed_new = wait
227
                self.client._watch_thread_limit(list())
228
                self.assertEqual(exp_limit, self.client._thread_limit)
229

    
230

    
231
#  TestCase auxiliary methods
232

    
233
def runTestCase(cls, test_name, args=[], failure_collector=[]):
234
    """
235
    :param cls: (TestCase) a set of Tests
236

237
    :param test_name: (str)
238

239
    :param args: (list) these are prefixed with test_ and used as params when
240
        instantiating cls
241

242
    :param failure_collector: (list) collects info of test failures
243

244
    :returns: (int) total # of run tests
245
    """
246
    suite = TestSuite()
247
    if args:
248
        suite.addTest(cls('_'.join(['test'] + args)))
249
    else:
250
        suite.addTest(makeSuite(cls))
251
    print('* Test * %s *' % test_name)
252
    r = TextTestRunner(verbosity=2).run(suite)
253
    failure_collector += r.failures
254
    return r.testsRun
255

    
256

    
257
def _add_value(foo, value):
258
    def wrap(self):
259
        return foo(self, value)
260
    return wrap
261

    
262

    
263
def get_test_classes(module=__import__(__name__), name=''):
264
    module_stack = [module]
265
    while module_stack:
266
        module = module_stack[-1]
267
        module_stack = module_stack[:-1]
268
        for objname, obj in getmembers(module):
269
            if (objname == name or not name):
270
                if isclass(obj) and objname != 'TestCase' and (
271
                        issubclass(obj, TestCase)):
272
                    yield (obj, objname)
273

    
274

    
275
def main(argv):
276
    found = False
277
    failure_collector = list()
278
    num_of_tests = 0
279
    for cls, name in get_test_classes(name=argv[1] if len(argv) > 1 else ''):
280
        found = True
281
        num_of_tests += runTestCase(cls, name, argv[2:], failure_collector)
282
    if not found:
283
        print('Test "%s" not found' % ' '.join(argv[1:]))
284
    else:
285
        for i, failure in enumerate(failure_collector):
286
            print('Failure %s: ' % (i + 1))
287
            for field in failure:
288
                print('\t%s' % field)
289
        print('\nTotal tests run: %s' % num_of_tests)
290
        print('Total failures: %s' % len(failure_collector))
291

    
292

    
293
if __name__ == '__main__':
294
    from sys import argv
295
    main(argv)