| 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 import datetime | 6 import datetime |
| 7 import hashlib | 7 import hashlib |
| 8 import logging | 8 import logging |
| 9 import os | 9 import os |
| 10 import random | 10 import random |
| 11 import sys | 11 import sys |
| 12 import timeit | 12 import timeit |
| 13 import unittest | 13 import unittest |
| 14 | 14 |
| 15 import test_env | 15 # Setups environment. |
| 16 test_env.setup_test_env() | 16 APP_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
| 17 sys.path.insert(0, APP_DIR) |
| 18 import test_env_handlers |
| 19 |
| 20 import webtest |
| 17 | 21 |
| 18 from google.appengine.ext import ndb | 22 from google.appengine.ext import ndb |
| 19 | 23 |
| 24 import handlers_backend |
| 25 |
| 20 from components import auth_testing | 26 from components import auth_testing |
| 21 from components import utils | 27 from components import utils |
| 22 from test_support import test_case | 28 from test_support import test_case |
| 23 | 29 |
| 24 from server import bot_management | 30 from server import bot_management |
| 25 from server import task_queues | 31 from server import task_queues |
| 26 from server import task_request | 32 from server import task_request |
| 27 from server import task_to_run | 33 from server import task_to_run |
| 28 | 34 |
| 29 | 35 |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 73 for _request, to_run in | 79 for _request, to_run in |
| 74 task_to_run.yield_next_available_task_to_dispatch( | 80 task_to_run.yield_next_available_task_to_dispatch( |
| 75 bot_dimensions, deadline) | 81 bot_dimensions, deadline) |
| 76 ] | 82 ] |
| 77 | 83 |
| 78 | 84 |
| 79 def _hash_dimensions(dimensions): | 85 def _hash_dimensions(dimensions): |
| 80 return task_to_run._hash_dimensions(utils.encode_to_json(dimensions)) | 86 return task_to_run._hash_dimensions(utils.encode_to_json(dimensions)) |
| 81 | 87 |
| 82 | 88 |
| 83 class TestCase(test_case.TestCase): | 89 class TaskToRunPrivateTest(test_case.TestCase): |
| 84 def setUp(self): | 90 def setUp(self): |
| 85 super(TestCase, self).setUp() | 91 super(TaskToRunPrivateTest, self).setUp() |
| 86 auth_testing.mock_get_current_identity(self) | 92 auth_testing.mock_get_current_identity(self) |
| 87 | 93 |
| 88 | |
| 89 class TaskToRunPrivateTest(TestCase): | |
| 90 def test_powerset(self): | 94 def test_powerset(self): |
| 91 # tuples of (input, expected). | 95 # tuples of (input, expected). |
| 92 # TODO(maruel): We'd want the code to deterministically try 'Windows-6.1' | 96 # TODO(maruel): We'd want the code to deterministically try 'Windows-6.1' |
| 93 # before 'Windows'. Probably do a reverse() on the values? | 97 # before 'Windows'. Probably do a reverse() on the values? |
| 94 data = [ | 98 data = [ |
| 95 ({'OS': 'Windows'}, [{'OS': 'Windows'}, {}]), | 99 ({'OS': 'Windows'}, [{'OS': 'Windows'}, {}]), |
| 96 ( | 100 ( |
| 97 {'OS': ['Windows', 'Windows-6.1']}, | 101 {'OS': ['Windows', 'Windows-6.1']}, |
| 98 [{'OS': 'Windows'}, {'OS': 'Windows-6.1'}, {}], | 102 [{'OS': 'Windows'}, {'OS': 'Windows-6.1'}, {}], |
| 99 ), | 103 ), |
| (...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 241 self.assertEqual(16384, len(items)) | 245 self.assertEqual(16384, len(items)) |
| 242 | 246 |
| 243 def test_dimensions_search_sizing_14_1(self): | 247 def test_dimensions_search_sizing_14_1(self): |
| 244 dimensions = {str(k): '01234567890123456789' for k in xrange(14)} | 248 dimensions = {str(k): '01234567890123456789' for k in xrange(14)} |
| 245 items = tuple(sorted( | 249 items = tuple(sorted( |
| 246 task_to_run._hash_dimensions(utils.encode_to_json(i)) | 250 task_to_run._hash_dimensions(utils.encode_to_json(i)) |
| 247 for i in task_to_run._powerset(dimensions))) | 251 for i in task_to_run._powerset(dimensions))) |
| 248 self.assertEqual(16384, len(items)) | 252 self.assertEqual(16384, len(items)) |
| 249 | 253 |
| 250 | 254 |
| 251 class TaskToRunApiTest(TestCase): | 255 class TaskToRunApiTest(test_env_handlers.AppTestBase): |
| 252 def setUp(self): | 256 def setUp(self): |
| 253 super(TaskToRunApiTest, self).setUp() | 257 super(TaskToRunApiTest, self).setUp() |
| 254 self.now = datetime.datetime(2014, 01, 02, 03, 04, 05, 06) | 258 self.now = datetime.datetime(2014, 01, 02, 03, 04, 05, 06) |
| 255 self.mock_now(self.now) | 259 self.mock_now(self.now) |
| 260 auth_testing.mock_get_current_identity(self) |
| 256 # The default expiration_secs for _gen_request(). | 261 # The default expiration_secs for _gen_request(). |
| 257 self.expiration_ts = self.now + datetime.timedelta(seconds=60) | 262 self.expiration_ts = self.now + datetime.timedelta(seconds=60) |
| 263 # Setup the backend to handle task queues for 'task-dimensions'. |
| 264 self.app = webtest.TestApp( |
| 265 handlers_backend.create_application(True), |
| 266 extra_environ={ |
| 267 'REMOTE_ADDR': self.source_ip, |
| 268 'SERVER_SOFTWARE': os.environ['SERVER_SOFTWARE'], |
| 269 }) |
| 270 self._enqueue_orig = self.mock(utils, 'enqueue_task', self._enqueue) |
| 258 | 271 |
| 259 def mkreq(self, req, nb_tasks=0): | 272 def _enqueue(self, *args, **kwargs): |
| 273 return self._enqueue_orig(*args, use_dedicated_module=False, **kwargs) |
| 274 |
| 275 def mkreq(self, req, nb_task=0): |
| 260 """Stores a new initialized TaskRequest. | 276 """Stores a new initialized TaskRequest. |
| 261 | 277 |
| 262 nb_task is 1 or 0. It is 1 when the request.properties.dimensions was new | 278 nb_task is 1 or 0. It is 1 when the request.properties.dimensions was new |
| 263 (unseen before) and 0 otherwise. | 279 (unseen before) and 0 otherwise. |
| 264 """ | 280 """ |
| 265 task_request.init_new_request(req, True, None) | 281 task_request.init_new_request(req, True, None) |
| 266 task_queues.assert_task(req) | 282 task_queues.assert_task(req) |
| 267 self.assertEqual(nb_tasks, self.execute_tasks()) | 283 self.assertEqual(nb_task, self.execute_tasks()) |
| 268 req.key = task_request.new_request_key() | 284 req.key = task_request.new_request_key() |
| 269 req.put() | 285 req.put() |
| 270 return req | 286 return req |
| 271 | 287 |
| 272 def _gen_new_task_to_run(self, **kwargs): | 288 def _gen_new_task_to_run(self, **kwargs): |
| 273 """Returns a TaskToRun saved in the DB.""" | 289 """Returns a TaskToRun saved in the DB.""" |
| 274 request = self.mkreq(_gen_request(**kwargs)) | 290 request = self.mkreq(_gen_request(**kwargs)) |
| 275 to_run = task_to_run.new_task_to_run(request) | 291 to_run = task_to_run.new_task_to_run(request) |
| 276 to_run.put() | 292 to_run.put() |
| 277 return to_run | 293 return to_run |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 352 data = _gen_request( | 368 data = _gen_request( |
| 353 properties={ | 369 properties={ |
| 354 'command': [u'command1', u'arg1'], | 370 'command': [u'command1', u'arg1'], |
| 355 'dimensions': request_dimensions, | 371 'dimensions': request_dimensions, |
| 356 'env': {u'foo': u'bar'}, | 372 'env': {u'foo': u'bar'}, |
| 357 'execution_timeout_secs': 30, | 373 'execution_timeout_secs': 30, |
| 358 }, | 374 }, |
| 359 priority=10, | 375 priority=10, |
| 360 created_ts=now, | 376 created_ts=now, |
| 361 expiration_ts=now+datetime.timedelta(seconds=31)) | 377 expiration_ts=now+datetime.timedelta(seconds=31)) |
| 362 task_to_run.new_task_to_run(self.mkreq(data, nb_tasks=0)).put() | 378 task_to_run.new_task_to_run(self.mkreq(data, nb_task=0)).put() |
| 363 | 379 |
| 364 expected = [ | 380 expected = [ |
| 365 { | 381 { |
| 366 'dimensions_hash': _hash_dimensions(request_dimensions), | 382 'dimensions_hash': _hash_dimensions(request_dimensions), |
| 367 'expiration_ts': self.now + datetime.timedelta(seconds=31), | 383 'expiration_ts': self.now + datetime.timedelta(seconds=31), |
| 368 'request_key': '0x7e296460f77ffdce', | 384 'request_key': '0x7e296460f77ffdce', |
| 369 # Lower priority value means higher priority. | 385 # Lower priority value means higher priority. |
| 370 'queue_number': '0x00060dc5849f1346', | 386 'queue_number': '0x00060dc5849f1346', |
| 371 }, | 387 }, |
| 372 { | 388 { |
| (...skipping 236 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 609 # Task added later but with higher priority are returned first. | 625 # Task added later but with higher priority are returned first. |
| 610 request_dimensions_1 = {u'os': u'Windows-3.1.1', u'pool': u'default'} | 626 request_dimensions_1 = {u'os': u'Windows-3.1.1', u'pool': u'default'} |
| 611 self._gen_new_task_to_run(properties=dict(dimensions=request_dimensions_1)) | 627 self._gen_new_task_to_run(properties=dict(dimensions=request_dimensions_1)) |
| 612 | 628 |
| 613 # This one is later but has higher priority. | 629 # This one is later but has higher priority. |
| 614 self.mock_now(self.now, 60) | 630 self.mock_now(self.now, 60) |
| 615 request_dimensions_2 = {u'os': u'Windows-3.1.1', u'pool': u'default'} | 631 request_dimensions_2 = {u'os': u'Windows-3.1.1', u'pool': u'default'} |
| 616 request = self.mkreq( | 632 request = self.mkreq( |
| 617 _gen_request( | 633 _gen_request( |
| 618 properties=dict(dimensions=request_dimensions_2), priority=10), | 634 properties=dict(dimensions=request_dimensions_2), priority=10), |
| 619 nb_tasks=0) | 635 nb_task=0) |
| 620 task_to_run.new_task_to_run(request).put() | 636 task_to_run.new_task_to_run(request).put() |
| 621 | 637 |
| 622 # It should return them all, in the expected order. | 638 # It should return them all, in the expected order. |
| 623 expected = [ | 639 expected = [ |
| 624 { | 640 { |
| 625 'dimensions_hash': _hash_dimensions(request_dimensions_1), | 641 'dimensions_hash': _hash_dimensions(request_dimensions_1), |
| 626 'expiration_ts': datetime.datetime(2014, 1, 2, 3, 6, 5, 6), | 642 'expiration_ts': datetime.datetime(2014, 1, 2, 3, 6, 5, 6), |
| 627 'queue_number': '0x00060dc588329a46', | 643 'queue_number': '0x00060dc588329a46', |
| 628 }, | 644 }, |
| 629 { | 645 { |
| (...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 751 task_to_run.set_lookup_cache(to_run.key, True) | 767 task_to_run.set_lookup_cache(to_run.key, True) |
| 752 self.assertEqual(False, task_to_run._lookup_cache_is_taken(to_run.key)) | 768 self.assertEqual(False, task_to_run._lookup_cache_is_taken(to_run.key)) |
| 753 | 769 |
| 754 | 770 |
| 755 if __name__ == '__main__': | 771 if __name__ == '__main__': |
| 756 if '-v' in sys.argv: | 772 if '-v' in sys.argv: |
| 757 unittest.TestCase.maxDiff = None | 773 unittest.TestCase.maxDiff = None |
| 758 logging.basicConfig( | 774 logging.basicConfig( |
| 759 level=logging.DEBUG if '-v' in sys.argv else logging.ERROR) | 775 level=logging.DEBUG if '-v' in sys.argv else logging.ERROR) |
| 760 unittest.main() | 776 unittest.main() |
| OLD | NEW |