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.application_configuration."""
21 from contextlib
import contextmanager
31 from google
.appengine
.api
import appinfo
32 from google
.appengine
.api
import appinfo_includes
33 from google
.appengine
.api
import backendinfo
34 from google
.appengine
.api
import dispatchinfo
35 from google
.appengine
.tools
.devappserver2
import application_configuration
36 from google
.appengine
.tools
.devappserver2
import errors
40 def _java_temporarily_supported():
41 """Make the java_supported() function return True temporarily.
44 with _java_temporarily_supported():
45 ...test that relies on Java being supported...
47 old_java_supported
= application_configuration
.java_supported
48 application_configuration
.java_supported
= lambda: True
50 application_configuration
.java_supported
= old_java_supported
53 _DEFAULT_HEALTH_CHECK
= appinfo
.VmHealthCheck(
54 enable_health_check
=True,
57 unhealthy_threshold
=2,
63 class TestModuleConfiguration(unittest
.TestCase
):
64 """Tests for application_configuration.ModuleConfiguration."""
68 self
.mox
.StubOutWithMock(appinfo_includes
, 'ParseAndReturnIncludePaths')
69 self
.mox
.StubOutWithMock(os
.path
, 'getmtime')
70 application_configuration
.open = self
._fake
_open
74 del application_configuration
.open
77 def _fake_open(unused_filename
):
80 def test_good_app_yaml_configuration(self
):
81 automatic_scaling
= appinfo
.AutomaticScaling(min_pending_latency
='1.0s',
82 max_pending_latency
='2.0s',
85 error_handlers
= [appinfo
.ErrorHandlers(file='error.html')]
86 handlers
= [appinfo
.URLMap()]
87 env_variables
= appinfo
.EnvironmentVariables()
88 info
= appinfo
.AppInfoExternal(
94 automatic_scaling
=automatic_scaling
,
96 error_handlers
=error_handlers
,
98 inbound_services
=['warmup'],
99 env_variables
=env_variables
,
101 appinfo_includes
.ParseAndReturnIncludePaths(mox
.IgnoreArg()).AndReturn(
103 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
106 config
= application_configuration
.ModuleConfiguration('/appdir/app.yaml')
109 self
.assertEqual(os
.path
.realpath('/appdir'), config
.application_root
)
110 self
.assertEqual(os
.path
.realpath('/appdir/app.yaml'), config
.config_path
)
111 self
.assertEqual('dev~app', config
.application
)
112 self
.assertEqual('app', config
.application_external_name
)
113 self
.assertEqual('dev', config
.partition
)
114 self
.assertEqual('module1', config
.module_name
)
115 self
.assertEqual('1', config
.major_version
)
116 self
.assertRegexpMatches(config
.version_id
, r
'module1:1\.\d+')
117 self
.assertEqual('python27', config
.runtime
)
118 self
.assertFalse(config
.threadsafe
)
119 self
.assertEqual(automatic_scaling
, config
.automatic_scaling
)
120 self
.assertEqual(info
.GetNormalizedLibraries(),
121 config
.normalized_libraries
)
122 self
.assertEqual(r
'\*.gif', config
.skip_files
)
123 self
.assertEqual(error_handlers
, config
.error_handlers
)
124 self
.assertEqual(handlers
, config
.handlers
)
125 self
.assertEqual(['warmup'], config
.inbound_services
)
126 self
.assertEqual(env_variables
, config
.env_variables
)
127 self
.assertEqual({'/appdir/app.yaml': 10}, config
._mtimes
)
128 self
.assertEqual(_DEFAULT_HEALTH_CHECK
, config
.vm_health_check
)
130 def test_vm_app_yaml_configuration(self
):
131 manual_scaling
= appinfo
.ManualScaling()
132 vm_settings
= appinfo
.VmSettings()
133 vm_settings
['vm_runtime'] = 'myawesomeruntime'
134 vm_settings
['forwarded_ports'] = '49111:49111,5002:49112,8000'
135 health_check
= appinfo
.VmHealthCheck()
136 health_check
.enable_health_check
= False
137 info
= appinfo
.AppInfoExternal(
142 vm_settings
=vm_settings
,
144 manual_scaling
=manual_scaling
,
145 vm_health_check
=health_check
148 appinfo_includes
.ParseAndReturnIncludePaths(mox
.IgnoreArg()).AndReturn(
150 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
153 config
= application_configuration
.ModuleConfiguration('/appdir/app.yaml')
156 self
.assertEqual(os
.path
.realpath('/appdir'), config
.application_root
)
157 self
.assertEqual(os
.path
.realpath('/appdir/app.yaml'), config
.config_path
)
158 self
.assertEqual('dev~app', config
.application
)
159 self
.assertEqual('app', config
.application_external_name
)
160 self
.assertEqual('dev', config
.partition
)
161 self
.assertEqual('module1', config
.module_name
)
162 self
.assertEqual('1', config
.major_version
)
163 self
.assertRegexpMatches(config
.version_id
, r
'module1:1\.\d+')
164 self
.assertEqual('vm', config
.runtime
)
165 self
.assertEqual(vm_settings
['vm_runtime'], config
.effective_runtime
)
166 self
.assertItemsEqual(
167 {49111: 49111, 5002: 49112, 8000: 8000},
168 config
.forwarded_ports
)
169 self
.assertFalse(config
.threadsafe
)
170 self
.assertEqual(manual_scaling
, config
.manual_scaling
)
171 self
.assertEqual({'/appdir/app.yaml': 10}, config
._mtimes
)
172 self
.assertEqual(info
.vm_health_check
, config
.vm_health_check
)
174 def test_set_health_check_defaults(self
):
177 _DEFAULT_HEALTH_CHECK
,
178 application_configuration
._set
_health
_check
_defaults
(None))
180 # Pass in an empty object.
182 _DEFAULT_HEALTH_CHECK
,
183 application_configuration
._set
_health
_check
_defaults
(
184 appinfo
.VmHealthCheck()))
187 health_check
= appinfo
.VmHealthCheck(restart_threshold
=7,
189 defaults_set
= application_configuration
._set
_health
_check
_defaults
(
192 self
.assertEqual(defaults_set
.enable_health_check
,
193 _DEFAULT_HEALTH_CHECK
.enable_health_check
)
194 self
.assertEqual(defaults_set
.check_interval_sec
,
195 _DEFAULT_HEALTH_CHECK
.check_interval_sec
)
196 self
.assertEqual(defaults_set
.timeout_sec
,
197 _DEFAULT_HEALTH_CHECK
.timeout_sec
)
198 self
.assertEqual(defaults_set
.unhealthy_threshold
,
199 _DEFAULT_HEALTH_CHECK
.unhealthy_threshold
)
200 self
.assertEqual(defaults_set
.healthy_threshold
, 4)
201 self
.assertEqual(defaults_set
.restart_threshold
, 7)
202 self
.assertEqual(defaults_set
.host
,
203 _DEFAULT_HEALTH_CHECK
.host
)
205 def test_override_app_id(self
):
206 info
= appinfo
.AppInfoExternal(
207 application
='ignored-app',
212 appinfo_includes
.ParseAndReturnIncludePaths(mox
.IgnoreArg()).AndReturn(
214 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
215 os
.path
.getmtime('/appdir/app.yaml').AndReturn(20)
216 os
.path
.getmtime('/appdir/app.yaml').AndReturn(20)
217 appinfo_includes
.ParseAndReturnIncludePaths(mox
.IgnoreArg()).AndReturn(
221 config
= application_configuration
.ModuleConfiguration(
222 '/appdir/app.yaml', 'overriding-app')
223 self
.assertEqual('overriding-app', config
.application_external_name
)
224 self
.assertEqual('dev~overriding-app', config
.application
)
225 config
.check_for_updates()
226 self
.assertEqual('overriding-app', config
.application_external_name
)
227 self
.assertEqual('dev~overriding-app', config
.application
)
230 def test_check_for_updates_unchanged_mtime(self
):
231 info
= appinfo
.AppInfoExternal(
237 appinfo_includes
.ParseAndReturnIncludePaths(mox
.IgnoreArg()).AndReturn(
239 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
240 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
243 config
= application_configuration
.ModuleConfiguration('/appdir/app.yaml')
244 self
.assertSequenceEqual(set(), config
.check_for_updates())
247 def test_check_for_updates_with_includes(self
):
248 info
= appinfo
.AppInfoExternal(
253 includes
=['/appdir/include.yaml'],
255 appinfo_includes
.ParseAndReturnIncludePaths(mox
.IgnoreArg()).AndReturn(
256 (info
, ['/appdir/include.yaml']))
257 os
.path
.getmtime('/appdir/app.yaml').InAnyOrder().AndReturn(10)
258 os
.path
.getmtime('/appdir/include.yaml').InAnyOrder().AndReturn(10)
259 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
260 os
.path
.getmtime('/appdir/include.yaml').AndReturn(11)
262 appinfo_includes
.ParseAndReturnIncludePaths(mox
.IgnoreArg()).AndReturn(
263 (info
, ['/appdir/include.yaml']))
264 os
.path
.getmtime('/appdir/app.yaml').InAnyOrder().AndReturn(10)
265 os
.path
.getmtime('/appdir/include.yaml').InAnyOrder().AndReturn(11)
268 config
= application_configuration
.ModuleConfiguration('/appdir/app.yaml')
269 self
.assertEqual({'/appdir/app.yaml': 10, '/appdir/include.yaml': 10},
271 config
._mtimes
= collections
.OrderedDict([('/appdir/app.yaml', 10),
272 ('/appdir/include.yaml', 10)])
273 self
.assertSequenceEqual(set(), config
.check_for_updates())
275 self
.assertEqual({'/appdir/app.yaml': 10, '/appdir/include.yaml': 11},
278 def test_check_for_updates_no_changes(self
):
279 info
= appinfo
.AppInfoExternal(
285 appinfo_includes
.ParseAndReturnIncludePaths(mox
.IgnoreArg()).AndReturn(
287 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
288 os
.path
.getmtime('/appdir/app.yaml').AndReturn(11)
289 appinfo_includes
.ParseAndReturnIncludePaths(mox
.IgnoreArg()).AndReturn(
291 os
.path
.getmtime('/appdir/app.yaml').AndReturn(11)
294 config
= application_configuration
.ModuleConfiguration('/appdir/app.yaml')
295 self
.assertSequenceEqual(set(), config
.check_for_updates())
297 self
.assertEqual({'/appdir/app.yaml': 11}, config
._mtimes
)
299 def test_check_for_updates_immutable_changes(self
):
300 automatic_scaling1
= appinfo
.AutomaticScaling(
301 min_pending_latency
='0.1s',
302 max_pending_latency
='1.0s',
303 min_idle_instances
=1,
304 max_idle_instances
=2)
305 info1
= appinfo
.AppInfoExternal(
311 automatic_scaling
=automatic_scaling1
)
313 info2
= appinfo
.AppInfoExternal(
319 automatic_scaling
=appinfo
.AutomaticScaling(
320 min_pending_latency
='1.0s',
321 max_pending_latency
='2.0s',
322 min_idle_instances
=1,
323 max_idle_instances
=2))
325 appinfo_includes
.ParseAndReturnIncludePaths(mox
.IgnoreArg()).AndReturn(
327 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
328 os
.path
.getmtime('/appdir/app.yaml').AndReturn(11)
329 appinfo_includes
.ParseAndReturnIncludePaths(mox
.IgnoreArg()).AndReturn(
331 os
.path
.getmtime('/appdir/app.yaml').AndReturn(11)
334 config
= application_configuration
.ModuleConfiguration('/appdir/app.yaml')
335 self
.assertSequenceEqual(set(), config
.check_for_updates())
338 self
.assertEqual('dev~app', config
.application
)
339 self
.assertEqual('default', config
.module_name
)
340 self
.assertEqual('version', config
.major_version
)
341 self
.assertRegexpMatches(config
.version_id
, r
'^version\.\d+$')
342 self
.assertEqual('python27', config
.runtime
)
343 self
.assertFalse(config
.threadsafe
)
344 self
.assertEqual(automatic_scaling1
, config
.automatic_scaling
)
346 def test_check_for_updates_mutable_changes(self
):
347 info1
= appinfo
.AppInfoExternal(
353 libraries
=[appinfo
.Library(name
='django', version
='latest')],
356 inbound_services
=['warmup'],
357 env_variables
=appinfo
.EnvironmentVariables(),
358 error_handlers
=[appinfo
.ErrorHandlers(file='error.html')],
360 info2
= appinfo
.AppInfoExternal(
366 libraries
=[appinfo
.Library(name
='jinja2', version
='latest')],
367 skip_files
=r
'.*\.py',
368 handlers
=[appinfo
.URLMap()],
372 appinfo_includes
.ParseAndReturnIncludePaths(mox
.IgnoreArg()).AndReturn(
374 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
375 os
.path
.getmtime('/appdir/app.yaml').AndReturn(11)
376 appinfo_includes
.ParseAndReturnIncludePaths(mox
.IgnoreArg()).AndReturn(
378 os
.path
.getmtime('/appdir/app.yaml').AndReturn(11)
381 config
= application_configuration
.ModuleConfiguration('/appdir/app.yaml')
382 self
.assertSequenceEqual(
383 set([application_configuration
.NORMALIZED_LIBRARIES_CHANGED
,
384 application_configuration
.SKIP_FILES_CHANGED
,
385 application_configuration
.HANDLERS_CHANGED
,
386 application_configuration
.INBOUND_SERVICES_CHANGED
,
387 application_configuration
.ENV_VARIABLES_CHANGED
,
388 application_configuration
.ERROR_HANDLERS_CHANGED
]),
389 config
.check_for_updates())
392 self
.assertEqual(info2
.GetNormalizedLibraries(),
393 config
.normalized_libraries
)
394 self
.assertEqual(info2
.skip_files
, config
.skip_files
)
395 self
.assertEqual(info2
.error_handlers
, config
.error_handlers
)
396 self
.assertEqual(info2
.handlers
, config
.handlers
)
397 self
.assertEqual(info2
.inbound_services
, config
.inbound_services
)
398 self
.assertEqual(info2
.env_variables
, config
.env_variables
)
402 class TestBackendsConfiguration(unittest
.TestCase
):
405 self
.mox
.StubOutWithMock(
406 application_configuration
.BackendsConfiguration
,
407 '_parse_configuration')
408 self
.mox
.StubOutWithMock(application_configuration
, 'BackendConfiguration')
411 self
.mox
.UnsetStubs()
413 def test_good_configuration(self
):
414 self
.mox
.StubOutWithMock(application_configuration
, 'ModuleConfiguration')
415 static_backend_entry
= backendinfo
.BackendEntry(name
='static')
416 dynamic_backend_entry
= backendinfo
.BackendEntry(name
='dynamic')
417 backend_info
= backendinfo
.BackendInfoExternal(
418 backends
=[static_backend_entry
, dynamic_backend_entry
])
419 module_config
= object()
420 application_configuration
.ModuleConfiguration(
421 '/appdir/app.yaml', None).AndReturn(module_config
)
422 application_configuration
.BackendsConfiguration
._parse
_configuration
(
423 '/appdir/backends.yaml').AndReturn(backend_info
)
424 static_configuration
= object()
425 dynamic_configuration
= object()
426 application_configuration
.BackendConfiguration(
429 static_backend_entry
).InAnyOrder().AndReturn(static_configuration
)
430 application_configuration
.BackendConfiguration(
433 dynamic_backend_entry
).InAnyOrder().AndReturn(dynamic_configuration
)
436 config
= application_configuration
.BackendsConfiguration(
438 '/appdir/backends.yaml')
439 self
.assertItemsEqual([static_configuration
, dynamic_configuration
],
440 config
.get_backend_configurations())
443 def test_no_backends(self
):
444 self
.mox
.StubOutWithMock(application_configuration
, 'ModuleConfiguration')
445 backend_info
= backendinfo
.BackendInfoExternal()
446 module_config
= object()
447 application_configuration
.ModuleConfiguration(
448 '/appdir/app.yaml', None).AndReturn(module_config
)
449 application_configuration
.BackendsConfiguration
._parse
_configuration
(
450 '/appdir/backends.yaml').AndReturn(backend_info
)
453 config
= application_configuration
.BackendsConfiguration(
455 '/appdir/backends.yaml')
456 self
.assertEqual([], config
.get_backend_configurations())
459 def test_check_for_changes(self
):
460 static_backend_entry
= backendinfo
.BackendEntry(name
='static')
461 dynamic_backend_entry
= backendinfo
.BackendEntry(name
='dynamic')
462 backend_info
= backendinfo
.BackendInfoExternal(
463 backends
=[static_backend_entry
, dynamic_backend_entry
])
464 module_config
= self
.mox
.CreateMock(
465 application_configuration
.ModuleConfiguration
)
466 self
.mox
.StubOutWithMock(application_configuration
, 'ModuleConfiguration')
467 application_configuration
.ModuleConfiguration(
468 '/appdir/app.yaml', None).AndReturn(module_config
)
469 application_configuration
.BackendsConfiguration
._parse
_configuration
(
470 '/appdir/backends.yaml').AndReturn(backend_info
)
471 module_config
.check_for_updates().AndReturn(set())
472 module_config
.check_for_updates().AndReturn(set([1]))
473 module_config
.check_for_updates().AndReturn(set([2]))
474 module_config
.check_for_updates().AndReturn(set())
477 config
= application_configuration
.BackendsConfiguration(
479 '/appdir/backends.yaml')
480 self
.assertEqual(set(), config
.check_for_updates('dynamic'))
481 self
.assertEqual(set([1]), config
.check_for_updates('static'))
482 self
.assertEqual(set([1, 2]), config
.check_for_updates('dynamic'))
483 self
.assertEqual(set([2]), config
.check_for_updates('static'))
487 class TestDispatchConfiguration(unittest
.TestCase
):
490 self
.mox
.StubOutWithMock(os
.path
, 'getmtime')
491 self
.mox
.StubOutWithMock(
492 application_configuration
.DispatchConfiguration
,
493 '_parse_configuration')
496 self
.mox
.UnsetStubs()
498 def test_good_configuration(self
):
499 info
= dispatchinfo
.DispatchInfoExternal(
502 dispatchinfo
.DispatchEntry(url
='*/path', module
='foo'),
503 dispatchinfo
.DispatchEntry(url
='domain.com/path', module
='bar'),
504 dispatchinfo
.DispatchEntry(url
='*/path/*', module
='baz'),
505 dispatchinfo
.DispatchEntry(url
='*.domain.com/path/*', module
='foo'),
508 os
.path
.getmtime('/appdir/dispatch.yaml').AndReturn(123.456)
509 application_configuration
.DispatchConfiguration
._parse
_configuration
(
510 '/appdir/dispatch.yaml').AndReturn(info
)
513 config
= application_configuration
.DispatchConfiguration(
514 '/appdir/dispatch.yaml')
517 self
.assertEqual(123.456, config
._mtime
)
518 self
.assertEqual(2, len(config
.dispatch
))
519 self
.assertEqual(vars(dispatchinfo
.ParsedURL('*/path')),
520 vars(config
.dispatch
[0][0]))
521 self
.assertEqual('foo', config
.dispatch
[0][1])
522 self
.assertEqual(vars(dispatchinfo
.ParsedURL('*/path/*')),
523 vars(config
.dispatch
[1][0]))
524 self
.assertEqual('baz', config
.dispatch
[1][1])
526 def test_check_for_updates_no_modification(self
):
527 info
= dispatchinfo
.DispatchInfoExternal(
531 os
.path
.getmtime('/appdir/dispatch.yaml').AndReturn(123.456)
532 application_configuration
.DispatchConfiguration
._parse
_configuration
(
533 '/appdir/dispatch.yaml').AndReturn(info
)
534 os
.path
.getmtime('/appdir/dispatch.yaml').AndReturn(123.456)
537 config
= application_configuration
.DispatchConfiguration(
538 '/appdir/dispatch.yaml')
539 config
.check_for_updates()
542 def test_check_for_updates_with_invalid_modification(self
):
543 info
= dispatchinfo
.DispatchInfoExternal(
546 dispatchinfo
.DispatchEntry(url
='*/path', module
='bar'),
549 os
.path
.getmtime('/appdir/dispatch.yaml').AndReturn(123.456)
550 application_configuration
.DispatchConfiguration
._parse
_configuration
(
551 '/appdir/dispatch.yaml').AndReturn(info
)
552 os
.path
.getmtime('/appdir/dispatch.yaml').AndReturn(124.456)
553 application_configuration
.DispatchConfiguration
._parse
_configuration
(
554 '/appdir/dispatch.yaml').AndRaise(Exception)
557 config
= application_configuration
.DispatchConfiguration(
558 '/appdir/dispatch.yaml')
559 self
.assertEqual('bar', config
.dispatch
[0][1])
560 config
.check_for_updates()
562 self
.assertEqual('bar', config
.dispatch
[0][1])
564 def test_check_for_updates_with_modification(self
):
565 info
= dispatchinfo
.DispatchInfoExternal(
568 dispatchinfo
.DispatchEntry(url
='*/path', module
='bar'),
570 new_info
= dispatchinfo
.DispatchInfoExternal(
573 dispatchinfo
.DispatchEntry(url
='*/path', module
='foo'),
576 os
.path
.getmtime('/appdir/dispatch.yaml').AndReturn(123.456)
577 application_configuration
.DispatchConfiguration
._parse
_configuration
(
578 '/appdir/dispatch.yaml').AndReturn(info
)
579 os
.path
.getmtime('/appdir/dispatch.yaml').AndReturn(124.456)
580 application_configuration
.DispatchConfiguration
._parse
_configuration
(
581 '/appdir/dispatch.yaml').AndReturn(new_info
)
584 config
= application_configuration
.DispatchConfiguration(
585 '/appdir/dispatch.yaml')
586 self
.assertEqual('bar', config
.dispatch
[0][1])
587 config
.check_for_updates()
589 self
.assertEqual('foo', config
.dispatch
[0][1])
592 class TestBackendConfiguration(unittest
.TestCase
):
595 self
.mox
.StubOutWithMock(
596 application_configuration
.ModuleConfiguration
,
597 '_parse_configuration')
598 self
.mox
.StubOutWithMock(os
.path
, 'getmtime')
601 self
.mox
.UnsetStubs()
603 def test_good_configuration(self
):
604 automatic_scaling
= appinfo
.AutomaticScaling(min_pending_latency
='1.0s',
605 max_pending_latency
='2.0s',
606 min_idle_instances
=1,
607 max_idle_instances
=2)
608 error_handlers
= [appinfo
.ErrorHandlers(file='error.html')]
609 handlers
= [appinfo
.URLMap()]
610 env_variables
= appinfo
.EnvironmentVariables()
611 info
= appinfo
.AppInfoExternal(
617 automatic_scaling
=automatic_scaling
,
618 skip_files
=r
'\*.gif',
619 error_handlers
=error_handlers
,
621 inbound_services
=['warmup'],
622 env_variables
=env_variables
,
624 backend_entry
= backendinfo
.BackendEntry(
629 application_configuration
.ModuleConfiguration
._parse
_configuration
(
630 '/appdir/app.yaml').AndReturn((info
, ['/appdir/app.yaml']))
631 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
634 module_config
= application_configuration
.ModuleConfiguration(
636 config
= application_configuration
.BackendConfiguration(
637 module_config
, None, backend_entry
)
640 self
.assertEqual(os
.path
.realpath('/appdir'), config
.application_root
)
641 self
.assertEqual('dev~app', config
.application
)
642 self
.assertEqual('app', config
.application_external_name
)
643 self
.assertEqual('dev', config
.partition
)
644 self
.assertEqual('static', config
.module_name
)
645 self
.assertEqual('1', config
.major_version
)
646 self
.assertRegexpMatches(config
.version_id
, r
'static:1\.\d+')
647 self
.assertEqual('python27', config
.runtime
)
648 self
.assertFalse(config
.threadsafe
)
649 self
.assertEqual(None, config
.automatic_scaling
)
650 self
.assertEqual(None, config
.basic_scaling
)
651 self
.assertEqual(appinfo
.ManualScaling(instances
='3'),
652 config
.manual_scaling
)
653 self
.assertEqual(info
.GetNormalizedLibraries(),
654 config
.normalized_libraries
)
655 self
.assertEqual(r
'\*.gif', config
.skip_files
)
656 self
.assertEqual(error_handlers
, config
.error_handlers
)
657 self
.assertEqual(handlers
, config
.handlers
)
658 self
.assertEqual(['warmup'], config
.inbound_services
)
659 self
.assertEqual(env_variables
, config
.env_variables
)
661 whitelist_fields
= ['module_name', 'version_id', 'automatic_scaling',
662 'manual_scaling', 'basic_scaling', 'is_backend',
664 # Check that all public attributes and methods in a ModuleConfiguration
665 # exist in a BackendConfiguration.
666 for field
in dir(module_config
):
667 if not field
.startswith('_'):
668 self
.assertTrue(hasattr(config
, field
), 'Missing field: %s' % field
)
669 value
= getattr(module_config
, field
)
670 if field
not in whitelist_fields
and not callable(value
):
671 # Check that the attributes other than those in the whitelist have
672 # equal values in the BackendConfiguration to the ModuleConfiguration
673 # from which it inherits.
674 self
.assertEqual(value
, getattr(config
, field
))
676 def test_vm_app_yaml_configuration(self
):
677 automatic_scaling
= appinfo
.AutomaticScaling(min_pending_latency
='1.0s',
678 max_pending_latency
='2.0s',
679 min_idle_instances
=1,
680 max_idle_instances
=2)
681 vm_settings
= appinfo
.VmSettings()
682 vm_settings
['vm_runtime'] = 'myawesomeruntime'
683 info
= appinfo
.AppInfoExternal(
688 vm_settings
=vm_settings
,
690 automatic_scaling
=automatic_scaling
,
692 backend_entry
= backendinfo
.BackendEntry(
696 application_configuration
.ModuleConfiguration
._parse
_configuration
(
697 '/appdir/app.yaml').AndReturn((info
, ['/appdir/app.yaml']))
698 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
701 module_config
= application_configuration
.ModuleConfiguration(
703 config
= application_configuration
.BackendConfiguration(
704 module_config
, None, backend_entry
)
707 self
.assertEqual(os
.path
.realpath('/appdir'), config
.application_root
)
708 self
.assertEqual('dev~app', config
.application
)
709 self
.assertEqual('app', config
.application_external_name
)
710 self
.assertEqual('dev', config
.partition
)
711 self
.assertEqual('static', config
.module_name
)
712 self
.assertEqual('1', config
.major_version
)
713 self
.assertRegexpMatches(config
.version_id
, r
'static:1\.\d+')
714 self
.assertEqual('vm', config
.runtime
)
715 self
.assertEqual(vm_settings
['vm_runtime'], config
.effective_runtime
)
716 self
.assertFalse(config
.threadsafe
)
717 # Resident backends are assigned manual scaling.
718 self
.assertEqual(None, config
.automatic_scaling
)
719 self
.assertEqual(None, config
.basic_scaling
)
720 self
.assertEqual(appinfo
.ManualScaling(instances
='3'),
721 config
.manual_scaling
)
723 def test_good_configuration_dynamic_scaling(self
):
724 automatic_scaling
= appinfo
.AutomaticScaling(min_pending_latency
='1.0s',
725 max_pending_latency
='2.0s',
726 min_idle_instances
=1,
727 max_idle_instances
=2)
728 error_handlers
= [appinfo
.ErrorHandlers(file='error.html')]
729 handlers
= [appinfo
.URLMap()]
730 env_variables
= appinfo
.EnvironmentVariables()
731 info
= appinfo
.AppInfoExternal(
737 automatic_scaling
=automatic_scaling
,
738 skip_files
=r
'\*.gif',
739 error_handlers
=error_handlers
,
741 inbound_services
=['warmup'],
742 env_variables
=env_variables
,
744 backend_entry
= backendinfo
.BackendEntry(
747 options
='public, dynamic',
750 application_configuration
.ModuleConfiguration
._parse
_configuration
(
751 '/appdir/app.yaml').AndReturn((info
, ['/appdir/app.yaml']))
752 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
755 module_config
= application_configuration
.ModuleConfiguration(
757 config
= application_configuration
.BackendConfiguration(
758 module_config
, None, backend_entry
)
761 self
.assertEqual(os
.path
.realpath('/appdir'), config
.application_root
)
762 self
.assertEqual('dev~app', config
.application
)
763 self
.assertEqual('dynamic', config
.module_name
)
764 self
.assertEqual('1', config
.major_version
)
765 self
.assertRegexpMatches(config
.version_id
, r
'dynamic:1\.\d+')
766 self
.assertEqual('python27', config
.runtime
)
767 self
.assertFalse(config
.threadsafe
)
768 self
.assertEqual(None, config
.automatic_scaling
)
769 self
.assertEqual(None, config
.manual_scaling
)
770 self
.assertEqual(appinfo
.BasicScaling(max_instances
='3'),
771 config
.basic_scaling
)
772 self
.assertEqual(info
.GetNormalizedLibraries(),
773 config
.normalized_libraries
)
774 self
.assertEqual(r
'\*.gif', config
.skip_files
)
775 self
.assertEqual(error_handlers
, config
.error_handlers
)
776 start_handler
= appinfo
.URLMap(url
='/_ah/start',
777 script
=backend_entry
.start
,
779 self
.assertEqual([start_handler
] + handlers
, config
.handlers
)
780 self
.assertEqual(['warmup'], config
.inbound_services
)
781 self
.assertEqual(env_variables
, config
.env_variables
)
783 def test_check_for_changes(self
):
784 backends_config
= self
.mox
.CreateMock(
785 application_configuration
.BackendsConfiguration
)
786 config
= application_configuration
.BackendConfiguration(
787 None, backends_config
, backendinfo
.BackendEntry(name
='backend'))
789 backends_config
.check_for_updates('backend').AndReturn([])
790 backends_config
.check_for_updates('backend').AndReturn(changes
)
791 minor_version
= config
.minor_version
793 self
.assertEqual([], config
.check_for_updates())
794 self
.assertEqual(minor_version
, config
.minor_version
)
795 self
.assertEqual(changes
, config
.check_for_updates())
796 self
.assertNotEqual(minor_version
, config
.minor_version
)
800 class ModuleConfigurationStub(object):
801 def __init__(self
, application
='myapp', module_name
='module'):
802 self
.application
= application
803 self
.module_name
= module_name
806 class DispatchConfigurationStub(object):
807 def __init__(self
, dispatch
):
808 self
.dispatch
= dispatch
811 class TestApplicationConfiguration(unittest
.TestCase
):
812 """Tests for application_configuration.ApplicationConfiguration."""
816 self
.mox
.StubOutWithMock(application_configuration
, 'ModuleConfiguration')
817 self
.mox
.StubOutWithMock(application_configuration
, 'BackendsConfiguration')
818 self
.mox
.StubOutWithMock(application_configuration
, 'DispatchConfiguration')
819 self
.tmpdir
= tempfile
.mkdtemp(dir=os
.getenv('TEST_TMPDIR'))
822 self
.mox
.UnsetStubs()
823 shutil
.rmtree(self
.tmpdir
)
825 def _make_file_hierarchy(self
, filenames
):
827 for filename
in filenames
:
828 absname
= os
.path
.normpath(self
.tmpdir
+ '/' + filename
)
829 absnames
+= [absname
]
830 dirname
= os
.path
.dirname(absname
)
831 if not os
.path
.exists(dirname
):
833 open(absname
, 'w').close()
836 def test_yaml_files(self
):
837 absnames
= self
._make
_file
_hierarchy
(
838 ['appdir/app.yaml', 'appdir/other.yaml'])
840 module_config1
= ModuleConfigurationStub()
841 application_configuration
.ModuleConfiguration(
842 absnames
[0], None).AndReturn(module_config1
)
844 module_config2
= ModuleConfigurationStub(module_name
='other')
845 application_configuration
.ModuleConfiguration(
846 absnames
[1], None).AndReturn(module_config2
)
849 config
= application_configuration
.ApplicationConfiguration(
852 self
.assertEqual('myapp', config
.app_id
)
853 self
.assertSequenceEqual([module_config1
, module_config2
], config
.modules
)
855 def test_yaml_files_with_different_app_ids(self
):
856 absnames
= self
._make
_file
_hierarchy
(
857 ['appdir/app.yaml', 'appdir/other.yaml'])
859 module_config1
= ModuleConfigurationStub()
860 application_configuration
.ModuleConfiguration(
861 absnames
[0], None).AndReturn(module_config1
)
863 module_config2
= ModuleConfigurationStub(application
='other_app',
865 application_configuration
.ModuleConfiguration(
866 absnames
[1], None).AndReturn(module_config2
)
869 self
.assertRaises(errors
.InvalidAppConfigError
,
870 application_configuration
.ApplicationConfiguration
,
874 def test_yaml_files_with_duplicate_module_names(self
):
875 absnames
= self
._make
_file
_hierarchy
(
876 ['appdir/app.yaml', 'appdir/other.yaml'])
878 application_configuration
.ModuleConfiguration(
879 absnames
[0], None).AndReturn(ModuleConfigurationStub())
881 application_configuration
.ModuleConfiguration(
882 absnames
[1], None).AndReturn(ModuleConfigurationStub())
885 self
.assertRaises(errors
.InvalidAppConfigError
,
886 application_configuration
.ApplicationConfiguration
,
890 def test_directory(self
):
891 absnames
= self
._make
_file
_hierarchy
(['appdir/app.yaml'])
893 module_config
= ModuleConfigurationStub()
894 application_configuration
.ModuleConfiguration(
895 absnames
[0], None).AndReturn(module_config
)
898 config
= application_configuration
.ApplicationConfiguration(
899 [os
.path
.dirname(absnames
[0])])
901 self
.assertEqual('myapp', config
.app_id
)
902 self
.assertSequenceEqual([module_config
], config
.modules
)
904 def test_directory_and_module(self
):
905 absnames
= self
._make
_file
_hierarchy
(
906 ['appdir/app.yaml', 'otherdir/mymodule.yaml'])
908 app_yaml_config
= ModuleConfigurationStub()
909 application_configuration
.ModuleConfiguration(
910 absnames
[0], None).AndReturn(app_yaml_config
)
911 my_module_config
= ModuleConfigurationStub(module_name
='my_module')
912 application_configuration
.ModuleConfiguration(
913 absnames
[1], None).AndReturn(my_module_config
)
915 config
= application_configuration
.ApplicationConfiguration(
916 [os
.path
.dirname(absnames
[0]), absnames
[1]])
918 self
.assertSequenceEqual(
919 [app_yaml_config
, my_module_config
], config
.modules
)
921 def test_directory_app_yml_only(self
):
922 absnames
= self
._make
_file
_hierarchy
(['appdir/app.yml'])
924 module_config
= ModuleConfigurationStub()
925 application_configuration
.ModuleConfiguration(
926 absnames
[0], None).AndReturn(module_config
)
929 config
= application_configuration
.ApplicationConfiguration(
930 [os
.path
.dirname(absnames
[0])])
932 self
.assertEqual('myapp', config
.app_id
)
933 self
.assertSequenceEqual([module_config
], config
.modules
)
935 def test_directory_app_yaml_and_app_yml(self
):
936 absnames
= self
._make
_file
_hierarchy
(['appdir/app.yaml', 'appdir/app.yml'])
938 self
.assertRaises(errors
.InvalidAppConfigError
,
939 application_configuration
.ApplicationConfiguration
,
940 [os
.path
.dirname(absnames
[0])])
943 def test_directory_no_app_yamls(self
):
944 absnames
= self
._make
_file
_hierarchy
(['appdir/somethingelse.yaml'])
947 self
.assertRaises(errors
.AppConfigNotFoundError
,
948 application_configuration
.ApplicationConfiguration
,
949 [os
.path
.dirname(absnames
[0])])
952 def test_directory_no_app_yamls_or_web_inf(self
):
953 absnames
= self
._make
_file
_hierarchy
(['appdir/somethingelse.yaml'])
956 with
_java_temporarily_supported():
957 self
.assertRaises(errors
.AppConfigNotFoundError
,
958 application_configuration
.ApplicationConfiguration
,
959 [os
.path
.dirname(absnames
[0])])
962 def test_app_yaml(self
):
963 absnames
= self
._make
_file
_hierarchy
(['appdir/app.yaml'])
965 module_config
= ModuleConfigurationStub()
966 application_configuration
.ModuleConfiguration(
967 absnames
[0], None).AndReturn(module_config
)
970 config
= application_configuration
.ApplicationConfiguration(absnames
)
972 self
.assertEqual('myapp', config
.app_id
)
973 self
.assertSequenceEqual([module_config
], config
.modules
)
975 def test_directory_with_backends_yaml(self
):
976 absnames
= self
._make
_file
_hierarchy
(
977 ['appdir/app.yaml', 'appdir/backends.yaml'])
979 module_config
= ModuleConfigurationStub()
980 application_configuration
.ModuleConfiguration(
981 absnames
[0], None).AndReturn(module_config
)
982 backend_config
= ModuleConfigurationStub(module_name
='backend')
983 backends_config
= self
.mox
.CreateMock(
984 application_configuration
.BackendsConfiguration
)
985 backends_config
.get_backend_configurations().AndReturn([backend_config
])
986 application_configuration
.BackendsConfiguration(
987 absnames
[0], absnames
[1], None).AndReturn(backends_config
)
990 config
= application_configuration
.ApplicationConfiguration(
991 [os
.path
.dirname(absnames
[0])])
993 self
.assertEqual('myapp', config
.app_id
)
994 self
.assertSequenceEqual([module_config
, backend_config
], config
.modules
)
996 def test_yaml_files_with_backends_yaml(self
):
997 absnames
= self
._make
_file
_hierarchy
(
998 ['appdir/app.yaml', 'appdir/backends.yaml'])
1000 module_config
= ModuleConfigurationStub()
1001 application_configuration
.ModuleConfiguration(
1002 absnames
[0], None).AndReturn(module_config
)
1004 backend_config
= ModuleConfigurationStub(module_name
='backend')
1005 backends_config
= self
.mox
.CreateMock(
1006 application_configuration
.BackendsConfiguration
)
1007 backends_config
.get_backend_configurations().AndReturn([backend_config
])
1008 application_configuration
.BackendsConfiguration(
1009 absnames
[0], absnames
[1], None).AndReturn(backends_config
)
1011 self
.mox
.ReplayAll()
1012 config
= application_configuration
.ApplicationConfiguration(absnames
)
1013 self
.mox
.VerifyAll()
1014 self
.assertEqual('myapp', config
.app_id
)
1015 self
.assertSequenceEqual([module_config
, backend_config
], config
.modules
)
1017 def test_yaml_files_with_backends_and_dispatch_yaml(self
):
1018 absnames
= self
._make
_file
_hierarchy
(
1019 ['appdir/app.yaml', 'appdir/backends.yaml', 'appdir/dispatch.yaml'])
1021 module_config
= ModuleConfigurationStub(module_name
='default')
1022 application_configuration
.ModuleConfiguration(
1023 absnames
[0], None).AndReturn(module_config
)
1025 backend_config
= ModuleConfigurationStub(module_name
='backend')
1026 backends_config
= self
.mox
.CreateMock(
1027 application_configuration
.BackendsConfiguration
)
1028 backends_config
.get_backend_configurations().AndReturn([backend_config
])
1029 application_configuration
.BackendsConfiguration(
1030 absnames
[0], absnames
[1], None).AndReturn(backends_config
)
1031 dispatch_config
= DispatchConfigurationStub(
1032 [(None, 'default'), (None, 'backend')])
1033 application_configuration
.DispatchConfiguration(
1034 absnames
[2]).AndReturn(dispatch_config
)
1036 self
.mox
.ReplayAll()
1037 config
= application_configuration
.ApplicationConfiguration(absnames
)
1038 self
.mox
.VerifyAll()
1039 self
.assertEqual('myapp', config
.app_id
)
1040 self
.assertSequenceEqual([module_config
, backend_config
], config
.modules
)
1041 self
.assertEqual(dispatch_config
, config
.dispatch
)
1043 def test_yaml_files_dispatch_yaml_and_no_default_module(self
):
1044 absnames
= self
._make
_file
_hierarchy
(
1045 ['appdir/app.yaml', 'appdir/dispatch.yaml'])
1047 module_config
= ModuleConfigurationStub(module_name
='not-default')
1048 application_configuration
.ModuleConfiguration(
1049 absnames
[0], None).AndReturn(module_config
)
1051 dispatch_config
= DispatchConfigurationStub([(None, 'default')])
1052 application_configuration
.DispatchConfiguration(
1053 absnames
[1]).AndReturn(dispatch_config
)
1055 self
.mox
.ReplayAll()
1056 self
.assertRaises(errors
.InvalidAppConfigError
,
1057 application_configuration
.ApplicationConfiguration
,
1059 self
.mox
.VerifyAll()
1061 def test_yaml_files_dispatch_yaml_and_missing_dispatch_target(self
):
1062 absnames
= self
._make
_file
_hierarchy
(
1063 ['appdir/app.yaml', 'appdir/dispatch.yaml'])
1065 module_config
= ModuleConfigurationStub(module_name
='default')
1066 application_configuration
.ModuleConfiguration(
1067 absnames
[0], None).AndReturn(module_config
)
1069 dispatch_config
= DispatchConfigurationStub(
1070 [(None, 'default'), (None, 'fake-module')])
1071 application_configuration
.DispatchConfiguration(
1072 absnames
[1]).AndReturn(dispatch_config
)
1074 self
.mox
.ReplayAll()
1075 self
.assertRaises(errors
.InvalidAppConfigError
,
1076 application_configuration
.ApplicationConfiguration
,
1078 self
.mox
.VerifyAll()
1080 def test_directory_web_inf(self
):
1081 absnames
= self
._make
_file
_hierarchy
(
1082 ['appdir/WEB-INF/appengine-web.xml', 'appdir/WEB-INF/web.xml'])
1083 appdir
= os
.path
.dirname(os
.path
.dirname(absnames
[0]))
1085 module_config
= ModuleConfigurationStub(module_name
='default')
1086 application_configuration
.ModuleConfiguration(
1087 absnames
[0], None).AndReturn(module_config
)
1089 self
.mox
.ReplayAll()
1090 with
_java_temporarily_supported():
1091 config
= application_configuration
.ApplicationConfiguration([appdir
])
1092 self
.mox
.VerifyAll()
1094 self
.assertEqual('myapp', config
.app_id
)
1095 self
.assertSequenceEqual([module_config
], config
.modules
)
1097 def test_directory_web_inf_missing_appengine_xml(self
):
1098 absnames
= self
._make
_file
_hierarchy
(['appdir/WEB-INF/web.xml'])
1099 appdir
= os
.path
.dirname(os
.path
.dirname(absnames
[0]))
1101 self
.mox
.ReplayAll()
1102 with
_java_temporarily_supported():
1103 self
.assertRaises(errors
.AppConfigNotFoundError
,
1104 application_configuration
.ApplicationConfiguration
,
1106 self
.mox
.VerifyAll()
1108 def test_directory_web_inf_missing_web_xml(self
):
1109 absnames
= self
._make
_file
_hierarchy
(['appdir/WEB-INF/appengine-web.xml'])
1110 appdir
= os
.path
.dirname(os
.path
.dirname(absnames
[0]))
1112 self
.mox
.ReplayAll()
1113 with
_java_temporarily_supported():
1114 self
.assertRaises(errors
.AppConfigNotFoundError
,
1115 application_configuration
.ApplicationConfiguration
,
1117 self
.mox
.VerifyAll()
1119 def test_config_with_yaml_and_xml(self
):
1120 absnames
= self
._make
_file
_hierarchy
(
1121 ['module1/app.yaml', 'module1/dispatch.yaml',
1122 'module2/WEB-INF/appengine-web.xml', 'module2/WEB-INF/web.xml'])
1123 app_yaml
= absnames
[0]
1124 dispatch_yaml
= absnames
[1]
1125 appengine_web_xml
= absnames
[2]
1126 module2
= os
.path
.dirname(os
.path
.dirname(appengine_web_xml
))
1128 module1_config
= ModuleConfigurationStub(module_name
='default')
1129 application_configuration
.ModuleConfiguration(
1130 app_yaml
, None).AndReturn(module1_config
)
1131 dispatch_config
= DispatchConfigurationStub(
1132 [(None, 'default'), (None, 'module2')])
1133 application_configuration
.DispatchConfiguration(
1134 dispatch_yaml
).AndReturn(dispatch_config
)
1135 module2_config
= ModuleConfigurationStub(module_name
='module2')
1136 application_configuration
.ModuleConfiguration(
1137 appengine_web_xml
, None).AndReturn(module2_config
)
1139 self
.mox
.ReplayAll()
1140 with
_java_temporarily_supported():
1141 config
= application_configuration
.ApplicationConfiguration(
1142 [app_yaml
, dispatch_yaml
, module2
])
1143 self
.mox
.VerifyAll()
1145 self
.assertEqual('myapp', config
.app_id
)
1146 self
.assertSequenceEqual(
1147 [module1_config
, module2_config
], config
.modules
)
1148 self
.assertEqual(dispatch_config
, config
.dispatch
)
1151 if __name__
== '__main__':