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