root / snf-django-lib / snf_django / utils / testing.py @ fde2c1f7
History | View | Annotate | Download (10 kB)
1 | f09da76e | Kostas Papadimitriou | # Copyright 2011-2013 GRNET S.A. All rights reserved.
|
---|---|---|---|
2 | f09da76e | Kostas Papadimitriou | #
|
3 | f09da76e | Kostas Papadimitriou | # Redistribution and use in source and binary forms, with or
|
4 | f09da76e | Kostas Papadimitriou | # without modification, are permitted provided that the following
|
5 | f09da76e | Kostas Papadimitriou | # conditions are met:
|
6 | f09da76e | Kostas Papadimitriou | #
|
7 | f09da76e | Kostas Papadimitriou | # 1. Redistributions of source code must retain the above
|
8 | f09da76e | Kostas Papadimitriou | # copyright notice, this list of conditions and the following
|
9 | f09da76e | Kostas Papadimitriou | # disclaimer.
|
10 | f09da76e | Kostas Papadimitriou | #
|
11 | f09da76e | Kostas Papadimitriou | # 2. Redistributions in binary form must reproduce the above
|
12 | f09da76e | Kostas Papadimitriou | # copyright notice, this list of conditions and the following
|
13 | f09da76e | Kostas Papadimitriou | # disclaimer in the documentation and/or other materials
|
14 | f09da76e | Kostas Papadimitriou | # provided with the distribution.
|
15 | f09da76e | Kostas Papadimitriou | #
|
16 | f09da76e | Kostas Papadimitriou | # THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
|
17 | f09da76e | Kostas Papadimitriou | # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
18 | f09da76e | Kostas Papadimitriou | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
19 | f09da76e | Kostas Papadimitriou | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
|
20 | f09da76e | Kostas Papadimitriou | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
21 | f09da76e | Kostas Papadimitriou | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
22 | f09da76e | Kostas Papadimitriou | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
23 | f09da76e | Kostas Papadimitriou | # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
24 | f09da76e | Kostas Papadimitriou | # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
25 | f09da76e | Kostas Papadimitriou | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
26 | f09da76e | Kostas Papadimitriou | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
27 | f09da76e | Kostas Papadimitriou | # POSSIBILITY OF SUCH DAMAGE.
|
28 | f09da76e | Kostas Papadimitriou | #
|
29 | f09da76e | Kostas Papadimitriou | # The views and conclusions contained in the software and
|
30 | f09da76e | Kostas Papadimitriou | # documentation are those of the authors and should not be
|
31 | f09da76e | Kostas Papadimitriou | # interpreted as representing official policies, either expressed
|
32 | f09da76e | Kostas Papadimitriou | # or implied, of GRNET S.A.
|
33 | f09da76e | Kostas Papadimitriou | |
34 | f09da76e | Kostas Papadimitriou | |
35 | f09da76e | Kostas Papadimitriou | from contextlib import contextmanager |
36 | d2b8ec7b | Christos Stavrakakis | from django.test import TestCase |
37 | d2b8ec7b | Christos Stavrakakis | from django.utils import simplejson as json |
38 | dcb8545f | Christos Stavrakakis | from synnefo.util import text |
39 | d2b8ec7b | Christos Stavrakakis | from mock import patch |
40 | f09da76e | Kostas Papadimitriou | |
41 | f09da76e | Kostas Papadimitriou | |
42 | f09da76e | Kostas Papadimitriou | @contextmanager
|
43 | f09da76e | Kostas Papadimitriou | def override_settings(settings, **kwargs): |
44 | f09da76e | Kostas Papadimitriou | """
|
45 | f09da76e | Kostas Papadimitriou | Helper context manager to override django settings within the provided
|
46 | f09da76e | Kostas Papadimitriou | context.
|
47 | f09da76e | Kostas Papadimitriou |
|
48 | f09da76e | Kostas Papadimitriou | All keyword arguments provided are set to the django settings object and
|
49 | f09da76e | Kostas Papadimitriou | get reverted/removed when the manager exits.
|
50 | f09da76e | Kostas Papadimitriou |
|
51 | f09da76e | Kostas Papadimitriou | >>> from synnefo.util.testing import override_settings
|
52 | f09da76e | Kostas Papadimitriou | >>> from django.conf import settings
|
53 | f09da76e | Kostas Papadimitriou | >>> with override_settings(settings, DEBUG=True):
|
54 | f09da76e | Kostas Papadimitriou | >>> assert settings.DEBUG == True
|
55 | f09da76e | Kostas Papadimitriou |
|
56 | f09da76e | Kostas Papadimitriou | The special arguemnt ``prefix`` can be set to prefix all setting keys with
|
57 | f09da76e | Kostas Papadimitriou | the provided value.
|
58 | f09da76e | Kostas Papadimitriou |
|
59 | f09da76e | Kostas Papadimitriou | >>> from django.conf import settings
|
60 | f09da76e | Kostas Papadimitriou | >>> from django.core import mail
|
61 | f09da76e | Kostas Papadimitriou | >>> with override_settings(settings, CONTACT_EMAILS=['kpap@grnet.gr'],
|
62 | f09da76e | Kostas Papadimitriou | >>> prefix='MYAPP_'):
|
63 | f09da76e | Kostas Papadimitriou | >>> from django.core.mail import send_mail
|
64 | f09da76e | Kostas Papadimitriou | >>> send_mail("hello", "I love you kpap", settings.DEFAULT_FROM_EMAIL,
|
65 | f09da76e | Kostas Papadimitriou | >>> settings.MYAPP_CONTACT_EMAILS)
|
66 | f09da76e | Kostas Papadimitriou | >>> assert 'kpap@grnet.gr' in mail.mailbox[0].recipients()
|
67 | f09da76e | Kostas Papadimitriou |
|
68 | f09da76e | Kostas Papadimitriou | If you plan to reuse it
|
69 | f09da76e | Kostas Papadimitriou |
|
70 | f09da76e | Kostas Papadimitriou | >>> import functools
|
71 | f09da76e | Kostas Papadimitriou | >>> from synnefo.util.testing import override_settings
|
72 | f09da76e | Kostas Papadimitriou | >>> from django.conf import settings
|
73 | f09da76e | Kostas Papadimitriou | >>> myapp_settings = functools.partial(override_settings, prefix='MYAPP_')
|
74 | f09da76e | Kostas Papadimitriou | >>> with myapp_settings(CONTACT_EMAILS=['kpap@grnet.gr'])
|
75 | f09da76e | Kostas Papadimitriou | >>> assert settings.MYAPP_CONTACT_EMAILS == ['kpap@grnet.gr']
|
76 | f09da76e | Kostas Papadimitriou |
|
77 | f09da76e | Kostas Papadimitriou | """
|
78 | f09da76e | Kostas Papadimitriou | |
79 | f09da76e | Kostas Papadimitriou | _prefix = kwargs.get('prefix', '') |
80 | f09da76e | Kostas Papadimitriou | prefix = lambda key: '%s%s' % (_prefix, key) |
81 | f09da76e | Kostas Papadimitriou | |
82 | f09da76e | Kostas Papadimitriou | oldkeys = [k for k in dir(settings) if k.upper() == k] |
83 | f09da76e | Kostas Papadimitriou | oldsettings = dict([(k, getattr(settings, k)) for k in oldkeys]) |
84 | f09da76e | Kostas Papadimitriou | |
85 | f09da76e | Kostas Papadimitriou | toremove = [] |
86 | f09da76e | Kostas Papadimitriou | for key, value in kwargs.iteritems(): |
87 | f09da76e | Kostas Papadimitriou | key = prefix(key) |
88 | f09da76e | Kostas Papadimitriou | if not hasattr(settings, key): |
89 | f09da76e | Kostas Papadimitriou | toremove.append(key) |
90 | f09da76e | Kostas Papadimitriou | setattr(settings, key, value)
|
91 | f09da76e | Kostas Papadimitriou | |
92 | f09da76e | Kostas Papadimitriou | yield
|
93 | f09da76e | Kostas Papadimitriou | |
94 | f09da76e | Kostas Papadimitriou | # Remove keys that didn't exist
|
95 | f09da76e | Kostas Papadimitriou | for key in toremove: |
96 | f09da76e | Kostas Papadimitriou | delattr(settings, key)
|
97 | f09da76e | Kostas Papadimitriou | |
98 | f09da76e | Kostas Papadimitriou | # Remove keys that added during the execution of the context
|
99 | f09da76e | Kostas Papadimitriou | if kwargs.get('reset_changes', True): |
100 | f09da76e | Kostas Papadimitriou | newkeys = [k for k in dir(settings) if k.upper() == k] |
101 | f09da76e | Kostas Papadimitriou | for key in newkeys: |
102 | f09da76e | Kostas Papadimitriou | if not key in oldkeys: |
103 | f09da76e | Kostas Papadimitriou | delattr(settings, key)
|
104 | f09da76e | Kostas Papadimitriou | |
105 | f09da76e | Kostas Papadimitriou | # Revert old keys
|
106 | f09da76e | Kostas Papadimitriou | for key in oldkeys: |
107 | f09da76e | Kostas Papadimitriou | if key == key.upper():
|
108 | f09da76e | Kostas Papadimitriou | setattr(settings, key, oldsettings.get(key))
|
109 | f09da76e | Kostas Papadimitriou | |
110 | f09da76e | Kostas Papadimitriou | |
111 | f09da76e | Kostas Papadimitriou | def with_settings(settings, prefix='', **override): |
112 | f09da76e | Kostas Papadimitriou | def wrapper(func): |
113 | f09da76e | Kostas Papadimitriou | def inner(*args, **kwargs): |
114 | f09da76e | Kostas Papadimitriou | with override_settings(settings, prefix=prefix, **override):
|
115 | f09da76e | Kostas Papadimitriou | ret = func(*args, **kwargs) |
116 | f09da76e | Kostas Papadimitriou | return ret
|
117 | f09da76e | Kostas Papadimitriou | return inner
|
118 | f09da76e | Kostas Papadimitriou | return wrapper
|
119 | d2b8ec7b | Christos Stavrakakis | |
120 | f3787696 | Sofia Papagiannaki | serial = 0
|
121 | f3787696 | Sofia Papagiannaki | |
122 | d2b8ec7b | Christos Stavrakakis | |
123 | d2b8ec7b | Christos Stavrakakis | @contextmanager
|
124 | d2b8ec7b | Christos Stavrakakis | def astakos_user(user): |
125 | d2b8ec7b | Christos Stavrakakis | """
|
126 | d2b8ec7b | Christos Stavrakakis | Context manager to mock astakos response.
|
127 | d2b8ec7b | Christos Stavrakakis |
|
128 | d2b8ec7b | Christos Stavrakakis | usage:
|
129 | d2b8ec7b | Christos Stavrakakis | with astakos_user("user@user.com"):
|
130 | d2b8ec7b | Christos Stavrakakis | .... make api calls ....
|
131 | d2b8ec7b | Christos Stavrakakis |
|
132 | d2b8ec7b | Christos Stavrakakis | """
|
133 | d2b8ec7b | Christos Stavrakakis | with patch("snf_django.lib.api.get_token") as get_token: |
134 | d2b8ec7b | Christos Stavrakakis | get_token.return_value = "DummyToken"
|
135 | d2b8ec7b | Christos Stavrakakis | with patch('astakosclient.AstakosClient.get_user_info') as m: |
136 | 0a70d2c5 | Christos Stavrakakis | m.return_value = {"uuid": text.udec(user, 'utf8')} |
137 | f3787696 | Sofia Papagiannaki | with patch('astakosclient.AstakosClient.get_quotas') as m2: |
138 | f3787696 | Sofia Papagiannaki | m2.return_value = { |
139 | f3787696 | Sofia Papagiannaki | "system": {
|
140 | f3787696 | Sofia Papagiannaki | "pithos.diskspace": {
|
141 | f3787696 | Sofia Papagiannaki | "usage": 0, |
142 | 3a19e99b | Sofia Papagiannaki | "limit": 1073741824, # 1GB |
143 | f3787696 | Sofia Papagiannaki | "pending": 0 |
144 | f3787696 | Sofia Papagiannaki | } |
145 | f3787696 | Sofia Papagiannaki | } |
146 | f3787696 | Sofia Papagiannaki | } |
147 | 7f59d209 | Christos Stavrakakis | issue_fun = "astakosclient.AstakosClient.issue_one_commission"
|
148 | 7f59d209 | Christos Stavrakakis | with patch(issue_fun) as m3: |
149 | f3787696 | Sofia Papagiannaki | serials = [] |
150 | f3787696 | Sofia Papagiannaki | append = serials.append |
151 | f3787696 | Sofia Papagiannaki | |
152 | f3787696 | Sofia Papagiannaki | def get_serial(*args, **kwargs): |
153 | f3787696 | Sofia Papagiannaki | global serial
|
154 | f3787696 | Sofia Papagiannaki | serial += 1
|
155 | f3787696 | Sofia Papagiannaki | append(serial) |
156 | f3787696 | Sofia Papagiannaki | return serial
|
157 | f3787696 | Sofia Papagiannaki | |
158 | f3787696 | Sofia Papagiannaki | m3.side_effect = get_serial |
159 | 7f59d209 | Christos Stavrakakis | resolv_fun = \ |
160 | 7f59d209 | Christos Stavrakakis | 'astakosclient.AstakosClient.resolve_commissions'
|
161 | 7f59d209 | Christos Stavrakakis | with patch(resolv_fun) as m4: |
162 | f3787696 | Sofia Papagiannaki | m4.return_value = {'accepted': serials,
|
163 | f3787696 | Sofia Papagiannaki | 'rejected': [],
|
164 | f3787696 | Sofia Papagiannaki | 'failed': []}
|
165 | 7f59d209 | Christos Stavrakakis | users_fun = 'astakosclient.AstakosClient.get_usernames'
|
166 | 7f59d209 | Christos Stavrakakis | with patch(users_fun) as m5: |
167 | f3787696 | Sofia Papagiannaki | |
168 | f3787696 | Sofia Papagiannaki | def get_usernames(*args, **kwargs): |
169 | f3787696 | Sofia Papagiannaki | uuids = args[-1]
|
170 | f3787696 | Sofia Papagiannaki | return dict((uuid, uuid) for uuid in uuids) |
171 | f3787696 | Sofia Papagiannaki | |
172 | f3787696 | Sofia Papagiannaki | m5.side_effect = get_usernames |
173 | f3787696 | Sofia Papagiannaki | yield
|
174 | 184a2a8c | Christos Stavrakakis | |
175 | 184a2a8c | Christos Stavrakakis | |
176 | 2c6ac437 | Christos Stavrakakis | serial = 0
|
177 | 2c6ac437 | Christos Stavrakakis | |
178 | 2c6ac437 | Christos Stavrakakis | |
179 | 184a2a8c | Christos Stavrakakis | @contextmanager
|
180 | 184a2a8c | Christos Stavrakakis | def mocked_quotaholder(success=True): |
181 | 27899d6f | Christos Stavrakakis | with patch("synnefo.quotas.Quotaholder.get") as astakos: |
182 | 184a2a8c | Christos Stavrakakis | global serial
|
183 | f2080d16 | Christos Stavrakakis | serial += 10
|
184 | f2080d16 | Christos Stavrakakis | |
185 | f2080d16 | Christos Stavrakakis | def foo(*args, **kwargs): |
186 | f2080d16 | Christos Stavrakakis | return (len(astakos.return_value.issue_one_commission.mock_calls) + |
187 | f2080d16 | Christos Stavrakakis | serial) |
188 | f2080d16 | Christos Stavrakakis | astakos.return_value.issue_one_commission.side_effect = foo |
189 | 27899d6f | Christos Stavrakakis | astakos.return_value.resolve_commissions.return_value = {"failed": []}
|
190 | 41a7fae7 | Christos Stavrakakis | yield astakos.return_value
|
191 | 184a2a8c | Christos Stavrakakis | |
192 | d2b8ec7b | Christos Stavrakakis | |
193 | d2b8ec7b | Christos Stavrakakis | class BaseAPITest(TestCase): |
194 | d2b8ec7b | Christos Stavrakakis | def get(self, url, user='user', *args, **kwargs): |
195 | d2b8ec7b | Christos Stavrakakis | with astakos_user(user):
|
196 | e5a47968 | Christos Stavrakakis | with mocked_quotaholder():
|
197 | e5a47968 | Christos Stavrakakis | response = self.client.get(url, *args, **kwargs)
|
198 | d2b8ec7b | Christos Stavrakakis | return response
|
199 | d2b8ec7b | Christos Stavrakakis | |
200 | d2b8ec7b | Christos Stavrakakis | def delete(self, url, user='user'): |
201 | d2b8ec7b | Christos Stavrakakis | with astakos_user(user):
|
202 | 5b8a9240 | Christos Stavrakakis | with mocked_quotaholder() as m: |
203 | 5b8a9240 | Christos Stavrakakis | self.mocked_quotaholder = m
|
204 | e5a47968 | Christos Stavrakakis | response = self.client.delete(url)
|
205 | d2b8ec7b | Christos Stavrakakis | return response
|
206 | d2b8ec7b | Christos Stavrakakis | |
207 | d2b8ec7b | Christos Stavrakakis | def post(self, url, user='user', params={}, ctype='json', *args, **kwargs): |
208 | d2b8ec7b | Christos Stavrakakis | if ctype == 'json': |
209 | d2b8ec7b | Christos Stavrakakis | content_type = 'application/json'
|
210 | d2b8ec7b | Christos Stavrakakis | with astakos_user(user):
|
211 | 5b8a9240 | Christos Stavrakakis | with mocked_quotaholder() as m: |
212 | 5b8a9240 | Christos Stavrakakis | self.mocked_quotaholder = m
|
213 | e5a47968 | Christos Stavrakakis | response = self.client.post(url, params,
|
214 | e5a47968 | Christos Stavrakakis | content_type=content_type, |
215 | e5a47968 | Christos Stavrakakis | *args, **kwargs) |
216 | d2b8ec7b | Christos Stavrakakis | return response
|
217 | d2b8ec7b | Christos Stavrakakis | |
218 | d2b8ec7b | Christos Stavrakakis | def put(self, url, user='user', params={}, ctype='json', *args, **kwargs): |
219 | d2b8ec7b | Christos Stavrakakis | if ctype == 'json': |
220 | d2b8ec7b | Christos Stavrakakis | content_type = 'application/json'
|
221 | d2b8ec7b | Christos Stavrakakis | with astakos_user(user):
|
222 | 5b8a9240 | Christos Stavrakakis | with mocked_quotaholder() as m: |
223 | 5b8a9240 | Christos Stavrakakis | self.mocked_quotaholder = m
|
224 | e5a47968 | Christos Stavrakakis | response = self.client.put(url, params,
|
225 | e5a47968 | Christos Stavrakakis | content_type=content_type, |
226 | e5a47968 | Christos Stavrakakis | *args, **kwargs) |
227 | d2b8ec7b | Christos Stavrakakis | return response
|
228 | d2b8ec7b | Christos Stavrakakis | |
229 | d2b8ec7b | Christos Stavrakakis | def assertSuccess(self, response): |
230 | f724142f | Christos Stavrakakis | self.assertTrue(response.status_code in [200, 202, 203, 204]) |
231 | d2b8ec7b | Christos Stavrakakis | |
232 | d2b8ec7b | Christos Stavrakakis | def assertFault(self, response, status_code, name): |
233 | d2b8ec7b | Christos Stavrakakis | self.assertEqual(response.status_code, status_code)
|
234 | d2b8ec7b | Christos Stavrakakis | fault = json.loads(response.content) |
235 | d2b8ec7b | Christos Stavrakakis | self.assertEqual(fault.keys(), [name])
|
236 | d2b8ec7b | Christos Stavrakakis | |
237 | d2b8ec7b | Christos Stavrakakis | def assertBadRequest(self, response): |
238 | d2b8ec7b | Christos Stavrakakis | self.assertFault(response, 400, 'badRequest') |
239 | d2b8ec7b | Christos Stavrakakis | |
240 | fde2c1f7 | Christos Stavrakakis | def assertConflict(self, response): |
241 | fde2c1f7 | Christos Stavrakakis | self.assertFault(response, 409, 'conflict') |
242 | fde2c1f7 | Christos Stavrakakis | |
243 | d2b8ec7b | Christos Stavrakakis | def assertItemNotFound(self, response): |
244 | d2b8ec7b | Christos Stavrakakis | self.assertFault(response, 404, 'itemNotFound') |
245 | aba462a2 | Giorgos Korfiatis | |
246 | 2ad4eb80 | Kostas Papadimitriou | def assertMethodNotAllowed(self, response): |
247 | 2ad4eb80 | Kostas Papadimitriou | self.assertFault(response, 400, 'badRequest') |
248 | 2ad4eb80 | Kostas Papadimitriou | try:
|
249 | 2ad4eb80 | Kostas Papadimitriou | error = json.loads(response.content) |
250 | 2ad4eb80 | Kostas Papadimitriou | except ValueError: |
251 | 2ad4eb80 | Kostas Papadimitriou | self.assertTrue(False) |
252 | 2ad4eb80 | Kostas Papadimitriou | self.assertEqual(error['badRequest']['message'], 'Method not allowed') |
253 | 2ad4eb80 | Kostas Papadimitriou | |
254 | aba462a2 | Giorgos Korfiatis | |
255 | aba462a2 | Giorgos Korfiatis | # Imitate unittest assertions new in Python 2.7
|
256 | aba462a2 | Giorgos Korfiatis | |
257 | aba462a2 | Giorgos Korfiatis | class _AssertRaisesContext(object): |
258 | aba462a2 | Giorgos Korfiatis | """
|
259 | aba462a2 | Giorgos Korfiatis | A context manager used to implement TestCase.assertRaises* methods.
|
260 | aba462a2 | Giorgos Korfiatis | Adapted from unittest2.
|
261 | aba462a2 | Giorgos Korfiatis | """
|
262 | aba462a2 | Giorgos Korfiatis | |
263 | aba462a2 | Giorgos Korfiatis | def __init__(self, expected): |
264 | aba462a2 | Giorgos Korfiatis | self.expected = expected
|
265 | aba462a2 | Giorgos Korfiatis | |
266 | aba462a2 | Giorgos Korfiatis | def __enter__(self): |
267 | aba462a2 | Giorgos Korfiatis | return self |
268 | aba462a2 | Giorgos Korfiatis | |
269 | aba462a2 | Giorgos Korfiatis | def __exit__(self, exc_type, exc_value, tb): |
270 | aba462a2 | Giorgos Korfiatis | if exc_type is None: |
271 | aba462a2 | Giorgos Korfiatis | try:
|
272 | aba462a2 | Giorgos Korfiatis | exc_name = self.expected.__name__
|
273 | aba462a2 | Giorgos Korfiatis | except AttributeError: |
274 | aba462a2 | Giorgos Korfiatis | exc_name = str(self.expected) |
275 | aba462a2 | Giorgos Korfiatis | raise AssertionError( |
276 | aba462a2 | Giorgos Korfiatis | "%s not raised" % (exc_name,))
|
277 | aba462a2 | Giorgos Korfiatis | if not issubclass(exc_type, self.expected): |
278 | aba462a2 | Giorgos Korfiatis | # let unexpected exceptions pass through
|
279 | aba462a2 | Giorgos Korfiatis | return False |
280 | aba462a2 | Giorgos Korfiatis | self.exception = exc_value # store for later retrieval |
281 | aba462a2 | Giorgos Korfiatis | return True |
282 | aba462a2 | Giorgos Korfiatis | |
283 | aba462a2 | Giorgos Korfiatis | |
284 | aba462a2 | Giorgos Korfiatis | def assertRaises(excClass): |
285 | aba462a2 | Giorgos Korfiatis | return _AssertRaisesContext(excClass)
|
286 | aba462a2 | Giorgos Korfiatis | |
287 | aba462a2 | Giorgos Korfiatis | |
288 | aba462a2 | Giorgos Korfiatis | def assertGreater(x, y): |
289 | aba462a2 | Giorgos Korfiatis | assert x > y
|
290 | aba462a2 | Giorgos Korfiatis | |
291 | aba462a2 | Giorgos Korfiatis | |
292 | aba462a2 | Giorgos Korfiatis | def assertIn(x, y): |
293 | aba462a2 | Giorgos Korfiatis | assert x in y |