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