cron_servlet_test.py revision a93a17c8d99d686bd4a1511e5504e5e6cc9fcadf
1#!/usr/bin/env python 2# Copyright 2013 The Chromium Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6import unittest 7 8from appengine_wrappers import GetAppVersion 9from app_yaml_helper import AppYamlHelper 10from cron_servlet import CronServlet 11from empty_dir_file_system import EmptyDirFileSystem 12from local_file_system import LocalFileSystem 13from mock_file_system import MockFileSystem 14from servlet import Request 15from test_branch_utility import TestBranchUtility 16from test_file_system import TestFileSystem 17from test_util import EnableLogging 18 19# NOTE(kalman): The ObjectStore created by the CronServlet is backed onto our 20# fake AppEngine memcache/datastore, so the tests aren't isolated. Of course, 21# if the host file systems have different identities, they will be, sort of. 22class _TestDelegate(CronServlet.Delegate): 23 def __init__(self, create_file_system): 24 self.file_systems = [] 25 # A callback taking a revision and returning a file system. 26 self._create_file_system = create_file_system 27 self._app_version = GetAppVersion() 28 29 def CreateBranchUtility(self, object_store_creator): 30 return TestBranchUtility() 31 32 def CreateHostFileSystemForBranchAndRevision(self, branch, revision): 33 file_system = self._create_file_system(revision) 34 self.file_systems.append(file_system) 35 return file_system 36 37 def CreateAppSamplesFileSystem(self, object_store_creator): 38 return EmptyDirFileSystem() 39 40 def GetAppVersion(self): 41 return self._app_version 42 43 # (non-Delegate method). 44 def SetAppVersion(self, app_version): 45 self._app_version = app_version 46 47class CronServletTest(unittest.TestCase): 48 @EnableLogging('info') 49 def testEverything(self): 50 # All these tests are dependent (see above comment) so lump everything in 51 # the one test. 52 delegate = _TestDelegate(lambda _: MockFileSystem(LocalFileSystem.Create())) 53 54 # Test that the cron runs successfully. 55 response = CronServlet(Request.ForTest('trunk'), 56 delegate_for_test=delegate).Get() 57 self.assertEqual(1, len(delegate.file_systems)) 58 self.assertEqual(200, response.status) 59 60 # When re-running, all file systems should be Stat()d the same number of 61 # times, but the second round shouldn't have been re-Read() since the 62 # Stats haven't changed. 63 response = CronServlet(Request.ForTest('trunk'), 64 delegate_for_test=delegate).Get() 65 self.assertEqual(2, len(delegate.file_systems)) 66 self.assertTrue(*delegate.file_systems[1].CheckAndReset( 67 read_count=0, 68 stat_count=delegate.file_systems[0].GetStatCount())) 69 70 def testSafeRevision(self): 71 test_data = { 72 'docs': { 73 'examples': { 74 'examples.txt': 'examples.txt contents' 75 }, 76 'server2': { 77 'app.yaml': AppYamlHelper.GenerateAppYaml('2-0-8') 78 }, 79 'static': { 80 'static.txt': 'static.txt contents' 81 }, 82 'templates': { 83 'public': { 84 'apps': { 85 'storage.html': 'storage.html contents' 86 }, 87 'extensions': { 88 'storage.html': 'storage.html contents' 89 }, 90 } 91 } 92 } 93 } 94 95 updates = [] 96 97 def app_yaml_update(version): 98 return {'docs': {'server2': { 99 'app.yaml': AppYamlHelper.GenerateAppYaml(version) 100 }}} 101 def storage_html_update(update): 102 return {'docs': {'templates': {'public': {'apps': { 103 'storage.html': update 104 }}}}} 105 def static_txt_update(update): 106 return {'docs': {'static': { 107 'static.txt': update 108 }}} 109 110 app_yaml_path = 'docs/server2/app.yaml' 111 storage_html_path = 'docs/templates/public/apps/storage.html' 112 static_txt_path = 'docs/static/static.txt' 113 114 def create_file_system(revision): 115 '''Creates a MockFileSystem at |revision| by applying that many |updates| 116 to it. 117 ''' 118 mock_file_system = MockFileSystem(TestFileSystem(test_data)) 119 for update in updates[:revision]: 120 mock_file_system.Update(update) 121 return mock_file_system 122 123 delegate = _TestDelegate(create_file_system) 124 delegate.SetAppVersion('2-0-8') 125 126 file_systems = delegate.file_systems 127 128 # No updates applied yet. 129 CronServlet(Request.ForTest('trunk'), delegate_for_test=delegate).Get() 130 self.assertEqual(AppYamlHelper.GenerateAppYaml('2-0-8'), 131 file_systems[-1].ReadSingle(app_yaml_path)) 132 self.assertEqual('storage.html contents', 133 file_systems[-1].ReadSingle(storage_html_path)) 134 135 # Apply updates to storage.html. 136 updates.append(storage_html_update('interim contents')) 137 updates.append(storage_html_update('new contents')) 138 139 CronServlet(Request.ForTest('trunk'), delegate_for_test=delegate).Get() 140 self.assertEqual(AppYamlHelper.GenerateAppYaml('2-0-8'), 141 file_systems[-1].ReadSingle(app_yaml_path)) 142 self.assertEqual('new contents', 143 file_systems[-1].ReadSingle(storage_html_path)) 144 145 # Apply several updates to storage.html and app.yaml. The file system 146 # should be pinned at the version before app.yaml changed. 147 updates.append(storage_html_update('stuck here contents')) 148 149 double_update = storage_html_update('newer contents') 150 double_update.update(app_yaml_update('2-0-10')) 151 updates.append(double_update) 152 153 updates.append(storage_html_update('never gonna reach here')) 154 155 CronServlet(Request.ForTest('trunk'), delegate_for_test=delegate).Get() 156 self.assertEqual(AppYamlHelper.GenerateAppYaml('2-0-8'), 157 file_systems[-1].ReadSingle(app_yaml_path)) 158 self.assertEqual('stuck here contents', 159 file_systems[-1].ReadSingle(storage_html_path)) 160 161 # Further pushes to storage.html will keep it pinned. 162 updates.append(storage_html_update('y u not update!')) 163 164 CronServlet(Request.ForTest('trunk'), delegate_for_test=delegate).Get() 165 self.assertEqual(AppYamlHelper.GenerateAppYaml('2-0-8'), 166 file_systems[-1].ReadSingle(app_yaml_path)) 167 self.assertEqual('stuck here contents', 168 file_systems[-1].ReadSingle(storage_html_path)) 169 170 # Likewise app.yaml. 171 updates.append(app_yaml_update('2-1-0')) 172 173 CronServlet(Request.ForTest('trunk'), delegate_for_test=delegate).Get() 174 self.assertEqual(AppYamlHelper.GenerateAppYaml('2-0-8'), 175 file_systems[-1].ReadSingle(app_yaml_path)) 176 self.assertEqual('stuck here contents', 177 file_systems[-1].ReadSingle(storage_html_path)) 178 179 # And updates to other content won't happen either. 180 updates.append(static_txt_update('important content!')) 181 182 CronServlet(Request.ForTest('trunk'), delegate_for_test=delegate).Get() 183 self.assertEqual(AppYamlHelper.GenerateAppYaml('2-0-8'), 184 file_systems[-1].ReadSingle(app_yaml_path)) 185 self.assertEqual('stuck here contents', 186 file_systems[-1].ReadSingle(storage_html_path)) 187 self.assertEqual('static.txt contents', 188 file_systems[-1].ReadSingle(static_txt_path)) 189 190 # Lastly - when the app version changes, everything should no longer be 191 # pinned. 192 delegate.SetAppVersion('2-1-0') 193 CronServlet(Request.ForTest('trunk'), delegate_for_test=delegate).Get() 194 self.assertEqual(AppYamlHelper.GenerateAppYaml('2-1-0'), 195 file_systems[-1].ReadSingle(app_yaml_path)) 196 self.assertEqual('y u not update!', 197 file_systems[-1].ReadSingle(storage_html_path)) 198 self.assertEqual('important content!', 199 file_systems[-1].ReadSingle(static_txt_path)) 200 201if __name__ == '__main__': 202 unittest.main() 203