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