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