3 # Copyright (c) 2016 Orange and others.
5 # All rights reserved. This program and the accompanying materials
6 # are made available under the terms of the Apache License, Version 2.0
7 # which accompanies this distribution, and is available at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # pylint: disable=missing-docstring
12 """Define the class required to fully cover testcase."""
14 from datetime import datetime
24 from xtesting.core import testcase
26 __author__ = "Cedric Ollivier <cedric.ollivier@orange.com>"
29 class FakeTestCase(testcase.TestCase):
30 # pylint: disable=too-many-instance-attributes
32 def run(self, **kwargs):
33 return testcase.TestCase.EX_OK
36 class AbstractTestCaseTesting(unittest.TestCase):
38 def test_run_unimplemented(self):
39 # pylint: disable=abstract-class-instantiated
40 with self.assertRaises(TypeError):
41 testcase.TestCase(case_name="base", project_name="xtesting")
44 class TestCaseTesting(unittest.TestCase):
45 # pylint: disable=too-many-instance-attributes,too-many-public-methods
48 _project_name = "xtesting"
49 _published_result = "PASS"
50 _test_db_url = "http://testresults.opnfv.org/test/api/v1/results"
51 _headers = {'Content-Type': 'application/json'}
54 self.test = FakeTestCase(
55 case_name=self._case_name, project_name=self._project_name)
56 self.test.start_time = 1
57 self.test.stop_time = 2
58 self.test.result = 100
59 self.test.details = {"Hello": "World"}
60 os.environ['TEST_DB_URL'] = TestCaseTesting._test_db_url
61 os.environ['TEST_DB_EXT_URL'] = TestCaseTesting._test_db_url
62 os.environ['INSTALLER_TYPE'] = "installer_type"
63 os.environ['DEPLOY_SCENARIO'] = "scenario"
64 os.environ['NODE_NAME'] = "node_name"
65 os.environ['BUILD_TAG'] = "foo-daily-master-bar"
66 os.environ['S3_ENDPOINT_URL'] = "http://127.0.0.1:9000"
67 os.environ['S3_DST_URL'] = "s3://xtesting/prefix"
68 os.environ['HTTP_DST_URL'] = "http://127.0.0.1/prefix"
70 def test_run_fake(self):
71 self.assertEqual(self.test.run(), testcase.TestCase.EX_OK)
73 def _test_pushdb_missing_attribute(self):
74 self.assertEqual(self.test.push_to_db(),
75 testcase.TestCase.EX_PUSH_TO_DB_ERROR)
77 def test_pushdb_no_project_name(self):
78 self.test.project_name = None
79 self._test_pushdb_missing_attribute()
81 def test_pushdb_no_case_name(self):
82 self.test.case_name = None
83 self._test_pushdb_missing_attribute()
85 def test_pushdb_no_start_time(self):
86 self.test.start_time = None
87 self._test_pushdb_missing_attribute()
89 def test_pushdb_no_stop_time(self):
90 self.test.stop_time = None
91 self._test_pushdb_missing_attribute()
93 def _test_pushdb_missing_env(self, var):
95 self.assertEqual(self.test.push_to_db(),
96 testcase.TestCase.EX_PUSH_TO_DB_ERROR)
98 def test_pushdb_no_db_url(self):
99 self._test_pushdb_missing_env('TEST_DB_URL')
101 def test_pushdb_no_installer_type(self):
102 self._test_pushdb_missing_env('INSTALLER_TYPE')
104 def test_pushdb_no_deploy_scenario(self):
105 self._test_pushdb_missing_env('DEPLOY_SCENARIO')
107 def test_pushdb_no_node_name(self):
108 self._test_pushdb_missing_env('NODE_NAME')
110 def test_pushdb_no_build_tag(self):
111 self._test_pushdb_missing_env('BUILD_TAG')
113 @mock.patch('requests.post')
114 def test_pushdb_bad_start_time(self, mock_function=None):
115 self.test.start_time = "1"
117 self.test.push_to_db(),
118 testcase.TestCase.EX_PUSH_TO_DB_ERROR)
119 mock_function.assert_not_called()
121 @mock.patch('requests.post')
122 def test_pushdb_bad_end_time(self, mock_function=None):
123 self.test.stop_time = "2"
125 self.test.push_to_db(),
126 testcase.TestCase.EX_PUSH_TO_DB_ERROR)
127 mock_function.assert_not_called()
129 @mock.patch('requests.post')
130 def test_pushdb_skipped_test(self, mock_function=None):
131 self.test.is_skipped = True
133 self.test.push_to_db(),
134 testcase.TestCase.EX_PUSH_TO_DB_ERROR)
135 mock_function.assert_not_called()
139 "build_tag": os.environ['BUILD_TAG'],
140 "case_name": self._case_name,
141 "criteria": 'PASS' if self.test.is_successful(
142 ) == self.test.EX_OK else 'FAIL',
143 "details": self.test.details,
144 "installer": os.environ['INSTALLER_TYPE'],
145 "pod_name": os.environ['NODE_NAME'],
146 "project_name": self.test.project_name,
147 "scenario": os.environ['DEPLOY_SCENARIO'],
148 "start_date": datetime.fromtimestamp(
149 self.test.start_time).strftime('%Y-%m-%d %H:%M:%S'),
150 "stop_date": datetime.fromtimestamp(
151 self.test.stop_time).strftime('%Y-%m-%d %H:%M:%S'),
154 @mock.patch('os.path.join', return_value='')
155 @mock.patch('re.sub', return_value='')
156 @mock.patch('requests.post')
157 def _test_pushdb_version(self, *args, **kwargs):
158 payload = self._get_data()
159 payload["version"] = kwargs.get("version", "unknown")
160 self.assertEqual(self.test.push_to_db(), testcase.TestCase.EX_OK)
161 args[0].assert_called_once_with(
162 os.environ['TEST_DB_URL'],
163 data=json.dumps(payload, sort_keys=True),
164 headers=self._headers)
166 def test_pushdb_daily_job(self):
167 self._test_pushdb_version(version="master")
169 def test_pushdb_weekly_job(self):
170 os.environ['BUILD_TAG'] = 'foo-weekly-master-bar'
171 self._test_pushdb_version(version="master")
173 def test_pushdb_random_build_tag(self):
174 os.environ['BUILD_TAG'] = 'whatever'
175 self._test_pushdb_version(version="unknown")
177 @mock.patch('requests.post', return_value=mock.Mock(
178 raise_for_status=mock.Mock(
179 side_effect=requests.exceptions.HTTPError)))
180 def test_pushdb_http_errors(self, mock_function=None):
182 self.test.push_to_db(),
183 testcase.TestCase.EX_PUSH_TO_DB_ERROR)
184 mock_function.assert_called_once_with(
185 os.environ['TEST_DB_URL'],
186 data=json.dumps(self._get_data(), sort_keys=True),
187 headers=self._headers)
189 def test_check_requirements(self):
190 self.test.check_requirements()
191 self.assertEqual(self.test.is_skipped, False)
193 def test_check_criteria_missing(self):
194 self.test.criteria = None
195 self.assertEqual(self.test.is_successful(),
196 testcase.TestCase.EX_TESTCASE_FAILED)
198 def test_check_result_missing(self):
199 self.test.result = None
200 self.assertEqual(self.test.is_successful(),
201 testcase.TestCase.EX_TESTCASE_FAILED)
203 def test_check_result_failed(self):
204 # Backward compatibility
205 # It must be removed as soon as TestCase subclasses
206 # stop setting result = 'PASS' or 'FAIL'.
207 self.test.result = 'FAIL'
208 self.assertEqual(self.test.is_successful(),
209 testcase.TestCase.EX_TESTCASE_FAILED)
211 def test_check_result_pass(self):
212 # Backward compatibility
213 # It must be removed as soon as TestCase subclasses
214 # stop setting result = 'PASS' or 'FAIL'.
215 self.test.result = 'PASS'
216 self.assertEqual(self.test.is_successful(),
217 testcase.TestCase.EX_OK)
219 def test_check_result_skip(self):
220 self.test.is_skipped = True
221 self.assertEqual(self.test.is_successful(),
222 testcase.TestCase.EX_TESTCASE_SKIPPED)
224 def test_check_result_lt(self):
225 self.test.result = 50
226 self.assertEqual(self.test.is_successful(),
227 testcase.TestCase.EX_TESTCASE_FAILED)
229 def test_check_result_eq(self):
230 self.test.result = 100
231 self.assertEqual(self.test.is_successful(),
232 testcase.TestCase.EX_OK)
234 def test_check_result_gt(self):
235 self.test.criteria = 50
236 self.test.result = 100
237 self.assertEqual(self.test.is_successful(),
238 testcase.TestCase.EX_OK)
240 def test_check_result_zero(self):
241 self.test.criteria = 0
243 self.assertEqual(self.test.is_successful(),
244 testcase.TestCase.EX_TESTCASE_FAILED)
246 def test_get_duration_start_ko(self):
247 self.test.start_time = None
248 self.assertEqual(self.test.get_duration(), "XX:XX")
249 self.test.start_time = 0
250 self.assertEqual(self.test.get_duration(), "XX:XX")
252 def test_get_duration_end_ko(self):
253 self.test.stop_time = None
254 self.assertEqual(self.test.get_duration(), "XX:XX")
255 self.test.stop_time = 0
256 self.assertEqual(self.test.get_duration(), "XX:XX")
258 def test_get_invalid_duration(self):
259 self.test.start_time = 2
260 self.test.stop_time = 1
261 self.assertEqual(self.test.get_duration(), "XX:XX")
263 def test_get_zero_duration(self):
264 self.test.start_time = 2
265 self.test.stop_time = 2
266 self.assertEqual(self.test.get_duration(), "00:00")
268 def test_get_duration(self):
269 self.test.start_time = 1
270 self.test.stop_time = 180
271 self.assertEqual(self.test.get_duration(), "02:59")
273 def test_get_duration_skipped_test(self):
274 self.test.is_skipped = True
275 self.assertEqual(self.test.get_duration(), "00:00")
277 def test_str_project_name_ko(self):
278 self.test.project_name = None
279 self.assertIn("FakeTestCase object at", str(self.test))
281 def test_str_case_name_ko(self):
282 self.test.case_name = None
283 self.assertIn("FakeTestCase object at", str(self.test))
285 def test_str_pass(self):
287 with mock.patch.object(self.test, 'get_duration',
288 return_value=duration), \
289 mock.patch.object(self.test, 'is_successful',
290 return_value=testcase.TestCase.EX_OK):
291 message = str(self.test)
292 self.assertIn(self._project_name, message)
293 self.assertIn(self._case_name, message)
294 self.assertIn(duration, message)
295 self.assertIn('PASS', message)
297 def test_str_fail(self):
299 with mock.patch.object(self.test, 'get_duration',
300 return_value=duration), \
302 self.test, 'is_successful',
303 return_value=testcase.TestCase.EX_TESTCASE_FAILED):
304 message = str(self.test)
305 self.assertIn(self._project_name, message)
306 self.assertIn(self._case_name, message)
307 self.assertIn(duration, message)
308 self.assertIn('FAIL', message)
310 def test_str_skip(self):
311 self.test.is_skipped = True
312 message = str(self.test)
313 self.assertIn(self._project_name, message)
314 self.assertIn(self._case_name, message)
315 self.assertIn("00:00", message)
316 self.assertIn('SKIP', message)
318 def test_clean(self):
319 self.assertEqual(self.test.clean(), None)
321 def _test_publish_artifacts_nokw(self, key):
323 self.assertEqual(self.test.publish_artifacts(),
324 testcase.TestCase.EX_PUBLISH_ARTIFACTS_ERROR)
326 def test_publish_artifacts_exc1(self):
327 for key in ["S3_ENDPOINT_URL", "S3_DST_URL", "HTTP_DST_URL"]:
328 self._test_publish_artifacts_nokw(key)
330 @mock.patch('boto3.resource',
331 side_effect=botocore.exceptions.NoCredentialsError)
332 def test_publish_artifacts_exc2(self, *args):
333 self.assertEqual(self.test.publish_artifacts(),
334 testcase.TestCase.EX_PUBLISH_ARTIFACTS_ERROR)
335 args[0].assert_called_once_with(
336 's3', endpoint_url=os.environ['S3_ENDPOINT_URL'])
338 @mock.patch('boto3.resource', side_effect=Exception)
339 def test_publish_artifacts_exc3(self, *args):
340 self.assertEqual(self.test.publish_artifacts(),
341 testcase.TestCase.EX_PUBLISH_ARTIFACTS_ERROR)
342 args[0].assert_called_once_with(
343 's3', endpoint_url=os.environ['S3_ENDPOINT_URL'])
345 @mock.patch('boto3.resource')
346 def test_publish_artifacts_exc4(self, *args):
347 args[0].return_value.meta.client.head_bucket.side_effect = Exception
348 self.assertEqual(self.test.publish_artifacts(),
349 testcase.TestCase.EX_PUBLISH_ARTIFACTS_ERROR)
350 args[0].assert_called_once_with(
351 's3', endpoint_url=os.environ['S3_ENDPOINT_URL'])
353 @mock.patch('boto3.resource')
354 def test_publish_artifacts_exc5(self, *args):
355 error_response = {'Error': {'Code': '403'}}
356 mock_head_bucket = args[0].return_value.meta.client.head_bucket
357 mock_head_bucket.side_effect = botocore.exceptions.ClientError(
358 error_response, 'Foo')
359 self.assertEqual(self.test.publish_artifacts(),
360 testcase.TestCase.EX_PUBLISH_ARTIFACTS_ERROR)
361 args[0].assert_called_once_with(
362 's3', endpoint_url=os.environ['S3_ENDPOINT_URL'])
364 @mock.patch('mimetypes.guess_type', return_value=(None, None))
365 @mock.patch('boto3.resource')
366 @mock.patch('os.walk', return_value=[])
367 def test_publish_artifacts1(self, *args):
368 self.assertEqual(self.test.publish_artifacts(),
369 testcase.TestCase.EX_OK)
370 args[0].assert_called_once_with(self.test.res_dir)
371 args[1].assert_called_once_with(
372 's3', endpoint_url=os.environ['S3_ENDPOINT_URL'])
374 @mock.patch('mimetypes.guess_type', return_value=(None, None))
375 @mock.patch('boto3.resource')
376 @mock.patch('os.walk', return_value=[])
377 def test_publish_artifacts2(self, *args):
378 error_response = {'Error': {'Code': '404'}}
379 mock_head_bucket = args[1].return_value.meta.client.head_bucket
380 mock_head_bucket.side_effect = botocore.exceptions.ClientError(
381 error_response, 'NoSuchBucket')
382 self.assertEqual(self.test.publish_artifacts(),
383 testcase.TestCase.EX_OK)
384 args[0].assert_called_once_with(self.test.res_dir)
385 args[1].assert_called_once_with(
386 's3', endpoint_url=os.environ['S3_ENDPOINT_URL'])
388 @mock.patch('mimetypes.guess_type', return_value=(None, None))
389 @mock.patch('os.path.exists', return_value=True)
390 @mock.patch('boto3.resource')
391 @mock.patch('os.walk',
393 (testcase.TestCase.dir_results, ('',), ('bar',))])
394 def test_publish_artifacts3(self, *args):
395 self.assertEqual(self.test.publish_artifacts(),
396 testcase.TestCase.EX_OK)
397 args[0].assert_called_once_with(self.test.res_dir)
399 mock.call('s3', endpoint_url=os.environ['S3_ENDPOINT_URL']),
400 mock.call().meta.client.head_bucket(Bucket='xtesting'),
401 mock.call().Bucket('xtesting'),
402 mock.call().Bucket().upload_file(
403 '/var/lib/xtesting/results/xtesting.log',
404 'prefix/xtesting.log',
406 ExtraArgs={'ContentType': 'application/octet-stream'}),
407 mock.call().Bucket('xtesting'),
408 mock.call().Bucket().upload_file(
409 '/var/lib/xtesting/results/xtesting.debug.log',
410 'prefix/xtesting.debug.log',
412 ExtraArgs={'ContentType': 'application/octet-stream'}),
413 mock.call().Bucket('xtesting'),
414 mock.call().Bucket().upload_file(
415 '/var/lib/xtesting/results/bar', 'prefix/bar',
417 ExtraArgs={'ContentType': 'application/octet-stream'})]
418 self.assertEqual(args[1].mock_calls, expected)
420 @mock.patch('mimetypes.guess_type', return_value=('text/plain', None))
421 @mock.patch('os.path.exists', return_value=True)
422 @mock.patch('boto3.resource')
423 @mock.patch('os.walk',
425 (testcase.TestCase.dir_results, ('',), ('bar',))])
426 def test_publish_artifacts4(self, *args):
427 self.assertEqual(self.test.publish_artifacts(),
428 testcase.TestCase.EX_OK)
429 args[0].assert_called_once_with(self.test.res_dir)
431 mock.call('s3', endpoint_url=os.environ['S3_ENDPOINT_URL']),
432 mock.call().meta.client.head_bucket(Bucket='xtesting'),
433 mock.call().Bucket('xtesting'),
434 mock.call().Bucket().upload_file(
435 '/var/lib/xtesting/results/xtesting.log',
436 'prefix/xtesting.log',
438 ExtraArgs={'ContentType': 'text/plain'}),
439 mock.call().Bucket('xtesting'),
440 mock.call().Bucket().upload_file(
441 '/var/lib/xtesting/results/xtesting.debug.log',
442 'prefix/xtesting.debug.log',
444 ExtraArgs={'ContentType': 'text/plain'}),
445 mock.call().Bucket('xtesting'),
446 mock.call().Bucket().upload_file(
447 '/var/lib/xtesting/results/bar', 'prefix/bar',
449 ExtraArgs={'ContentType': 'text/plain'})]
450 self.assertEqual(args[1].mock_calls, expected)
453 if __name__ == "__main__":
454 logging.disable(logging.CRITICAL)
455 unittest.main(verbosity=2)