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
30 from google
.appengine
.api
import appinfo
31 from google
.appengine
.api
import backendinfo
32 from google
.appengine
.api
import dispatchinfo
33 from google
.appengine
.tools
.devappserver2
import application_configuration
34 from google
.appengine
.tools
.devappserver2
import errors
38 def _java_temporarily_supported():
39 """Make the java_supported() function return True temporarily.
42 with _java_temporarily_supported():
43 ...test that relies on Java being supported...
45 old_java_supported
= application_configuration
.java_supported
46 application_configuration
.java_supported
= lambda: True
48 application_configuration
.java_supported
= old_java_supported
51 class TestModuleConfiguration(unittest
.TestCase
):
52 """Tests for application_configuration.ModuleConfiguration."""
56 self
.mox
.StubOutWithMock(
57 application_configuration
.ModuleConfiguration
,
58 '_parse_configuration')
59 self
.mox
.StubOutWithMock(os
.path
, 'getmtime')
64 def test_good_app_yaml_configuration(self
):
65 automatic_scaling
= appinfo
.AutomaticScaling(min_pending_latency
='1.0s',
66 max_pending_latency
='2.0s',
69 error_handlers
= [appinfo
.ErrorHandlers(file='error.html')]
70 handlers
= [appinfo
.URLMap()]
71 env_variables
= appinfo
.EnvironmentVariables()
72 info
= appinfo
.AppInfoExternal(
78 automatic_scaling
=automatic_scaling
,
80 error_handlers
=error_handlers
,
82 inbound_services
=['warmup'],
83 env_variables
=env_variables
,
85 application_configuration
.ModuleConfiguration
._parse
_configuration
(
86 '/appdir/app.yaml').AndReturn((info
, ['/appdir/app.yaml']))
87 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
90 config
= application_configuration
.ModuleConfiguration(
94 self
.assertEqual(os
.path
.realpath('/appdir'), config
.application_root
)
95 self
.assertEqual('dev~app', config
.application
)
96 self
.assertEqual('app', config
.application_external_name
)
97 self
.assertEqual('dev', config
.partition
)
98 self
.assertEqual('module1', config
.module_name
)
99 self
.assertEqual('1', config
.major_version
)
100 self
.assertRegexpMatches(config
.version_id
, r
'module1:1\.\d+')
101 self
.assertEqual('python27', config
.runtime
)
102 self
.assertFalse(config
.threadsafe
)
103 self
.assertEqual(automatic_scaling
, config
.automatic_scaling
)
104 self
.assertEqual(info
.GetNormalizedLibraries(),
105 config
.normalized_libraries
)
106 self
.assertEqual(r
'\*.gif', config
.skip_files
)
107 self
.assertEqual(error_handlers
, config
.error_handlers
)
108 self
.assertEqual(handlers
, config
.handlers
)
109 self
.assertEqual(['warmup'], config
.inbound_services
)
110 self
.assertEqual(env_variables
, config
.env_variables
)
111 self
.assertEqual({'/appdir/app.yaml': 10}, config
._mtimes
)
113 def test_vm_app_yaml_configuration(self
):
114 manual_scaling
= appinfo
.ManualScaling()
115 vm_settings
= appinfo
.VmSettings()
116 vm_settings
['vm_runtime'] = 'myawesomeruntime'
117 info
= appinfo
.AppInfoExternal(
122 vm_settings
=vm_settings
,
124 manual_scaling
=manual_scaling
,
126 application_configuration
.ModuleConfiguration
._parse
_configuration
(
127 '/appdir/app.yaml').AndReturn((info
, ['/appdir/app.yaml']))
128 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
131 config
= application_configuration
.ModuleConfiguration('/appdir/app.yaml')
134 self
.assertEqual(os
.path
.realpath('/appdir'), config
.application_root
)
135 self
.assertEqual('dev~app', config
.application
)
136 self
.assertEqual('app', config
.application_external_name
)
137 self
.assertEqual('dev', config
.partition
)
138 self
.assertEqual('module1', config
.module_name
)
139 self
.assertEqual('1', config
.major_version
)
140 self
.assertRegexpMatches(config
.version_id
, r
'module1:1\.\d+')
141 self
.assertEqual('vm', config
.runtime
)
142 self
.assertEqual(vm_settings
['vm_runtime'], config
.effective_runtime
)
143 self
.assertFalse(config
.threadsafe
)
144 self
.assertEqual(manual_scaling
, config
.manual_scaling
)
145 self
.assertEqual({'/appdir/app.yaml': 10}, config
._mtimes
)
147 def test_check_for_updates_unchanged_mtime(self
):
148 info
= appinfo
.AppInfoExternal(
154 application_configuration
.ModuleConfiguration
._parse
_configuration
(
155 '/appdir/app.yaml').AndReturn((info
, ['/appdir/app.yaml']))
156 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
157 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
160 config
= application_configuration
.ModuleConfiguration('/appdir/app.yaml')
161 self
.assertSequenceEqual(set(), config
.check_for_updates())
164 def test_check_for_updates_with_includes(self
):
165 info
= appinfo
.AppInfoExternal(
170 includes
=['/appdir/include.yaml'],
172 application_configuration
.ModuleConfiguration
._parse
_configuration
(
173 '/appdir/app.yaml').AndReturn(
174 (info
, ['/appdir/app.yaml', '/appdir/include.yaml']))
175 os
.path
.getmtime('/appdir/app.yaml').InAnyOrder().AndReturn(10)
176 os
.path
.getmtime('/appdir/include.yaml').InAnyOrder().AndReturn(10)
177 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
178 os
.path
.getmtime('/appdir/include.yaml').AndReturn(11)
180 application_configuration
.ModuleConfiguration
._parse
_configuration
(
181 '/appdir/app.yaml').AndReturn(
182 (info
, ['/appdir/app.yaml', '/appdir/include.yaml']))
183 os
.path
.getmtime('/appdir/app.yaml').InAnyOrder().AndReturn(10)
184 os
.path
.getmtime('/appdir/include.yaml').InAnyOrder().AndReturn(11)
187 config
= application_configuration
.ModuleConfiguration('/appdir/app.yaml')
188 self
.assertEqual({'/appdir/app.yaml': 10, '/appdir/include.yaml': 10},
190 config
._mtimes
= collections
.OrderedDict([('/appdir/app.yaml', 10),
191 ('/appdir/include.yaml', 10)])
192 self
.assertSequenceEqual(set(), config
.check_for_updates())
194 self
.assertEqual({'/appdir/app.yaml': 10, '/appdir/include.yaml': 11},
197 def test_check_for_updates_no_changes(self
):
198 info
= appinfo
.AppInfoExternal(
204 application_configuration
.ModuleConfiguration
._parse
_configuration
(
205 '/appdir/app.yaml').AndReturn((info
, ['/appdir/app.yaml']))
206 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
207 os
.path
.getmtime('/appdir/app.yaml').AndReturn(11)
208 application_configuration
.ModuleConfiguration
._parse
_configuration
(
209 '/appdir/app.yaml').AndReturn((info
, ['/appdir/app.yaml']))
210 os
.path
.getmtime('/appdir/app.yaml').AndReturn(11)
213 config
= application_configuration
.ModuleConfiguration('/appdir/app.yaml')
214 self
.assertSequenceEqual(set(), config
.check_for_updates())
216 self
.assertEqual({'/appdir/app.yaml': 11}, config
._mtimes
)
218 def test_check_for_updates_immutable_changes(self
):
219 automatic_scaling1
= appinfo
.AutomaticScaling(
220 min_pending_latency
='0.1s',
221 max_pending_latency
='1.0s',
222 min_idle_instances
=1,
223 max_idle_instances
=2)
224 info1
= appinfo
.AppInfoExternal(
230 automatic_scaling
=automatic_scaling1
)
232 info2
= appinfo
.AppInfoExternal(
238 automatic_scaling
=appinfo
.AutomaticScaling(
239 min_pending_latency
='1.0s',
240 max_pending_latency
='2.0s',
241 min_idle_instances
=1,
242 max_idle_instances
=2))
244 application_configuration
.ModuleConfiguration
._parse
_configuration
(
245 '/appdir/app.yaml').AndReturn((info1
, ['/appdir/app.yaml']))
246 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
247 os
.path
.getmtime('/appdir/app.yaml').AndReturn(11)
248 application_configuration
.ModuleConfiguration
._parse
_configuration
(
249 '/appdir/app.yaml').AndReturn((info2
, ['/appdir/app.yaml']))
250 os
.path
.getmtime('/appdir/app.yaml').AndReturn(11)
253 config
= application_configuration
.ModuleConfiguration('/appdir/app.yaml')
254 self
.assertSequenceEqual(set(), config
.check_for_updates())
257 self
.assertEqual('dev~app', config
.application
)
258 self
.assertEqual('default', config
.module_name
)
259 self
.assertEqual('version', config
.major_version
)
260 self
.assertRegexpMatches(config
.version_id
, r
'^version\.\d+$')
261 self
.assertEqual('python27', config
.runtime
)
262 self
.assertFalse(config
.threadsafe
)
263 self
.assertEqual(automatic_scaling1
, config
.automatic_scaling
)
265 def test_check_for_mutable_changes(self
):
266 info1
= appinfo
.AppInfoExternal(
272 libraries
=[appinfo
.Library(name
='django', version
='latest')],
275 inbound_services
=['warmup'],
276 env_variables
=appinfo
.EnvironmentVariables(),
277 error_handlers
=[appinfo
.ErrorHandlers(file='error.html')],
279 info2
= appinfo
.AppInfoExternal(
285 libraries
=[appinfo
.Library(name
='jinja2', version
='latest')],
286 skip_files
=r
'.*\.py',
287 handlers
=[appinfo
.URLMap()],
291 application_configuration
.ModuleConfiguration
._parse
_configuration
(
292 '/appdir/app.yaml').AndReturn((info1
, ['/appdir/app.yaml']))
293 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
294 os
.path
.getmtime('/appdir/app.yaml').AndReturn(11)
295 application_configuration
.ModuleConfiguration
._parse
_configuration
(
296 '/appdir/app.yaml').AndReturn((info2
, ['/appdir/app.yaml']))
297 os
.path
.getmtime('/appdir/app.yaml').AndReturn(11)
300 config
= application_configuration
.ModuleConfiguration('/appdir/app.yaml')
301 self
.assertSequenceEqual(
302 set([application_configuration
.NORMALIZED_LIBRARIES_CHANGED
,
303 application_configuration
.SKIP_FILES_CHANGED
,
304 application_configuration
.HANDLERS_CHANGED
,
305 application_configuration
.INBOUND_SERVICES_CHANGED
,
306 application_configuration
.ENV_VARIABLES_CHANGED
,
307 application_configuration
.ERROR_HANDLERS_CHANGED
]),
308 config
.check_for_updates())
311 self
.assertEqual(info2
.GetNormalizedLibraries(),
312 config
.normalized_libraries
)
313 self
.assertEqual(info2
.skip_files
, config
.skip_files
)
314 self
.assertEqual(info2
.error_handlers
, config
.error_handlers
)
315 self
.assertEqual(info2
.handlers
, config
.handlers
)
316 self
.assertEqual(info2
.inbound_services
, config
.inbound_services
)
317 self
.assertEqual(info2
.env_variables
, config
.env_variables
)
321 class TestBackendsConfiguration(unittest
.TestCase
):
324 self
.mox
.StubOutWithMock(
325 application_configuration
.BackendsConfiguration
,
326 '_parse_configuration')
327 self
.mox
.StubOutWithMock(application_configuration
, 'BackendConfiguration')
330 self
.mox
.UnsetStubs()
332 def test_good_configuration(self
):
333 self
.mox
.StubOutWithMock(application_configuration
, 'ModuleConfiguration')
334 static_backend_entry
= backendinfo
.BackendEntry(name
='static')
335 dynamic_backend_entry
= backendinfo
.BackendEntry(name
='dynamic')
336 backend_info
= backendinfo
.BackendInfoExternal(
337 backends
=[static_backend_entry
, dynamic_backend_entry
])
338 module_config
= object()
339 application_configuration
.ModuleConfiguration(
340 '/appdir/app.yaml').AndReturn(module_config
)
341 application_configuration
.BackendsConfiguration
._parse
_configuration
(
342 '/appdir/backends.yaml').AndReturn(backend_info
)
343 static_configuration
= object()
344 dynamic_configuration
= object()
345 application_configuration
.BackendConfiguration(
348 static_backend_entry
).InAnyOrder().AndReturn(static_configuration
)
349 application_configuration
.BackendConfiguration(
352 dynamic_backend_entry
).InAnyOrder().AndReturn(dynamic_configuration
)
355 config
= application_configuration
.BackendsConfiguration(
357 '/appdir/backends.yaml')
358 self
.assertItemsEqual([static_configuration
, dynamic_configuration
],
359 config
.get_backend_configurations())
362 def test_no_backends(self
):
363 self
.mox
.StubOutWithMock(application_configuration
, 'ModuleConfiguration')
364 backend_info
= backendinfo
.BackendInfoExternal()
365 module_config
= object()
366 application_configuration
.ModuleConfiguration(
367 '/appdir/app.yaml').AndReturn(module_config
)
368 application_configuration
.BackendsConfiguration
._parse
_configuration
(
369 '/appdir/backends.yaml').AndReturn(backend_info
)
372 config
= application_configuration
.BackendsConfiguration(
374 '/appdir/backends.yaml')
375 self
.assertEqual([], config
.get_backend_configurations())
378 def test_check_for_changes(self
):
379 static_backend_entry
= backendinfo
.BackendEntry(name
='static')
380 dynamic_backend_entry
= backendinfo
.BackendEntry(name
='dynamic')
381 backend_info
= backendinfo
.BackendInfoExternal(
382 backends
=[static_backend_entry
, dynamic_backend_entry
])
383 module_config
= self
.mox
.CreateMock(
384 application_configuration
.ModuleConfiguration
)
385 self
.mox
.StubOutWithMock(application_configuration
, 'ModuleConfiguration')
386 application_configuration
.ModuleConfiguration(
387 '/appdir/app.yaml').AndReturn(module_config
)
388 application_configuration
.BackendsConfiguration
._parse
_configuration
(
389 '/appdir/backends.yaml').AndReturn(backend_info
)
390 module_config
.check_for_updates().AndReturn(set())
391 module_config
.check_for_updates().AndReturn(set([1]))
392 module_config
.check_for_updates().AndReturn(set([2]))
393 module_config
.check_for_updates().AndReturn(set())
396 config
= application_configuration
.BackendsConfiguration(
398 '/appdir/backends.yaml')
399 self
.assertEqual(set(), config
.check_for_updates('dynamic'))
400 self
.assertEqual(set([1]), config
.check_for_updates('static'))
401 self
.assertEqual(set([1, 2]), config
.check_for_updates('dynamic'))
402 self
.assertEqual(set([2]), config
.check_for_updates('static'))
406 class TestDispatchConfiguration(unittest
.TestCase
):
409 self
.mox
.StubOutWithMock(os
.path
, 'getmtime')
410 self
.mox
.StubOutWithMock(
411 application_configuration
.DispatchConfiguration
,
412 '_parse_configuration')
415 self
.mox
.UnsetStubs()
417 def test_good_configuration(self
):
418 info
= dispatchinfo
.DispatchInfoExternal(
421 dispatchinfo
.DispatchEntry(url
='*/path', module
='foo'),
422 dispatchinfo
.DispatchEntry(url
='domain.com/path', module
='bar'),
423 dispatchinfo
.DispatchEntry(url
='*/path/*', module
='baz'),
424 dispatchinfo
.DispatchEntry(url
='*.domain.com/path/*', module
='foo'),
427 os
.path
.getmtime('/appdir/dispatch.yaml').AndReturn(123.456)
428 application_configuration
.DispatchConfiguration
._parse
_configuration
(
429 '/appdir/dispatch.yaml').AndReturn(info
)
432 config
= application_configuration
.DispatchConfiguration(
433 '/appdir/dispatch.yaml')
436 self
.assertEqual(123.456, config
._mtime
)
437 self
.assertEqual(2, len(config
.dispatch
))
438 self
.assertEqual(vars(dispatchinfo
.ParsedURL('*/path')),
439 vars(config
.dispatch
[0][0]))
440 self
.assertEqual('foo', config
.dispatch
[0][1])
441 self
.assertEqual(vars(dispatchinfo
.ParsedURL('*/path/*')),
442 vars(config
.dispatch
[1][0]))
443 self
.assertEqual('baz', config
.dispatch
[1][1])
445 def test_check_for_updates_no_modification(self
):
446 info
= dispatchinfo
.DispatchInfoExternal(
450 os
.path
.getmtime('/appdir/dispatch.yaml').AndReturn(123.456)
451 application_configuration
.DispatchConfiguration
._parse
_configuration
(
452 '/appdir/dispatch.yaml').AndReturn(info
)
453 os
.path
.getmtime('/appdir/dispatch.yaml').AndReturn(123.456)
456 config
= application_configuration
.DispatchConfiguration(
457 '/appdir/dispatch.yaml')
458 config
.check_for_updates()
461 def test_check_for_updates_with_invalid_modification(self
):
462 info
= dispatchinfo
.DispatchInfoExternal(
465 dispatchinfo
.DispatchEntry(url
='*/path', module
='bar'),
468 os
.path
.getmtime('/appdir/dispatch.yaml').AndReturn(123.456)
469 application_configuration
.DispatchConfiguration
._parse
_configuration
(
470 '/appdir/dispatch.yaml').AndReturn(info
)
471 os
.path
.getmtime('/appdir/dispatch.yaml').AndReturn(124.456)
472 application_configuration
.DispatchConfiguration
._parse
_configuration
(
473 '/appdir/dispatch.yaml').AndRaise(Exception)
476 config
= application_configuration
.DispatchConfiguration(
477 '/appdir/dispatch.yaml')
478 self
.assertEqual('bar', config
.dispatch
[0][1])
479 config
.check_for_updates()
481 self
.assertEqual('bar', config
.dispatch
[0][1])
483 def test_check_for_updates_with_modification(self
):
484 info
= dispatchinfo
.DispatchInfoExternal(
487 dispatchinfo
.DispatchEntry(url
='*/path', module
='bar'),
489 new_info
= dispatchinfo
.DispatchInfoExternal(
492 dispatchinfo
.DispatchEntry(url
='*/path', module
='foo'),
495 os
.path
.getmtime('/appdir/dispatch.yaml').AndReturn(123.456)
496 application_configuration
.DispatchConfiguration
._parse
_configuration
(
497 '/appdir/dispatch.yaml').AndReturn(info
)
498 os
.path
.getmtime('/appdir/dispatch.yaml').AndReturn(124.456)
499 application_configuration
.DispatchConfiguration
._parse
_configuration
(
500 '/appdir/dispatch.yaml').AndReturn(new_info
)
503 config
= application_configuration
.DispatchConfiguration(
504 '/appdir/dispatch.yaml')
505 self
.assertEqual('bar', config
.dispatch
[0][1])
506 config
.check_for_updates()
508 self
.assertEqual('foo', config
.dispatch
[0][1])
511 class TestBackendConfiguration(unittest
.TestCase
):
514 self
.mox
.StubOutWithMock(
515 application_configuration
.ModuleConfiguration
,
516 '_parse_configuration')
517 self
.mox
.StubOutWithMock(os
.path
, 'getmtime')
520 self
.mox
.UnsetStubs()
522 def test_good_configuration(self
):
523 automatic_scaling
= appinfo
.AutomaticScaling(min_pending_latency
='1.0s',
524 max_pending_latency
='2.0s',
525 min_idle_instances
=1,
526 max_idle_instances
=2)
527 error_handlers
= [appinfo
.ErrorHandlers(file='error.html')]
528 handlers
= [appinfo
.URLMap()]
529 env_variables
= appinfo
.EnvironmentVariables()
530 info
= appinfo
.AppInfoExternal(
536 automatic_scaling
=automatic_scaling
,
537 skip_files
=r
'\*.gif',
538 error_handlers
=error_handlers
,
540 inbound_services
=['warmup'],
541 env_variables
=env_variables
,
543 backend_entry
= backendinfo
.BackendEntry(
548 application_configuration
.ModuleConfiguration
._parse
_configuration
(
549 '/appdir/app.yaml').AndReturn((info
, ['/appdir/app.yaml']))
550 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
553 module_config
= application_configuration
.ModuleConfiguration(
555 config
= application_configuration
.BackendConfiguration(
556 module_config
, None, backend_entry
)
559 self
.assertEqual(os
.path
.realpath('/appdir'), config
.application_root
)
560 self
.assertEqual('dev~app', config
.application
)
561 self
.assertEqual('app', config
.application_external_name
)
562 self
.assertEqual('dev', config
.partition
)
563 self
.assertEqual('static', config
.module_name
)
564 self
.assertEqual('1', config
.major_version
)
565 self
.assertRegexpMatches(config
.version_id
, r
'static:1\.\d+')
566 self
.assertEqual('python27', config
.runtime
)
567 self
.assertFalse(config
.threadsafe
)
568 self
.assertEqual(None, config
.automatic_scaling
)
569 self
.assertEqual(None, config
.basic_scaling
)
570 self
.assertEqual(appinfo
.ManualScaling(instances
='3'),
571 config
.manual_scaling
)
572 self
.assertEqual(info
.GetNormalizedLibraries(),
573 config
.normalized_libraries
)
574 self
.assertEqual(r
'\*.gif', config
.skip_files
)
575 self
.assertEqual(error_handlers
, config
.error_handlers
)
576 self
.assertEqual(handlers
, config
.handlers
)
577 self
.assertEqual(['warmup'], config
.inbound_services
)
578 self
.assertEqual(env_variables
, config
.env_variables
)
580 whitelist_fields
= ['module_name', 'version_id', 'automatic_scaling',
581 'manual_scaling', 'basic_scaling', 'is_backend',
583 # Check that all public attributes and methods in a ModuleConfiguration
584 # exist in a BackendConfiguration.
585 for field
in dir(module_config
):
586 if not field
.startswith('_'):
587 self
.assertTrue(hasattr(config
, field
), 'Missing field: %s' % field
)
588 value
= getattr(module_config
, field
)
589 if field
not in whitelist_fields
and not callable(value
):
590 # Check that the attributes other than those in the whitelist have
591 # equal values in the BackendConfiguration to the ModuleConfiguration
592 # from which it inherits.
593 self
.assertEqual(value
, getattr(config
, field
))
595 def test_vm_app_yaml_configuration(self
):
596 automatic_scaling
= appinfo
.AutomaticScaling(min_pending_latency
='1.0s',
597 max_pending_latency
='2.0s',
598 min_idle_instances
=1,
599 max_idle_instances
=2)
600 vm_settings
= appinfo
.VmSettings()
601 vm_settings
['vm_runtime'] = 'myawesomeruntime'
602 info
= appinfo
.AppInfoExternal(
607 vm_settings
=vm_settings
,
609 automatic_scaling
=automatic_scaling
,
611 backend_entry
= backendinfo
.BackendEntry(
615 application_configuration
.ModuleConfiguration
._parse
_configuration
(
616 '/appdir/app.yaml').AndReturn((info
, ['/appdir/app.yaml']))
617 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
620 module_config
= application_configuration
.ModuleConfiguration(
622 config
= application_configuration
.BackendConfiguration(
623 module_config
, None, backend_entry
)
626 self
.assertEqual(os
.path
.realpath('/appdir'), config
.application_root
)
627 self
.assertEqual('dev~app', config
.application
)
628 self
.assertEqual('app', config
.application_external_name
)
629 self
.assertEqual('dev', config
.partition
)
630 self
.assertEqual('static', config
.module_name
)
631 self
.assertEqual('1', config
.major_version
)
632 self
.assertRegexpMatches(config
.version_id
, r
'static:1\.\d+')
633 self
.assertEqual('vm', config
.runtime
)
634 self
.assertEqual(vm_settings
['vm_runtime'], config
.effective_runtime
)
635 self
.assertFalse(config
.threadsafe
)
636 # Resident backends are assigned manual scaling.
637 self
.assertEqual(None, config
.automatic_scaling
)
638 self
.assertEqual(None, config
.basic_scaling
)
639 self
.assertEqual(appinfo
.ManualScaling(instances
='3'),
640 config
.manual_scaling
)
642 def test_good_configuration_dynamic_scaling(self
):
643 automatic_scaling
= appinfo
.AutomaticScaling(min_pending_latency
='1.0s',
644 max_pending_latency
='2.0s',
645 min_idle_instances
=1,
646 max_idle_instances
=2)
647 error_handlers
= [appinfo
.ErrorHandlers(file='error.html')]
648 handlers
= [appinfo
.URLMap()]
649 env_variables
= appinfo
.EnvironmentVariables()
650 info
= appinfo
.AppInfoExternal(
656 automatic_scaling
=automatic_scaling
,
657 skip_files
=r
'\*.gif',
658 error_handlers
=error_handlers
,
660 inbound_services
=['warmup'],
661 env_variables
=env_variables
,
663 backend_entry
= backendinfo
.BackendEntry(
666 options
='public, dynamic',
669 application_configuration
.ModuleConfiguration
._parse
_configuration
(
670 '/appdir/app.yaml').AndReturn((info
, ['/appdir/app.yaml']))
671 os
.path
.getmtime('/appdir/app.yaml').AndReturn(10)
674 module_config
= application_configuration
.ModuleConfiguration(
676 config
= application_configuration
.BackendConfiguration(
677 module_config
, None, backend_entry
)
680 self
.assertEqual(os
.path
.realpath('/appdir'), config
.application_root
)
681 self
.assertEqual('dev~app', config
.application
)
682 self
.assertEqual('dynamic', config
.module_name
)
683 self
.assertEqual('1', config
.major_version
)
684 self
.assertRegexpMatches(config
.version_id
, r
'dynamic:1\.\d+')
685 self
.assertEqual('python27', config
.runtime
)
686 self
.assertFalse(config
.threadsafe
)
687 self
.assertEqual(None, config
.automatic_scaling
)
688 self
.assertEqual(None, config
.manual_scaling
)
689 self
.assertEqual(appinfo
.BasicScaling(max_instances
='3'),
690 config
.basic_scaling
)
691 self
.assertEqual(info
.GetNormalizedLibraries(),
692 config
.normalized_libraries
)
693 self
.assertEqual(r
'\*.gif', config
.skip_files
)
694 self
.assertEqual(error_handlers
, config
.error_handlers
)
695 start_handler
= appinfo
.URLMap(url
='/_ah/start',
696 script
=backend_entry
.start
,
698 self
.assertEqual([start_handler
] + handlers
, config
.handlers
)
699 self
.assertEqual(['warmup'], config
.inbound_services
)
700 self
.assertEqual(env_variables
, config
.env_variables
)
702 def test_check_for_changes(self
):
703 backends_config
= self
.mox
.CreateMock(
704 application_configuration
.BackendsConfiguration
)
705 config
= application_configuration
.BackendConfiguration(
706 None, backends_config
, backendinfo
.BackendEntry(name
='backend'))
708 backends_config
.check_for_updates('backend').AndReturn([])
709 backends_config
.check_for_updates('backend').AndReturn(changes
)
710 minor_version
= config
.minor_version
712 self
.assertEqual([], config
.check_for_updates())
713 self
.assertEqual(minor_version
, config
.minor_version
)
714 self
.assertEqual(changes
, config
.check_for_updates())
715 self
.assertNotEqual(minor_version
, config
.minor_version
)
719 class ModuleConfigurationStub(object):
720 def __init__(self
, application
='myapp', module_name
='module'):
721 self
.application
= application
722 self
.module_name
= module_name
725 class DispatchConfigurationStub(object):
726 def __init__(self
, dispatch
):
727 self
.dispatch
= dispatch
730 class TestApplicationConfiguration(unittest
.TestCase
):
731 """Tests for application_configuration.ApplicationConfiguration."""
735 self
.mox
.StubOutWithMock(application_configuration
, 'ModuleConfiguration')
736 self
.mox
.StubOutWithMock(application_configuration
, 'BackendsConfiguration')
737 self
.mox
.StubOutWithMock(application_configuration
, 'DispatchConfiguration')
738 self
.tmpdir
= tempfile
.mkdtemp(dir=os
.getenv('TEST_TMPDIR'))
741 self
.mox
.UnsetStubs()
742 shutil
.rmtree(self
.tmpdir
)
744 def _make_file_hierarchy(self
, filenames
):
746 for filename
in filenames
:
747 absname
= os
.path
.normpath(self
.tmpdir
+ '/' + filename
)
748 absnames
+= [absname
]
749 dirname
= os
.path
.dirname(absname
)
750 if not os
.path
.exists(dirname
):
752 open(absname
, 'w').close()
755 def test_yaml_files(self
):
756 absnames
= self
._make
_file
_hierarchy
(
757 ['appdir/app.yaml', 'appdir/other.yaml'])
759 module_config1
= ModuleConfigurationStub()
760 application_configuration
.ModuleConfiguration(
761 absnames
[0]).AndReturn(module_config1
)
763 module_config2
= ModuleConfigurationStub(module_name
='other')
764 application_configuration
.ModuleConfiguration(
765 absnames
[1]).AndReturn(module_config2
)
768 config
= application_configuration
.ApplicationConfiguration(
771 self
.assertEqual('myapp', config
.app_id
)
772 self
.assertSequenceEqual([module_config1
, module_config2
], config
.modules
)
774 def test_yaml_files_with_different_app_ids(self
):
775 absnames
= self
._make
_file
_hierarchy
(
776 ['appdir/app.yaml', 'appdir/other.yaml'])
778 module_config1
= ModuleConfigurationStub()
779 application_configuration
.ModuleConfiguration(
780 absnames
[0]).AndReturn(module_config1
)
782 module_config2
= ModuleConfigurationStub(application
='other_app',
784 application_configuration
.ModuleConfiguration(
785 absnames
[1]).AndReturn(module_config2
)
788 self
.assertRaises(errors
.InvalidAppConfigError
,
789 application_configuration
.ApplicationConfiguration
,
793 def test_yaml_files_with_duplicate_module_names(self
):
794 absnames
= self
._make
_file
_hierarchy
(
795 ['appdir/app.yaml', 'appdir/other.yaml'])
797 application_configuration
.ModuleConfiguration(
798 absnames
[0]).AndReturn(ModuleConfigurationStub())
800 application_configuration
.ModuleConfiguration(
801 absnames
[1]).AndReturn(ModuleConfigurationStub())
804 self
.assertRaises(errors
.InvalidAppConfigError
,
805 application_configuration
.ApplicationConfiguration
,
809 def test_directory(self
):
810 absnames
= self
._make
_file
_hierarchy
(['appdir/app.yaml'])
812 module_config
= ModuleConfigurationStub()
813 application_configuration
.ModuleConfiguration(
814 absnames
[0]).AndReturn(module_config
)
817 config
= application_configuration
.ApplicationConfiguration(
818 [os
.path
.dirname(absnames
[0])])
820 self
.assertEqual('myapp', config
.app_id
)
821 self
.assertSequenceEqual([module_config
], config
.modules
)
823 def test_directory_and_module(self
):
824 absnames
= self
._make
_file
_hierarchy
(
825 ['appdir/app.yaml', 'otherdir/mymodule.yaml'])
827 app_yaml_config
= ModuleConfigurationStub()
828 application_configuration
.ModuleConfiguration(
829 absnames
[0]).AndReturn(app_yaml_config
)
830 my_module_config
= ModuleConfigurationStub(module_name
='my_module')
831 application_configuration
.ModuleConfiguration(
832 absnames
[1]).AndReturn(my_module_config
)
834 config
= application_configuration
.ApplicationConfiguration(
835 [os
.path
.dirname(absnames
[0]), absnames
[1]])
837 self
.assertSequenceEqual(
838 [app_yaml_config
, my_module_config
], config
.modules
)
840 def test_directory_app_yml_only(self
):
841 absnames
= self
._make
_file
_hierarchy
(['appdir/app.yml'])
843 module_config
= ModuleConfigurationStub()
844 application_configuration
.ModuleConfiguration(
845 absnames
[0]).AndReturn(module_config
)
848 config
= application_configuration
.ApplicationConfiguration(
849 [os
.path
.dirname(absnames
[0])])
851 self
.assertEqual('myapp', config
.app_id
)
852 self
.assertSequenceEqual([module_config
], config
.modules
)
854 def test_directory_app_yaml_and_app_yml(self
):
855 absnames
= self
._make
_file
_hierarchy
(['appdir/app.yaml', 'appdir/app.yml'])
857 self
.assertRaises(errors
.InvalidAppConfigError
,
858 application_configuration
.ApplicationConfiguration
,
859 [os
.path
.dirname(absnames
[0])])
862 def test_directory_no_app_yamls(self
):
863 absnames
= self
._make
_file
_hierarchy
(['appdir/somethingelse.yaml'])
866 self
.assertRaises(errors
.AppConfigNotFoundError
,
867 application_configuration
.ApplicationConfiguration
,
868 [os
.path
.dirname(absnames
[0])])
871 def test_directory_no_app_yamls_or_web_inf(self
):
872 absnames
= self
._make
_file
_hierarchy
(['appdir/somethingelse.yaml'])
875 with
_java_temporarily_supported():
876 self
.assertRaises(errors
.AppConfigNotFoundError
,
877 application_configuration
.ApplicationConfiguration
,
878 [os
.path
.dirname(absnames
[0])])
881 def test_app_yaml(self
):
882 absnames
= self
._make
_file
_hierarchy
(['appdir/app.yaml'])
884 module_config
= ModuleConfigurationStub()
885 application_configuration
.ModuleConfiguration(
886 absnames
[0]).AndReturn(module_config
)
889 config
= application_configuration
.ApplicationConfiguration(absnames
)
891 self
.assertEqual('myapp', config
.app_id
)
892 self
.assertSequenceEqual([module_config
], config
.modules
)
894 def test_directory_with_backends_yaml(self
):
895 absnames
= self
._make
_file
_hierarchy
(
896 ['appdir/app.yaml', 'appdir/backends.yaml'])
898 module_config
= ModuleConfigurationStub()
899 application_configuration
.ModuleConfiguration(
900 absnames
[0]).AndReturn(module_config
)
901 backend_config
= ModuleConfigurationStub(module_name
='backend')
902 backends_config
= self
.mox
.CreateMock(
903 application_configuration
.BackendsConfiguration
)
904 backends_config
.get_backend_configurations().AndReturn([backend_config
])
905 application_configuration
.BackendsConfiguration(
906 absnames
[0], absnames
[1]).AndReturn(backends_config
)
909 config
= application_configuration
.ApplicationConfiguration(
910 [os
.path
.dirname(absnames
[0])])
912 self
.assertEqual('myapp', config
.app_id
)
913 self
.assertSequenceEqual([module_config
, backend_config
], config
.modules
)
915 def test_yaml_files_with_backends_yaml(self
):
916 absnames
= self
._make
_file
_hierarchy
(
917 ['appdir/app.yaml', 'appdir/backends.yaml'])
919 module_config
= ModuleConfigurationStub()
920 application_configuration
.ModuleConfiguration(
921 absnames
[0]).AndReturn(module_config
)
923 backend_config
= ModuleConfigurationStub(module_name
='backend')
924 backends_config
= self
.mox
.CreateMock(
925 application_configuration
.BackendsConfiguration
)
926 backends_config
.get_backend_configurations().AndReturn([backend_config
])
927 application_configuration
.BackendsConfiguration(
928 absnames
[0], absnames
[1]).AndReturn(backends_config
)
931 config
= application_configuration
.ApplicationConfiguration(absnames
)
933 self
.assertEqual('myapp', config
.app_id
)
934 self
.assertSequenceEqual([module_config
, backend_config
], config
.modules
)
936 def test_yaml_files_with_backends_and_dispatch_yaml(self
):
937 absnames
= self
._make
_file
_hierarchy
(
938 ['appdir/app.yaml', 'appdir/backends.yaml', 'appdir/dispatch.yaml'])
940 module_config
= ModuleConfigurationStub(module_name
='default')
941 application_configuration
.ModuleConfiguration(
942 absnames
[0]).AndReturn(module_config
)
944 backend_config
= ModuleConfigurationStub(module_name
='backend')
945 backends_config
= self
.mox
.CreateMock(
946 application_configuration
.BackendsConfiguration
)
947 backends_config
.get_backend_configurations().AndReturn([backend_config
])
948 application_configuration
.BackendsConfiguration(
949 absnames
[0], absnames
[1]).AndReturn(backends_config
)
950 dispatch_config
= DispatchConfigurationStub(
951 [(None, 'default'), (None, 'backend')])
952 application_configuration
.DispatchConfiguration(
953 absnames
[2]).AndReturn(dispatch_config
)
956 config
= application_configuration
.ApplicationConfiguration(absnames
)
958 self
.assertEqual('myapp', config
.app_id
)
959 self
.assertSequenceEqual([module_config
, backend_config
], config
.modules
)
960 self
.assertEqual(dispatch_config
, config
.dispatch
)
962 def test_yaml_files_dispatch_yaml_and_no_default_module(self
):
963 absnames
= self
._make
_file
_hierarchy
(
964 ['appdir/app.yaml', 'appdir/dispatch.yaml'])
966 module_config
= ModuleConfigurationStub(module_name
='not-default')
967 application_configuration
.ModuleConfiguration(
968 absnames
[0]).AndReturn(module_config
)
970 dispatch_config
= DispatchConfigurationStub([(None, 'default')])
971 application_configuration
.DispatchConfiguration(
972 absnames
[1]).AndReturn(dispatch_config
)
975 self
.assertRaises(errors
.InvalidAppConfigError
,
976 application_configuration
.ApplicationConfiguration
,
980 def test_yaml_files_dispatch_yaml_and_missing_dispatch_target(self
):
981 absnames
= self
._make
_file
_hierarchy
(
982 ['appdir/app.yaml', 'appdir/dispatch.yaml'])
984 module_config
= ModuleConfigurationStub(module_name
='default')
985 application_configuration
.ModuleConfiguration(
986 absnames
[0]).AndReturn(module_config
)
988 dispatch_config
= DispatchConfigurationStub(
989 [(None, 'default'), (None, 'fake-module')])
990 application_configuration
.DispatchConfiguration(
991 absnames
[1]).AndReturn(dispatch_config
)
994 self
.assertRaises(errors
.InvalidAppConfigError
,
995 application_configuration
.ApplicationConfiguration
,
999 def test_directory_web_inf(self
):
1000 absnames
= self
._make
_file
_hierarchy
(
1001 ['appdir/WEB-INF/appengine-web.xml', 'appdir/WEB-INF/web.xml'])
1002 appdir
= os
.path
.dirname(os
.path
.dirname(absnames
[0]))
1004 module_config
= ModuleConfigurationStub(module_name
='default')
1005 application_configuration
.ModuleConfiguration(
1006 absnames
[0]).AndReturn(module_config
)
1008 self
.mox
.ReplayAll()
1009 with
_java_temporarily_supported():
1010 config
= application_configuration
.ApplicationConfiguration([appdir
])
1011 self
.mox
.VerifyAll()
1013 self
.assertEqual('myapp', config
.app_id
)
1014 self
.assertSequenceEqual([module_config
], config
.modules
)
1016 def test_directory_web_inf_missing_appengine_xml(self
):
1017 absnames
= self
._make
_file
_hierarchy
(['appdir/WEB-INF/web.xml'])
1018 appdir
= os
.path
.dirname(os
.path
.dirname(absnames
[0]))
1020 self
.mox
.ReplayAll()
1021 with
_java_temporarily_supported():
1022 self
.assertRaises(errors
.AppConfigNotFoundError
,
1023 application_configuration
.ApplicationConfiguration
,
1025 self
.mox
.VerifyAll()
1027 def test_directory_web_inf_missing_web_xml(self
):
1028 absnames
= self
._make
_file
_hierarchy
(['appdir/WEB-INF/appengine-web.xml'])
1029 appdir
= os
.path
.dirname(os
.path
.dirname(absnames
[0]))
1031 self
.mox
.ReplayAll()
1032 with
_java_temporarily_supported():
1033 self
.assertRaises(errors
.AppConfigNotFoundError
,
1034 application_configuration
.ApplicationConfiguration
,
1036 self
.mox
.VerifyAll()
1038 def test_config_with_yaml_and_xml(self
):
1039 absnames
= self
._make
_file
_hierarchy
(
1040 ['module1/app.yaml', 'module1/dispatch.yaml',
1041 'module2/WEB-INF/appengine-web.xml', 'module2/WEB-INF/web.xml'])
1042 app_yaml
= absnames
[0]
1043 dispatch_yaml
= absnames
[1]
1044 appengine_web_xml
= absnames
[2]
1045 module2
= os
.path
.dirname(os
.path
.dirname(appengine_web_xml
))
1047 module1_config
= ModuleConfigurationStub(module_name
='default')
1048 application_configuration
.ModuleConfiguration(
1049 app_yaml
).AndReturn(module1_config
)
1050 dispatch_config
= DispatchConfigurationStub(
1051 [(None, 'default'), (None, 'module2')])
1052 application_configuration
.DispatchConfiguration(
1053 dispatch_yaml
).AndReturn(dispatch_config
)
1054 module2_config
= ModuleConfigurationStub(module_name
='module2')
1055 application_configuration
.ModuleConfiguration(
1056 appengine_web_xml
).AndReturn(module2_config
)
1058 self
.mox
.ReplayAll()
1059 with
_java_temporarily_supported():
1060 config
= application_configuration
.ApplicationConfiguration(
1061 [app_yaml
, dispatch_yaml
, module2
])
1062 self
.mox
.VerifyAll()
1064 self
.assertEqual('myapp', config
.app_id
)
1065 self
.assertSequenceEqual(
1066 [module1_config
, module2_config
], config
.modules
)
1067 self
.assertEqual(dispatch_config
, config
.dispatch
)
1070 if __name__
== '__main__':