3 # Copyright 2007 Google Inc.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 """Tests for google.apphosting.tools.devappserver2.instance."""
26 from google
.appengine
.tools
.devappserver2
import instance
27 from google
.appengine
.tools
.devappserver2
import wsgi_request_info
30 class TestInstance(unittest
.TestCase
):
31 """Tests for instance.Instance."""
35 self
.proxy
= self
.mox
.CreateMock(instance
.RuntimeProxy
)
36 self
.environ
= object()
37 self
.start_response
= object()
38 self
.url_map
= object()
40 self
.request_id
= object()
41 self
.response
= [object()]
42 self
.request_data
= self
.mox
.CreateMock(wsgi_request_info
.WSGIRequestInfo
)
47 def test_new_instance(self
):
48 inst
= instance
.Instance(
49 self
.request_data
, 'name', self
.proxy
, max_concurrent_requests
=5,
50 expect_ready_request
=True)
51 self
.assertEqual(0, inst
.total_requests
)
52 self
.assertEqual(5, inst
.remaining_request_capacity
)
53 self
.assertEqual(0, inst
.num_outstanding_requests
)
54 self
.assertFalse(inst
.can_accept_requests
)
55 self
.assertTrue(inst
.handling_ready_request
)
56 self
.assertAlmostEqual(0, inst
.idle_seconds
, places
=2)
57 self
.assertEqual(0, inst
.get_latency_60s())
58 self
.assertEqual(0, inst
.get_qps_60s())
59 self
.assertEqual('name', inst
.instance_id
)
61 def test_handle(self
):
62 inst
= instance
.Instance(self
.request_data
, 'name', self
.proxy
,
63 max_concurrent_requests
=5)
64 self
.mox
.StubOutWithMock(inst
._condition
, 'notify')
68 self
.request_data
.set_request_instance(self
.request_id
, inst
)
69 self
.proxy
.handle(self
.environ
,
74 instance
.NORMAL_REQUEST
).AndReturn(self
.response
)
75 inst
._condition
.notify()
78 inst
._request
_history
.append((now
- 100, now
- 80))
80 self
.assertTrue(inst
.can_accept_requests
)
83 list(inst
.handle(self
.environ
,
88 instance
.NORMAL_REQUEST
)))
91 self
.assertEqual(1, len(inst
._request
_history
))
92 self
.assertEqual(1, inst
.total_requests
)
93 self
.assertEqual(5, inst
.remaining_request_capacity
)
94 self
.assertEqual(0, inst
.num_outstanding_requests
)
95 self
.assertTrue(0 < inst
.get_qps_60s())
97 def test_handle_ready_request(self
):
98 inst
= instance
.Instance(self
.request_data
, 'name', self
.proxy
,
99 max_concurrent_requests
=5,
100 expect_ready_request
=True)
101 self
.mox
.StubOutWithMock(inst
._condition
, 'notify')
105 self
.request_data
.set_request_instance(self
.request_id
, inst
)
106 self
.proxy
.handle(self
.environ
,
111 instance
.READY_REQUEST
).AndReturn(self
.response
)
112 inst
._condition
.notify(5)
115 self
.assertFalse(inst
.can_accept_requests
)
116 self
.assertRaises(instance
.CannotAcceptRequests
,
123 instance
.NORMAL_REQUEST
)
126 list(inst
.handle(self
.environ
,
131 instance
.READY_REQUEST
)))
134 self
.assertEqual(1, inst
.total_requests
)
135 self
.assertEqual(5, inst
.remaining_request_capacity
)
136 self
.assertEqual(0, inst
.num_outstanding_requests
)
137 self
.assertTrue(0 < inst
.get_qps_60s())
138 self
.assertFalse(inst
.handling_ready_request
)
140 def test_handle_background_request(self
):
141 inst
= instance
.Instance(self
.request_data
, 'name', self
.proxy
,
142 max_concurrent_requests
=5,
143 max_background_threads
=2)
144 inst
._num
_running
_background
_threads
= 1
145 self
.mox
.StubOutWithMock(inst
._condition
, 'notify')
149 self
.request_data
.set_request_instance(self
.request_id
, inst
)
150 self
.proxy
.handle(self
.environ
,
155 instance
.BACKGROUND_REQUEST
).AndReturn(self
.response
)
158 self
.assertTrue(inst
.can_accept_requests
)
159 self
.assertEqual(1, inst
.remaining_background_thread_capacity
)
162 list(inst
.handle(self
.environ
,
167 instance
.BACKGROUND_REQUEST
)))
170 self
.assertEqual(1, inst
.total_requests
)
171 self
.assertEqual(5, inst
.remaining_request_capacity
)
172 self
.assertEqual(0, inst
.num_outstanding_requests
)
173 self
.assertTrue(0 < inst
.get_qps_60s())
174 self
.assertEqual(2, inst
.remaining_background_thread_capacity
)
175 self
.assertFalse(inst
.handling_ready_request
)
177 def test_handle_shutdown_request(self
):
178 inst
= instance
.Instance(self
.request_data
, 'name', self
.proxy
,
179 max_concurrent_requests
=1)
180 inst
._num
_outstanding
_requests
= 0
181 self
.mox
.StubOutWithMock(inst
._condition
, 'notify_all')
185 self
.request_data
.set_request_instance(self
.request_id
, inst
)
186 self
.proxy
.handle(self
.environ
,
191 instance
.SHUTDOWN_REQUEST
).AndReturn(self
.response
)
193 inst
._condition
.notify_all()
196 self
.assertTrue(inst
.can_accept_requests
)
197 self
.assertFalse(inst
.has_quit
)
198 inst
.quit(expect_shutdown
=True)
199 self
.assertFalse(inst
.can_accept_requests
)
200 self
.assertTrue(inst
.has_quit
)
203 list(inst
.handle(self
.environ
,
208 instance
.SHUTDOWN_REQUEST
)))
211 self
.assertEqual(1, inst
.total_requests
)
212 self
.assertEqual(1, inst
.remaining_request_capacity
)
213 self
.assertEqual(0, inst
.num_outstanding_requests
)
214 self
.assertTrue(0 < inst
.get_qps_60s())
215 self
.assertFalse(inst
._quitting
)
216 self
.assertTrue(inst
._quit
)
218 def test_handle_shutdown_request_running_request(self
):
219 inst
= instance
.Instance(self
.request_data
, 'name', self
.proxy
,
220 max_concurrent_requests
=1)
221 inst
._num
_outstanding
_requests
= 1
222 self
.mox
.StubOutWithMock(inst
._condition
, 'notify_all')
226 self
.request_data
.set_request_instance(self
.request_id
, inst
)
227 self
.proxy
.handle(self
.environ
,
232 instance
.SHUTDOWN_REQUEST
).AndReturn(self
.response
)
235 self
.assertTrue(inst
.can_accept_requests
)
236 self
.assertFalse(inst
.has_quit
)
237 inst
.quit(expect_shutdown
=True)
238 self
.assertFalse(inst
.can_accept_requests
)
239 self
.assertTrue(inst
.has_quit
)
242 list(inst
.handle(self
.environ
,
247 instance
.SHUTDOWN_REQUEST
)))
250 self
.assertEqual(1, inst
.total_requests
)
251 self
.assertEqual(0, inst
.remaining_request_capacity
)
252 self
.assertEqual(1, inst
.num_outstanding_requests
)
253 self
.assertEqual(0, inst
.idle_seconds
)
254 self
.assertTrue(0 < inst
.get_qps_60s())
255 self
.assertTrue(inst
._quitting
)
256 self
.assertFalse(inst
._quit
)
258 def test_handle_before_start(self
):
259 inst
= instance
.Instance(self
.request_data
, 'name', self
.proxy
,
260 max_concurrent_requests
=5)
261 self
.mox
.StubOutWithMock(inst
._condition
, 'notify')
263 self
.assertRaises(instance
.CannotAcceptRequests
,
270 instance
.NORMAL_REQUEST
)
272 def test_handle_after_quit(self
):
273 inst
= instance
.Instance(self
.request_data
, 'name', self
.proxy
,
274 max_concurrent_requests
=5)
275 self
.mox
.StubOutWithMock(inst
._condition
, 'notify')
276 self
.mox
.StubOutWithMock(inst
._condition
, 'notify_all')
280 inst
._condition
.notify_all()
286 self
.assertRaises(instance
.CannotAcceptRequests
,
293 instance
.NORMAL_REQUEST
)
296 def test_handle_while_quitting(self
):
297 inst
= instance
.Instance(self
.request_data
, 'name', self
.proxy
,
298 max_concurrent_requests
=5)
299 self
.mox
.StubOutWithMock(inst
._condition
, 'notify')
300 inst
._num
_outstanding
_requests
= 1
305 inst
.quit(allow_async
=True)
308 self
.assertRaises(instance
.CannotAcceptRequests
,
315 instance
.NORMAL_REQUEST
)
317 def test_handle_no_capacity(self
):
318 inst
= instance
.Instance(self
.request_data
, 'name', self
.proxy
,
319 max_concurrent_requests
=1)
320 self
.mox
.StubOutWithMock(inst
._condition
, 'notify')
321 inst
._num
_outstanding
_requests
= 1
323 self
.assertRaises(instance
.CannotAcceptRequests
,
330 instance
.NORMAL_REQUEST
)
332 def test_reserve_background_thread_success(self
):
333 inst
= instance
.Instance(self
.request_data
, 'name', self
.proxy
,
334 max_concurrent_requests
=5,
335 max_background_threads
=2)
339 self
.assertEqual(2, inst
.remaining_background_thread_capacity
)
340 inst
.reserve_background_thread()
341 self
.assertEqual(1, inst
.remaining_background_thread_capacity
)
344 def test_reserve_background_thread_quitting(self
):
345 inst
= instance
.Instance(self
.request_data
, 'name', self
.proxy
,
346 max_concurrent_requests
=5,
347 max_background_threads
=2)
349 inst
._quitting
= True
352 self
.assertEqual(2, inst
.remaining_background_thread_capacity
)
353 inst
.reserve_background_thread()
354 self
.assertEqual(1, inst
.remaining_background_thread_capacity
)
357 def test_reserve_background_thread_no_capacity(self
):
358 inst
= instance
.Instance(self
.request_data
, 'name', self
.proxy
,
359 max_concurrent_requests
=5,
360 max_background_threads
=0)
363 self
.assertEqual(0, inst
.remaining_background_thread_capacity
)
364 self
.assertRaises(instance
.CannotAcceptRequests
,
365 inst
.reserve_background_thread
)
367 self
.assertEqual(0, inst
.remaining_background_thread_capacity
)
369 def test_reserve_background_thread_not_started(self
):
370 inst
= instance
.Instance(self
.request_data
, 'name', self
.proxy
,
371 max_concurrent_requests
=5,
372 max_background_threads
=1)
374 self
.assertEqual(1, inst
.remaining_background_thread_capacity
)
375 self
.assertRaises(instance
.CannotAcceptRequests
,
376 inst
.reserve_background_thread
)
378 self
.assertEqual(1, inst
.remaining_background_thread_capacity
)
380 def test_reserve_background_thread_quit(self
):
381 inst
= instance
.Instance(self
.request_data
, 'name', self
.proxy
,
382 max_concurrent_requests
=5,
383 max_background_threads
=1)
387 self
.assertEqual(1, inst
.remaining_background_thread_capacity
)
388 self
.assertRaises(instance
.CannotAcceptRequests
,
389 inst
.reserve_background_thread
)
391 self
.assertEqual(1, inst
.remaining_background_thread_capacity
)
393 def test_reserve_background_thread_not_ready(self
):
394 inst
= instance
.Instance(self
.request_data
, 'name', self
.proxy
,
395 max_concurrent_requests
=5,
396 max_background_threads
=2,
397 expect_ready_request
=True)
400 self
.assertEqual(2, inst
.remaining_background_thread_capacity
)
401 inst
.reserve_background_thread()
403 self
.assertEqual(1, inst
.remaining_background_thread_capacity
)
405 def test_wait_with_capacity(self
):
406 inst
= instance
.Instance(self
.request_data
, 'name', self
.proxy
,
407 max_concurrent_requests
=1)
409 self
.mox
.StubOutWithMock(inst
._condition
, 'wait')
410 self
.mox
.stubs
.Set(time
, 'time', lambda: 0)
412 self
.assertTrue(inst
.wait(1))
415 def test_wait_waiting_for_can_accept(self
):
416 inst
= instance
.Instance(self
.request_data
, 'name', self
.proxy
,
417 max_concurrent_requests
=1,
418 expect_ready_request
=True)
420 self
.mox
.StubOutWithMock(inst
._condition
, 'wait')
422 self
.mox
.stubs
.Set(time
, 'time', lambda: self
.time
)
424 def advance_time(*unused_args
):
427 inst
._condition
.wait(1).WithSideEffects(advance_time
)
429 self
.assertFalse(inst
.wait(1))
432 def test_wait_timed_out_with_capacity(self
):
433 inst
= instance
.Instance(self
.request_data
, 'name', self
.proxy
,
434 max_concurrent_requests
=1)
436 self
.mox
.StubOutWithMock(inst
._condition
, 'wait')
438 self
.assertTrue(inst
.wait(0))
441 def test_wait_without_capacity(self
):
442 inst
= instance
.Instance(self
.request_data
, 'name', self
.proxy
,
443 max_concurrent_requests
=0)
445 self
.mox
.StubOutWithMock(inst
._condition
, 'wait')
447 self
.mox
.stubs
.Set(time
, 'time', lambda: self
.time
)
449 def advance_time(*unused_args
):
452 inst
._condition
.wait(1).WithSideEffects(advance_time
)
454 self
.assertFalse(inst
.wait(1))
457 def test_wait_timed_out_without_capacity(self
):
458 inst
= instance
.Instance(self
.request_data
, 'name', self
.proxy
,
459 max_concurrent_requests
=0)
461 self
.mox
.StubOutWithMock(inst
._condition
, 'wait')
463 self
.assertFalse(inst
.wait(0))
466 def test_wait_quit_while_starting(self
):
467 inst
= instance
.Instance(self
.request_data
, 'name', self
.proxy
,
468 max_concurrent_requests
=5)
469 self
.mox
.StubOutWithMock(inst
._condition
, 'notify_all')
470 self
.proxy
.start().WithSideEffects(inst
.quit
)
477 self
.assertFalse(inst
.can_accept_requests
)
479 def test_wait_quit_while_waiting(self
):
480 self
.mox
.stubs
.Set(time
, 'time', lambda: 0)
481 inst
= instance
.Instance(self
.request_data
, 'name', self
.proxy
,
482 max_concurrent_requests
=0)
483 self
.mox
.StubOutWithMock(inst
._condition
, 'wait')
484 inst
._condition
.wait(1).WithSideEffects(lambda *unused_args
: inst
.quit())
486 self
.assertFalse(inst
.wait(1))
489 def test_health(self
):
490 inst
= instance
.Instance(self
.request_data
, 'name', self
.proxy
,
491 max_concurrent_requests
=5)
492 self
.mox
.StubOutWithMock(inst
._condition
, 'notify_all')
496 inst
._condition
.notify_all()
500 self
.assertTrue(inst
.can_accept_requests
)
501 inst
.set_health(False)
502 self
.assertFalse(inst
.can_accept_requests
)
503 inst
.set_health(True)
504 self
.assertTrue(inst
.can_accept_requests
)
507 inst
= instance
.Instance(self
.request_data
, 'name', self
.proxy
,
508 max_concurrent_requests
=5)
509 self
.mox
.StubOutWithMock(inst
._condition
, 'notify_all')
513 inst
._condition
.notify_all()
517 self
.assertTrue(inst
.can_accept_requests
)
520 self
.assertFalse(inst
.can_accept_requests
)
522 def test_quit_with_request(self
):
523 inst
= instance
.Instance(self
.request_data
, 'name', self
.proxy
,
524 max_concurrent_requests
=5)
525 self
.mox
.StubOutWithMock(inst
._condition
, 'notify_all')
531 inst
._num
_outstanding
_requests
= 1
532 self
.assertRaises(instance
.CannotQuitServingInstance
,
535 def test_quit_with_request_force(self
):
536 inst
= instance
.Instance(self
.request_data
, 'name', self
.proxy
,
537 max_concurrent_requests
=5)
538 self
.mox
.StubOutWithMock(inst
._condition
, 'notify_all')
540 inst
._num
_outstanding
_requests
= 1
543 inst
._condition
.notify_all()
547 inst
.quit(force
=True)
550 def test_quit_with_request_force_and_allow_async(self
):
551 inst
= instance
.Instance(self
.request_data
, 'name', self
.proxy
,
552 max_concurrent_requests
=5)
553 self
.mox
.StubOutWithMock(inst
._condition
, 'notify_all')
555 inst
._num
_outstanding
_requests
= 1
558 inst
._condition
.notify_all()
562 inst
.quit(force
=True, allow_async
=True)
565 def test_quit_with_request_allow_async(self
):
566 inst
= instance
.Instance(self
.request_data
, 'name', self
.proxy
,
567 max_concurrent_requests
=5)
568 self
.mox
.StubOutWithMock(inst
._condition
, 'notify_all')
570 inst
._num
_outstanding
_requests
= 1
575 inst
.quit(allow_async
=True)
577 self
.assertTrue(inst
._quitting
)
579 def test_quit_shutdown(self
):
580 inst
= instance
.Instance(self
.request_data
, 'name', self
.proxy
,
581 max_concurrent_requests
=5)
582 self
.mox
.StubOutWithMock(inst
._condition
, 'notify_all')
584 inst
._num
_outstanding
_requests
= 1
589 inst
.quit(expect_shutdown
=True)
591 self
.assertTrue(inst
._expecting
_shutdown
_request
)
592 self
.assertFalse(inst
._quitting
)
594 def test_get_latency_60s(self
):
595 inst
= instance
.Instance(self
.request_data
, 'name', self
.proxy
,
596 max_concurrent_requests
=5)
598 inst
._request
_history
= [(now
, now
+1), (now
+2, now
+4)]
599 self
.assertEqual(1.5, inst
.get_latency_60s())
601 def test_get_qps_60s(self
):
602 inst
= instance
.Instance(self
.request_data
, 'name', self
.proxy
,
603 max_concurrent_requests
=5)
605 inst
._request
_history
= [(now
, now
+1)] * 120
606 self
.assertEqual(2.0, inst
.get_qps_60s())
608 def test__trim_request_history_to_60s(self
):
609 inst
= instance
.Instance(self
.request_data
, 'name', self
.proxy
,
610 max_concurrent_requests
=5)
611 inst
._request
_history
.append((0, 100))
612 inst
._request
_history
.append((1.0, 101))
613 inst
._request
_history
.append((1.2, 102))
614 inst
._request
_history
.append((2.5, 103))
617 inst
._request
_history
.append((now
, 42))
618 inst
._request
_history
.append((now
+ 1, 43))
619 inst
._request
_history
.append((now
+ 3, 44))
620 inst
._request
_history
.append((now
+ 4, 45))
622 inst
._trim
_request
_history
_to
_60s
()
623 self
.assertEqual([(now
, 42), (now
+ 1, 43), (now
+ 3, 44), (now
+ 4, 45)],
624 list(inst
._request
_history
))
627 if __name__
== '__main__':