App Engine Python SDK version 1.9.2
[gae.git] / python / google / appengine / tools / devappserver2 / application_configuration_test.py
blob84c7221306e09d9b1f7a619717751fea960febc8
1 #!/usr/bin/env python
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."""
20 import collections
21 from contextlib import contextmanager
22 import os.path
23 import shutil
24 import tempfile
25 import unittest
27 import google
28 import mox
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
37 @contextmanager
38 def _java_temporarily_supported():
39 """Make the java_supported() function return True temporarily.
41 Use as:
42 with _java_temporarily_supported():
43 ...test that relies on Java being supported...
44 """
45 old_java_supported = application_configuration.java_supported
46 application_configuration.java_supported = lambda: True
47 yield
48 application_configuration.java_supported = old_java_supported
51 class TestModuleConfiguration(unittest.TestCase):
52 """Tests for application_configuration.ModuleConfiguration."""
54 def setUp(self):
55 self.mox = mox.Mox()
56 self.mox.StubOutWithMock(
57 application_configuration.ModuleConfiguration,
58 '_parse_configuration')
59 self.mox.StubOutWithMock(os.path, 'getmtime')
61 def tearDown(self):
62 self.mox.UnsetStubs()
64 def test_good_app_yaml_configuration(self):
65 automatic_scaling = appinfo.AutomaticScaling(min_pending_latency='1.0s',
66 max_pending_latency='2.0s',
67 min_idle_instances=1,
68 max_idle_instances=2)
69 error_handlers = [appinfo.ErrorHandlers(file='error.html')]
70 handlers = [appinfo.URLMap()]
71 env_variables = appinfo.EnvironmentVariables()
72 info = appinfo.AppInfoExternal(
73 application='app',
74 module='module1',
75 version='1',
76 runtime='python27',
77 threadsafe=False,
78 automatic_scaling=automatic_scaling,
79 skip_files=r'\*.gif',
80 error_handlers=error_handlers,
81 handlers=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)
89 self.mox.ReplayAll()
90 config = application_configuration.ModuleConfiguration(
91 '/appdir/app.yaml')
92 self.mox.VerifyAll()
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(
118 application='app',
119 module='module1',
120 version='1',
121 runtime='vm',
122 vm_settings=vm_settings,
123 threadsafe=False,
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)
130 self.mox.ReplayAll()
131 config = application_configuration.ModuleConfiguration('/appdir/app.yaml')
133 self.mox.VerifyAll()
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(
149 application='app',
150 module='default',
151 version='version',
152 runtime='python27',
153 threadsafe=False)
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)
159 self.mox.ReplayAll()
160 config = application_configuration.ModuleConfiguration('/appdir/app.yaml')
161 self.assertSequenceEqual(set(), config.check_for_updates())
162 self.mox.VerifyAll()
164 def test_check_for_updates_with_includes(self):
165 info = appinfo.AppInfoExternal(
166 application='app',
167 module='default',
168 version='version',
169 runtime='python27',
170 includes=['/appdir/include.yaml'],
171 threadsafe=False)
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)
186 self.mox.ReplayAll()
187 config = application_configuration.ModuleConfiguration('/appdir/app.yaml')
188 self.assertEqual({'/appdir/app.yaml': 10, '/appdir/include.yaml': 10},
189 config._mtimes)
190 config._mtimes = collections.OrderedDict([('/appdir/app.yaml', 10),
191 ('/appdir/include.yaml', 10)])
192 self.assertSequenceEqual(set(), config.check_for_updates())
193 self.mox.VerifyAll()
194 self.assertEqual({'/appdir/app.yaml': 10, '/appdir/include.yaml': 11},
195 config._mtimes)
197 def test_check_for_updates_no_changes(self):
198 info = appinfo.AppInfoExternal(
199 application='app',
200 module='default',
201 version='version',
202 runtime='python27',
203 threadsafe=False)
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)
212 self.mox.ReplayAll()
213 config = application_configuration.ModuleConfiguration('/appdir/app.yaml')
214 self.assertSequenceEqual(set(), config.check_for_updates())
215 self.mox.VerifyAll()
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(
225 application='app',
226 module='default',
227 version='version',
228 runtime='python27',
229 threadsafe=False,
230 automatic_scaling=automatic_scaling1)
232 info2 = appinfo.AppInfoExternal(
233 application='app2',
234 module='default2',
235 version='version2',
236 runtime='python',
237 threadsafe=True,
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)
252 self.mox.ReplayAll()
253 config = application_configuration.ModuleConfiguration('/appdir/app.yaml')
254 self.assertSequenceEqual(set(), config.check_for_updates())
255 self.mox.VerifyAll()
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(
267 application='app',
268 module='default',
269 version='version',
270 runtime='python27',
271 threadsafe=False,
272 libraries=[appinfo.Library(name='django', version='latest')],
273 skip_files='.*',
274 handlers=[],
275 inbound_services=['warmup'],
276 env_variables=appinfo.EnvironmentVariables(),
277 error_handlers=[appinfo.ErrorHandlers(file='error.html')],
279 info2 = appinfo.AppInfoExternal(
280 application='app',
281 module='default',
282 version='version',
283 runtime='python27',
284 threadsafe=False,
285 libraries=[appinfo.Library(name='jinja2', version='latest')],
286 skip_files=r'.*\.py',
287 handlers=[appinfo.URLMap()],
288 inbound_services=[],
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)
299 self.mox.ReplayAll()
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())
309 self.mox.VerifyAll()
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):
322 def setUp(self):
323 self.mox = mox.Mox()
324 self.mox.StubOutWithMock(
325 application_configuration.BackendsConfiguration,
326 '_parse_configuration')
327 self.mox.StubOutWithMock(application_configuration, 'BackendConfiguration')
329 def tearDown(self):
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(
346 module_config,
347 mox.IgnoreArg(),
348 static_backend_entry).InAnyOrder().AndReturn(static_configuration)
349 application_configuration.BackendConfiguration(
350 module_config,
351 mox.IgnoreArg(),
352 dynamic_backend_entry).InAnyOrder().AndReturn(dynamic_configuration)
354 self.mox.ReplayAll()
355 config = application_configuration.BackendsConfiguration(
356 '/appdir/app.yaml',
357 '/appdir/backends.yaml')
358 self.assertItemsEqual([static_configuration, dynamic_configuration],
359 config.get_backend_configurations())
360 self.mox.VerifyAll()
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)
371 self.mox.ReplayAll()
372 config = application_configuration.BackendsConfiguration(
373 '/appdir/app.yaml',
374 '/appdir/backends.yaml')
375 self.assertEqual([], config.get_backend_configurations())
376 self.mox.VerifyAll()
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())
395 self.mox.ReplayAll()
396 config = application_configuration.BackendsConfiguration(
397 '/appdir/app.yaml',
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'))
403 self.mox.VerifyAll()
406 class TestDispatchConfiguration(unittest.TestCase):
407 def setUp(self):
408 self.mox = mox.Mox()
409 self.mox.StubOutWithMock(os.path, 'getmtime')
410 self.mox.StubOutWithMock(
411 application_configuration.DispatchConfiguration,
412 '_parse_configuration')
414 def tearDown(self):
415 self.mox.UnsetStubs()
417 def test_good_configuration(self):
418 info = dispatchinfo.DispatchInfoExternal(
419 application='appid',
420 dispatch=[
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)
431 self.mox.ReplayAll()
432 config = application_configuration.DispatchConfiguration(
433 '/appdir/dispatch.yaml')
434 self.mox.VerifyAll()
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(
447 application='appid',
448 dispatch=[])
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)
455 self.mox.ReplayAll()
456 config = application_configuration.DispatchConfiguration(
457 '/appdir/dispatch.yaml')
458 config.check_for_updates()
459 self.mox.VerifyAll()
461 def test_check_for_updates_with_invalid_modification(self):
462 info = dispatchinfo.DispatchInfoExternal(
463 application='appid',
464 dispatch=[
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)
475 self.mox.ReplayAll()
476 config = application_configuration.DispatchConfiguration(
477 '/appdir/dispatch.yaml')
478 self.assertEqual('bar', config.dispatch[0][1])
479 config.check_for_updates()
480 self.mox.VerifyAll()
481 self.assertEqual('bar', config.dispatch[0][1])
483 def test_check_for_updates_with_modification(self):
484 info = dispatchinfo.DispatchInfoExternal(
485 application='appid',
486 dispatch=[
487 dispatchinfo.DispatchEntry(url='*/path', module='bar'),
489 new_info = dispatchinfo.DispatchInfoExternal(
490 application='appid',
491 dispatch=[
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)
502 self.mox.ReplayAll()
503 config = application_configuration.DispatchConfiguration(
504 '/appdir/dispatch.yaml')
505 self.assertEqual('bar', config.dispatch[0][1])
506 config.check_for_updates()
507 self.mox.VerifyAll()
508 self.assertEqual('foo', config.dispatch[0][1])
511 class TestBackendConfiguration(unittest.TestCase):
512 def setUp(self):
513 self.mox = mox.Mox()
514 self.mox.StubOutWithMock(
515 application_configuration.ModuleConfiguration,
516 '_parse_configuration')
517 self.mox.StubOutWithMock(os.path, 'getmtime')
519 def tearDown(self):
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(
531 application='app',
532 module='module1',
533 version='1',
534 runtime='python27',
535 threadsafe=False,
536 automatic_scaling=automatic_scaling,
537 skip_files=r'\*.gif',
538 error_handlers=error_handlers,
539 handlers=handlers,
540 inbound_services=['warmup'],
541 env_variables=env_variables,
543 backend_entry = backendinfo.BackendEntry(
544 name='static',
545 instances='3',
546 options='public')
548 application_configuration.ModuleConfiguration._parse_configuration(
549 '/appdir/app.yaml').AndReturn((info, ['/appdir/app.yaml']))
550 os.path.getmtime('/appdir/app.yaml').AndReturn(10)
552 self.mox.ReplayAll()
553 module_config = application_configuration.ModuleConfiguration(
554 '/appdir/app.yaml')
555 config = application_configuration.BackendConfiguration(
556 module_config, None, backend_entry)
557 self.mox.VerifyAll()
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',
582 'minor_version']
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(
603 application='app',
604 module='module1',
605 version='1',
606 runtime='vm',
607 vm_settings=vm_settings,
608 threadsafe=False,
609 automatic_scaling=automatic_scaling,
611 backend_entry = backendinfo.BackendEntry(
612 name='static',
613 instances='3',
614 options='public')
615 application_configuration.ModuleConfiguration._parse_configuration(
616 '/appdir/app.yaml').AndReturn((info, ['/appdir/app.yaml']))
617 os.path.getmtime('/appdir/app.yaml').AndReturn(10)
619 self.mox.ReplayAll()
620 module_config = application_configuration.ModuleConfiguration(
621 '/appdir/app.yaml')
622 config = application_configuration.BackendConfiguration(
623 module_config, None, backend_entry)
625 self.mox.VerifyAll()
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(
651 application='app',
652 module='module1',
653 version='1',
654 runtime='python27',
655 threadsafe=False,
656 automatic_scaling=automatic_scaling,
657 skip_files=r'\*.gif',
658 error_handlers=error_handlers,
659 handlers=handlers,
660 inbound_services=['warmup'],
661 env_variables=env_variables,
663 backend_entry = backendinfo.BackendEntry(
664 name='dynamic',
665 instances='3',
666 options='public, dynamic',
667 start='handler')
669 application_configuration.ModuleConfiguration._parse_configuration(
670 '/appdir/app.yaml').AndReturn((info, ['/appdir/app.yaml']))
671 os.path.getmtime('/appdir/app.yaml').AndReturn(10)
673 self.mox.ReplayAll()
674 module_config = application_configuration.ModuleConfiguration(
675 '/appdir/app.yaml')
676 config = application_configuration.BackendConfiguration(
677 module_config, None, backend_entry)
678 self.mox.VerifyAll()
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,
697 login='admin')
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'))
707 changes = object()
708 backends_config.check_for_updates('backend').AndReturn([])
709 backends_config.check_for_updates('backend').AndReturn(changes)
710 minor_version = config.minor_version
711 self.mox.ReplayAll()
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)
716 self.mox.VerifyAll()
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."""
733 def setUp(self):
734 self.mox = mox.Mox()
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'))
740 def tearDown(self):
741 self.mox.UnsetStubs()
742 shutil.rmtree(self.tmpdir)
744 def _make_file_hierarchy(self, filenames):
745 absnames = []
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):
751 os.makedirs(dirname)
752 open(absname, 'w').close()
753 return absnames
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)
767 self.mox.ReplayAll()
768 config = application_configuration.ApplicationConfiguration(
769 absnames)
770 self.mox.VerifyAll()
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',
783 module_name='other')
784 application_configuration.ModuleConfiguration(
785 absnames[1]).AndReturn(module_config2)
787 self.mox.ReplayAll()
788 self.assertRaises(errors.InvalidAppConfigError,
789 application_configuration.ApplicationConfiguration,
790 absnames)
791 self.mox.VerifyAll()
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())
803 self.mox.ReplayAll()
804 self.assertRaises(errors.InvalidAppConfigError,
805 application_configuration.ApplicationConfiguration,
806 absnames)
807 self.mox.VerifyAll()
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)
816 self.mox.ReplayAll()
817 config = application_configuration.ApplicationConfiguration(
818 [os.path.dirname(absnames[0])])
819 self.mox.VerifyAll()
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)
833 self.mox.ReplayAll()
834 config = application_configuration.ApplicationConfiguration(
835 [os.path.dirname(absnames[0]), absnames[1]])
836 self.mox.VerifyAll()
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)
847 self.mox.ReplayAll()
848 config = application_configuration.ApplicationConfiguration(
849 [os.path.dirname(absnames[0])])
850 self.mox.VerifyAll()
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'])
856 self.mox.ReplayAll()
857 self.assertRaises(errors.InvalidAppConfigError,
858 application_configuration.ApplicationConfiguration,
859 [os.path.dirname(absnames[0])])
860 self.mox.VerifyAll()
862 def test_directory_no_app_yamls(self):
863 absnames = self._make_file_hierarchy(['appdir/somethingelse.yaml'])
865 self.mox.ReplayAll()
866 self.assertRaises(errors.AppConfigNotFoundError,
867 application_configuration.ApplicationConfiguration,
868 [os.path.dirname(absnames[0])])
869 self.mox.VerifyAll()
871 def test_directory_no_app_yamls_or_web_inf(self):
872 absnames = self._make_file_hierarchy(['appdir/somethingelse.yaml'])
874 self.mox.ReplayAll()
875 with _java_temporarily_supported():
876 self.assertRaises(errors.AppConfigNotFoundError,
877 application_configuration.ApplicationConfiguration,
878 [os.path.dirname(absnames[0])])
879 self.mox.VerifyAll()
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)
888 self.mox.ReplayAll()
889 config = application_configuration.ApplicationConfiguration(absnames)
890 self.mox.VerifyAll()
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)
908 self.mox.ReplayAll()
909 config = application_configuration.ApplicationConfiguration(
910 [os.path.dirname(absnames[0])])
911 self.mox.VerifyAll()
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)
930 self.mox.ReplayAll()
931 config = application_configuration.ApplicationConfiguration(absnames)
932 self.mox.VerifyAll()
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)
955 self.mox.ReplayAll()
956 config = application_configuration.ApplicationConfiguration(absnames)
957 self.mox.VerifyAll()
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)
974 self.mox.ReplayAll()
975 self.assertRaises(errors.InvalidAppConfigError,
976 application_configuration.ApplicationConfiguration,
977 absnames)
978 self.mox.VerifyAll()
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)
993 self.mox.ReplayAll()
994 self.assertRaises(errors.InvalidAppConfigError,
995 application_configuration.ApplicationConfiguration,
996 absnames)
997 self.mox.VerifyAll()
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,
1024 [appdir])
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,
1035 [appdir])
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__':
1071 unittest.main()