OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright 2014 The LUCI Authors. All rights reserved. | 2 # Copyright 2014 The LUCI Authors. All rights reserved. |
3 # Use of this source code is governed under the Apache License, Version 2.0 | 3 # Use of this source code is governed under the Apache License, Version 2.0 |
4 # that can be found in the LICENSE file. | 4 # that can be found in the LICENSE file. |
5 | 5 |
6 """Integration test for the Swarming server, Swarming bot and Swarming client. | 6 """Integration test for the Swarming server, Swarming bot and Swarming client. |
7 | 7 |
8 It starts both a Swarming server and a Swarming bot and triggers tasks with the | 8 It starts both a Swarming server and a Swarming bot and triggers tasks with the |
9 Swarming client to ensure the system works end to end. | 9 Swarming client to ensure the system works end to end. |
10 """ | 10 """ |
11 | 11 |
12 import base64 | 12 import base64 |
13 import json | 13 import json |
14 import glob | 14 import glob |
15 import logging | 15 import logging |
16 import os | 16 import os |
| 17 import re |
17 import signal | 18 import signal |
18 import socket | 19 import socket |
19 import sys | 20 import sys |
20 import tempfile | 21 import tempfile |
21 import time | 22 import time |
22 import unittest | 23 import unittest |
23 import urllib | 24 import urllib |
24 | 25 |
25 APP_DIR = os.path.dirname(os.path.abspath(__file__)) | 26 APP_DIR = os.path.dirname(os.path.abspath(__file__)) |
26 BOT_DIR = os.path.join(APP_DIR, 'swarming_bot') | 27 BOT_DIR = os.path.join(APP_DIR, 'swarming_bot') |
(...skipping 240 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
267 'python', '-u', '-c', 'print(\'' + invalid_bytes + '\')', | 268 'python', '-u', '-c', 'print(\'' + invalid_bytes + '\')', |
268 ] | 269 ] |
269 summary = self.gen_expected( | 270 summary = self.gen_expected( |
270 name=u'non_utf8', | 271 name=u'non_utf8', |
271 # The string is mostly converted to 'Replacement Character'. | 272 # The string is mostly converted to 'Replacement Character'. |
272 outputs=[u'A\ufeff\ufffd\ufffd\ufffdsfs\ufffd(B\n']) | 273 outputs=[u'A\ufeff\ufffd\ufffd\ufffdsfs\ufffd(B\n']) |
273 self.assertOneTask(args, summary, {}) | 274 self.assertOneTask(args, summary, {}) |
274 | 275 |
275 def test_invalid_command(self): | 276 def test_invalid_command(self): |
276 args = ['-T', 'invalid', '--', 'unknown_invalid_command'] | 277 args = ['-T', 'invalid', '--', 'unknown_invalid_command'] |
277 err = ( | |
278 '[Error 2] The system cannot find the file specified' | |
279 if sys.platform == 'win32' else '[Errno 2] No such file or directory') | |
280 summary = self.gen_expected( | 278 summary = self.gen_expected( |
281 name=u'invalid', | 279 name=u'invalid', |
282 exit_codes=[1], | 280 exit_codes=[1], |
283 failure=True, | 281 failure=True, |
284 outputs=[ | 282 outputs=re.compile( |
285 u'Command "unknown_invalid_command" failed to start.\n' | 283 u'^<The executable does not exist or a dependent library is ' |
286 u'Error: %s' % err, | 284 u'missing>')) |
287 ]) | |
288 self.assertOneTask(args, summary, {}) | 285 self.assertOneTask(args, summary, {}) |
289 | 286 |
290 def test_hard_timeout(self): | 287 def test_hard_timeout(self): |
291 args = [ | 288 args = [ |
292 # Need to flush to ensure it will be sent to the server. | 289 # Need to flush to ensure it will be sent to the server. |
293 '-T', 'hard_timeout', '--hard-timeout', '1', '--', | 290 '-T', 'hard_timeout', '--hard-timeout', '1', '--', |
294 'python', '-u', '-c', | 291 'python', '-u', '-c', |
295 'import time,sys; sys.stdout.write(\'hi\\n\'); ' | 292 'import time,sys; sys.stdout.write(\'hi\\n\'); ' |
296 'sys.stdout.flush(); time.sleep(120)', | 293 'sys.stdout.flush(); time.sleep(120)', |
297 ] | 294 ] |
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
510 actual_files.pop('summary.json') | 507 actual_files.pop('summary.json') |
511 self.assertEqual(expected_files, actual_files) | 508 self.assertEqual(expected_files, actual_files) |
512 finally: | 509 finally: |
513 file_path.rmtree(tmpdir) | 510 file_path.rmtree(tmpdir) |
514 | 511 |
515 def assertResults(self, expected, result): | 512 def assertResults(self, expected, result): |
516 self.assertEqual(['shards'], result.keys()) | 513 self.assertEqual(['shards'], result.keys()) |
517 self.assertEqual(1, len(result['shards'])) | 514 self.assertEqual(1, len(result['shards'])) |
518 self.assertTrue(result['shards'][0]) | 515 self.assertTrue(result['shards'][0]) |
519 result = result['shards'][0].copy() | 516 result = result['shards'][0].copy() |
| 517 self.assertFalse(result.get('abandoned_ts')) |
520 # These are not deterministic (or I'm too lazy to calculate the value). | 518 # These are not deterministic (or I'm too lazy to calculate the value). |
521 if expected.get('performance_stats'): | 519 if expected.get('performance_stats'): |
522 self.assertLess( | 520 self.assertLess( |
523 0, result['performance_stats'].pop('bot_overhead')) | 521 0, result['performance_stats'].pop('bot_overhead')) |
524 self.assertLess( | 522 self.assertLess( |
525 0, result['performance_stats']['isolated_download'].pop('duration')) | 523 0, result['performance_stats']['isolated_download'].pop('duration')) |
526 self.assertLess( | 524 self.assertLess( |
527 0, result['performance_stats']['isolated_upload'].pop('duration')) | 525 0, result['performance_stats']['isolated_upload'].pop('duration')) |
528 for k in ('isolated_download', 'isolated_upload'): | 526 for k in ('isolated_download', 'isolated_upload'): |
529 for j in ('items_cold', 'items_hot'): | 527 for j in ('items_cold', 'items_hot'): |
530 result['performance_stats'][k][j] = large.unpack( | 528 result['performance_stats'][k][j] = large.unpack( |
531 base64.b64decode(result['performance_stats'][k].get(j, ''))) | 529 base64.b64decode(result['performance_stats'][k].get(j, ''))) |
532 else: | 530 else: |
533 perf_stats = result.get('performance_stats') | 531 perf_stats = result.pop('performance_stats', None) |
534 if perf_stats: | 532 if perf_stats: |
535 # Ignore bot_overhead, everything else should be empty. | 533 # Ignore bot_overhead, everything else should be empty. |
536 perf_stats.pop('bot_overhead', None) | 534 perf_stats.pop('bot_overhead', None) |
537 self.assertFalse(perf_stats) | 535 self.assertFalse(perf_stats) |
538 | 536 |
539 bot_version = result.pop('bot_version') | 537 bot_version = result.pop('bot_version') |
540 self.assertTrue(bot_version) | 538 self.assertTrue(bot_version) |
541 self.assertLess(0, result.pop('costs_usd')) | 539 self.assertLess(0, result.pop('costs_usd')) |
542 self.assertTrue(result.pop('created_ts')) | 540 self.assertTrue(result.pop('created_ts')) |
543 self.assertTrue(result.pop('completed_ts')) | 541 self.assertTrue(result.pop('completed_ts')) |
544 self.assertLess(0, result.pop('durations')) | 542 self.assertLess(0, result.pop('durations')) |
545 self.assertTrue(result.pop('id')) | 543 self.assertTrue(result.pop('id')) |
546 self.assertTrue(result.pop('modified_ts')) | 544 self.assertTrue(result.pop('modified_ts')) |
547 self.assertTrue(result.pop('started_ts')) | 545 self.assertTrue(result.pop('started_ts')) |
| 546 |
| 547 if getattr(expected.get('outputs'), 'match', None): |
| 548 expected_outputs = expected.pop('outputs') |
| 549 outputs = '\n'.join(result.pop('outputs')) |
| 550 self.assertTrue( |
| 551 expected_outputs.match(outputs), |
| 552 '%s does not match %s' % (outputs, expected_outputs.pattern)) |
| 553 |
548 self.assertEqual(expected, result) | 554 self.assertEqual(expected, result) |
549 return bot_version | 555 return bot_version |
550 | 556 |
551 def assertOneTask(self, args, expected_summary, expected_files): | 557 def assertOneTask(self, args, expected_summary, expected_files): |
552 """Runs a single task at a time.""" | 558 """Runs a single task at a time.""" |
553 task_id = self.client.task_trigger_raw(args) | 559 task_id = self.client.task_trigger_raw(args) |
554 actual_summary, actual_files = self.client.task_collect(task_id) | 560 actual_summary, actual_files = self.client.task_collect(task_id) |
555 bot_version = self.assertResults(expected_summary, actual_summary) | 561 bot_version = self.assertResults(expected_summary, actual_summary) |
556 actual_files.pop('summary.json') | 562 actual_files.pop('summary.json') |
557 self.assertEqual(expected_files, actual_files) | 563 self.assertEqual(expected_files, actual_files) |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
633 if bot.poll() is None: | 639 if bot.poll() is None: |
634 bot.kill() | 640 bot.kill() |
635 bot.wait() | 641 bot.wait() |
636 finally: | 642 finally: |
637 cleanup(bot, client, servers, failed or verbose, leak) | 643 cleanup(bot, client, servers, failed or verbose, leak) |
638 return int(failed) | 644 return int(failed) |
639 | 645 |
640 | 646 |
641 if __name__ == '__main__': | 647 if __name__ == '__main__': |
642 sys.exit(main()) | 648 sys.exit(main()) |
OLD | NEW |