cron_servlet_test.py revision effb81e5f8246d0db0270817048dc992db66e9fb
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, CHROME_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          'articles': {
113            'activeTab.html': 'activeTab.html contents'
114          },
115          'intros': {
116            'browserAction.html': 'activeTab.html contents'
117          },
118          'private': {
119            'table_of_contents.html': 'table_of_contents.html contents',
120          },
121          'public': {
122            'apps': {
123              'storage.html': '<h1>storage.html</h1> contents'
124            },
125            'extensions': {
126              'storage.html': '<h1>storage.html</h1> contents'
127            },
128          },
129          'json': {
130            'chrome_sidenav.json': '{}',
131            'content_providers.json': ReadFile(CONTENT_PROVIDERS),
132            'manifest.json': '{}',
133            'permissions.json': '{}',
134            'strings.json': '{}',
135            'whats_new.json': '{}',
136          },
137        }
138      }
139    }
140
141    updates = []
142
143    def app_yaml_update(version):
144      return MoveTo(SERVER2, {
145        'app.yaml': AppYamlHelper.GenerateAppYaml(version)
146      })
147    def storage_html_update(update):
148      return MoveTo(PUBLIC_TEMPLATES, {
149        'apps': {'storage.html': update}
150      })
151    def static_txt_update(update):
152      return MoveTo(STATIC_DOCS, {
153        'static.txt': update
154      })
155
156    storage_html_path = PUBLIC_TEMPLATES + 'apps/storage.html'
157    static_txt_path = STATIC_DOCS + 'static.txt'
158
159    def create_file_system(revision=None):
160      '''Creates a MockFileSystem at |revision| by applying that many |updates|
161      to it.
162      '''
163      mock_file_system = MockFileSystem(
164          TestFileSystem(test_data, relative_to=CHROME_EXTENSIONS))
165      updates_for_revision = (
166          updates if revision is None else updates[:int(revision)])
167      for update in updates_for_revision:
168        mock_file_system.Update(update)
169      return mock_file_system
170
171    delegate = _TestDelegate(create_file_system)
172    delegate.SetAppVersion('2-0-8')
173
174    file_systems = delegate.file_systems
175
176    # No updates applied yet.
177    CronServlet(Request.ForTest('trunk'), delegate_for_test=delegate).Get()
178    self.assertEqual(AppYamlHelper.GenerateAppYaml('2-0-8'),
179                     file_systems[-1].ReadSingle(APP_YAML).Get())
180    self.assertEqual('<h1>storage.html</h1> contents',
181                     file_systems[-1].ReadSingle(storage_html_path).Get())
182
183    # Apply updates to storage.html.
184    updates.append(storage_html_update('interim contents'))
185    updates.append(storage_html_update('<h1>new</h1> contents'))
186
187    CronServlet(Request.ForTest('trunk'), delegate_for_test=delegate).Get()
188    self.assertEqual(AppYamlHelper.GenerateAppYaml('2-0-8'),
189                     file_systems[-1].ReadSingle(APP_YAML).Get())
190    self.assertEqual('<h1>new</h1> contents',
191                     file_systems[-1].ReadSingle(storage_html_path).Get())
192
193    # Apply several updates to storage.html and app.yaml. The file system
194    # should be pinned at the version before app.yaml changed.
195    updates.append(storage_html_update('<h1>stuck here</h1> contents'))
196
197    double_update = storage_html_update('<h1>newer</h1> contents')
198    double_update.update(app_yaml_update('2-0-10'))
199    updates.append(double_update)
200
201    updates.append(storage_html_update('never gonna reach here'))
202
203    CronServlet(Request.ForTest('trunk'), delegate_for_test=delegate).Get()
204    self.assertEqual(AppYamlHelper.GenerateAppYaml('2-0-8'),
205                     file_systems[-1].ReadSingle(APP_YAML).Get())
206    self.assertEqual('<h1>stuck here</h1> contents',
207                     file_systems[-1].ReadSingle(storage_html_path).Get())
208
209    # Further pushes to storage.html will keep it pinned.
210    updates.append(storage_html_update('<h1>y</h1> u not update!'))
211
212    CronServlet(Request.ForTest('trunk'), delegate_for_test=delegate).Get()
213    self.assertEqual(AppYamlHelper.GenerateAppYaml('2-0-8'),
214                     file_systems[-1].ReadSingle(APP_YAML).Get())
215    self.assertEqual('<h1>stuck here</h1> contents',
216                     file_systems[-1].ReadSingle(storage_html_path).Get())
217
218    # Likewise app.yaml.
219    updates.append(app_yaml_update('2-1-0'))
220
221    CronServlet(Request.ForTest('trunk'), delegate_for_test=delegate).Get()
222    self.assertEqual(AppYamlHelper.GenerateAppYaml('2-0-8'),
223                     file_systems[-1].ReadSingle(APP_YAML).Get())
224    self.assertEqual('<h1>stuck here</h1> contents',
225                     file_systems[-1].ReadSingle(storage_html_path).Get())
226
227    # And updates to other content won't happen either.
228    updates.append(static_txt_update('important content!'))
229
230    CronServlet(Request.ForTest('trunk'), delegate_for_test=delegate).Get()
231    self.assertEqual(AppYamlHelper.GenerateAppYaml('2-0-8'),
232                     file_systems[-1].ReadSingle(APP_YAML).Get())
233    self.assertEqual('<h1>stuck here</h1> contents',
234                     file_systems[-1].ReadSingle(storage_html_path).Get())
235    self.assertEqual('static.txt contents',
236                     file_systems[-1].ReadSingle(static_txt_path).Get())
237
238    # Lastly - when the app version changes, everything should no longer be
239    # pinned.
240    delegate.SetAppVersion('2-1-0')
241    CronServlet(Request.ForTest('trunk'), delegate_for_test=delegate).Get()
242    self.assertEqual(AppYamlHelper.GenerateAppYaml('2-1-0'),
243                     file_systems[-1].ReadSingle(APP_YAML).Get())
244    self.assertEqual('<h1>y</h1> u not update!',
245                     file_systems[-1].ReadSingle(storage_html_path).Get())
246    self.assertEqual('important content!',
247                     file_systems[-1].ReadSingle(static_txt_path).Get())
248
249
250if __name__ == '__main__':
251  unittest.main()
252