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.server."""
30 from google
.appengine
.api
import appinfo
31 from google
.appengine
.api
import request_info
32 from google
.appengine
.tools
.devappserver2
import api_server
33 from google
.appengine
.tools
.devappserver2
import application_configuration
34 from google
.appengine
.tools
.devappserver2
import constants
35 from google
.appengine
.tools
.devappserver2
import dispatcher
36 from google
.appengine
.tools
.devappserver2
import instance
37 from google
.appengine
.tools
.devappserver2
import server
38 from google
.appengine
.tools
.devappserver2
import start_response_utils
39 from google
.appengine
.tools
.devappserver2
import wsgi_server
42 class ServerConfigurationStub(object):
44 application_root
='/root',
46 server_name
='default',
47 automatic_scaling
=appinfo
.AutomaticScaling(),
52 inbound_services
=['warmup'],
53 handlers
=[appinfo
.URLMap(url
=r
'/python-(.*)',
55 normalized_libraries
=None,
59 self
.application_root
= application_root
60 self
.application
= application
61 self
.server_name
= server_name
62 self
.automatic_scaling
= automatic_scaling
63 self
.manual_scaling
= manual_scaling
64 self
.basic_scaling
= basic_scaling
65 self
.major_version
= version
66 self
.runtime
= runtime
67 self
.threadsafe
= threadsafe
68 self
.skip_files
= skip_files
69 self
.inbound_services
= inbound_services
70 self
.handlers
= handlers
71 self
.normalized_libraries
= normalized_libraries
or []
72 self
.env_variables
= env_variables
or []
73 self
.version_id
= '%s:%s.%s' % (server_name
, version
, '12345')
74 self
.is_backend
= False
76 def check_for_updates(self
):
80 class ServerFacade(server
.Server
):
82 server_configuration
=ServerConfigurationStub(),
83 instance_factory
=None,
85 super(ServerFacade
, self
).__init
__(
90 runtime_stderr_loglevel
=1,
93 cloud_sql_config
=None,
94 default_version_port
=8080,
95 port_registry
=dispatcher
.PortRegistry(),
99 use_mtime_file_watcher
=False,
100 automatic_restarts
=True)
101 if instance_factory
is not None:
102 self
._instance
_factory
= instance_factory
110 def balanced_port(self
):
111 return self
._balanced
_port
114 class AutoScalingServerFacade(server
.AutoScalingServer
):
116 server_configuration
=ServerConfigurationStub(),
118 instance_factory
=None,
121 super(AutoScalingServerFacade
, self
).__init
__(
122 server_configuration
,
124 balanced_port
=balanced_port
,
126 runtime_stderr_loglevel
=1,
129 cloud_sql_config
=None,
130 default_version_port
=8080,
131 port_registry
=dispatcher
.PortRegistry(),
134 max_instances
=max_instances
,
135 use_mtime_file_watcher
=False,
136 automatic_restarts
=True)
137 if instance_factory
is not None:
138 self
._instance
_factory
= instance_factory
146 def balanced_port(self
):
147 return self
._balanced
_port
150 class ManualScalingServerFacade(server
.ManualScalingServer
):
152 server_configuration
=ServerConfigurationStub(),
154 instance_factory
=None,
156 super(ManualScalingServerFacade
, self
).__init
__(
157 server_configuration
,
159 balanced_port
=balanced_port
,
161 runtime_stderr_loglevel
=1,
164 cloud_sql_config
=None,
165 default_version_port
=8080,
166 port_registry
=dispatcher
.PortRegistry(),
170 use_mtime_file_watcher
=False,
171 automatic_restarts
=True)
172 if instance_factory
is not None:
173 self
._instance
_factory
= instance_factory
181 def balanced_port(self
):
182 return self
._balanced
_port
185 class BasicScalingServerFacade(server
.BasicScalingServer
):
188 server_configuration
=ServerConfigurationStub(),
190 instance_factory
=None,
192 super(BasicScalingServerFacade
, self
).__init
__(
193 server_configuration
,
195 balanced_port
=balanced_port
,
197 runtime_stderr_loglevel
=1,
200 cloud_sql_config
=None,
201 default_version_port
=8080,
202 port_registry
=dispatcher
.PortRegistry(),
206 use_mtime_file_watcher
=False,
207 automatic_restarts
=True)
208 if instance_factory
is not None:
209 self
._instance
_factory
= instance_factory
217 def balanced_port(self
):
218 return self
._balanced
_port
221 class BuildRequestEnvironTest(unittest
.TestCase
):
224 api_server
.test_setup_stubs()
225 self
.server
= ServerFacade()
227 def test_build_request_environ(self
):
229 constants
.FAKE_IS_ADMIN_HEADER
: '1',
230 'HTTP_HOST': 'fakehost:8080',
231 'HTTP_HEADER': 'Value',
232 'HTTP_OTHER': 'Values',
233 'CONTENT_LENGTH': '4',
235 'QUERY_STRING': 'bar=baz',
236 'REQUEST_METHOD': 'PUT',
237 'REMOTE_ADDR': '1.2.3.4',
238 'SERVER_NAME': 'fakehost',
239 'SERVER_PORT': '8080',
240 'SERVER_PROTOCOL': 'HTTP/1.1',
241 'wsgi.version': (1, 0),
242 'wsgi.url_scheme': 'http',
243 'wsgi.multithread': True,
244 'wsgi.multiprocess': True}
245 environ
= self
.server
.build_request_environ(
246 'PUT', '/foo?bar=baz', [('Header', 'Value'), ('Other', 'Values')],
247 'body', '1.2.3.4', 8080)
248 self
.assertEqual('', environ
.pop('wsgi.errors').getvalue())
249 self
.assertEqual('body', environ
.pop('wsgi.input').getvalue())
250 self
.assertEqual(expected_environ
, environ
)
252 def test_build_request_environ_fake_is_logged_in(self
):
254 constants
.FAKE_IS_ADMIN_HEADER
: '1',
255 constants
.FAKE_LOGGED_IN_HEADER
: '1',
256 'HTTP_HOST': 'fakehost:8080',
257 'HTTP_HEADER': 'Value',
258 'HTTP_OTHER': 'Values',
259 'CONTENT_LENGTH': '4',
261 'QUERY_STRING': 'bar=baz',
262 'REQUEST_METHOD': 'PUT',
263 'REMOTE_ADDR': '1.2.3.4',
264 'SERVER_NAME': 'fakehost',
265 'SERVER_PORT': '8080',
266 'SERVER_PROTOCOL': 'HTTP/1.1',
267 'wsgi.version': (1, 0),
268 'wsgi.url_scheme': 'http',
269 'wsgi.multithread': True,
270 'wsgi.multiprocess': True}
271 environ
= self
.server
.build_request_environ(
272 'PUT', '/foo?bar=baz', [('Header', 'Value'), ('Other', 'Values')],
273 'body', '1.2.3.4', 8080, fake_login
=True)
274 self
.assertEqual('', environ
.pop('wsgi.errors').getvalue())
275 self
.assertEqual('body', environ
.pop('wsgi.input').getvalue())
276 self
.assertEqual(expected_environ
, environ
)
278 def test_build_request_environ_unicode_body(self
):
280 constants
.FAKE_IS_ADMIN_HEADER
: '1',
281 'HTTP_HOST': 'fakehost',
282 'HTTP_HEADER': 'Value',
283 'HTTP_OTHER': 'Values',
284 'CONTENT_LENGTH': '4',
286 'QUERY_STRING': 'bar=baz',
287 'REQUEST_METHOD': 'PUT',
288 'REMOTE_ADDR': '1.2.3.4',
289 'SERVER_NAME': 'fakehost',
291 'SERVER_PROTOCOL': 'HTTP/1.1',
292 'wsgi.version': (1, 0),
293 'wsgi.url_scheme': 'http',
294 'wsgi.multithread': True,
295 'wsgi.multiprocess': True}
296 environ
= self
.server
.build_request_environ(
297 'PUT', '/foo?bar=baz', [('Header', 'Value'), ('Other', 'Values')],
298 u
'body', '1.2.3.4', 80)
299 self
.assertEqual('', environ
.pop('wsgi.errors').getvalue())
300 self
.assertEqual('body', environ
.pop('wsgi.input').getvalue())
301 self
.assertEqual(expected_environ
, environ
)
304 class TestServerCreateUrlHandlers(unittest
.TestCase
):
305 """Tests for server.Server._create_url_handlers."""
308 self
.server_configuration
= ServerConfigurationStub()
309 self
.instance_factory
= instance
.InstanceFactory(None, 1)
310 self
.servr
= ServerFacade(instance_factory
=self
.instance_factory
,
311 server_configuration
=self
.server_configuration
)
312 self
.instance_factory
.START_URL_MAP
= appinfo
.URLMap(
314 script
='start_handler',
316 self
.instance_factory
.WARMUP_URL_MAP
= appinfo
.URLMap(
318 script
='warmup_handler',
321 def test_match_all(self
):
322 self
.server_configuration
.handlers
= [appinfo
.URLMap(url
=r
'.*',
324 handlers
= self
.servr
._create
_url
_handlers
()
325 self
.assertEqual(6, len(handlers
))
327 def test_match_start_only(self
):
328 self
.server_configuration
.handlers
= [appinfo
.URLMap(url
=r
'/_ah/start',
330 handlers
= self
.servr
._create
_url
_handlers
()
331 self
.assertEqual(7, len(handlers
))
332 self
.assertEqual(self
.instance_factory
.WARMUP_URL_MAP
, handlers
[0].url_map
)
334 def test_match_warmup_only(self
):
335 self
.server_configuration
.handlers
= [appinfo
.URLMap(url
=r
'/_ah/warmup',
337 handlers
= self
.servr
._create
_url
_handlers
()
338 self
.assertEqual(7, len(handlers
))
339 self
.assertEqual(self
.instance_factory
.START_URL_MAP
, handlers
[0].url_map
)
341 def test_match_neither_warmup_nor_start(self
):
342 self
.server_configuration
.handlers
= [appinfo
.URLMap(url
=r
'/',
344 handlers
= self
.servr
._create
_url
_handlers
()
345 self
.assertEqual(8, len(handlers
))
346 self
.assertEqual(self
.instance_factory
.WARMUP_URL_MAP
, handlers
[0].url_map
)
347 self
.assertEqual(self
.instance_factory
.START_URL_MAP
, handlers
[1].url_map
)
349 def test_match_static_only(self
):
350 self
.server_configuration
.handlers
= [
351 appinfo
.URLMap(url
=r
'/_ah/start', static_dir
='foo'),
352 appinfo
.URLMap(url
=r
'/_ah/warmup', static_files
='foo', upload
='foo')]
353 handlers
= self
.servr
._create
_url
_handlers
()
354 self
.assertEqual(9, len(handlers
))
355 self
.assertEqual(self
.instance_factory
.WARMUP_URL_MAP
, handlers
[0].url_map
)
356 self
.assertEqual(self
.instance_factory
.START_URL_MAP
, handlers
[1].url_map
)
358 def test_match_start_only_no_inbound_warmup(self
):
359 self
.server_configuration
.inbound_services
= None
360 self
.server_configuration
.handlers
= [appinfo
.URLMap(url
=r
'/_ah/start',
362 handlers
= self
.servr
._create
_url
_handlers
()
363 self
.assertEqual(6, len(handlers
))
365 def test_match_warmup_only_no_inbound_warmup(self
):
366 self
.server_configuration
.inbound_services
= None
367 self
.server_configuration
.handlers
= [appinfo
.URLMap(url
=r
'/_ah/warmup',
369 handlers
= self
.servr
._create
_url
_handlers
()
370 self
.assertEqual(7, len(handlers
))
371 self
.assertEqual(self
.instance_factory
.START_URL_MAP
, handlers
[0].url_map
)
373 def test_match_neither_warmup_nor_start_no_inbound_warmup(self
):
374 self
.server_configuration
.inbound_services
= None
375 self
.server_configuration
.handlers
= [appinfo
.URLMap(url
=r
'/',
377 handlers
= self
.servr
._create
_url
_handlers
()
378 self
.assertEqual(7, len(handlers
))
379 self
.assertEqual(self
.instance_factory
.START_URL_MAP
, handlers
[0].url_map
)
382 class TestServerGetRuntimeConfig(unittest
.TestCase
):
383 """Tests for server.Server._get_runtime_config."""
386 self
.server_configuration
= ServerConfigurationStub()
387 self
.instance_factory
= instance
.InstanceFactory(None, 1)
388 self
.servr
= ServerFacade(instance_factory
=self
.instance_factory
,
389 server_configuration
=self
.server_configuration
)
391 def test_static_files_regex(self
):
392 self
.server_configuration
.handlers
= [
393 appinfo
.URLMap(url
=r
'/static', static_dir
='static'),
394 appinfo
.URLMap(url
=r
'/app_read_static', static_dir
='app_read_static',
395 application_readable
=True),
396 appinfo
.URLMap(url
=r
'/static_images/*.png',
397 static_files
=r
'static_images/\\1',
398 upload
=r
'static_images/*.png'),
399 appinfo
.URLMap(url
=r
'/app_readable_static_images/*.png',
400 static_files
=r
'app_readable_static_images/\\1',
401 upload
=r
'app_readable_static_images/*.png',
402 application_readable
=True),
404 config
= self
.servr
._get
_runtime
_config
()
405 self
.assertEqual(r
'^(static%s.*)|(static_images/*.png)$' %
406 re
.escape(os
.path
.sep
),
410 class TestServerShutdownInstance(unittest
.TestCase
):
411 """Tests for server.Server._shutdown_instance."""
414 api_server
.test_setup_stubs()
416 self
.server_configuration
= ServerConfigurationStub()
417 self
.instance_factory
= instance
.InstanceFactory(None, 1)
418 self
.servr
= ServerFacade(instance_factory
=self
.instance_factory
,
419 server_configuration
=self
.server_configuration
)
420 self
.mox
.StubOutWithMock(logging
, 'exception')
421 self
.mox
.StubOutWithMock(self
.servr
, '_handle_request')
422 self
.mox
.StubOutWithMock(self
.servr
._quit
_event
, 'wait')
423 self
.mox
.StubOutWithMock(server
.Server
, 'build_request_environ')
424 self
.inst
= self
.mox
.CreateMock(instance
.Instance
)
426 self
.mox
.stubs
.Set(time
, 'time', lambda: self
.time
)
429 self
.mox
.UnsetStubs()
431 def test_shutdown_instance(self
):
433 def advance_time(*unused_args
, **unused_kwargs
):
437 self
.servr
.build_request_environ(
438 'GET', '/_ah/stop', [], '', '0.1.0.3', 9000, fake_login
=True).AndReturn(
440 self
.servr
._handle
_request
(
442 start_response_utils
.null_start_response
,
444 request_type
=instance
.SHUTDOWN_REQUEST
).WithSideEffects(advance_time
)
445 self
.servr
._quit
_event
.wait(20)
446 self
.inst
.quit(force
=True)
448 self
.servr
._shutdown
_instance
(self
.inst
, 9000)
452 class TestAutoScalingServerWarmup(unittest
.TestCase
):
453 """Tests for server.AutoScalingServer._warmup."""
456 api_server
.test_setup_stubs()
458 self
.mox
.StubOutWithMock(server
.Server
, 'build_request_environ')
461 self
.mox
.UnsetStubs()
463 def test_warmup(self
):
464 s
= AutoScalingServerFacade(balanced_port
=8080)
465 self
.mox
.StubOutWithMock(s
, '_handle_request')
466 self
.mox
.StubOutWithMock(s
._condition
, 'notify')
468 inst
= self
.mox
.CreateMock(instance
.Instance
)
471 s
.build_request_environ('GET', '/_ah/warmup', [], '', '0.1.0.3', 8080,
472 fake_login
=True).AndReturn(environ
)
473 s
._handle
_request
(environ
,
476 request_type
=instance
.READY_REQUEST
)
477 s
._condition
.notify(1)
484 class TestAutoScalingServerAddInstance(unittest
.TestCase
):
485 """Tests for server.AutoScalingServer._add_instance."""
488 api_server
.test_setup_stubs()
490 self
.factory
= self
.mox
.CreateMock(instance
.InstanceFactory
)
491 self
.factory
.max_concurrent_requests
= 10
494 self
.mox
.UnsetStubs()
496 def test_permit_warmup(self
):
497 s
= AutoScalingServerFacade(instance_factory
=self
.factory
)
498 self
.mox
.StubOutWithMock(s
, '_async_warmup')
499 self
.mox
.StubOutWithMock(s
._condition
, 'notify')
501 inst
= self
.mox
.CreateMock(instance
.Instance
)
502 self
.factory
.new_instance(mox
.Regex('[a-f0-9]{36}'),
503 expect_ready_request
=True).AndReturn(inst
)
504 inst
.start().AndReturn(True)
505 s
._async
_warmup
(inst
)
508 self
.assertEqual(inst
, s
._add
_instance
(permit_warmup
=True))
511 self
.assertEqual(1, len(s
._instances
))
513 def test_no_permit_warmup(self
):
514 s
= AutoScalingServerFacade(instance_factory
=self
.factory
)
515 self
.mox
.StubOutWithMock(s
._condition
, 'notify')
517 inst
= self
.mox
.CreateMock(instance
.Instance
)
518 self
.factory
.new_instance(mox
.Regex('[a-f0-9]{36}'),
519 expect_ready_request
=False).AndReturn(inst
)
520 inst
.start().AndReturn(True)
521 s
._condition
.notify(10)
524 self
.assertEqual(inst
, s
._add
_instance
(permit_warmup
=False))
527 self
.assertIn(inst
, s
._instances
)
529 def test_failed_to_start(self
):
530 s
= AutoScalingServerFacade(instance_factory
=self
.factory
)
531 self
.mox
.StubOutWithMock(s
, '_async_warmup')
532 self
.mox
.StubOutWithMock(s
._condition
, 'notify')
534 inst
= self
.mox
.CreateMock(instance
.Instance
)
535 self
.factory
.new_instance(mox
.Regex('[a-f0-9]{36}'),
536 expect_ready_request
=True).AndReturn(inst
)
537 inst
.start().AndReturn(False)
540 self
.assertIsNone(s
._add
_instance
(permit_warmup
=True))
543 self
.assertEqual(1, len(s
._instances
))
545 def test_max_instances(self
):
546 s
= AutoScalingServerFacade(instance_factory
=self
.factory
,
548 self
.mox
.StubOutWithMock(s
._condition
, 'notify')
550 inst
= self
.mox
.CreateMock(instance
.Instance
)
551 self
.factory
.new_instance(mox
.Regex('[a-f0-9]{36}'),
552 expect_ready_request
=False).AndReturn(inst
)
553 inst
.start().AndReturn(True)
554 s
._condition
.notify(10)
557 self
.assertEqual(inst
, s
._add
_instance
(permit_warmup
=False))
558 self
.assertEqual(None, s
._add
_instance
(permit_warmup
=False))
561 self
.assertEqual(1, len(s
._instances
))
564 class TestAutoScalingInstancePoolHandleScriptRequest(unittest
.TestCase
):
565 """Tests for server.AutoScalingServer.handle."""
568 api_server
.test_setup_stubs()
571 self
.inst
= self
.mox
.CreateMock(instance
.Instance
)
573 self
.start_response
= object()
574 self
.response
= [object()]
575 self
.url_map
= object()
576 self
.match
= object()
577 self
.request_id
= object()
578 self
.auto_server
= AutoScalingServerFacade(
579 instance_factory
=instance
.InstanceFactory(object(), 10))
580 self
.mox
.StubOutWithMock(self
.auto_server
, '_choose_instance')
581 self
.mox
.StubOutWithMock(self
.auto_server
, '_add_instance')
582 self
.mox
.stubs
.Set(time
, 'time', lambda: 0.0)
585 self
.mox
.UnsetStubs()
587 def test_handle_script_request(self
):
588 self
.auto_server
._choose
_instance
(0.1).AndReturn(self
.inst
)
589 self
.inst
.handle(self
.environ
,
594 instance
.NORMAL_REQUEST
).AndReturn(self
.response
)
599 self
.auto_server
._handle
_script
_request
(self
.environ
,
605 self
.assertEqual([(mox
.IgnoreArg(), 1)],
606 list(self
.auto_server
._outstanding
_request
_history
))
608 def test_handle_cannot_accept_request(self
):
609 self
.auto_server
._choose
_instance
(0.1).AndReturn(self
.inst
)
610 self
.auto_server
._choose
_instance
(0.1).AndReturn(self
.inst
)
612 self
.environ
, self
.start_response
, self
.url_map
, self
.match
,
613 self
.request_id
, instance
.NORMAL_REQUEST
).AndRaise(
614 instance
.CannotAcceptRequests
)
616 self
.environ
, self
.start_response
, self
.url_map
, self
.match
,
617 self
.request_id
, instance
.NORMAL_REQUEST
).AndReturn(
623 self
.auto_server
._handle
_script
_request
(self
.environ
,
629 self
.assertEqual([(mox
.IgnoreArg(), 1)],
630 list(self
.auto_server
._outstanding
_request
_history
))
632 def test_handle_new_instance(self
):
633 self
.auto_server
._choose
_instance
(0.1).AndReturn(None)
634 self
.auto_server
._add
_instance
(permit_warmup
=False).AndReturn(self
.inst
)
637 self
.environ
, self
.start_response
, self
.url_map
, self
.match
,
638 self
.request_id
, instance
.NORMAL_REQUEST
).AndReturn(
644 self
.auto_server
._handle
_script
_request
(self
.environ
,
651 def test_handle_new_instance_none_returned(self
):
652 self
.auto_server
._choose
_instance
(0.1).AndReturn(None)
653 self
.auto_server
._add
_instance
(permit_warmup
=False).AndReturn(None)
654 self
.auto_server
._choose
_instance
(0.2).AndReturn(self
.inst
)
657 self
.environ
, self
.start_response
, self
.url_map
, self
.match
,
658 self
.request_id
, instance
.NORMAL_REQUEST
).AndReturn(
664 self
.auto_server
._handle
_script
_request
(self
.environ
,
672 class TestAutoScalingInstancePoolTrimRequestTimesAndOutstanding(
674 """Tests for AutoScalingServer._trim_outstanding_request_history."""
677 api_server
.test_setup_stubs()
679 def test_trim_outstanding_request_history(self
):
680 servr
= AutoScalingServerFacade(
681 instance_factory
=instance
.InstanceFactory(object(), 10))
682 servr
._outstanding
_request
_history
.append((0, 100))
683 servr
._outstanding
_request
_history
.append((1.0, 101))
684 servr
._outstanding
_request
_history
.append((1.2, 102))
685 servr
._outstanding
_request
_history
.append((2.5, 103))
688 servr
._outstanding
_request
_history
.append((now
, 42))
689 servr
._outstanding
_request
_history
.append((now
+ 1, 43))
690 servr
._outstanding
_request
_history
.append((now
+ 3, 44))
691 servr
._outstanding
_request
_history
.append((now
+ 4, 45))
693 servr
._trim
_outstanding
_request
_history
()
694 self
.assertEqual([(now
, 42), (now
+ 1, 43), (now
+ 3, 44), (now
+ 4, 45)],
695 list(servr
._outstanding
_request
_history
))
698 class TestAutoScalingInstancePoolGetNumRequiredInstances(unittest
.TestCase
):
699 """Tests for AutoScalingServer._outstanding_request_history."""
702 api_server
.test_setup_stubs()
703 self
.servr
= AutoScalingServerFacade(
704 instance_factory
=instance
.InstanceFactory(object(), 5))
706 def test_get_num_required_instances(self
):
708 self
.servr
._outstanding
_request
_history
.append((now
, 42))
709 self
.servr
._outstanding
_request
_history
.append((now
+ 1, 43))
710 self
.servr
._outstanding
_request
_history
.append((now
+ 3, 44))
711 self
.servr
._outstanding
_request
_history
.append((now
+ 4, 45))
712 self
.assertEqual(9, self
.servr
._get
_num
_required
_instances
())
714 def test_no_requests(self
):
715 self
.assertEqual(0, self
.servr
._get
_num
_required
_instances
())
718 class TestAutoScalingInstancePoolSplitInstances(unittest
.TestCase
):
719 """Tests for server.AutoScalingServer._split_instances."""
721 class Instance(object):
722 def __init__(self
, num_outstanding_requests
, can_accept_requests
=True):
723 self
.num_outstanding_requests
= num_outstanding_requests
724 self
.can_accept_requests
= can_accept_requests
727 return str(self
.num_outstanding_requests
)
730 api_server
.test_setup_stubs()
733 self
.servr
= AutoScalingServerFacade(
734 instance_factory
=instance
.InstanceFactory(object(), 10))
735 self
.mox
.StubOutWithMock(self
.servr
, '_get_num_required_instances')
738 self
.mox
.UnsetStubs()
740 def test_split_instances(self
):
741 instance1
= self
.Instance(1)
742 instance2
= self
.Instance(2, can_accept_requests
=False)
743 instance3
= self
.Instance(3)
744 instance4
= self
.Instance(4)
745 instance5
= self
.Instance(5)
746 instance6
= self
.Instance(6)
747 instance7
= self
.Instance(7)
748 instance8
= self
.Instance(8, can_accept_requests
=False)
749 instance9
= self
.Instance(9)
750 instance10
= self
.Instance(10)
752 self
.servr
._get
_num
_required
_instances
().AndReturn(5)
753 self
.servr
._instances
= set([instance1
, instance2
, instance3
, instance4
,
754 instance5
, instance6
, instance7
, instance8
,
755 instance9
, instance10
])
759 (set([instance10
, instance9
, instance7
,
760 instance6
, instance5
]),
761 set([instance1
, instance2
, instance3
, instance4
, instance8
])),
762 self
.servr
._split
_instances
())
765 def test_split_instances_no_instances(self
):
766 self
.servr
._get
_num
_required
_instances
().AndReturn(5)
767 self
.servr
._instances
= set([])
770 self
.assertEqual((set([]), set([])),
771 self
.servr
._split
_instances
())
774 def test_split_instances_no_instances_not_enough_accepting_requests(self
):
775 instance1
= self
.Instance(1)
776 instance2
= self
.Instance(1, can_accept_requests
=False)
777 instance3
= self
.Instance(2, can_accept_requests
=False)
779 self
.servr
._get
_num
_required
_instances
().AndReturn(5)
780 self
.servr
._instances
= set([instance1
, instance2
, instance3
])
783 self
.assertEqual((set([instance1
]), set([instance2
, instance3
])),
784 self
.servr
._split
_instances
())
787 def test_split_instances_no_required_instances(self
):
788 instance1
= self
.Instance(1)
789 instance2
= self
.Instance(2, can_accept_requests
=False)
790 instance3
= self
.Instance(3, can_accept_requests
=False)
791 instance4
= self
.Instance(4)
792 instance5
= self
.Instance(5)
793 instance6
= self
.Instance(6)
794 instance7
= self
.Instance(7)
795 instance8
= self
.Instance(8)
797 self
.servr
._get
_num
_required
_instances
().AndReturn(0)
798 self
.servr
._instances
= set([instance1
, instance2
, instance3
, instance4
,
799 instance5
, instance6
, instance7
, instance8
])
804 set([instance8
, instance7
, instance6
, instance5
, instance4
,
805 instance3
, instance2
, instance1
])),
806 self
.servr
._split
_instances
())
810 class TestAutoScalingInstancePoolChooseInstances(unittest
.TestCase
):
811 """Tests for server.AutoScalingServer._choose_instance."""
813 class Instance(object):
814 def __init__(self
, num_outstanding_requests
, can_accept_requests
=True):
815 self
.num_outstanding_requests
= num_outstanding_requests
816 self
.remaining_request_capacity
= 10 - num_outstanding_requests
817 self
.can_accept_requests
= can_accept_requests
820 api_server
.test_setup_stubs()
823 self
.servr
= AutoScalingServerFacade(
824 instance_factory
=instance
.InstanceFactory(object(), 10))
825 self
.mox
.StubOutWithMock(self
.servr
, '_split_instances')
826 self
.mox
.StubOutWithMock(self
.servr
._condition
, 'wait')
828 self
.mox
.stubs
.Set(time
, 'time', lambda: self
.time
)
830 def advance_time(self
, *unused_args
):
834 self
.mox
.UnsetStubs()
836 def test_choose_instance_required_available(self
):
837 instance1
= self
.Instance(1)
838 instance2
= self
.Instance(2)
839 instance3
= self
.Instance(3)
840 instance4
= self
.Instance(4)
842 self
.servr
._split
_instances
().AndReturn((set([instance3
, instance4
]),
843 set([instance1
, instance2
])))
846 self
.assertEqual(instance3
, # Least busy required instance.
847 self
.servr
._choose
_instance
(15))
850 def test_choose_instance_no_instances(self
):
851 self
.servr
._split
_instances
().AndReturn((set([]), set([])))
852 self
.servr
._condition
.wait(5).WithSideEffects(self
.advance_time
)
855 self
.assertEqual(None, self
.servr
._choose
_instance
(15))
858 def test_choose_instance_no_instance_that_can_accept_requests(self
):
859 instance1
= self
.Instance(1, can_accept_requests
=False)
860 self
.servr
._split
_instances
().AndReturn((set([]), set([instance1
])))
861 self
.servr
._condition
.wait(5).WithSideEffects(self
.advance_time
)
864 self
.assertEqual(None, self
.servr
._choose
_instance
(15))
867 def test_choose_instance_required_full(self
):
868 instance1
= self
.Instance(1)
869 instance2
= self
.Instance(2)
870 instance3
= self
.Instance(10)
871 instance4
= self
.Instance(10)
873 self
.servr
._split
_instances
().AndReturn((set([instance3
, instance4
]),
874 set([instance1
, instance2
])))
877 self
.assertEqual(instance2
, # Busyest non-required instance.
878 self
.servr
._choose
_instance
(15))
881 def test_choose_instance_must_wait(self
):
882 instance1
= self
.Instance(10)
883 instance2
= self
.Instance(10)
885 self
.servr
._split
_instances
().AndReturn((set([instance1
]),
887 self
.servr
._condition
.wait(5).WithSideEffects(self
.advance_time
)
890 self
.assertIsNone(self
.servr
._choose
_instance
(15))
894 class TestAutoScalingInstancePoolAdjustInstances(unittest
.TestCase
):
895 """Tests for server.AutoScalingServer._adjust_instances."""
897 class Instance(object):
898 def __init__(self
, num_outstanding_requests
):
899 self
.num_outstanding_requests
= num_outstanding_requests
905 api_server
.test_setup_stubs()
907 self
.servr
= AutoScalingServerFacade(
908 server_configuration
=ServerConfigurationStub(
909 automatic_scaling
=appinfo
.AutomaticScaling(
910 min_pending_latency
='0.1s',
911 max_pending_latency
='1.0s',
912 min_idle_instances
=1,
913 max_idle_instances
=2)),
914 instance_factory
=instance
.InstanceFactory(object(), 10))
916 self
.mox
.StubOutWithMock(self
.servr
, '_split_instances')
917 self
.mox
.StubOutWithMock(self
.servr
, '_add_instance')
920 self
.mox
.UnsetStubs()
922 def test_adjust_instances_create_new(self
):
923 instance1
= self
.Instance(0)
924 instance2
= self
.Instance(2)
925 instance3
= self
.Instance(3)
926 instance4
= self
.Instance(4)
928 self
.servr
._instances
= set([instance1
, instance2
, instance3
, instance4
])
929 self
.servr
._split
_instances
().AndReturn(
930 (set([instance1
, instance2
, instance3
, instance4
]),
932 self
.servr
._add
_instance
(permit_warmup
=True)
935 self
.servr
._adjust
_instances
()
938 def test_adjust_instances_quit_idle(self
):
939 instance1
= self
.Instance(0)
940 instance2
= self
.Instance(2)
941 instance3
= self
.Instance(3)
942 instance4
= self
.Instance(4)
944 self
.mox
.StubOutWithMock(instance1
, 'quit')
946 self
.servr
._instances
= set([instance1
, instance2
, instance3
, instance4
])
947 self
.servr
._split
_instances
().AndReturn(
949 set([instance1
, instance2
, instance3
, instance4
])))
953 self
.servr
._adjust
_instances
()
956 def test_adjust_instances_quit_idle_with_race(self
):
957 instance1
= self
.Instance(0)
958 instance2
= self
.Instance(2)
959 instance3
= self
.Instance(3)
960 instance4
= self
.Instance(4)
962 self
.mox
.StubOutWithMock(instance1
, 'quit')
964 self
.servr
._instances
= set([instance1
, instance2
, instance3
, instance4
])
965 self
.servr
._split
_instances
().AndReturn(
967 set([instance1
, instance2
, instance3
, instance4
])))
968 instance1
.quit().AndRaise(instance
.CannotQuitServingInstance
)
971 self
.servr
._adjust
_instances
()
975 class TestAutoScalingInstancePoolHandleChanges(unittest
.TestCase
):
976 """Tests for server.AutoScalingServer._handle_changes."""
979 api_server
.test_setup_stubs()
982 self
.instance_factory
= instance
.InstanceFactory(object(), 10)
983 self
.servr
= AutoScalingServerFacade(
984 instance_factory
=self
.instance_factory
)
985 self
.mox
.StubOutWithMock(self
.instance_factory
, 'files_changed')
986 self
.mox
.StubOutWithMock(self
.instance_factory
, 'configuration_changed')
987 self
.mox
.StubOutWithMock(self
.servr
, '_maybe_restart_instances')
988 self
.mox
.StubOutWithMock(self
.servr
, '_create_url_handlers')
989 self
.mox
.StubOutWithMock(self
.servr
._server
_configuration
,
991 self
.mox
.StubOutWithMock(self
.servr
._watcher
, 'has_changes')
994 self
.mox
.UnsetStubs()
996 def test_no_changes(self
):
997 self
.servr
._server
_configuration
.check_for_updates().AndReturn(frozenset())
998 self
.servr
._watcher
.has_changes().AndReturn(False)
999 self
.servr
._maybe
_restart
_instances
(config_changed
=False,
1001 self
.mox
.ReplayAll()
1002 self
.servr
._handle
_changes
()
1003 self
.mox
.VerifyAll()
1005 def test_irrelevant_config_change(self
):
1006 self
.servr
._server
_configuration
.check_for_updates().AndReturn(frozenset())
1007 self
.servr
._watcher
.has_changes().AndReturn(False)
1008 self
.servr
._maybe
_restart
_instances
(config_changed
=False,
1011 self
.mox
.ReplayAll()
1012 self
.servr
._handle
_changes
()
1013 self
.mox
.VerifyAll()
1015 def test_restart_config_change(self
):
1016 conf_change
= frozenset([application_configuration
.ENV_VARIABLES_CHANGED
])
1017 self
.servr
._server
_configuration
.check_for_updates().AndReturn(conf_change
)
1018 self
.servr
._watcher
.has_changes().AndReturn(False)
1019 self
.instance_factory
.configuration_changed(conf_change
)
1020 self
.servr
._maybe
_restart
_instances
(config_changed
=True, file_changed
=False)
1022 self
.mox
.ReplayAll()
1023 self
.servr
._handle
_changes
()
1024 self
.mox
.VerifyAll()
1026 def test_handler_change(self
):
1027 conf_change
= frozenset([application_configuration
.HANDLERS_CHANGED
])
1028 self
.servr
._server
_configuration
.check_for_updates().AndReturn(conf_change
)
1029 self
.servr
._watcher
.has_changes().AndReturn(False)
1030 self
.servr
._create
_url
_handlers
()
1031 self
.instance_factory
.configuration_changed(conf_change
)
1032 self
.servr
._maybe
_restart
_instances
(config_changed
=True, file_changed
=False)
1034 self
.mox
.ReplayAll()
1035 self
.servr
._handle
_changes
()
1036 self
.mox
.VerifyAll()
1038 def test_file_change(self
):
1039 self
.servr
._server
_configuration
.check_for_updates().AndReturn(frozenset())
1040 self
.servr
._watcher
.has_changes().AndReturn(True)
1041 self
.instance_factory
.files_changed()
1042 self
.servr
._maybe
_restart
_instances
(config_changed
=False, file_changed
=True)
1044 self
.mox
.ReplayAll()
1045 self
.servr
._handle
_changes
()
1046 self
.mox
.VerifyAll()
1049 class TestAutoScalingInstancePoolMaybeRestartInstances(unittest
.TestCase
):
1050 """Tests for server.AutoScalingServer._maybe_restart_instances."""
1053 api_server
.test_setup_stubs()
1055 self
.mox
= mox
.Mox()
1056 self
.instance_factory
= instance
.InstanceFactory(object(), 10)
1057 self
.instance_factory
.FILE_CHANGE_INSTANCE_RESTART_POLICY
= instance
.ALWAYS
1058 self
.servr
= AutoScalingServerFacade(instance_factory
=self
.instance_factory
)
1060 self
.inst1
= self
.mox
.CreateMock(instance
.Instance
)
1061 self
.inst2
= self
.mox
.CreateMock(instance
.Instance
)
1062 self
.inst3
= self
.mox
.CreateMock(instance
.Instance
)
1063 self
.inst1
.total_requests
= 2
1064 self
.inst2
.total_requests
= 0
1065 self
.inst3
.total_requests
= 4
1066 self
.servr
._instances
.add(self
.inst1
)
1067 self
.servr
._instances
.add(self
.inst2
)
1068 self
.servr
._instances
.add(self
.inst3
)
1071 self
.mox
.UnsetStubs()
1073 def test_no_changes(self
):
1074 self
.mox
.ReplayAll()
1075 self
.servr
._maybe
_restart
_instances
(config_changed
=False,
1077 self
.mox
.VerifyAll()
1079 def test_config_change(self
):
1080 self
.inst1
.quit(allow_async
=True).InAnyOrder()
1081 self
.inst2
.quit(allow_async
=True).InAnyOrder()
1082 self
.inst3
.quit(allow_async
=True).InAnyOrder()
1084 self
.mox
.ReplayAll()
1085 self
.servr
._maybe
_restart
_instances
(config_changed
=True,
1087 self
.mox
.VerifyAll()
1089 def test_file_change_restart_always(self
):
1090 self
.instance_factory
.FILE_CHANGE_INSTANCE_RESTART_POLICY
= instance
.ALWAYS
1091 self
.inst1
.quit(allow_async
=True).InAnyOrder()
1092 self
.inst2
.quit(allow_async
=True).InAnyOrder()
1093 self
.inst3
.quit(allow_async
=True).InAnyOrder()
1095 self
.mox
.ReplayAll()
1096 self
.servr
._maybe
_restart
_instances
(config_changed
=False,
1098 self
.mox
.VerifyAll()
1099 self
.assertSequenceEqual(set(), self
.servr
._instances
)
1101 def test_file_change_restart_after_first_request(self
):
1102 self
.instance_factory
.FILE_CHANGE_INSTANCE_RESTART_POLICY
= (
1103 instance
.AFTER_FIRST_REQUEST
)
1104 self
.inst1
.quit(allow_async
=True).InAnyOrder()
1105 self
.inst3
.quit(allow_async
=True).InAnyOrder()
1107 self
.mox
.ReplayAll()
1108 self
.servr
._maybe
_restart
_instances
(config_changed
=False,
1110 self
.mox
.VerifyAll()
1111 self
.assertSequenceEqual(set([self
.inst2
]), self
.servr
._instances
)
1113 def test_file_change_restart_never(self
):
1114 self
.instance_factory
.FILE_CHANGE_INSTANCE_RESTART_POLICY
= instance
.NEVER
1116 self
.mox
.ReplayAll()
1117 self
.servr
._maybe
_restart
_instances
(config_changed
=False,
1119 self
.mox
.VerifyAll()
1120 self
.assertSequenceEqual(set([self
.inst1
, self
.inst2
, self
.inst3
]),
1121 self
.servr
._instances
)
1124 class TestAutoScalingInstancePoolLoopAdjustingInstances(unittest
.TestCase
):
1125 """Tests for server.AutoScalingServer._adjust_instances."""
1128 api_server
.test_setup_stubs()
1130 self
.mox
= mox
.Mox()
1131 self
.servr
= AutoScalingServerFacade(
1132 instance_factory
=instance
.InstanceFactory(object(), 10))
1135 self
.mox
.UnsetStubs()
1137 def test_loop_and_quit(self
):
1138 self
.mox
.StubOutWithMock(self
.servr
, '_adjust_instances')
1139 self
.mox
.StubOutWithMock(self
.servr
, '_handle_changes')
1141 inst1
= self
.mox
.CreateMock(instance
.Instance
)
1142 inst2
= self
.mox
.CreateMock(instance
.Instance
)
1143 inst3
= self
.mox
.CreateMock(instance
.Instance
)
1144 self
.servr
._instances
.add(inst1
)
1145 self
.servr
._instances
.add(inst2
)
1146 self
.servr
._instances
.add(inst3
)
1148 self
.servr
._handle
_changes
()
1150 def do_quit(*unused_args
):
1151 self
.servr
._quit
_event
.set()
1153 self
.servr
._adjust
_instances
().WithSideEffects(do_quit
)
1155 self
.mox
.ReplayAll()
1156 self
.servr
._loop
_adjusting
_instances
()
1157 self
.mox
.VerifyAll()
1160 class TestAutoScalingInstancePoolAutomaticScaling(unittest
.TestCase
):
1163 api_server
.test_setup_stubs()
1165 def _create_server(self
, automatic_scaling
):
1166 return AutoScalingServerFacade(
1167 server_configuration
=ServerConfigurationStub(
1168 automatic_scaling
=automatic_scaling
),
1169 instance_factory
=instance
.InstanceFactory(object(), 10))
1171 def test_unset_automatic_settings(self
):
1172 settings
= appinfo
.AutomaticScaling()
1173 pool
= self
._create
_server
(settings
)
1174 self
.assertEqual(0.1, pool
._min
_pending
_latency
)
1175 self
.assertEqual(0.5, pool
._max
_pending
_latency
)
1176 self
.assertEqual(1, pool
._min
_idle
_instances
)
1177 self
.assertEqual(1000, pool
._max
_idle
_instances
)
1179 def test_automatic_automatic_settings(self
):
1180 settings
= appinfo
.AutomaticScaling(
1181 min_pending_latency
='automatic',
1182 max_pending_latency
='automatic',
1183 min_idle_instances
='automatic',
1184 max_idle_instances
='automatic')
1185 pool
= self
._create
_server
(settings
)
1186 self
.assertEqual(0.1, pool
._min
_pending
_latency
)
1187 self
.assertEqual(0.5, pool
._max
_pending
_latency
)
1188 self
.assertEqual(1, pool
._min
_idle
_instances
)
1189 self
.assertEqual(1000, pool
._max
_idle
_instances
)
1191 def test_explicit_automatic_settings(self
):
1192 settings
= appinfo
.AutomaticScaling(
1193 min_pending_latency
='1234ms',
1194 max_pending_latency
='5.67s',
1195 min_idle_instances
='3',
1196 max_idle_instances
='20')
1197 pool
= self
._create
_server
(settings
)
1198 self
.assertEqual(1.234, pool
._min
_pending
_latency
)
1199 self
.assertEqual(5.67, pool
._max
_pending
_latency
)
1200 self
.assertEqual(3, pool
._min
_idle
_instances
)
1201 self
.assertEqual(20, pool
._max
_idle
_instances
)
1204 class TestManualScalingServerStart(unittest
.TestCase
):
1205 """Tests for server.ManualScalingServer._start_instance."""
1208 api_server
.test_setup_stubs()
1209 self
.mox
= mox
.Mox()
1210 self
.mox
.StubOutWithMock(server
.Server
, 'build_request_environ')
1213 self
.mox
.UnsetStubs()
1215 def test_instance_start_success(self
):
1216 s
= ManualScalingServerFacade(balanced_port
=8080)
1217 self
.mox
.StubOutWithMock(s
, '_handle_request')
1218 self
.mox
.StubOutWithMock(s
._condition
, 'notify')
1220 wsgi_servr
= self
.mox
.CreateMock(wsgi_server
.WsgiServer
)
1221 wsgi_servr
.port
= 12345
1222 inst
= self
.mox
.CreateMock(instance
.Instance
)
1223 inst
.instance_id
= 0
1224 inst
.start().AndReturn(True)
1227 s
.build_request_environ('GET', '/_ah/start', [], '', '0.1.0.3', 12345,
1228 fake_login
=True).AndReturn(environ
)
1229 s
._handle
_request
(environ
,
1232 request_type
=instance
.READY_REQUEST
)
1233 s
._condition
.notify(1)
1235 self
.mox
.ReplayAll()
1236 s
._start
_instance
(wsgi_servr
, inst
)
1237 self
.mox
.VerifyAll()
1239 def test_instance_start_failure(self
):
1240 s
= ManualScalingServerFacade(balanced_port
=8080)
1241 self
.mox
.StubOutWithMock(s
, '_handle_request')
1242 self
.mox
.StubOutWithMock(s
._condition
, 'notify')
1244 wsgi_servr
= self
.mox
.CreateMock(wsgi_server
.WsgiServer
)
1245 wsgi_servr
.port
= 12345
1246 inst
= self
.mox
.CreateMock(instance
.Instance
)
1247 inst
.instance_id
= 0
1248 inst
.start().AndReturn(False)
1250 self
.mox
.ReplayAll()
1251 s
._start
_instance
(wsgi_servr
, inst
)
1252 self
.mox
.VerifyAll()
1255 class TestManualScalingServerAddInstance(unittest
.TestCase
):
1256 """Tests for server.ManualScalingServer._add_instance."""
1258 class WsgiServer(object):
1259 def __init__(self
, port
):
1263 api_server
.test_setup_stubs()
1264 self
.mox
= mox
.Mox()
1265 self
.factory
= self
.mox
.CreateMock(instance
.InstanceFactory
)
1266 self
.factory
.max_concurrent_requests
= 10
1269 self
.mox
.UnsetStubs()
1271 def test_add_while_started(self
):
1272 servr
= ManualScalingServerFacade(instance_factory
=self
.factory
)
1274 inst
= self
.mox
.CreateMock(instance
.Instance
)
1275 self
.mox
.StubOutWithMock(server
._THREAD
_POOL
, 'submit')
1276 self
.mox
.StubOutWithMock(wsgi_server
.WsgiServer
, 'start')
1277 self
.mox
.StubOutWithMock(wsgi_server
.WsgiServer
, 'port')
1278 wsgi_server
.WsgiServer
.port
= 12345
1279 self
.factory
.new_instance(0, expect_ready_request
=True).AndReturn(inst
)
1280 wsgi_server
.WsgiServer
.start()
1281 server
._THREAD
_POOL
.submit(servr
._start
_instance
,
1282 mox
.IsA(wsgi_server
.WsgiServer
), inst
)
1284 self
.mox
.ReplayAll()
1285 servr
._add
_instance
()
1286 self
.mox
.VerifyAll()
1287 self
.assertIn(inst
, servr
._instances
)
1288 self
.assertEqual((servr
, inst
), servr
._port
_registry
.get(12345))
1290 def test_add_while_stopped(self
):
1291 servr
= ManualScalingServerFacade(instance_factory
=self
.factory
)
1292 servr
._suspended
= True
1294 inst
= self
.mox
.CreateMock(instance
.Instance
)
1295 self
.mox
.StubOutWithMock(wsgi_server
.WsgiServer
, 'start')
1296 self
.mox
.StubOutWithMock(wsgi_server
.WsgiServer
, 'port')
1297 wsgi_server
.WsgiServer
.port
= 12345
1298 self
.mox
.StubOutWithMock(server
._THREAD
_POOL
, 'submit')
1299 self
.factory
.new_instance(0, expect_ready_request
=True).AndReturn(inst
)
1300 wsgi_server
.WsgiServer
.start()
1302 self
.mox
.ReplayAll()
1303 servr
._add
_instance
()
1304 self
.mox
.VerifyAll()
1306 self
.assertIn(inst
, servr
._instances
)
1307 self
.assertEqual((servr
, inst
), servr
._port
_registry
.get(12345))
1310 class TestManualScalingInstancePoolHandleScriptRequest(unittest
.TestCase
):
1311 """Tests for server.ManualScalingServer.handle."""
1314 api_server
.test_setup_stubs()
1315 self
.mox
= mox
.Mox()
1317 self
.inst
= self
.mox
.CreateMock(instance
.Instance
)
1318 self
.inst
.instance_id
= 0
1320 self
.start_response
= object()
1321 self
.response
= [object()]
1322 self
.url_map
= object()
1323 self
.match
= object()
1324 self
.request_id
= object()
1325 self
.manual_server
= ManualScalingServerFacade(
1326 instance_factory
=instance
.InstanceFactory(object(), 10))
1327 self
.mox
.StubOutWithMock(self
.manual_server
, '_choose_instance')
1328 self
.mox
.StubOutWithMock(self
.manual_server
, '_add_instance')
1329 self
.mox
.StubOutWithMock(self
.manual_server
._condition
, 'notify')
1330 self
.mox
.stubs
.Set(time
, 'time', lambda: 0.0)
1333 self
.mox
.UnsetStubs()
1335 def test_handle_script_request(self
):
1336 self
.manual_server
._choose
_instance
(10.0).AndReturn(self
.inst
)
1337 self
.inst
.handle(self
.environ
,
1338 self
.start_response
,
1342 instance
.NORMAL_REQUEST
).AndReturn(self
.response
)
1343 self
.manual_server
._condition
.notify()
1345 self
.mox
.ReplayAll()
1348 self
.manual_server
._handle
_script
_request
(self
.environ
,
1349 self
.start_response
,
1353 self
.mox
.VerifyAll()
1355 def test_handle_cannot_accept_request(self
):
1356 self
.manual_server
._choose
_instance
(10.0).AndReturn(self
.inst
)
1357 self
.manual_server
._choose
_instance
(10.0).AndReturn(self
.inst
)
1359 self
.environ
, self
.start_response
, self
.url_map
, self
.match
,
1360 self
.request_id
, instance
.NORMAL_REQUEST
).AndRaise(
1361 instance
.CannotAcceptRequests
)
1362 self
.manual_server
._condition
.notify()
1364 self
.environ
, self
.start_response
, self
.url_map
, self
.match
,
1365 self
.request_id
, instance
.NORMAL_REQUEST
).AndReturn(
1367 self
.manual_server
._condition
.notify()
1369 self
.mox
.ReplayAll()
1372 self
.manual_server
._handle
_script
_request
(self
.environ
,
1373 self
.start_response
,
1377 self
.mox
.VerifyAll()
1379 def test_handle_must_wait(self
):
1380 self
.manual_server
._choose
_instance
(10.0).AndReturn(None)
1381 self
.manual_server
._choose
_instance
(10.0).AndReturn(self
.inst
)
1384 self
.environ
, self
.start_response
, self
.url_map
, self
.match
,
1385 self
.request_id
, instance
.NORMAL_REQUEST
).AndReturn(
1387 self
.manual_server
._condition
.notify()
1389 self
.mox
.ReplayAll()
1392 self
.manual_server
._handle
_script
_request
(self
.environ
,
1393 self
.start_response
,
1397 self
.mox
.VerifyAll()
1399 def test_handle_timeout(self
):
1402 def advance_time(*unused_args
):
1405 self
.mox
.stubs
.Set(time
, 'time', lambda: self
.time
)
1406 self
.mox
.StubOutWithMock(self
.manual_server
, '_error_response')
1408 self
.manual_server
._choose
_instance
(10.0).WithSideEffects(advance_time
)
1409 self
.manual_server
._error
_response
(self
.environ
, self
.start_response
,
1410 503).AndReturn(self
.response
)
1411 self
.mox
.ReplayAll()
1414 self
.manual_server
._handle
_script
_request
(self
.environ
,
1415 self
.start_response
,
1419 self
.mox
.VerifyAll()
1422 class TestManualScalingInstancePoolChooseInstances(unittest
.TestCase
):
1423 """Tests for server.ManualScalingServer._choose_instance."""
1425 class Instance(object):
1426 def __init__(self
, can_accept_requests
):
1427 self
.can_accept_requests
= can_accept_requests
1430 self
.mox
= mox
.Mox()
1431 api_server
.test_setup_stubs()
1432 self
.servr
= ManualScalingServerFacade(
1433 instance_factory
=instance
.InstanceFactory(object(), 10))
1434 self
.mox
.StubOutWithMock(self
.servr
._condition
, 'wait')
1436 self
.mox
.stubs
.Set(time
, 'time', lambda: self
.time
)
1438 def advance_time(self
, *unused_args
):
1442 self
.mox
.UnsetStubs()
1444 def test_choose_instance_first_can_accept(self
):
1445 instance1
= self
.Instance(True)
1446 instance2
= self
.Instance(True)
1447 self
.servr
._instances
= [instance1
, instance2
]
1448 self
.mox
.ReplayAll()
1449 self
.assertEqual(instance1
, self
.servr
._choose
_instance
(1))
1450 self
.mox
.VerifyAll()
1452 def test_choose_instance_first_cannot_accept(self
):
1453 instance1
= self
.Instance(False)
1454 instance2
= self
.Instance(True)
1455 self
.servr
._instances
= [instance1
, instance2
]
1456 self
.mox
.ReplayAll()
1457 self
.assertEqual(instance2
, self
.servr
._choose
_instance
(1))
1458 self
.mox
.VerifyAll()
1460 def test_choose_instance_none_can_accept(self
):
1461 instance1
= self
.Instance(False)
1462 instance2
= self
.Instance(False)
1463 self
.servr
._instances
= [instance1
, instance2
]
1464 self
.servr
._condition
.wait(5).WithSideEffects(self
.advance_time
)
1465 self
.mox
.ReplayAll()
1466 self
.assertEqual(None, self
.servr
._choose
_instance
(5))
1467 self
.mox
.VerifyAll()
1469 def test_choose_instance_no_instances(self
):
1470 self
.servr
._condition
.wait(5).WithSideEffects(self
.advance_time
)
1471 self
.mox
.ReplayAll()
1472 self
.assertEqual(None, self
.servr
._choose
_instance
(5))
1473 self
.mox
.VerifyAll()
1476 class TestManualScalingInstancePoolSetNumInstances(unittest
.TestCase
):
1477 """Tests for server.ManualScalingServer.set_num_instances."""
1480 self
.mox
= mox
.Mox()
1481 api_server
.test_setup_stubs()
1482 self
.server
= ManualScalingServerFacade(
1483 instance_factory
=instance
.InstanceFactory(object(), 10))
1484 self
._instance
= self
.mox
.CreateMock(instance
.Instance
)
1485 self
._wsgi
_server
= self
.mox
.CreateMock(wsgi_server
.WsgiServer
)
1486 self
._wsgi
_server
.port
= 8080
1487 self
.server
._instances
= [self
._instance
]
1488 self
.server
._wsgi
_servers
= [self
._wsgi
_server
]
1489 self
.mox
.StubOutWithMock(server
._THREAD
_POOL
, 'submit')
1490 self
.mox
.StubOutWithMock(self
.server
, '_add_instance')
1491 self
.mox
.StubOutWithMock(self
.server
, '_shutdown_instance')
1494 self
.mox
.UnsetStubs()
1496 def test_no_op(self
):
1497 self
.mox
.ReplayAll()
1498 self
.assertEqual(1, self
.server
.get_num_instances())
1499 self
.server
.set_num_instances(1)
1500 self
.mox
.VerifyAll()
1502 def test_add_an_instance(self
):
1503 self
.server
._add
_instance
()
1504 self
.mox
.ReplayAll()
1505 self
.assertEqual(1, self
.server
.get_num_instances())
1506 self
.server
.set_num_instances(2)
1507 self
.mox
.VerifyAll()
1509 def test_remove_an_instance(self
):
1510 server
._THREAD
_POOL
.submit(self
.server
._quit
_instance
,
1513 self
._instance
.quit(expect_shutdown
=True)
1514 self
._wsgi
_server
.quit()
1515 self
.server
._shutdown
_instance
(self
._instance
, 8080)
1516 self
.mox
.ReplayAll()
1517 self
.assertEqual(1, self
.server
.get_num_instances())
1518 self
.server
.set_num_instances(0)
1519 self
.server
._quit
_instance
(self
._instance
,
1521 self
.mox
.VerifyAll()
1524 class TestManualScalingInstancePoolSuspendAndResume(unittest
.TestCase
):
1525 """Tests for server.ManualScalingServer.suspend and resume."""
1528 self
.mox
= mox
.Mox()
1529 api_server
.test_setup_stubs()
1530 self
.factory
= self
.mox
.CreateMock(instance
.InstanceFactory
)
1531 self
.server
= ManualScalingServerFacade(
1532 instance_factory
=self
.factory
)
1533 self
._instance
= self
.mox
.CreateMock(instance
.Instance
)
1534 self
._wsgi
_server
= wsgi_server
.WsgiServer(('localhost', 0), None)
1535 self
.server
._instances
= [self
._instance
]
1536 self
.server
._wsgi
_servers
= [self
._wsgi
_server
]
1537 self
.mox
.StubOutWithMock(server
._THREAD
_POOL
, 'submit')
1538 self
.mox
.StubOutWithMock(self
.server
, '_shutdown_instance')
1539 self
._wsgi
_server
.start()
1542 self
._wsgi
_server
.quit()
1543 self
.mox
.UnsetStubs()
1545 def test_already_suspended(self
):
1546 self
.server
._suspended
= True
1547 self
.assertRaises(request_info
.ServerAlreadyStoppedError
,
1548 self
.server
.suspend
)
1550 def test_already_resumed(self
):
1551 self
.assertRaises(request_info
.ServerAlreadyStartedError
,
1554 def test_suspend_instance(self
):
1555 server
._THREAD
_POOL
.submit(self
.server
._suspend
_instance
, self
._instance
,
1556 self
._wsgi
_server
.port
)
1557 self
._instance
.quit(expect_shutdown
=True)
1559 self
.server
._shutdown
_instance
(self
._instance
, port
)
1560 self
.mox
.ReplayAll()
1561 self
.server
.suspend()
1562 self
.server
._suspend
_instance
(self
._instance
, port
)
1563 self
.mox
.VerifyAll()
1564 self
.assertEqual(404, self
._wsgi
_server
._error
)
1565 self
.assertEqual(None, self
._wsgi
_server
._app
)
1566 self
.assertTrue(self
.server
._suspended
)
1568 def test_resume(self
):
1569 self
.server
._suspended
= True
1570 self
.server
._instances
= [object()]
1571 self
.factory
.new_instance(0, expect_ready_request
=True).AndReturn(
1573 server
._THREAD
_POOL
.submit(self
.server
._start
_instance
, self
._wsgi
_server
,
1575 self
.mox
.ReplayAll()
1576 self
.server
.resume()
1577 self
.mox
.VerifyAll()
1578 self
.assertEqual(self
.server
._handle
_request
,
1579 self
._wsgi
_server
._app
.func
)
1580 self
.assertEqual({'inst': self
._instance
},
1581 self
._wsgi
_server
._app
.keywords
)
1582 self
.assertFalse(self
.server
._suspended
)
1584 def test_restart(self
):
1585 self
._new
_instance
= self
.mox
.CreateMock(instance
.Instance
)
1586 self
.factory
.new_instance(0, expect_ready_request
=True).AndReturn(
1588 server
._THREAD
_POOL
.submit(self
.server
._suspend
_instance
, self
._instance
,
1589 self
._wsgi
_server
.port
)
1590 server
._THREAD
_POOL
.submit(self
.server
._start
_instance
, self
._wsgi
_server
,
1592 self
._instance
.quit(expect_shutdown
=True)
1594 self
.server
._shutdown
_instance
(self
._instance
, port
)
1595 self
.mox
.ReplayAll()
1596 self
.server
.restart()
1597 self
.server
._suspend
_instance
(self
._instance
, port
)
1598 self
.mox
.VerifyAll()
1599 self
.assertEqual(self
.server
._handle
_request
,
1600 self
._wsgi
_server
._app
.func
)
1601 self
.assertEqual({'inst': self
._new
_instance
},
1602 self
._wsgi
_server
._app
.keywords
)
1603 self
.assertFalse(self
.server
._suspended
)
1606 class TestManualScalingInstancePoolHandleChanges(unittest
.TestCase
):
1607 """Tests for server.ManualScalingServer._handle_changes."""
1610 api_server
.test_setup_stubs()
1612 self
.mox
= mox
.Mox()
1613 self
.instance_factory
= instance
.InstanceFactory(object(), 10)
1614 self
.servr
= ManualScalingServerFacade(
1615 instance_factory
=self
.instance_factory
)
1616 self
.mox
.StubOutWithMock(self
.instance_factory
, 'files_changed')
1617 self
.mox
.StubOutWithMock(self
.instance_factory
, 'configuration_changed')
1618 self
.mox
.StubOutWithMock(self
.servr
, 'restart')
1619 self
.mox
.StubOutWithMock(self
.servr
, '_create_url_handlers')
1620 self
.mox
.StubOutWithMock(self
.servr
._server
_configuration
,
1621 'check_for_updates')
1622 self
.mox
.StubOutWithMock(self
.servr
._watcher
, 'has_changes')
1625 self
.mox
.UnsetStubs()
1627 def test_no_changes(self
):
1628 self
.servr
._server
_configuration
.check_for_updates().AndReturn(frozenset())
1629 self
.servr
._watcher
.has_changes().AndReturn(False)
1631 self
.mox
.ReplayAll()
1632 self
.servr
._handle
_changes
()
1633 self
.mox
.VerifyAll()
1635 def test_irrelevant_config_change(self
):
1636 self
.servr
._server
_configuration
.check_for_updates().AndReturn(frozenset())
1637 self
.servr
._watcher
.has_changes().AndReturn(False)
1639 self
.mox
.ReplayAll()
1640 self
.servr
._handle
_changes
()
1641 self
.mox
.VerifyAll()
1643 def test_restart_config_change(self
):
1644 conf_change
= frozenset([application_configuration
.ENV_VARIABLES_CHANGED
])
1645 self
.servr
._server
_configuration
.check_for_updates().AndReturn(conf_change
)
1646 self
.servr
._watcher
.has_changes().AndReturn(False)
1647 self
.instance_factory
.configuration_changed(conf_change
)
1648 self
.servr
.restart()
1650 self
.mox
.ReplayAll()
1651 self
.servr
._handle
_changes
()
1652 self
.mox
.VerifyAll()
1654 def test_handler_change(self
):
1655 conf_change
= frozenset([application_configuration
.HANDLERS_CHANGED
])
1656 self
.servr
._server
_configuration
.check_for_updates().AndReturn(conf_change
)
1657 self
.servr
._watcher
.has_changes().AndReturn(False)
1658 self
.servr
._create
_url
_handlers
()
1659 self
.instance_factory
.configuration_changed(conf_change
)
1661 self
.servr
.restart()
1663 self
.mox
.ReplayAll()
1664 self
.servr
._handle
_changes
()
1665 self
.mox
.VerifyAll()
1667 def test_file_change(self
):
1668 self
.servr
._server
_configuration
.check_for_updates().AndReturn(frozenset())
1669 self
.servr
._watcher
.has_changes().AndReturn(True)
1670 self
.instance_factory
.files_changed()
1671 self
.servr
.restart()
1673 self
.mox
.ReplayAll()
1674 self
.servr
._handle
_changes
()
1675 self
.mox
.VerifyAll()
1677 def test_restart_config_change_suspended(self
):
1678 self
.servr
._suspended
= True
1679 conf_change
= frozenset([application_configuration
.ENV_VARIABLES_CHANGED
])
1680 self
.servr
._server
_configuration
.check_for_updates().AndReturn(conf_change
)
1681 self
.servr
._watcher
.has_changes().AndReturn(False)
1682 self
.instance_factory
.configuration_changed(conf_change
)
1684 self
.mox
.ReplayAll()
1685 self
.servr
._handle
_changes
()
1686 self
.mox
.VerifyAll()
1688 def test_handler_change_suspended(self
):
1689 self
.servr
._suspended
= True
1690 conf_change
= frozenset([application_configuration
.HANDLERS_CHANGED
])
1691 self
.servr
._server
_configuration
.check_for_updates().AndReturn(conf_change
)
1692 self
.servr
._watcher
.has_changes().AndReturn(False)
1693 self
.servr
._create
_url
_handlers
()
1694 self
.instance_factory
.configuration_changed(conf_change
)
1696 self
.mox
.ReplayAll()
1697 self
.servr
._handle
_changes
()
1698 self
.mox
.VerifyAll()
1700 def test_file_change_suspended(self
):
1701 self
.servr
._suspended
= True
1702 self
.servr
._server
_configuration
.check_for_updates().AndReturn(frozenset())
1703 self
.servr
._watcher
.has_changes().AndReturn(True)
1704 self
.instance_factory
.files_changed()
1706 self
.mox
.ReplayAll()
1707 self
.servr
._handle
_changes
()
1708 self
.mox
.VerifyAll()
1711 class TestBasicScalingServerStart(unittest
.TestCase
):
1712 """Tests for server.BasicScalingServer._start_instance."""
1715 api_server
.test_setup_stubs()
1716 self
.mox
= mox
.Mox()
1717 self
.mox
.StubOutWithMock(server
.Server
, 'build_request_environ')
1720 self
.mox
.UnsetStubs()
1722 def test_instance_start_success(self
):
1723 s
= BasicScalingServerFacade(balanced_port
=8080)
1724 self
.mox
.StubOutWithMock(s
, '_handle_request')
1725 self
.mox
.StubOutWithMock(s
._condition
, 'notify')
1727 wsgi_servr
= self
.mox
.CreateMock(wsgi_server
.WsgiServer
)
1728 wsgi_servr
.port
= 12345
1729 s
._wsgi
_servers
[0] = wsgi_servr
1730 inst
= self
.mox
.CreateMock(instance
.Instance
)
1731 inst
.instance_id
= 0
1732 s
._instances
[0] = inst
1733 inst
.start().AndReturn(True)
1736 s
.build_request_environ('GET', '/_ah/start', [], '', '0.1.0.3', 12345,
1737 fake_login
=True).AndReturn(environ
)
1738 s
._handle
_request
(environ
,
1741 request_type
=instance
.READY_REQUEST
)
1742 s
._condition
.notify(1)
1744 self
.mox
.ReplayAll()
1745 s
._start
_instance
(0)
1746 self
.mox
.VerifyAll()
1748 def test_instance_start_failure(self
):
1749 s
= BasicScalingServerFacade(balanced_port
=8080)
1750 self
.mox
.StubOutWithMock(s
, '_handle_request')
1751 self
.mox
.StubOutWithMock(s
._condition
, 'notify')
1753 wsgi_servr
= self
.mox
.CreateMock(wsgi_server
.WsgiServer
)
1754 wsgi_servr
.port
= 12345
1755 s
._wsgi
_servers
[0] = wsgi_servr
1756 inst
= self
.mox
.CreateMock(instance
.Instance
)
1757 inst
.instance_id
= 0
1758 s
._instances
[0] = inst
1759 inst
.start().AndReturn(False)
1761 self
.mox
.ReplayAll()
1762 s
._start
_instance
(0)
1763 self
.mox
.VerifyAll()
1765 def test_start_any_instance_success(self
):
1766 s
= BasicScalingServerFacade(balanced_port
=8080)
1767 s
._instance
_running
= [True, False, False, True]
1769 s
._instances
= [None, inst
, None, None]
1770 self
.mox
.StubOutWithMock(server
._THREAD
_POOL
, 'submit')
1771 server
._THREAD
_POOL
.submit(s
._start
_instance
, 1)
1772 self
.mox
.ReplayAll()
1773 self
.assertEqual(inst
, s
._start
_any
_instance
())
1774 self
.mox
.VerifyAll()
1775 self
.assertEqual([True, True, False, True], s
._instance
_running
)
1777 def test_start_any_instance_all_already_running(self
):
1778 s
= BasicScalingServerFacade(balanced_port
=8080)
1779 s
._instance
_running
= [True, True, True, True]
1780 self
.mox
.StubOutWithMock(server
._THREAD
_POOL
, 'submit')
1781 self
.mox
.ReplayAll()
1782 self
.assertIsNone(s
._start
_any
_instance
())
1783 self
.mox
.VerifyAll()
1784 self
.assertEqual([True, True, True, True], s
._instance
_running
)
1787 class TestBasicScalingInstancePoolHandleScriptRequest(unittest
.TestCase
):
1788 """Tests for server.BasicScalingServer.handle."""
1791 api_server
.test_setup_stubs()
1792 self
.mox
= mox
.Mox()
1794 self
.inst
= self
.mox
.CreateMock(instance
.Instance
)
1795 self
.inst
.instance_id
= 0
1797 self
.start_response
= object()
1798 self
.response
= [object()]
1799 self
.url_map
= object()
1800 self
.match
= object()
1801 self
.request_id
= object()
1802 self
.basic_server
= BasicScalingServerFacade(
1803 instance_factory
=instance
.InstanceFactory(object(), 10))
1804 self
.mox
.StubOutWithMock(self
.basic_server
, '_choose_instance')
1805 self
.mox
.StubOutWithMock(self
.basic_server
, '_start_any_instance')
1806 self
.mox
.StubOutWithMock(self
.basic_server
, '_start_instance')
1807 self
.mox
.StubOutWithMock(self
.basic_server
._condition
, 'wait')
1808 self
.mox
.StubOutWithMock(self
.basic_server
._condition
, 'notify')
1810 self
.mox
.stubs
.Set(time
, 'time', lambda: self
.time
)
1812 def advance_time(self
, *unused_args
):
1816 self
.mox
.UnsetStubs()
1818 def test_handle_script_request(self
):
1819 self
.basic_server
._choose
_instance
(20).AndReturn(self
.inst
)
1820 self
.inst
.handle(self
.environ
,
1821 self
.start_response
,
1825 instance
.NORMAL_REQUEST
).AndReturn(self
.response
)
1826 self
.basic_server
._condition
.notify()
1828 self
.mox
.ReplayAll()
1831 self
.basic_server
._handle
_script
_request
(self
.environ
,
1832 self
.start_response
,
1836 self
.mox
.VerifyAll()
1838 def test_handle_cannot_accept_request(self
):
1839 self
.basic_server
._choose
_instance
(20).AndReturn(self
.inst
)
1841 self
.environ
, self
.start_response
, self
.url_map
, self
.match
,
1842 self
.request_id
, instance
.NORMAL_REQUEST
).AndRaise(
1843 instance
.CannotAcceptRequests
)
1844 self
.basic_server
._condition
.notify()
1845 self
.basic_server
._choose
_instance
(20).AndReturn(self
.inst
)
1847 self
.environ
, self
.start_response
, self
.url_map
, self
.match
,
1848 self
.request_id
, instance
.NORMAL_REQUEST
).AndReturn(
1850 self
.basic_server
._condition
.notify()
1852 self
.mox
.ReplayAll()
1855 self
.basic_server
._handle
_script
_request
(self
.environ
,
1856 self
.start_response
,
1860 self
.mox
.VerifyAll()
1862 def test_handle_timeout(self
):
1863 self
.mox
.StubOutWithMock(self
.basic_server
, '_error_response')
1865 self
.basic_server
._choose
_instance
(20).WithSideEffects(self
.advance_time
)
1866 self
.basic_server
._error
_response
(self
.environ
, self
.start_response
,
1867 503).AndReturn(self
.response
)
1869 self
.mox
.ReplayAll()
1872 self
.basic_server
._handle
_script
_request
(self
.environ
,
1873 self
.start_response
,
1877 self
.mox
.VerifyAll()
1879 def test_handle_instance(self
):
1880 self
.inst
.instance_id
= 0
1881 self
.inst
.has_quit
= False
1884 self
.environ
, self
.start_response
, self
.url_map
, self
.match
,
1885 self
.request_id
, instance
.NORMAL_REQUEST
).AndReturn(
1887 self
.basic_server
._condition
.notify()
1889 self
.mox
.ReplayAll()
1892 self
.basic_server
._handle
_script
_request
(self
.environ
,
1893 self
.start_response
,
1898 self
.mox
.VerifyAll()
1900 def test_handle_instance_start_the_instance(self
):
1901 self
.inst
.instance_id
= 0
1902 self
.inst
.has_quit
= False
1905 self
.environ
, self
.start_response
, self
.url_map
, self
.match
,
1906 self
.request_id
, instance
.NORMAL_REQUEST
).AndRaise(
1907 instance
.CannotAcceptRequests
)
1908 self
.basic_server
._start
_instance
(0).AndReturn(True)
1910 self
.environ
, self
.start_response
, self
.url_map
, self
.match
,
1911 self
.request_id
, instance
.NORMAL_REQUEST
).AndReturn(
1913 self
.basic_server
._condition
.notify()
1915 self
.mox
.ReplayAll()
1918 self
.basic_server
._handle
_script
_request
(self
.environ
,
1919 self
.start_response
,
1924 self
.mox
.VerifyAll()
1926 def test_handle_instance_already_running(self
):
1927 self
.inst
.instance_id
= 0
1928 self
.inst
.has_quit
= False
1930 self
.basic_server
._instance
_running
[0] = True
1932 self
.environ
, self
.start_response
, self
.url_map
, self
.match
,
1933 self
.request_id
, instance
.NORMAL_REQUEST
).AndRaise(
1934 instance
.CannotAcceptRequests
)
1937 self
.environ
, self
.start_response
, self
.url_map
, self
.match
,
1938 self
.request_id
, instance
.NORMAL_REQUEST
).AndReturn(
1940 self
.basic_server
._condition
.notify()
1942 self
.mox
.ReplayAll()
1945 self
.basic_server
._handle
_script
_request
(self
.environ
,
1946 self
.start_response
,
1951 self
.mox
.VerifyAll()
1953 def test_handle_instance_timeout(self
):
1954 self
.mox
.StubOutWithMock(self
.basic_server
, '_error_response')
1956 self
.inst
.instance_id
= 0
1957 self
.inst
.has_quit
= False
1959 self
.basic_server
._instance
_running
[0] = True
1961 self
.environ
, self
.start_response
, self
.url_map
, self
.match
,
1962 self
.request_id
, instance
.NORMAL_REQUEST
).AndRaise(
1963 instance
.CannotAcceptRequests
)
1964 self
.inst
.wait(20).WithSideEffects(self
.advance_time
)
1965 self
.basic_server
._error
_response
(self
.environ
, self
.start_response
,
1966 503).AndReturn(self
.response
)
1967 self
.basic_server
._condition
.notify()
1969 self
.mox
.ReplayAll()
1972 self
.basic_server
._handle
_script
_request
(self
.environ
,
1973 self
.start_response
,
1978 self
.mox
.VerifyAll()
1981 class TestBasicScalingInstancePoolChooseInstances(unittest
.TestCase
):
1982 """Tests for server.BasicScalingServer._choose_instance."""
1984 class Instance(object):
1985 def __init__(self
, can_accept_requests
):
1986 self
.can_accept_requests
= can_accept_requests
1989 api_server
.test_setup_stubs()
1990 self
.mox
= mox
.Mox()
1991 self
.servr
= BasicScalingServerFacade(
1992 instance_factory
=instance
.InstanceFactory(object(), 10))
1993 self
.mox
.stubs
.Set(time
, 'time', lambda: self
.time
)
1994 self
.mox
.StubOutWithMock(self
.servr
._condition
, 'wait')
1995 self
.mox
.StubOutWithMock(self
.servr
, '_start_any_instance')
1999 self
.mox
.UnsetStubs()
2001 def advance_time(self
, *unused_args
):
2004 def test_choose_instance_first_can_accept(self
):
2005 instance1
= self
.Instance(True)
2006 instance2
= self
.Instance(True)
2007 self
.servr
._instances
= [instance1
, instance2
]
2008 self
.mox
.ReplayAll()
2009 self
.assertEqual(instance1
, self
.servr
._choose
_instance
(1))
2010 self
.mox
.VerifyAll()
2012 def test_choose_instance_first_cannot_accept(self
):
2013 instance1
= self
.Instance(False)
2014 instance2
= self
.Instance(True)
2015 self
.servr
._instances
= [instance1
, instance2
]
2016 self
.mox
.ReplayAll()
2017 self
.assertEqual(instance2
, self
.servr
._choose
_instance
(1))
2018 self
.mox
.VerifyAll()
2020 def test_choose_instance_none_can_accept(self
):
2021 instance1
= self
.Instance(False)
2022 instance2
= self
.Instance(False)
2023 self
.servr
._instance
_running
= [True, True]
2024 self
.servr
._instances
= [instance1
, instance2
]
2025 self
.servr
._start
_any
_instance
().AndReturn(None)
2026 self
.servr
._condition
.wait(1).WithSideEffects(self
.advance_time
)
2027 self
.mox
.ReplayAll()
2028 self
.assertEqual(None, self
.servr
._choose
_instance
(1))
2029 self
.mox
.VerifyAll()
2031 def test_choose_instance_start_an_instance(self
):
2032 instance1
= self
.Instance(False)
2033 instance2
= self
.Instance(False)
2034 mock_instance
= self
.mox
.CreateMock(instance
.Instance
)
2035 self
.servr
._instances
= [instance1
, instance2
]
2036 self
.servr
._instance
_running
= [True, False]
2037 self
.servr
._start
_any
_instance
().AndReturn(mock_instance
)
2038 mock_instance
.wait(1)
2039 self
.mox
.ReplayAll()
2040 self
.assertEqual(mock_instance
, self
.servr
._choose
_instance
(1))
2041 self
.mox
.VerifyAll()
2043 def test_choose_instance_no_instances(self
):
2044 self
.servr
._start
_any
_instance
().AndReturn(None)
2045 self
.servr
._condition
.wait(1).WithSideEffects(self
.advance_time
)
2046 self
.mox
.ReplayAll()
2047 self
.assertEqual(None, self
.servr
._choose
_instance
(1))
2048 self
.mox
.VerifyAll()
2051 class TestBasicScalingInstancePoolInstanceManagement(unittest
.TestCase
):
2054 api_server
.test_setup_stubs()
2055 self
.mox
= mox
.Mox()
2056 self
.factory
= self
.mox
.CreateMock(instance
.InstanceFactory
)
2057 self
.factory
.max_concurrent_requests
= 10
2058 self
.mox
.StubOutWithMock(server
._THREAD
_POOL
, 'submit')
2059 self
.server
= BasicScalingServerFacade(instance_factory
=self
.factory
,
2061 self
.wsgi_server
= self
.server
._wsgi
_servers
[0]
2062 self
.wsgi_server
.start()
2065 self
.wsgi_server
.quit()
2066 self
.mox
.UnsetStubs()
2068 def test_restart(self
):
2069 old_instances
= [self
.mox
.CreateMock(instance
.Instance
),
2070 self
.mox
.CreateMock(instance
.Instance
)]
2071 self
.server
._instances
= old_instances
[:]
2072 self
.server
._instance
_running
= [True, False]
2073 new_instance
= self
.mox
.CreateMock(instance
.Instance
)
2074 self
.factory
.new_instance(0, expect_ready_request
=True).AndReturn(
2076 server
._THREAD
_POOL
.submit(self
.server
._start
_instance
, 0)
2077 old_instances
[0].quit(expect_shutdown
=True)
2078 server
._THREAD
_POOL
.submit(self
.server
._shutdown
_instance
, old_instances
[0],
2079 self
.wsgi_server
.port
)
2081 self
.mox
.ReplayAll()
2082 self
.server
.restart()
2083 self
.mox
.VerifyAll()
2084 self
.assertEqual([True, False], self
.server
._instance
_running
)
2085 self
.assertEqual(new_instance
, self
.server
._instances
[0])
2086 self
.assertEqual(self
.server
._handle
_request
,
2087 self
.server
._wsgi
_servers
[0]._app
.func
)
2088 self
.assertEqual({'inst': new_instance
},
2089 self
.server
._wsgi
_servers
[0]._app
.keywords
)
2091 def test_shutdown_idle_instances(self
):
2092 s
= BasicScalingServerFacade(instance_factory
=self
.factory
)
2093 old_instances
= [self
.mox
.CreateMock(instance
.Instance
),
2094 self
.mox
.CreateMock(instance
.Instance
),
2095 self
.mox
.CreateMock(instance
.Instance
)]
2096 self
.server
._instances
= old_instances
[:]
2097 old_instances
[0].idle_seconds
= (self
.server
._instance
_idle
_timeout
+ 1)
2098 old_instances
[1].idle_seconds
= 0
2099 old_instances
[2].idle_seconds
= (self
.server
._instance
_idle
_timeout
+ 1)
2100 self
.server
._instance
_running
= [True, True, False]
2101 new_instance
= self
.mox
.CreateMock(instance
.Instance
)
2102 self
.factory
.new_instance(0, expect_ready_request
=True).AndReturn(
2104 old_instances
[0].quit(expect_shutdown
=True)
2105 server
._THREAD
_POOL
.submit(self
.server
._shutdown
_instance
, old_instances
[0],
2106 self
.wsgi_server
.port
)
2108 self
.mox
.ReplayAll()
2109 self
.server
._shutdown
_idle
_instances
()
2110 self
.mox
.VerifyAll()
2111 self
.assertEqual([False, True, False], self
.server
._instance
_running
)
2112 self
.assertEqual(new_instance
, self
.server
._instances
[0])
2113 self
.assertEqual(self
.server
._handle
_request
,
2114 self
.server
._wsgi
_servers
[0]._app
.func
)
2115 self
.assertEqual({'inst': new_instance
},
2116 self
.server
._wsgi
_servers
[0]._app
.keywords
)
2119 class TestBasicScalingInstancePoolHandleChanges(unittest
.TestCase
):
2120 """Tests for server.BasicScalingServer._handle_changes."""
2123 api_server
.test_setup_stubs()
2125 self
.mox
= mox
.Mox()
2126 self
.instance_factory
= instance
.InstanceFactory(object(), 10)
2127 self
.servr
= BasicScalingServerFacade(
2128 instance_factory
=self
.instance_factory
)
2129 self
.mox
.StubOutWithMock(self
.instance_factory
, 'files_changed')
2130 self
.mox
.StubOutWithMock(self
.instance_factory
, 'configuration_changed')
2131 self
.mox
.StubOutWithMock(self
.servr
, 'restart')
2132 self
.mox
.StubOutWithMock(self
.servr
, '_create_url_handlers')
2133 self
.mox
.StubOutWithMock(self
.servr
._server
_configuration
,
2134 'check_for_updates')
2135 self
.mox
.StubOutWithMock(self
.servr
._watcher
, 'has_changes')
2138 self
.mox
.UnsetStubs()
2140 def test_no_changes(self
):
2141 self
.servr
._server
_configuration
.check_for_updates().AndReturn(frozenset())
2142 self
.servr
._watcher
.has_changes().AndReturn(False)
2144 self
.mox
.ReplayAll()
2145 self
.servr
._handle
_changes
()
2146 self
.mox
.VerifyAll()
2148 def test_irrelevant_config_change(self
):
2149 self
.servr
._server
_configuration
.check_for_updates().AndReturn(frozenset())
2150 self
.servr
._watcher
.has_changes().AndReturn(False)
2152 self
.mox
.ReplayAll()
2153 self
.servr
._handle
_changes
()
2154 self
.mox
.VerifyAll()
2156 def test_restart_config_change(self
):
2157 conf_change
= frozenset([application_configuration
.ENV_VARIABLES_CHANGED
])
2158 self
.servr
._server
_configuration
.check_for_updates().AndReturn(conf_change
)
2159 self
.servr
._watcher
.has_changes().AndReturn(False)
2160 self
.instance_factory
.configuration_changed(conf_change
)
2161 self
.servr
.restart()
2163 self
.mox
.ReplayAll()
2164 self
.servr
._handle
_changes
()
2165 self
.mox
.VerifyAll()
2167 def test_handler_change(self
):
2168 conf_change
= frozenset([application_configuration
.HANDLERS_CHANGED
])
2169 self
.servr
._server
_configuration
.check_for_updates().AndReturn(conf_change
)
2170 self
.servr
._watcher
.has_changes().AndReturn(False)
2171 self
.servr
._create
_url
_handlers
()
2172 self
.instance_factory
.configuration_changed(conf_change
)
2173 self
.servr
.restart()
2175 self
.mox
.ReplayAll()
2176 self
.servr
._handle
_changes
()
2177 self
.mox
.VerifyAll()
2179 def test_file_change(self
):
2180 self
.servr
._server
_configuration
.check_for_updates().AndReturn(frozenset())
2181 self
.servr
._watcher
.has_changes().AndReturn(True)
2182 self
.instance_factory
.files_changed().AndReturn(True)
2183 self
.servr
.restart()
2185 self
.mox
.ReplayAll()
2186 self
.servr
._handle
_changes
()
2187 self
.mox
.VerifyAll()
2190 class TestInteractiveCommandServer(unittest
.TestCase
):
2192 api_server
.test_setup_stubs()
2194 self
.mox
= mox
.Mox()
2195 self
.inst
= self
.mox
.CreateMock(instance
.Instance
)
2196 self
.inst
.instance_id
= 0
2197 self
.environ
= object()
2198 self
.start_response
= object()
2199 self
.response
= [object()]
2200 self
.url_map
= object()
2201 self
.match
= object()
2202 self
.request_id
= object()
2204 self
.servr
= server
.InteractiveCommandServer(
2205 ServerConfigurationStub(),
2209 runtime_stderr_loglevel
=1,
2212 cloud_sql_config
=None,
2213 default_version_port
=8080,
2214 port_registry
=dispatcher
.PortRegistry(),
2217 use_mtime_file_watcher
=False)
2218 self
.mox
.StubOutWithMock(self
.servr
._instance
_factory
, 'new_instance')
2219 self
.mox
.StubOutWithMock(self
.servr
, '_handle_request')
2220 self
.mox
.StubOutWithMock(self
.servr
, 'build_request_environ')
2222 def test_send_interactive_command(self
):
2223 def good_response(unused_environ
, start_response
, request_type
):
2224 start_response('200 OK', [])
2228 self
.servr
.build_request_environ(
2229 'POST', '/', [], 'print 5+5', '192.0.2.0', 8000).AndReturn(environ
)
2230 self
.servr
._handle
_request
(
2233 request_type
=instance
.INTERACTIVE_REQUEST
).WithSideEffects(
2236 self
.mox
.ReplayAll()
2237 self
.assertEqual('10\n', self
.servr
.send_interactive_command('print 5+5'))
2238 self
.mox
.VerifyAll()
2240 def test_send_interactive_command_handle_request_exception(self
):
2242 self
.servr
.build_request_environ(
2243 'POST', '/', [], 'print 5+5', '192.0.2.0', 8000).AndReturn(environ
)
2244 self
.servr
._handle
_request
(
2247 request_type
=instance
.INTERACTIVE_REQUEST
).AndRaise(Exception('error'))
2249 self
.mox
.ReplayAll()
2250 self
.assertRaisesRegexp(server
.InteractiveCommandError
,
2252 self
.servr
.send_interactive_command
,
2254 self
.mox
.VerifyAll()
2256 def test_send_interactive_command_handle_request_failure(self
):
2257 def good_response(unused_environ
, start_response
, request_type
):
2258 start_response('503 Service Unavailable', [])
2259 return ['Instance was restarted while executing command']
2262 self
.servr
.build_request_environ(
2263 'POST', '/', [], 'print 5+5', '192.0.2.0', 8000).AndReturn(environ
)
2264 self
.servr
._handle
_request
(
2267 request_type
=instance
.INTERACTIVE_REQUEST
).WithSideEffects(
2270 self
.mox
.ReplayAll()
2271 self
.assertRaisesRegexp(server
.InteractiveCommandError
,
2272 'Instance was restarted while executing command',
2273 self
.servr
.send_interactive_command
,
2275 self
.mox
.VerifyAll()
2277 def test_handle_script_request(self
):
2278 self
.servr
._instance
_factory
.new_instance(
2280 expect_ready_request
=False).AndReturn(self
.inst
)
2282 self
.inst
.handle(self
.environ
,
2283 self
.start_response
,
2287 instance
.INTERACTIVE_REQUEST
).AndReturn(['10\n'])
2289 self
.mox
.ReplayAll()
2292 self
.servr
._handle
_script
_request
(self
.environ
,
2293 self
.start_response
,
2297 self
.mox
.VerifyAll()
2299 def test_handle_script_request_busy(self
):
2300 self
.servr
._instance
_factory
.new_instance(
2302 expect_ready_request
=False).AndReturn(self
.inst
)
2306 self
.start_response
,
2310 instance
.INTERACTIVE_REQUEST
).AndRaise(instance
.CannotAcceptRequests())
2311 self
.inst
.wait(mox
.IgnoreArg())
2312 self
.inst
.handle(self
.environ
,
2313 self
.start_response
,
2317 instance
.INTERACTIVE_REQUEST
).AndReturn(['10\n'])
2319 self
.mox
.ReplayAll()
2322 self
.servr
._handle
_script
_request
(self
.environ
,
2323 self
.start_response
,
2327 self
.mox
.VerifyAll()
2329 def test_handle_script_request_timeout(self
):
2330 self
.servr
._MAX
_REQUEST
_WAIT
_TIME
= 0
2331 start_response
= start_response_utils
.CapturingStartResponse()
2333 self
.mox
.ReplayAll()
2335 ['The command timed-out while waiting for another one to complete'],
2336 self
.servr
._handle
_script
_request
(self
.environ
,
2341 self
.mox
.VerifyAll()
2342 self
.assertEqual('503 Service Unavailable',
2343 start_response
.status
)
2345 def test_handle_script_request_restart(self
):
2346 def restart_and_raise(*args
):
2347 self
.servr
._inst
= None
2348 raise httplib
.BadStatusLine('line')
2350 start_response
= start_response_utils
.CapturingStartResponse()
2351 self
.servr
._instance
_factory
.new_instance(
2353 expect_ready_request
=False).AndReturn(self
.inst
)
2361 instance
.INTERACTIVE_REQUEST
).WithSideEffects(restart_and_raise
)
2363 self
.mox
.ReplayAll()
2365 ['Instance was restarted while executing command'],
2366 self
.servr
._handle
_script
_request
(self
.environ
,
2371 self
.mox
.VerifyAll()
2372 self
.assertEqual('503 Service Unavailable',
2373 start_response
.status
)
2375 def test_handle_script_request_unexpected_instance_exception(self
):
2376 self
.servr
._instance
_factory
.new_instance(
2378 expect_ready_request
=False).AndReturn(self
.inst
)
2382 self
.start_response
,
2386 instance
.INTERACTIVE_REQUEST
).AndRaise(httplib
.BadStatusLine('line'))
2388 self
.mox
.ReplayAll()
2390 httplib
.BadStatusLine
,
2391 self
.servr
._handle
_script
_request
,
2393 self
.start_response
,
2397 self
.mox
.VerifyAll()
2399 if __name__
== '__main__':