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