| #!/usr/bin/env python |
| # |
| # Copyright 2007 Google Inc. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # |
| """Tests for google.apphosting.tools.devappserver2.instance.""" |
| |
| |
| import time |
| import unittest |
| |
| import google |
| import mox |
| |
| from google.appengine.tools.devappserver2 import instance |
| from google.appengine.tools.devappserver2 import wsgi_request_info |
| |
| |
| class TestInstance(unittest.TestCase): |
| """Tests for instance.Instance.""" |
| |
| def setUp(self): |
| self.mox = mox.Mox() |
| self.proxy = self.mox.CreateMock(instance.RuntimeProxy) |
| self.environ = object() |
| self.start_response = object() |
| self.url_map = object() |
| self.match = object() |
| self.request_id = object() |
| self.response = [object()] |
| self.request_data = self.mox.CreateMock(wsgi_request_info.WSGIRequestInfo) |
| |
| def tearDown(self): |
| self.mox.UnsetStubs() |
| |
| def test_new_instance(self): |
| inst = instance.Instance( |
| self.request_data, 'name', self.proxy, max_concurrent_requests=5, |
| expect_ready_request=True) |
| self.assertEqual(0, inst.total_requests) |
| self.assertEqual(5, inst.remaining_request_capacity) |
| self.assertEqual(0, inst.num_outstanding_requests) |
| self.assertFalse(inst.can_accept_requests) |
| self.assertTrue(inst.handling_ready_request) |
| self.assertAlmostEqual(0, inst.idle_seconds, places=2) |
| self.assertEqual(0, inst.get_latency_60s()) |
| self.assertEqual(0, inst.get_qps_60s()) |
| self.assertEqual('name', inst.instance_id) |
| |
| def test_handle(self): |
| inst = instance.Instance(self.request_data, 'name', self.proxy, |
| max_concurrent_requests=5) |
| self.mox.StubOutWithMock(inst._condition, 'notify') |
| |
| self.proxy.start() |
| self.environ = {} |
| self.request_data.set_request_instance(self.request_id, inst) |
| self.proxy.handle(self.environ, |
| self.start_response, |
| self.url_map, |
| self.match, |
| self.request_id, |
| instance.NORMAL_REQUEST).AndReturn(self.response) |
| inst._condition.notify() |
| self.mox.ReplayAll() |
| now = time.time() |
| inst._request_history.append((now - 100, now - 80)) |
| inst.start() |
| self.assertTrue(inst.can_accept_requests) |
| self.assertEqual( |
| self.response, |
| list(inst.handle(self.environ, |
| self.start_response, |
| self.url_map, |
| self.match, |
| self.request_id, |
| instance.NORMAL_REQUEST))) |
| self.mox.VerifyAll() |
| |
| self.assertEqual(1, len(inst._request_history)) |
| self.assertEqual(1, inst.total_requests) |
| self.assertEqual(5, inst.remaining_request_capacity) |
| self.assertEqual(0, inst.num_outstanding_requests) |
| self.assertTrue(0 < inst.get_qps_60s()) |
| |
| def test_handle_ready_request(self): |
| inst = instance.Instance(self.request_data, 'name', self.proxy, |
| max_concurrent_requests=5, |
| expect_ready_request=True) |
| self.mox.StubOutWithMock(inst._condition, 'notify') |
| |
| self.proxy.start() |
| self.environ = {} |
| self.request_data.set_request_instance(self.request_id, inst) |
| self.proxy.handle(self.environ, |
| self.start_response, |
| self.url_map, |
| self.match, |
| self.request_id, |
| instance.READY_REQUEST).AndReturn(self.response) |
| inst._condition.notify(5) |
| self.mox.ReplayAll() |
| inst.start() |
| self.assertFalse(inst.can_accept_requests) |
| self.assertRaises(instance.CannotAcceptRequests, |
| inst.handle, |
| self.environ, |
| self.start_response, |
| self.url_map, |
| self.match, |
| self.request_id, |
| instance.NORMAL_REQUEST) |
| self.assertEqual( |
| self.response, |
| list(inst.handle(self.environ, |
| self.start_response, |
| self.url_map, |
| self.match, |
| self.request_id, |
| instance.READY_REQUEST))) |
| self.mox.VerifyAll() |
| |
| self.assertEqual(1, inst.total_requests) |
| self.assertEqual(5, inst.remaining_request_capacity) |
| self.assertEqual(0, inst.num_outstanding_requests) |
| self.assertTrue(0 < inst.get_qps_60s()) |
| self.assertFalse(inst.handling_ready_request) |
| |
| def test_handle_background_request(self): |
| inst = instance.Instance(self.request_data, 'name', self.proxy, |
| max_concurrent_requests=5, |
| max_background_threads=2) |
| inst._num_running_background_threads = 1 |
| self.mox.StubOutWithMock(inst._condition, 'notify') |
| |
| self.proxy.start() |
| self.environ = {} |
| self.request_data.set_request_instance(self.request_id, inst) |
| self.proxy.handle(self.environ, |
| self.start_response, |
| self.url_map, |
| self.match, |
| self.request_id, |
| instance.BACKGROUND_REQUEST).AndReturn(self.response) |
| self.mox.ReplayAll() |
| inst.start() |
| self.assertTrue(inst.can_accept_requests) |
| self.assertEqual(1, inst.remaining_background_thread_capacity) |
| self.assertEqual( |
| self.response, |
| list(inst.handle(self.environ, |
| self.start_response, |
| self.url_map, |
| self.match, |
| self.request_id, |
| instance.BACKGROUND_REQUEST))) |
| self.mox.VerifyAll() |
| |
| self.assertEqual(1, inst.total_requests) |
| self.assertEqual(5, inst.remaining_request_capacity) |
| self.assertEqual(0, inst.num_outstanding_requests) |
| self.assertTrue(0 < inst.get_qps_60s()) |
| self.assertEqual(2, inst.remaining_background_thread_capacity) |
| self.assertFalse(inst.handling_ready_request) |
| |
| def test_handle_shutdown_request(self): |
| inst = instance.Instance(self.request_data, 'name', self.proxy, |
| max_concurrent_requests=1) |
| inst._num_outstanding_requests = 0 |
| self.mox.StubOutWithMock(inst._condition, 'notify_all') |
| |
| self.proxy.start() |
| self.environ = {} |
| self.request_data.set_request_instance(self.request_id, inst) |
| self.proxy.handle(self.environ, |
| self.start_response, |
| self.url_map, |
| self.match, |
| self.request_id, |
| instance.SHUTDOWN_REQUEST).AndReturn(self.response) |
| self.proxy.quit() |
| inst._condition.notify_all() |
| self.mox.ReplayAll() |
| inst.start() |
| self.assertTrue(inst.can_accept_requests) |
| self.assertFalse(inst.has_quit) |
| inst.quit(expect_shutdown=True) |
| self.assertFalse(inst.can_accept_requests) |
| self.assertTrue(inst.has_quit) |
| self.assertEqual( |
| self.response, |
| list(inst.handle(self.environ, |
| self.start_response, |
| self.url_map, |
| self.match, |
| self.request_id, |
| instance.SHUTDOWN_REQUEST))) |
| self.mox.VerifyAll() |
| |
| self.assertEqual(1, inst.total_requests) |
| self.assertEqual(1, inst.remaining_request_capacity) |
| self.assertEqual(0, inst.num_outstanding_requests) |
| self.assertTrue(0 < inst.get_qps_60s()) |
| self.assertFalse(inst._quitting) |
| self.assertTrue(inst._quit) |
| |
| def test_handle_shutdown_request_running_request(self): |
| inst = instance.Instance(self.request_data, 'name', self.proxy, |
| max_concurrent_requests=1) |
| inst._num_outstanding_requests = 1 |
| self.mox.StubOutWithMock(inst._condition, 'notify_all') |
| |
| self.proxy.start() |
| self.environ = {} |
| self.request_data.set_request_instance(self.request_id, inst) |
| self.proxy.handle(self.environ, |
| self.start_response, |
| self.url_map, |
| self.match, |
| self.request_id, |
| instance.SHUTDOWN_REQUEST).AndReturn(self.response) |
| self.mox.ReplayAll() |
| inst.start() |
| self.assertTrue(inst.can_accept_requests) |
| self.assertFalse(inst.has_quit) |
| inst.quit(expect_shutdown=True) |
| self.assertFalse(inst.can_accept_requests) |
| self.assertTrue(inst.has_quit) |
| self.assertEqual( |
| self.response, |
| list(inst.handle(self.environ, |
| self.start_response, |
| self.url_map, |
| self.match, |
| self.request_id, |
| instance.SHUTDOWN_REQUEST))) |
| self.mox.VerifyAll() |
| |
| self.assertEqual(1, inst.total_requests) |
| self.assertEqual(0, inst.remaining_request_capacity) |
| self.assertEqual(1, inst.num_outstanding_requests) |
| self.assertEqual(0, inst.idle_seconds) |
| self.assertTrue(0 < inst.get_qps_60s()) |
| self.assertTrue(inst._quitting) |
| self.assertFalse(inst._quit) |
| |
| def test_handle_before_start(self): |
| inst = instance.Instance(self.request_data, 'name', self.proxy, |
| max_concurrent_requests=5) |
| self.mox.StubOutWithMock(inst._condition, 'notify') |
| |
| self.assertRaises(instance.CannotAcceptRequests, |
| inst.handle, |
| self.environ, |
| self.start_response, |
| self.url_map, |
| self.match, |
| self.request_id, |
| instance.NORMAL_REQUEST) |
| |
| def test_handle_after_quit(self): |
| inst = instance.Instance(self.request_data, 'name', self.proxy, |
| max_concurrent_requests=5) |
| self.mox.StubOutWithMock(inst._condition, 'notify') |
| self.mox.StubOutWithMock(inst._condition, 'notify_all') |
| |
| self.proxy.start() |
| self.proxy.quit() |
| inst._condition.notify_all() |
| |
| self.mox.ReplayAll() |
| inst.start() |
| inst.quit() |
| |
| self.assertRaises(instance.CannotAcceptRequests, |
| inst.handle, |
| self.environ, |
| self.start_response, |
| self.url_map, |
| self.match, |
| self.request_id, |
| instance.NORMAL_REQUEST) |
| self.mox.VerifyAll() |
| |
| def test_handle_while_quitting(self): |
| inst = instance.Instance(self.request_data, 'name', self.proxy, |
| max_concurrent_requests=5) |
| self.mox.StubOutWithMock(inst._condition, 'notify') |
| inst._num_outstanding_requests = 1 |
| |
| self.proxy.start() |
| self.mox.ReplayAll() |
| inst.start() |
| inst.quit(allow_async=True) |
| self.mox.VerifyAll() |
| |
| self.assertRaises(instance.CannotAcceptRequests, |
| inst.handle, |
| self.environ, |
| self.start_response, |
| self.url_map, |
| self.match, |
| self.request_id, |
| instance.NORMAL_REQUEST) |
| |
| def test_handle_no_capacity(self): |
| inst = instance.Instance(self.request_data, 'name', self.proxy, |
| max_concurrent_requests=1) |
| self.mox.StubOutWithMock(inst._condition, 'notify') |
| inst._num_outstanding_requests = 1 |
| self.proxy.start() |
| self.assertRaises(instance.CannotAcceptRequests, |
| inst.handle, |
| self.environ, |
| self.start_response, |
| self.url_map, |
| self.match, |
| self.request_id, |
| instance.NORMAL_REQUEST) |
| |
| def test_reserve_background_thread_success(self): |
| inst = instance.Instance(self.request_data, 'name', self.proxy, |
| max_concurrent_requests=5, |
| max_background_threads=2) |
| inst._started = True |
| |
| self.mox.ReplayAll() |
| self.assertEqual(2, inst.remaining_background_thread_capacity) |
| inst.reserve_background_thread() |
| self.assertEqual(1, inst.remaining_background_thread_capacity) |
| self.mox.VerifyAll() |
| |
| def test_reserve_background_thread_quitting(self): |
| inst = instance.Instance(self.request_data, 'name', self.proxy, |
| max_concurrent_requests=5, |
| max_background_threads=2) |
| inst._started = True |
| inst._quitting = True |
| |
| self.mox.ReplayAll() |
| self.assertEqual(2, inst.remaining_background_thread_capacity) |
| inst.reserve_background_thread() |
| self.assertEqual(1, inst.remaining_background_thread_capacity) |
| self.mox.VerifyAll() |
| |
| def test_reserve_background_thread_no_capacity(self): |
| inst = instance.Instance(self.request_data, 'name', self.proxy, |
| max_concurrent_requests=5, |
| max_background_threads=0) |
| inst._started = True |
| self.mox.ReplayAll() |
| self.assertEqual(0, inst.remaining_background_thread_capacity) |
| self.assertRaises(instance.CannotAcceptRequests, |
| inst.reserve_background_thread) |
| self.mox.VerifyAll() |
| self.assertEqual(0, inst.remaining_background_thread_capacity) |
| |
| def test_reserve_background_thread_not_started(self): |
| inst = instance.Instance(self.request_data, 'name', self.proxy, |
| max_concurrent_requests=5, |
| max_background_threads=1) |
| self.mox.ReplayAll() |
| self.assertEqual(1, inst.remaining_background_thread_capacity) |
| self.assertRaises(instance.CannotAcceptRequests, |
| inst.reserve_background_thread) |
| self.mox.VerifyAll() |
| self.assertEqual(1, inst.remaining_background_thread_capacity) |
| |
| def test_reserve_background_thread_quit(self): |
| inst = instance.Instance(self.request_data, 'name', self.proxy, |
| max_concurrent_requests=5, |
| max_background_threads=1) |
| inst._started = True |
| inst._quit = True |
| self.mox.ReplayAll() |
| self.assertEqual(1, inst.remaining_background_thread_capacity) |
| self.assertRaises(instance.CannotAcceptRequests, |
| inst.reserve_background_thread) |
| self.mox.VerifyAll() |
| self.assertEqual(1, inst.remaining_background_thread_capacity) |
| |
| def test_reserve_background_thread_not_ready(self): |
| inst = instance.Instance(self.request_data, 'name', self.proxy, |
| max_concurrent_requests=5, |
| max_background_threads=2, |
| expect_ready_request=True) |
| inst._started = True |
| self.mox.ReplayAll() |
| self.assertEqual(2, inst.remaining_background_thread_capacity) |
| inst.reserve_background_thread() |
| self.mox.VerifyAll() |
| self.assertEqual(1, inst.remaining_background_thread_capacity) |
| |
| def test_wait_with_capacity(self): |
| inst = instance.Instance(self.request_data, 'name', self.proxy, |
| max_concurrent_requests=1) |
| inst._started = True |
| self.mox.StubOutWithMock(inst._condition, 'wait') |
| self.mox.stubs.Set(time, 'time', lambda: 0) |
| self.mox.ReplayAll() |
| self.assertTrue(inst.wait(1)) |
| self.mox.VerifyAll() |
| |
| def test_wait_waiting_for_can_accept(self): |
| inst = instance.Instance(self.request_data, 'name', self.proxy, |
| max_concurrent_requests=1, |
| expect_ready_request=True) |
| inst._started = True |
| self.mox.StubOutWithMock(inst._condition, 'wait') |
| self.time = 0 |
| self.mox.stubs.Set(time, 'time', lambda: self.time) |
| |
| def advance_time(*unused_args): |
| self.time += 10 |
| |
| inst._condition.wait(1).WithSideEffects(advance_time) |
| self.mox.ReplayAll() |
| self.assertFalse(inst.wait(1)) |
| self.mox.VerifyAll() |
| |
| def test_wait_timed_out_with_capacity(self): |
| inst = instance.Instance(self.request_data, 'name', self.proxy, |
| max_concurrent_requests=1) |
| inst._started = True |
| self.mox.StubOutWithMock(inst._condition, 'wait') |
| self.mox.ReplayAll() |
| self.assertTrue(inst.wait(0)) |
| self.mox.VerifyAll() |
| |
| def test_wait_without_capacity(self): |
| inst = instance.Instance(self.request_data, 'name', self.proxy, |
| max_concurrent_requests=0) |
| inst._started = True |
| self.mox.StubOutWithMock(inst._condition, 'wait') |
| self.time = 0 |
| self.mox.stubs.Set(time, 'time', lambda: self.time) |
| |
| def advance_time(*unused_args): |
| self.time += 10 |
| |
| inst._condition.wait(1).WithSideEffects(advance_time) |
| self.mox.ReplayAll() |
| self.assertFalse(inst.wait(1)) |
| self.mox.VerifyAll() |
| |
| def test_wait_timed_out_without_capacity(self): |
| inst = instance.Instance(self.request_data, 'name', self.proxy, |
| max_concurrent_requests=0) |
| inst._started = True |
| self.mox.StubOutWithMock(inst._condition, 'wait') |
| self.mox.ReplayAll() |
| self.assertFalse(inst.wait(0)) |
| self.mox.VerifyAll() |
| |
| def test_wait_quit_while_starting(self): |
| inst = instance.Instance(self.request_data, 'name', self.proxy, |
| max_concurrent_requests=5) |
| self.mox.StubOutWithMock(inst._condition, 'notify_all') |
| self.proxy.start().WithSideEffects(inst.quit) |
| |
| self.proxy.quit() |
| |
| self.mox.ReplayAll() |
| inst.start() |
| self.mox.VerifyAll() |
| self.assertFalse(inst.can_accept_requests) |
| |
| def test_wait_quit_while_waiting(self): |
| self.mox.stubs.Set(time, 'time', lambda: 0) |
| inst = instance.Instance(self.request_data, 'name', self.proxy, |
| max_concurrent_requests=0) |
| self.mox.StubOutWithMock(inst._condition, 'wait') |
| inst._condition.wait(1).WithSideEffects(lambda *unused_args: inst.quit()) |
| self.mox.ReplayAll() |
| self.assertFalse(inst.wait(1)) |
| self.mox.VerifyAll() |
| |
| def test_quit(self): |
| inst = instance.Instance(self.request_data, 'name', self.proxy, |
| max_concurrent_requests=5) |
| self.mox.StubOutWithMock(inst._condition, 'notify_all') |
| self.proxy.start() |
| |
| self.proxy.quit() |
| inst._condition.notify_all() |
| |
| self.mox.ReplayAll() |
| inst.start() |
| self.assertTrue(inst.can_accept_requests) |
| inst.quit() |
| self.mox.VerifyAll() |
| self.assertFalse(inst.can_accept_requests) |
| |
| def test_quit_with_request(self): |
| inst = instance.Instance(self.request_data, 'name', self.proxy, |
| max_concurrent_requests=5) |
| self.mox.StubOutWithMock(inst._condition, 'notify_all') |
| self.proxy.start() |
| |
| self.mox.ReplayAll() |
| inst.start() |
| self.mox.VerifyAll() |
| inst._num_outstanding_requests = 1 |
| self.assertRaises(instance.CannotQuitServingInstance, |
| inst.quit) |
| |
| def test_quit_with_request_force(self): |
| inst = instance.Instance(self.request_data, 'name', self.proxy, |
| max_concurrent_requests=5) |
| self.mox.StubOutWithMock(inst._condition, 'notify_all') |
| |
| inst._num_outstanding_requests = 1 |
| self.proxy.start() |
| self.proxy.quit() |
| inst._condition.notify_all() |
| |
| self.mox.ReplayAll() |
| inst.start() |
| inst.quit(force=True) |
| self.mox.VerifyAll() |
| |
| def test_quit_with_request_force_and_allow_async(self): |
| inst = instance.Instance(self.request_data, 'name', self.proxy, |
| max_concurrent_requests=5) |
| self.mox.StubOutWithMock(inst._condition, 'notify_all') |
| |
| inst._num_outstanding_requests = 1 |
| self.proxy.start() |
| self.proxy.quit() |
| inst._condition.notify_all() |
| |
| self.mox.ReplayAll() |
| inst.start() |
| inst.quit(force=True, allow_async=True) |
| self.mox.VerifyAll() |
| |
| def test_quit_with_request_allow_async(self): |
| inst = instance.Instance(self.request_data, 'name', self.proxy, |
| max_concurrent_requests=5) |
| self.mox.StubOutWithMock(inst._condition, 'notify_all') |
| |
| inst._num_outstanding_requests = 1 |
| self.proxy.start() |
| |
| self.mox.ReplayAll() |
| inst.start() |
| inst.quit(allow_async=True) |
| self.mox.VerifyAll() |
| self.assertTrue(inst._quitting) |
| |
| def test_quit_shutdown(self): |
| inst = instance.Instance(self.request_data, 'name', self.proxy, |
| max_concurrent_requests=5) |
| self.mox.StubOutWithMock(inst._condition, 'notify_all') |
| |
| inst._num_outstanding_requests = 1 |
| self.proxy.start() |
| |
| self.mox.ReplayAll() |
| inst.start() |
| inst.quit(expect_shutdown=True) |
| self.mox.VerifyAll() |
| self.assertTrue(inst._expecting_shutdown_request) |
| self.assertFalse(inst._quitting) |
| |
| def test_get_latency_60s(self): |
| inst = instance.Instance(self.request_data, 'name', self.proxy, |
| max_concurrent_requests=5) |
| now = time.time() |
| inst._request_history = [(now, now+1), (now+2, now+4)] |
| self.assertEqual(1.5, inst.get_latency_60s()) |
| |
| def test_get_qps_60s(self): |
| inst = instance.Instance(self.request_data, 'name', self.proxy, |
| max_concurrent_requests=5) |
| now = time.time() |
| inst._request_history = [(now, now+1)] * 120 |
| self.assertEqual(2.0, inst.get_qps_60s()) |
| |
| def test__trim_request_history_to_60s(self): |
| inst = instance.Instance(self.request_data, 'name', self.proxy, |
| max_concurrent_requests=5) |
| inst._request_history.append((0, 100)) |
| inst._request_history.append((1.0, 101)) |
| inst._request_history.append((1.2, 102)) |
| inst._request_history.append((2.5, 103)) |
| |
| now = time.time() |
| inst._request_history.append((now, 42)) |
| inst._request_history.append((now + 1, 43)) |
| inst._request_history.append((now + 3, 44)) |
| inst._request_history.append((now + 4, 45)) |
| |
| inst._trim_request_history_to_60s() |
| self.assertEqual([(now, 42), (now + 1, 43), (now + 3, 44), (now + 4, 45)], |
| list(inst._request_history)) |
| |
| |
| if __name__ == '__main__': |
| unittest.main() |