| Index: appengine/swarming/local_smoke_test.py
|
| diff --git a/appengine/swarming/local_smoke_test.py b/appengine/swarming/local_smoke_test.py
|
| index d6dbf5a6b61faae9bb63670c3de40f08bdbd4e99..2dcdbba98ca228ac67bf85f6bdecd044123b846e 100755
|
| --- a/appengine/swarming/local_smoke_test.py
|
| +++ b/appengine/swarming/local_smoke_test.py
|
| @@ -43,6 +43,33 @@ from api import os_utilities
|
| SIGNAL_TERM = -1073741510 if sys.platform == 'win32' else -signal.SIGTERM
|
|
|
|
|
| +# For the isolated tests that outputs a file named result.txt containing 'hey'.
|
| +ISOLATE_HELLO_WORLD = {
|
| + 'variables': {
|
| + 'command': ['python', '-u', 'hello_world.py'],
|
| + 'files': ['hello_world.py'],
|
| + },
|
| +}
|
| +
|
| +RESULT_HEY_ISOLATED_OUT = {
|
| + u'isolated': u'f10f4c42b38ca01726610f9575ba695468c32108',
|
| + u'isolatedserver': u'http://localhost:10050',
|
| + u'namespace': u'default-gzip',
|
| + u'view_url':
|
| + u'http://localhost:10050/browse?namespace=default-gzip'
|
| + '&hash=f10f4c42b38ca01726610f9575ba695468c32108',
|
| +}
|
| +
|
| +RESULT_HEY_OUTPUTS_REF = {
|
| + u'isolated': u'f10f4c42b38ca01726610f9575ba695468c32108',
|
| + u'isolatedserver': u'http://localhost:10050',
|
| + u'namespace': u'default-gzip',
|
| + u'view_url':
|
| + u'http://localhost:10050/browse?namespace=default-gzip'
|
| + '&hash=f10f4c42b38ca01726610f9575ba695468c32108',
|
| +}
|
| +
|
| +
|
| class SwarmingClient(object):
|
| def __init__(self, swarming_server, isolate_server):
|
| self._swarming_server = swarming_server
|
| @@ -337,12 +364,6 @@ class Test(unittest.TestCase):
|
|
|
| def test_isolated(self):
|
| # Make an isolated file, archive it.
|
| - isolate = {
|
| - 'variables': {
|
| - 'command': ['python', 'hello_world.py'],
|
| - 'files': ['hello_world.py'],
|
| - },
|
| - }
|
| hello_world = '\n'.join((
|
| 'import os',
|
| 'import sys',
|
| @@ -350,38 +371,97 @@ class Test(unittest.TestCase):
|
| 'with open(os.path.join(sys.argv[1], \'result.txt\'), \'wb\') as f:',
|
| ' f.write(\'hey\')'))
|
| expected_summary = self.gen_expected(
|
| - name=u'yo',
|
| - isolated_out={
|
| - u'isolated': u'f10f4c42b38ca01726610f9575ba695468c32108',
|
| - u'isolatedserver': u'http://localhost:10050',
|
| - u'namespace': u'default-gzip',
|
| - u'view_url':
|
| - u'http://localhost:10050/browse?namespace=default-gzip'
|
| - '&hash=f10f4c42b38ca01726610f9575ba695468c32108',
|
| - },
|
| - outputs=[
|
| - u'hi\n'
|
| - ],
|
| - outputs_ref={
|
| - u'isolated': u'f10f4c42b38ca01726610f9575ba695468c32108',
|
| - u'isolatedserver': u'http://localhost:10050',
|
| - u'namespace': u'default-gzip',
|
| - u'view_url':
|
| - u'http://localhost:10050/browse?namespace=default-gzip'
|
| - '&hash=f10f4c42b38ca01726610f9575ba695468c32108',
|
| - })
|
| + name=u'isolated_task',
|
| + isolated_out=RESULT_HEY_ISOLATED_OUT,
|
| + outputs=[u'hi\n'],
|
| + outputs_ref=RESULT_HEY_OUTPUTS_REF)
|
| + expected_files = {os.path.join('0', 'result.txt'): 'hey'}
|
| + self._run_isolated(
|
| + hello_world, 'isolated_task', ['--', '${ISOLATED_OUTDIR}'],
|
| + expected_summary, expected_files)
|
| +
|
| + def test_isolated_hard_timeout(self):
|
| + # Make an isolated file, archive it, have it time out. Similar to
|
| + # test_hard_timeout. The script doesn't handle signal so it failed the grace
|
| + # period.
|
| + hello_world = '\n'.join((
|
| + 'import os',
|
| + 'import sys',
|
| + 'import time',
|
| + 'sys.stdout.write(\'hi\\n\')',
|
| + 'sys.stdout.flush()',
|
| + 'time.sleep(120)',
|
| + 'with open(os.path.join(sys.argv[1], \'result.txt\'), \'wb\') as f:',
|
| + ' f.write(\'hey\')'))
|
| + expected_summary = self.gen_expected(
|
| + name=u'isolated_hard_timeout',
|
| + exit_codes=[SIGNAL_TERM],
|
| + failure=True,
|
| + state=0x40) # task_result.State.TIMED_OUT
|
| + self._run_isolated(
|
| + hello_world, 'isolated_hard_timeout',
|
| + ['--hard-timeout', '1', '--', '${ISOLATED_OUTDIR}'],
|
| + expected_summary, {})
|
| +
|
| + def test_isolated_hard_timeout_grace(self):
|
| + # Make an isolated file, archive it, have it time out. Similar to
|
| + # test_hard_timeout. The script handles signal so it send results back.
|
| + hello_world = '\n'.join((
|
| + 'import os',
|
| + 'import signal',
|
| + 'import sys',
|
| + 'import time',
|
| + 'l = []',
|
| + 'def handler(signum, _):',
|
| + ' l.append(signum)',
|
| + ' sys.stdout.write(\'got signal %d\\n\' % signum)',
|
| + ' sys.stdout.flush()',
|
| + 'signal.signal(signal.%s, handler)' %
|
| + ('SIGBREAK' if sys.platform == 'win32' else 'SIGTERM'),
|
| + 'sys.stdout.write(\'hi\\n\')',
|
| + 'sys.stdout.flush()',
|
| + 'while not l:',
|
| + ' try:',
|
| + ' time.sleep(0.01)',
|
| + ' except IOError:',
|
| + ' print(\'ioerror\')',
|
| + 'with open(os.path.join(sys.argv[1], \'result.txt\'), \'wb\') as f:',
|
| + ' f.write(\'hey\')'))
|
| + expected_summary = self.gen_expected(
|
| + name=u'isolated_hard_timeout_grace',
|
| + isolated_out=RESULT_HEY_ISOLATED_OUT,
|
| + outputs=[u'hi\ngot signal 15\n'],
|
| + outputs_ref=RESULT_HEY_OUTPUTS_REF,
|
| + failure=True,
|
| + state=0x40) # task_result.State.TIMED_OUT
|
| expected_files = {os.path.join('0', 'result.txt'): 'hey'}
|
| + # Sadly we have to use a slow timeout here as this test can be flaky; it
|
| + # will not result in the expected result if run_isolated receives the signal
|
| + # before it had time to download the files, map them and start the child
|
| + # process, which then had time to setup its handler.
|
| + # TODO(maruel): When using run_isolated, have run_isolated enforces the hard
|
| + # timeout, while I/O timeout is still enforced by task_runner. This is due
|
| + # to run_isolated not piping stdout so it doesn't know about stdout/stderr
|
| + # output. Once this is fixed, the timeout can be reduced back to 1s.
|
| + self._run_isolated(
|
| + hello_world, 'isolated_hard_timeout_grace',
|
| + ['--hard-timeout', '3', '--', '${ISOLATED_OUTDIR}'],
|
| + expected_summary, expected_files)
|
| +
|
| + def _run_isolated(self, hello_world, name, args, expected_summary,
|
| + expected_files):
|
| + # Shared code for all test_isolated_* test cases.
|
| tmpdir = tempfile.mkdtemp(prefix='swarming_smoke')
|
| try:
|
| isolate_path = os.path.join(tmpdir, 'i.isolate')
|
| isolated_path = os.path.join(tmpdir, 'i.isolated')
|
| with open(isolate_path, 'wb') as f:
|
| - json.dump(isolate, f)
|
| + json.dump(ISOLATE_HELLO_WORLD, f)
|
| with open(os.path.join(tmpdir, 'hello_world.py'), 'wb') as f:
|
| f.write(hello_world)
|
| isolated_hash = self.client.isolate(isolate_path, isolated_path)
|
| task_id = self.client.task_trigger_isolated(
|
| - 'yo', isolated_hash, extra=['--', '${ISOLATED_OUTDIR}'])
|
| + name, isolated_hash, extra=args)
|
| actual_summary, actual_files = self.client.task_collect(task_id)
|
| self.assertResults(expected_summary, actual_summary)
|
| actual_files.pop('summary.json')
|
| @@ -417,16 +497,16 @@ class Test(unittest.TestCase):
|
| return bot_version
|
|
|
|
|
| -def cleanup(bot, client, servers, print_all):
|
| +def cleanup(bot, client, servers, print_all, leak):
|
| """Kills bot, kills server, print logs if failed, delete tmpdir."""
|
| try:
|
| try:
|
| try:
|
| if bot:
|
| - bot.stop()
|
| + bot.stop(leak)
|
| finally:
|
| if servers:
|
| - servers.stop()
|
| + servers.stop(leak)
|
| finally:
|
| if print_all:
|
| if bot:
|
| @@ -436,12 +516,15 @@ def cleanup(bot, client, servers, print_all):
|
| if client:
|
| client.dump_log()
|
| finally:
|
| - if client:
|
| + if client and not leak:
|
| client.cleanup()
|
|
|
|
|
| def main():
|
| verbose = '-v' in sys.argv
|
| + leak = bool('--leak' in sys.argv)
|
| + if leak:
|
| + sys.argv.remove('--leak')
|
| if verbose:
|
| logging.basicConfig(level=logging.INFO)
|
| unittest.TestCase.maxDiff = None
|
| @@ -474,7 +557,7 @@ def main():
|
| if bot:
|
| bot.kill()
|
| finally:
|
| - cleanup(bot, client, servers, failed or verbose)
|
| + cleanup(bot, client, servers, failed or verbose, leak)
|
| return int(failed)
|
|
|
|
|
|
|