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 class TestModuleConfiguration(unittest
.TestCase
):
54 """Tests for application_configuration.ModuleConfiguration."""
58 self
.mox
.StubOutWithMock(appinfo_includes
, 'ParseAndReturnIncludePaths')
59 self
.mox
.StubOutWithMock(os
.path
, 'getmtime')
60 application_configuration
.open = self
._fake
_open
64 del application_configuration
.open
67 def _fake_open(unused_filename
):
70 def test_good_app_yaml_configuration(self
):
71 automatic_scaling
= appinfo
.AutomaticScaling(min_pending_latency
='1.0s',
72 max_pending_latency
='2.0s',
75 error_handlers
= [appinfo
.ErrorHandlers(file='error.html')]
76 handlers
= [appinfo
.URLMap()]
77 env_variables
= appinfo
.EnvironmentVariables()
78 info
= appinfo
.AppInfoExternal(
84 automatic_scaling
=automatic_scaling
,
86 error_handlers
=error_handlers
,
88 inbound_services
=['warmup'],
89 env_variables
=env_variables
,
91 appinfo_includes
.ParseAndReturnIncludePaths(mox
.IgnoreArg()).AndReturn(
93 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
96 config
= application_configuration
.ModuleConfiguration('/appdir/app.yaml')
99 self
.assertEqual(os
.path
.realpath('/appdir'), config
.application_root
)
100 self
.assertEqual(os
.path
.realpath('/appdir/app.yaml'), config
.config_path
)
101 self
.assertEqual('dev~app', config
.application
)
102 self
.assertEqual('app', config
.application_external_name
)
103 self
.assertEqual('dev', config
.partition
)
104 self
.assertEqual('module1', config
.module_name
)
105 self
.assertEqual('1', config
.major_version
)
106 self
.assertRegexpMatches(config
.version_id
, r
'module1:1\.\d+')
107 self
.assertEqual('python27', config
.runtime
)
108 self
.assertFalse(config
.threadsafe
)
109 self
.assertEqual(automatic_scaling
, config
.automatic_scaling
)
110 self
.assertEqual(info
.GetNormalizedLibraries(),
111 config
.normalized_libraries
)
112 self
.assertEqual(r
'\*.gif', config
.skip_files
)
113 self
.assertEqual(error_handlers
, config
.error_handlers
)
114 self
.assertEqual(handlers
, config
.handlers
)
115 self
.assertEqual(['warmup'], config
.inbound_services
)
116 self
.assertEqual(env_variables
, config
.env_variables
)
117 self
.assertEqual({'/appdir/app.yaml': 10}, config
._mtimes
)
119 def test_vm_app_yaml_configuration(self
):
120 manual_scaling
= appinfo
.ManualScaling()
121 vm_settings
= appinfo
.VmSettings()
122 vm_settings
['vm_runtime'] = 'myawesomeruntime'
123 info
= appinfo
.AppInfoExternal(
128 vm_settings
=vm_settings
,
130 manual_scaling
=manual_scaling
,
132 appinfo_includes
.ParseAndReturnIncludePaths(mox
.IgnoreArg()).AndReturn(
134 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
137 config
= application_configuration
.ModuleConfiguration('/appdir/app.yaml')
140 self
.assertEqual(os
.path
.realpath('/appdir'), config
.application_root
)
141 self
.assertEqual(os
.path
.realpath('/appdir/app.yaml'), config
.config_path
)
142 self
.assertEqual('dev~app', config
.application
)
143 self
.assertEqual('app', config
.application_external_name
)
144 self
.assertEqual('dev', config
.partition
)
145 self
.assertEqual('module1', config
.module_name
)
146 self
.assertEqual('1', config
.major_version
)
147 self
.assertRegexpMatches(config
.version_id
, r
'module1:1\.\d+')
148 self
.assertEqual('vm', config
.runtime
)
149 self
.assertEqual(vm_settings
['vm_runtime'], config
.effective_runtime
)
150 self
.assertFalse(config
.threadsafe
)
151 self
.assertEqual(manual_scaling
, config
.manual_scaling
)
152 self
.assertEqual({'/appdir/app.yaml': 10}, config
._mtimes
)
154 def test_override_app_id(self
):
155 info
= appinfo
.AppInfoExternal(
156 application
='ignored-app',
161 appinfo_includes
.ParseAndReturnIncludePaths(mox
.IgnoreArg()).AndReturn(
163 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
164 os
.path
.getmtime('/appdir/app.yaml').AndReturn(20)
165 os
.path
.getmtime('/appdir/app.yaml').AndReturn(20)
166 appinfo_includes
.ParseAndReturnIncludePaths(mox
.IgnoreArg()).AndReturn(
170 config
= application_configuration
.ModuleConfiguration(
171 '/appdir/app.yaml', 'overriding-app')
172 self
.assertEqual('overriding-app', config
.application_external_name
)
173 self
.assertEqual('dev~overriding-app', config
.application
)
174 config
.check_for_updates()
175 self
.assertEqual('overriding-app', config
.application_external_name
)
176 self
.assertEqual('dev~overriding-app', config
.application
)
179 def test_check_for_updates_unchanged_mtime(self
):
180 info
= appinfo
.AppInfoExternal(
186 appinfo_includes
.ParseAndReturnIncludePaths(mox
.IgnoreArg()).AndReturn(
188 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
189 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
192 config
= application_configuration
.ModuleConfiguration('/appdir/app.yaml')
193 self
.assertSequenceEqual(set(), config
.check_for_updates())
196 def test_check_for_updates_with_includes(self
):
197 info
= appinfo
.AppInfoExternal(
202 includes
=['/appdir/include.yaml'],
204 appinfo_includes
.ParseAndReturnIncludePaths(mox
.IgnoreArg()).AndReturn(
205 (info
, ['/appdir/include.yaml']))
206 os
.path
.getmtime('/appdir/app.yaml').InAnyOrder().AndReturn(10)
207 os
.path
.getmtime('/appdir/include.yaml').InAnyOrder().AndReturn(10)
208 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
209 os
.path
.getmtime('/appdir/include.yaml').AndReturn(11)
211 appinfo_includes
.ParseAndReturnIncludePaths(mox
.IgnoreArg()).AndReturn(
212 (info
, ['/appdir/include.yaml']))
213 os
.path
.getmtime('/appdir/app.yaml').InAnyOrder().AndReturn(10)
214 os
.path
.getmtime('/appdir/include.yaml').InAnyOrder().AndReturn(11)
217 config
= application_configuration
.ModuleConfiguration('/appdir/app.yaml')
218 self
.assertEqual({'/appdir/app.yaml': 10, '/appdir/include.yaml': 10},
220 config
._mtimes
= collections
.OrderedDict([('/appdir/app.yaml', 10),
221 ('/appdir/include.yaml', 10)])
222 self
.assertSequenceEqual(set(), config
.check_for_updates())
224 self
.assertEqual({'/appdir/app.yaml': 10, '/appdir/include.yaml': 11},
227 def test_check_for_updates_no_changes(self
):
228 info
= appinfo
.AppInfoExternal(
234 appinfo_includes
.ParseAndReturnIncludePaths(mox
.IgnoreArg()).AndReturn(
236 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
237 os
.path
.getmtime('/appdir/app.yaml').AndReturn(11)
238 appinfo_includes
.ParseAndReturnIncludePaths(mox
.IgnoreArg()).AndReturn(
240 os
.path
.getmtime('/appdir/app.yaml').AndReturn(11)
243 config
= application_configuration
.ModuleConfiguration('/appdir/app.yaml')
244 self
.assertSequenceEqual(set(), config
.check_for_updates())
246 self
.assertEqual({'/appdir/app.yaml': 11}, config
._mtimes
)
248 def test_check_for_updates_immutable_changes(self
):
249 automatic_scaling1
= appinfo
.AutomaticScaling(
250 min_pending_latency
='0.1s',
251 max_pending_latency
='1.0s',
252 min_idle_instances
=1,
253 max_idle_instances
=2)
254 info1
= appinfo
.AppInfoExternal(
260 automatic_scaling
=automatic_scaling1
)
262 info2
= appinfo
.AppInfoExternal(
268 automatic_scaling
=appinfo
.AutomaticScaling(
269 min_pending_latency
='1.0s',
270 max_pending_latency
='2.0s',
271 min_idle_instances
=1,
272 max_idle_instances
=2))
274 appinfo_includes
.ParseAndReturnIncludePaths(mox
.IgnoreArg()).AndReturn(
276 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
277 os
.path
.getmtime('/appdir/app.yaml').AndReturn(11)
278 appinfo_includes
.ParseAndReturnIncludePaths(mox
.IgnoreArg()).AndReturn(
280 os
.path
.getmtime('/appdir/app.yaml').AndReturn(11)
283 config
= application_configuration
.ModuleConfiguration('/appdir/app.yaml')
284 self
.assertSequenceEqual(set(), config
.check_for_updates())
287 self
.assertEqual('dev~app', config
.application
)
288 self
.assertEqual('default', config
.module_name
)
289 self
.assertEqual('version', config
.major_version
)
290 self
.assertRegexpMatches(config
.version_id
, r
'^version\.\d+$')
291 self
.assertEqual('python27', config
.runtime
)
292 self
.assertFalse(config
.threadsafe
)
293 self
.assertEqual(automatic_scaling1
, config
.automatic_scaling
)
295 def test_check_for_updates_mutable_changes(self
):
296 info1
= appinfo
.AppInfoExternal(
302 libraries
=[appinfo
.Library(name
='django', version
='latest')],
305 inbound_services
=['warmup'],
306 env_variables
=appinfo
.EnvironmentVariables(),
307 error_handlers
=[appinfo
.ErrorHandlers(file='error.html')],
309 info2
= appinfo
.AppInfoExternal(
315 libraries
=[appinfo
.Library(name
='jinja2', version
='latest')],
316 skip_files
=r
'.*\.py',
317 handlers
=[appinfo
.URLMap()],
321 appinfo_includes
.ParseAndReturnIncludePaths(mox
.IgnoreArg()).AndReturn(
323 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
324 os
.path
.getmtime('/appdir/app.yaml').AndReturn(11)
325 appinfo_includes
.ParseAndReturnIncludePaths(mox
.IgnoreArg()).AndReturn(
327 os
.path
.getmtime('/appdir/app.yaml').AndReturn(11)
330 config
= application_configuration
.ModuleConfiguration('/appdir/app.yaml')
331 self
.assertSequenceEqual(
332 set([application_configuration
.NORMALIZED_LIBRARIES_CHANGED
,
333 application_configuration
.SKIP_FILES_CHANGED
,
334 application_configuration
.HANDLERS_CHANGED
,
335 application_configuration
.INBOUND_SERVICES_CHANGED
,
336 application_configuration
.ENV_VARIABLES_CHANGED
,
337 application_configuration
.ERROR_HANDLERS_CHANGED
]),
338 config
.check_for_updates())
341 self
.assertEqual(info2
.GetNormalizedLibraries(),
342 config
.normalized_libraries
)
343 self
.assertEqual(info2
.skip_files
, config
.skip_files
)
344 self
.assertEqual(info2
.error_handlers
, config
.error_handlers
)
345 self
.assertEqual(info2
.handlers
, config
.handlers
)
346 self
.assertEqual(info2
.inbound_services
, config
.inbound_services
)
347 self
.assertEqual(info2
.env_variables
, config
.env_variables
)
351 class TestBackendsConfiguration(unittest
.TestCase
):
354 self
.mox
.StubOutWithMock(
355 application_configuration
.BackendsConfiguration
,
356 '_parse_configuration')
357 self
.mox
.StubOutWithMock(application_configuration
, 'BackendConfiguration')
360 self
.mox
.UnsetStubs()
362 def test_good_configuration(self
):
363 self
.mox
.StubOutWithMock(application_configuration
, 'ModuleConfiguration')
364 static_backend_entry
= backendinfo
.BackendEntry(name
='static')
365 dynamic_backend_entry
= backendinfo
.BackendEntry(name
='dynamic')
366 backend_info
= backendinfo
.BackendInfoExternal(
367 backends
=[static_backend_entry
, dynamic_backend_entry
])
368 module_config
= object()
369 application_configuration
.ModuleConfiguration(
370 '/appdir/app.yaml', None).AndReturn(module_config
)
371 application_configuration
.BackendsConfiguration
._parse
_configuration
(
372 '/appdir/backends.yaml').AndReturn(backend_info
)
373 static_configuration
= object()
374 dynamic_configuration
= object()
375 application_configuration
.BackendConfiguration(
378 static_backend_entry
).InAnyOrder().AndReturn(static_configuration
)
379 application_configuration
.BackendConfiguration(
382 dynamic_backend_entry
).InAnyOrder().AndReturn(dynamic_configuration
)
385 config
= application_configuration
.BackendsConfiguration(
387 '/appdir/backends.yaml')
388 self
.assertItemsEqual([static_configuration
, dynamic_configuration
],
389 config
.get_backend_configurations())
392 def test_no_backends(self
):
393 self
.mox
.StubOutWithMock(application_configuration
, 'ModuleConfiguration')
394 backend_info
= backendinfo
.BackendInfoExternal()
395 module_config
= object()
396 application_configuration
.ModuleConfiguration(
397 '/appdir/app.yaml', None).AndReturn(module_config
)
398 application_configuration
.BackendsConfiguration
._parse
_configuration
(
399 '/appdir/backends.yaml').AndReturn(backend_info
)
402 config
= application_configuration
.BackendsConfiguration(
404 '/appdir/backends.yaml')
405 self
.assertEqual([], config
.get_backend_configurations())
408 def test_check_for_changes(self
):
409 static_backend_entry
= backendinfo
.BackendEntry(name
='static')
410 dynamic_backend_entry
= backendinfo
.BackendEntry(name
='dynamic')
411 backend_info
= backendinfo
.BackendInfoExternal(
412 backends
=[static_backend_entry
, dynamic_backend_entry
])
413 module_config
= self
.mox
.CreateMock(
414 application_configuration
.ModuleConfiguration
)
415 self
.mox
.StubOutWithMock(application_configuration
, 'ModuleConfiguration')
416 application_configuration
.ModuleConfiguration(
417 '/appdir/app.yaml', None).AndReturn(module_config
)
418 application_configuration
.BackendsConfiguration
._parse
_configuration
(
419 '/appdir/backends.yaml').AndReturn(backend_info
)
420 module_config
.check_for_updates().AndReturn(set())
421 module_config
.check_for_updates().AndReturn(set([1]))
422 module_config
.check_for_updates().AndReturn(set([2]))
423 module_config
.check_for_updates().AndReturn(set())
426 config
= application_configuration
.BackendsConfiguration(
428 '/appdir/backends.yaml')
429 self
.assertEqual(set(), config
.check_for_updates('dynamic'))
430 self
.assertEqual(set([1]), config
.check_for_updates('static'))
431 self
.assertEqual(set([1, 2]), config
.check_for_updates('dynamic'))
432 self
.assertEqual(set([2]), config
.check_for_updates('static'))
436 class TestDispatchConfiguration(unittest
.TestCase
):
439 self
.mox
.StubOutWithMock(os
.path
, 'getmtime')
440 self
.mox
.StubOutWithMock(
441 application_configuration
.DispatchConfiguration
,
442 '_parse_configuration')
445 self
.mox
.UnsetStubs()
447 def test_good_configuration(self
):
448 info
= dispatchinfo
.DispatchInfoExternal(
451 dispatchinfo
.DispatchEntry(url
='*/path', module
='foo'),
452 dispatchinfo
.DispatchEntry(url
='domain.com/path', module
='bar'),
453 dispatchinfo
.DispatchEntry(url
='*/path/*', module
='baz'),
454 dispatchinfo
.DispatchEntry(url
='*.domain.com/path/*', module
='foo'),
457 os
.path
.getmtime('/appdir/dispatch.yaml').AndReturn(123.456)
458 application_configuration
.DispatchConfiguration
._parse
_configuration
(
459 '/appdir/dispatch.yaml').AndReturn(info
)
462 config
= application_configuration
.DispatchConfiguration(
463 '/appdir/dispatch.yaml')
466 self
.assertEqual(123.456, config
._mtime
)
467 self
.assertEqual(2, len(config
.dispatch
))
468 self
.assertEqual(vars(dispatchinfo
.ParsedURL('*/path')),
469 vars(config
.dispatch
[0][0]))
470 self
.assertEqual('foo', config
.dispatch
[0][1])
471 self
.assertEqual(vars(dispatchinfo
.ParsedURL('*/path/*')),
472 vars(config
.dispatch
[1][0]))
473 self
.assertEqual('baz', config
.dispatch
[1][1])
475 def test_check_for_updates_no_modification(self
):
476 info
= dispatchinfo
.DispatchInfoExternal(
480 os
.path
.getmtime('/appdir/dispatch.yaml').AndReturn(123.456)
481 application_configuration
.DispatchConfiguration
._parse
_configuration
(
482 '/appdir/dispatch.yaml').AndReturn(info
)
483 os
.path
.getmtime('/appdir/dispatch.yaml').AndReturn(123.456)
486 config
= application_configuration
.DispatchConfiguration(
487 '/appdir/dispatch.yaml')
488 config
.check_for_updates()
491 def test_check_for_updates_with_invalid_modification(self
):
492 info
= dispatchinfo
.DispatchInfoExternal(
495 dispatchinfo
.DispatchEntry(url
='*/path', module
='bar'),
498 os
.path
.getmtime('/appdir/dispatch.yaml').AndReturn(123.456)
499 application_configuration
.DispatchConfiguration
._parse
_configuration
(
500 '/appdir/dispatch.yaml').AndReturn(info
)
501 os
.path
.getmtime('/appdir/dispatch.yaml').AndReturn(124.456)
502 application_configuration
.DispatchConfiguration
._parse
_configuration
(
503 '/appdir/dispatch.yaml').AndRaise(Exception)
506 config
= application_configuration
.DispatchConfiguration(
507 '/appdir/dispatch.yaml')
508 self
.assertEqual('bar', config
.dispatch
[0][1])
509 config
.check_for_updates()
511 self
.assertEqual('bar', config
.dispatch
[0][1])
513 def test_check_for_updates_with_modification(self
):
514 info
= dispatchinfo
.DispatchInfoExternal(
517 dispatchinfo
.DispatchEntry(url
='*/path', module
='bar'),
519 new_info
= dispatchinfo
.DispatchInfoExternal(
522 dispatchinfo
.DispatchEntry(url
='*/path', module
='foo'),
525 os
.path
.getmtime('/appdir/dispatch.yaml').AndReturn(123.456)
526 application_configuration
.DispatchConfiguration
._parse
_configuration
(
527 '/appdir/dispatch.yaml').AndReturn(info
)
528 os
.path
.getmtime('/appdir/dispatch.yaml').AndReturn(124.456)
529 application_configuration
.DispatchConfiguration
._parse
_configuration
(
530 '/appdir/dispatch.yaml').AndReturn(new_info
)
533 config
= application_configuration
.DispatchConfiguration(
534 '/appdir/dispatch.yaml')
535 self
.assertEqual('bar', config
.dispatch
[0][1])
536 config
.check_for_updates()
538 self
.assertEqual('foo', config
.dispatch
[0][1])
541 class TestBackendConfiguration(unittest
.TestCase
):
544 self
.mox
.StubOutWithMock(
545 application_configuration
.ModuleConfiguration
,
546 '_parse_configuration')
547 self
.mox
.StubOutWithMock(os
.path
, 'getmtime')
550 self
.mox
.UnsetStubs()
552 def test_good_configuration(self
):
553 automatic_scaling
= appinfo
.AutomaticScaling(min_pending_latency
='1.0s',
554 max_pending_latency
='2.0s',
555 min_idle_instances
=1,
556 max_idle_instances
=2)
557 error_handlers
= [appinfo
.ErrorHandlers(file='error.html')]
558 handlers
= [appinfo
.URLMap()]
559 env_variables
= appinfo
.EnvironmentVariables()
560 info
= appinfo
.AppInfoExternal(
566 automatic_scaling
=automatic_scaling
,
567 skip_files
=r
'\*.gif',
568 error_handlers
=error_handlers
,
570 inbound_services
=['warmup'],
571 env_variables
=env_variables
,
573 backend_entry
= backendinfo
.BackendEntry(
578 application_configuration
.ModuleConfiguration
._parse
_configuration
(
579 '/appdir/app.yaml').AndReturn((info
, ['/appdir/app.yaml']))
580 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
583 module_config
= application_configuration
.ModuleConfiguration(
585 config
= application_configuration
.BackendConfiguration(
586 module_config
, None, backend_entry
)
589 self
.assertEqual(os
.path
.realpath('/appdir'), config
.application_root
)
590 self
.assertEqual('dev~app', config
.application
)
591 self
.assertEqual('app', config
.application_external_name
)
592 self
.assertEqual('dev', config
.partition
)
593 self
.assertEqual('static', config
.module_name
)
594 self
.assertEqual('1', config
.major_version
)
595 self
.assertRegexpMatches(config
.version_id
, r
'static:1\.\d+')
596 self
.assertEqual('python27', config
.runtime
)
597 self
.assertFalse(config
.threadsafe
)
598 self
.assertEqual(None, config
.automatic_scaling
)
599 self
.assertEqual(None, config
.basic_scaling
)
600 self
.assertEqual(appinfo
.ManualScaling(instances
='3'),
601 config
.manual_scaling
)
602 self
.assertEqual(info
.GetNormalizedLibraries(),
603 config
.normalized_libraries
)
604 self
.assertEqual(r
'\*.gif', config
.skip_files
)
605 self
.assertEqual(error_handlers
, config
.error_handlers
)
606 self
.assertEqual(handlers
, config
.handlers
)
607 self
.assertEqual(['warmup'], config
.inbound_services
)
608 self
.assertEqual(env_variables
, config
.env_variables
)
610 whitelist_fields
= ['module_name', 'version_id', 'automatic_scaling',
611 'manual_scaling', 'basic_scaling', 'is_backend',
613 # Check that all public attributes and methods in a ModuleConfiguration
614 # exist in a BackendConfiguration.
615 for field
in dir(module_config
):
616 if not field
.startswith('_'):
617 self
.assertTrue(hasattr(config
, field
), 'Missing field: %s' % field
)
618 value
= getattr(module_config
, field
)
619 if field
not in whitelist_fields
and not callable(value
):
620 # Check that the attributes other than those in the whitelist have
621 # equal values in the BackendConfiguration to the ModuleConfiguration
622 # from which it inherits.
623 self
.assertEqual(value
, getattr(config
, field
))
625 def test_vm_app_yaml_configuration(self
):
626 automatic_scaling
= appinfo
.AutomaticScaling(min_pending_latency
='1.0s',
627 max_pending_latency
='2.0s',
628 min_idle_instances
=1,
629 max_idle_instances
=2)
630 vm_settings
= appinfo
.VmSettings()
631 vm_settings
['vm_runtime'] = 'myawesomeruntime'
632 info
= appinfo
.AppInfoExternal(
637 vm_settings
=vm_settings
,
639 automatic_scaling
=automatic_scaling
,
641 backend_entry
= backendinfo
.BackendEntry(
645 application_configuration
.ModuleConfiguration
._parse
_configuration
(
646 '/appdir/app.yaml').AndReturn((info
, ['/appdir/app.yaml']))
647 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
650 module_config
= application_configuration
.ModuleConfiguration(
652 config
= application_configuration
.BackendConfiguration(
653 module_config
, None, backend_entry
)
656 self
.assertEqual(os
.path
.realpath('/appdir'), config
.application_root
)
657 self
.assertEqual('dev~app', config
.application
)
658 self
.assertEqual('app', config
.application_external_name
)
659 self
.assertEqual('dev', config
.partition
)
660 self
.assertEqual('static', config
.module_name
)
661 self
.assertEqual('1', config
.major_version
)
662 self
.assertRegexpMatches(config
.version_id
, r
'static:1\.\d+')
663 self
.assertEqual('vm', config
.runtime
)
664 self
.assertEqual(vm_settings
['vm_runtime'], config
.effective_runtime
)
665 self
.assertFalse(config
.threadsafe
)
666 # Resident backends are assigned manual scaling.
667 self
.assertEqual(None, config
.automatic_scaling
)
668 self
.assertEqual(None, config
.basic_scaling
)
669 self
.assertEqual(appinfo
.ManualScaling(instances
='3'),
670 config
.manual_scaling
)
672 def test_good_configuration_dynamic_scaling(self
):
673 automatic_scaling
= appinfo
.AutomaticScaling(min_pending_latency
='1.0s',
674 max_pending_latency
='2.0s',
675 min_idle_instances
=1,
676 max_idle_instances
=2)
677 error_handlers
= [appinfo
.ErrorHandlers(file='error.html')]
678 handlers
= [appinfo
.URLMap()]
679 env_variables
= appinfo
.EnvironmentVariables()
680 info
= appinfo
.AppInfoExternal(
686 automatic_scaling
=automatic_scaling
,
687 skip_files
=r
'\*.gif',
688 error_handlers
=error_handlers
,
690 inbound_services
=['warmup'],
691 env_variables
=env_variables
,
693 backend_entry
= backendinfo
.BackendEntry(
696 options
='public, dynamic',
699 application_configuration
.ModuleConfiguration
._parse
_configuration
(
700 '/appdir/app.yaml').AndReturn((info
, ['/appdir/app.yaml']))
701 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
704 module_config
= application_configuration
.ModuleConfiguration(
706 config
= application_configuration
.BackendConfiguration(
707 module_config
, None, backend_entry
)
710 self
.assertEqual(os
.path
.realpath('/appdir'), config
.application_root
)
711 self
.assertEqual('dev~app', config
.application
)
712 self
.assertEqual('dynamic', config
.module_name
)
713 self
.assertEqual('1', config
.major_version
)
714 self
.assertRegexpMatches(config
.version_id
, r
'dynamic:1\.\d+')
715 self
.assertEqual('python27', config
.runtime
)
716 self
.assertFalse(config
.threadsafe
)
717 self
.assertEqual(None, config
.automatic_scaling
)
718 self
.assertEqual(None, config
.manual_scaling
)
719 self
.assertEqual(appinfo
.BasicScaling(max_instances
='3'),
720 config
.basic_scaling
)
721 self
.assertEqual(info
.GetNormalizedLibraries(),
722 config
.normalized_libraries
)
723 self
.assertEqual(r
'\*.gif', config
.skip_files
)
724 self
.assertEqual(error_handlers
, config
.error_handlers
)
725 start_handler
= appinfo
.URLMap(url
='/_ah/start',
726 script
=backend_entry
.start
,
728 self
.assertEqual([start_handler
] + handlers
, config
.handlers
)
729 self
.assertEqual(['warmup'], config
.inbound_services
)
730 self
.assertEqual(env_variables
, config
.env_variables
)
732 def test_check_for_changes(self
):
733 backends_config
= self
.mox
.CreateMock(
734 application_configuration
.BackendsConfiguration
)
735 config
= application_configuration
.BackendConfiguration(
736 None, backends_config
, backendinfo
.BackendEntry(name
='backend'))
738 backends_config
.check_for_updates('backend').AndReturn([])
739 backends_config
.check_for_updates('backend').AndReturn(changes
)
740 minor_version
= config
.minor_version
742 self
.assertEqual([], config
.check_for_updates())
743 self
.assertEqual(minor_version
, config
.minor_version
)
744 self
.assertEqual(changes
, config
.check_for_updates())
745 self
.assertNotEqual(minor_version
, config
.minor_version
)
749 class ModuleConfigurationStub(object):
750 def __init__(self
, application
='myapp', module_name
='module'):
751 self
.application
= application
752 self
.module_name
= module_name
755 class DispatchConfigurationStub(object):
756 def __init__(self
, dispatch
):
757 self
.dispatch
= dispatch
760 class TestApplicationConfiguration(unittest
.TestCase
):
761 """Tests for application_configuration.ApplicationConfiguration."""
765 self
.mox
.StubOutWithMock(application_configuration
, 'ModuleConfiguration')
766 self
.mox
.StubOutWithMock(application_configuration
, 'BackendsConfiguration')
767 self
.mox
.StubOutWithMock(application_configuration
, 'DispatchConfiguration')
768 self
.tmpdir
= tempfile
.mkdtemp(dir=os
.getenv('TEST_TMPDIR'))
771 self
.mox
.UnsetStubs()
772 shutil
.rmtree(self
.tmpdir
)
774 def _make_file_hierarchy(self
, filenames
):
776 for filename
in filenames
:
777 absname
= os
.path
.normpath(self
.tmpdir
+ '/' + filename
)
778 absnames
+= [absname
]
779 dirname
= os
.path
.dirname(absname
)
780 if not os
.path
.exists(dirname
):
782 open(absname
, 'w').close()
785 def test_yaml_files(self
):
786 absnames
= self
._make
_file
_hierarchy
(
787 ['appdir/app.yaml', 'appdir/other.yaml'])
789 module_config1
= ModuleConfigurationStub()
790 application_configuration
.ModuleConfiguration(
791 absnames
[0], None).AndReturn(module_config1
)
793 module_config2
= ModuleConfigurationStub(module_name
='other')
794 application_configuration
.ModuleConfiguration(
795 absnames
[1], None).AndReturn(module_config2
)
798 config
= application_configuration
.ApplicationConfiguration(
801 self
.assertEqual('myapp', config
.app_id
)
802 self
.assertSequenceEqual([module_config1
, module_config2
], config
.modules
)
804 def test_yaml_files_with_different_app_ids(self
):
805 absnames
= self
._make
_file
_hierarchy
(
806 ['appdir/app.yaml', 'appdir/other.yaml'])
808 module_config1
= ModuleConfigurationStub()
809 application_configuration
.ModuleConfiguration(
810 absnames
[0], None).AndReturn(module_config1
)
812 module_config2
= ModuleConfigurationStub(application
='other_app',
814 application_configuration
.ModuleConfiguration(
815 absnames
[1], None).AndReturn(module_config2
)
818 self
.assertRaises(errors
.InvalidAppConfigError
,
819 application_configuration
.ApplicationConfiguration
,
823 def test_yaml_files_with_duplicate_module_names(self
):
824 absnames
= self
._make
_file
_hierarchy
(
825 ['appdir/app.yaml', 'appdir/other.yaml'])
827 application_configuration
.ModuleConfiguration(
828 absnames
[0], None).AndReturn(ModuleConfigurationStub())
830 application_configuration
.ModuleConfiguration(
831 absnames
[1], None).AndReturn(ModuleConfigurationStub())
834 self
.assertRaises(errors
.InvalidAppConfigError
,
835 application_configuration
.ApplicationConfiguration
,
839 def test_directory(self
):
840 absnames
= self
._make
_file
_hierarchy
(['appdir/app.yaml'])
842 module_config
= ModuleConfigurationStub()
843 application_configuration
.ModuleConfiguration(
844 absnames
[0], None).AndReturn(module_config
)
847 config
= application_configuration
.ApplicationConfiguration(
848 [os
.path
.dirname(absnames
[0])])
850 self
.assertEqual('myapp', config
.app_id
)
851 self
.assertSequenceEqual([module_config
], config
.modules
)
853 def test_directory_and_module(self
):
854 absnames
= self
._make
_file
_hierarchy
(
855 ['appdir/app.yaml', 'otherdir/mymodule.yaml'])
857 app_yaml_config
= ModuleConfigurationStub()
858 application_configuration
.ModuleConfiguration(
859 absnames
[0], None).AndReturn(app_yaml_config
)
860 my_module_config
= ModuleConfigurationStub(module_name
='my_module')
861 application_configuration
.ModuleConfiguration(
862 absnames
[1], None).AndReturn(my_module_config
)
864 config
= application_configuration
.ApplicationConfiguration(
865 [os
.path
.dirname(absnames
[0]), absnames
[1]])
867 self
.assertSequenceEqual(
868 [app_yaml_config
, my_module_config
], config
.modules
)
870 def test_directory_app_yml_only(self
):
871 absnames
= self
._make
_file
_hierarchy
(['appdir/app.yml'])
873 module_config
= ModuleConfigurationStub()
874 application_configuration
.ModuleConfiguration(
875 absnames
[0], None).AndReturn(module_config
)
878 config
= application_configuration
.ApplicationConfiguration(
879 [os
.path
.dirname(absnames
[0])])
881 self
.assertEqual('myapp', config
.app_id
)
882 self
.assertSequenceEqual([module_config
], config
.modules
)
884 def test_directory_app_yaml_and_app_yml(self
):
885 absnames
= self
._make
_file
_hierarchy
(['appdir/app.yaml', 'appdir/app.yml'])
887 self
.assertRaises(errors
.InvalidAppConfigError
,
888 application_configuration
.ApplicationConfiguration
,
889 [os
.path
.dirname(absnames
[0])])
892 def test_directory_no_app_yamls(self
):
893 absnames
= self
._make
_file
_hierarchy
(['appdir/somethingelse.yaml'])
896 self
.assertRaises(errors
.AppConfigNotFoundError
,
897 application_configuration
.ApplicationConfiguration
,
898 [os
.path
.dirname(absnames
[0])])
901 def test_directory_no_app_yamls_or_web_inf(self
):
902 absnames
= self
._make
_file
_hierarchy
(['appdir/somethingelse.yaml'])
905 with
_java_temporarily_supported():
906 self
.assertRaises(errors
.AppConfigNotFoundError
,
907 application_configuration
.ApplicationConfiguration
,
908 [os
.path
.dirname(absnames
[0])])
911 def test_app_yaml(self
):
912 absnames
= self
._make
_file
_hierarchy
(['appdir/app.yaml'])
914 module_config
= ModuleConfigurationStub()
915 application_configuration
.ModuleConfiguration(
916 absnames
[0], None).AndReturn(module_config
)
919 config
= application_configuration
.ApplicationConfiguration(absnames
)
921 self
.assertEqual('myapp', config
.app_id
)
922 self
.assertSequenceEqual([module_config
], config
.modules
)
924 def test_directory_with_backends_yaml(self
):
925 absnames
= self
._make
_file
_hierarchy
(
926 ['appdir/app.yaml', 'appdir/backends.yaml'])
928 module_config
= ModuleConfigurationStub()
929 application_configuration
.ModuleConfiguration(
930 absnames
[0], None).AndReturn(module_config
)
931 backend_config
= ModuleConfigurationStub(module_name
='backend')
932 backends_config
= self
.mox
.CreateMock(
933 application_configuration
.BackendsConfiguration
)
934 backends_config
.get_backend_configurations().AndReturn([backend_config
])
935 application_configuration
.BackendsConfiguration(
936 absnames
[0], absnames
[1], None).AndReturn(backends_config
)
939 config
= application_configuration
.ApplicationConfiguration(
940 [os
.path
.dirname(absnames
[0])])
942 self
.assertEqual('myapp', config
.app_id
)
943 self
.assertSequenceEqual([module_config
, backend_config
], config
.modules
)
945 def test_yaml_files_with_backends_yaml(self
):
946 absnames
= self
._make
_file
_hierarchy
(
947 ['appdir/app.yaml', 'appdir/backends.yaml'])
949 module_config
= ModuleConfigurationStub()
950 application_configuration
.ModuleConfiguration(
951 absnames
[0], None).AndReturn(module_config
)
953 backend_config
= ModuleConfigurationStub(module_name
='backend')
954 backends_config
= self
.mox
.CreateMock(
955 application_configuration
.BackendsConfiguration
)
956 backends_config
.get_backend_configurations().AndReturn([backend_config
])
957 application_configuration
.BackendsConfiguration(
958 absnames
[0], absnames
[1], None).AndReturn(backends_config
)
961 config
= application_configuration
.ApplicationConfiguration(absnames
)
963 self
.assertEqual('myapp', config
.app_id
)
964 self
.assertSequenceEqual([module_config
, backend_config
], config
.modules
)
966 def test_yaml_files_with_backends_and_dispatch_yaml(self
):
967 absnames
= self
._make
_file
_hierarchy
(
968 ['appdir/app.yaml', 'appdir/backends.yaml', 'appdir/dispatch.yaml'])
970 module_config
= ModuleConfigurationStub(module_name
='default')
971 application_configuration
.ModuleConfiguration(
972 absnames
[0], None).AndReturn(module_config
)
974 backend_config
= ModuleConfigurationStub(module_name
='backend')
975 backends_config
= self
.mox
.CreateMock(
976 application_configuration
.BackendsConfiguration
)
977 backends_config
.get_backend_configurations().AndReturn([backend_config
])
978 application_configuration
.BackendsConfiguration(
979 absnames
[0], absnames
[1], None).AndReturn(backends_config
)
980 dispatch_config
= DispatchConfigurationStub(
981 [(None, 'default'), (None, 'backend')])
982 application_configuration
.DispatchConfiguration(
983 absnames
[2]).AndReturn(dispatch_config
)
986 config
= application_configuration
.ApplicationConfiguration(absnames
)
988 self
.assertEqual('myapp', config
.app_id
)
989 self
.assertSequenceEqual([module_config
, backend_config
], config
.modules
)
990 self
.assertEqual(dispatch_config
, config
.dispatch
)
992 def test_yaml_files_dispatch_yaml_and_no_default_module(self
):
993 absnames
= self
._make
_file
_hierarchy
(
994 ['appdir/app.yaml', 'appdir/dispatch.yaml'])
996 module_config
= ModuleConfigurationStub(module_name
='not-default')
997 application_configuration
.ModuleConfiguration(
998 absnames
[0], None).AndReturn(module_config
)
1000 dispatch_config
= DispatchConfigurationStub([(None, 'default')])
1001 application_configuration
.DispatchConfiguration(
1002 absnames
[1]).AndReturn(dispatch_config
)
1004 self
.mox
.ReplayAll()
1005 self
.assertRaises(errors
.InvalidAppConfigError
,
1006 application_configuration
.ApplicationConfiguration
,
1008 self
.mox
.VerifyAll()
1010 def test_yaml_files_dispatch_yaml_and_missing_dispatch_target(self
):
1011 absnames
= self
._make
_file
_hierarchy
(
1012 ['appdir/app.yaml', 'appdir/dispatch.yaml'])
1014 module_config
= ModuleConfigurationStub(module_name
='default')
1015 application_configuration
.ModuleConfiguration(
1016 absnames
[0], None).AndReturn(module_config
)
1018 dispatch_config
= DispatchConfigurationStub(
1019 [(None, 'default'), (None, 'fake-module')])
1020 application_configuration
.DispatchConfiguration(
1021 absnames
[1]).AndReturn(dispatch_config
)
1023 self
.mox
.ReplayAll()
1024 self
.assertRaises(errors
.InvalidAppConfigError
,
1025 application_configuration
.ApplicationConfiguration
,
1027 self
.mox
.VerifyAll()
1029 def test_directory_web_inf(self
):
1030 absnames
= self
._make
_file
_hierarchy
(
1031 ['appdir/WEB-INF/appengine-web.xml', 'appdir/WEB-INF/web.xml'])
1032 appdir
= os
.path
.dirname(os
.path
.dirname(absnames
[0]))
1034 module_config
= ModuleConfigurationStub(module_name
='default')
1035 application_configuration
.ModuleConfiguration(
1036 absnames
[0], None).AndReturn(module_config
)
1038 self
.mox
.ReplayAll()
1039 with
_java_temporarily_supported():
1040 config
= application_configuration
.ApplicationConfiguration([appdir
])
1041 self
.mox
.VerifyAll()
1043 self
.assertEqual('myapp', config
.app_id
)
1044 self
.assertSequenceEqual([module_config
], config
.modules
)
1046 def test_directory_web_inf_missing_appengine_xml(self
):
1047 absnames
= self
._make
_file
_hierarchy
(['appdir/WEB-INF/web.xml'])
1048 appdir
= os
.path
.dirname(os
.path
.dirname(absnames
[0]))
1050 self
.mox
.ReplayAll()
1051 with
_java_temporarily_supported():
1052 self
.assertRaises(errors
.AppConfigNotFoundError
,
1053 application_configuration
.ApplicationConfiguration
,
1055 self
.mox
.VerifyAll()
1057 def test_directory_web_inf_missing_web_xml(self
):
1058 absnames
= self
._make
_file
_hierarchy
(['appdir/WEB-INF/appengine-web.xml'])
1059 appdir
= os
.path
.dirname(os
.path
.dirname(absnames
[0]))
1061 self
.mox
.ReplayAll()
1062 with
_java_temporarily_supported():
1063 self
.assertRaises(errors
.AppConfigNotFoundError
,
1064 application_configuration
.ApplicationConfiguration
,
1066 self
.mox
.VerifyAll()
1068 def test_config_with_yaml_and_xml(self
):
1069 absnames
= self
._make
_file
_hierarchy
(
1070 ['module1/app.yaml', 'module1/dispatch.yaml',
1071 'module2/WEB-INF/appengine-web.xml', 'module2/WEB-INF/web.xml'])
1072 app_yaml
= absnames
[0]
1073 dispatch_yaml
= absnames
[1]
1074 appengine_web_xml
= absnames
[2]
1075 module2
= os
.path
.dirname(os
.path
.dirname(appengine_web_xml
))
1077 module1_config
= ModuleConfigurationStub(module_name
='default')
1078 application_configuration
.ModuleConfiguration(
1079 app_yaml
, None).AndReturn(module1_config
)
1080 dispatch_config
= DispatchConfigurationStub(
1081 [(None, 'default'), (None, 'module2')])
1082 application_configuration
.DispatchConfiguration(
1083 dispatch_yaml
).AndReturn(dispatch_config
)
1084 module2_config
= ModuleConfigurationStub(module_name
='module2')
1085 application_configuration
.ModuleConfiguration(
1086 appengine_web_xml
, None).AndReturn(module2_config
)
1088 self
.mox
.ReplayAll()
1089 with
_java_temporarily_supported():
1090 config
= application_configuration
.ApplicationConfiguration(
1091 [app_yaml
, dispatch_yaml
, module2
])
1092 self
.mox
.VerifyAll()
1094 self
.assertEqual('myapp', config
.app_id
)
1095 self
.assertSequenceEqual(
1096 [module1_config
, module2_config
], config
.modules
)
1097 self
.assertEqual(dispatch_config
, config
.dispatch
)
1100 if __name__
== '__main__':