Home | History | Annotate | Download | only in server2
      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 
      6 import unittest
      7 
      8 from appengine_wrappers import GetAppVersion
      9 from app_yaml_helper import AppYamlHelper
     10 from cron_servlet import CronServlet
     11 from empty_dir_file_system import EmptyDirFileSystem
     12 from host_file_system_creator import HostFileSystemCreator
     13 from local_file_system import LocalFileSystem
     14 from mock_file_system import MockFileSystem
     15 from servlet import Request
     16 from test_branch_utility import TestBranchUtility
     17 from test_file_system import TestFileSystem
     18 from 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.
     23 class _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 
     51 class 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       'docs': {
     83         'examples': {
     84           'examples.txt': 'examples.txt contents'
     85         },
     86         'server2': {
     87           'app.yaml': AppYamlHelper.GenerateAppYaml('2-0-8')
     88         },
     89         'static': {
     90           'static.txt': 'static.txt contents'
     91         },
     92         'templates': {
     93           'public': {
     94             'apps': {
     95               'storage.html': 'storage.html contents'
     96             },
     97             'extensions': {
     98               'storage.html': 'storage.html contents'
     99             },
    100           }
    101         }
    102       }
    103     }
    104 
    105     updates = []
    106 
    107     def app_yaml_update(version):
    108       return {'docs': {'server2': {
    109         'app.yaml': AppYamlHelper.GenerateAppYaml(version)
    110       }}}
    111     def storage_html_update(update):
    112       return {'docs': {'templates': {'public': {'apps': {
    113         'storage.html': update
    114       }}}}}
    115     def static_txt_update(update):
    116       return {'docs': {'static': {
    117         'static.txt': update
    118       }}}
    119 
    120     app_yaml_path = 'docs/server2/app.yaml'
    121     storage_html_path = 'docs/templates/public/apps/storage.html'
    122     static_txt_path = 'docs/static/static.txt'
    123 
    124     def create_file_system(revision=None):
    125       '''Creates a MockFileSystem at |revision| by applying that many |updates|
    126       to it.
    127       '''
    128       mock_file_system = MockFileSystem(TestFileSystem(test_data))
    129       for update in updates[:revision]:
    130         mock_file_system.Update(update)
    131       return mock_file_system
    132 
    133     delegate = _TestDelegate(create_file_system)
    134     delegate.SetAppVersion('2-0-8')
    135 
    136     file_systems = delegate.file_systems
    137 
    138     # No updates applied yet.
    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('storage.html contents',
    143                      file_systems[-1].ReadSingle(storage_html_path))
    144 
    145     # Apply updates to storage.html.
    146     updates.append(storage_html_update('interim contents'))
    147     updates.append(storage_html_update('new contents'))
    148 
    149     CronServlet(Request.ForTest('trunk'), delegate_for_test=delegate).Get()
    150     self.assertEqual(AppYamlHelper.GenerateAppYaml('2-0-8'),
    151                      file_systems[-1].ReadSingle(app_yaml_path))
    152     self.assertEqual('new contents',
    153                      file_systems[-1].ReadSingle(storage_html_path))
    154 
    155     # Apply several updates to storage.html and app.yaml. The file system
    156     # should be pinned at the version before app.yaml changed.
    157     updates.append(storage_html_update('stuck here contents'))
    158 
    159     double_update = storage_html_update('newer contents')
    160     double_update.update(app_yaml_update('2-0-10'))
    161     updates.append(double_update)
    162 
    163     updates.append(storage_html_update('never gonna reach here'))
    164 
    165     CronServlet(Request.ForTest('trunk'), delegate_for_test=delegate).Get()
    166     self.assertEqual(AppYamlHelper.GenerateAppYaml('2-0-8'),
    167                      file_systems[-1].ReadSingle(app_yaml_path))
    168     self.assertEqual('stuck here contents',
    169                      file_systems[-1].ReadSingle(storage_html_path))
    170 
    171     # Further pushes to storage.html will keep it pinned.
    172     updates.append(storage_html_update('y u not update!'))
    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     # Likewise app.yaml.
    181     updates.append(app_yaml_update('2-1-0'))
    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     # And updates to other content won't happen either.
    190     updates.append(static_txt_update('important content!'))
    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     self.assertEqual('static.txt contents',
    198                      file_systems[-1].ReadSingle(static_txt_path))
    199 
    200     # Lastly - when the app version changes, everything should no longer be
    201     # pinned.
    202     delegate.SetAppVersion('2-1-0')
    203     CronServlet(Request.ForTest('trunk'), delegate_for_test=delegate).Get()
    204     self.assertEqual(AppYamlHelper.GenerateAppYaml('2-1-0'),
    205                      file_systems[-1].ReadSingle(app_yaml_path))
    206     self.assertEqual('y u not update!',
    207                      file_systems[-1].ReadSingle(storage_html_path))
    208     self.assertEqual('important content!',
    209                      file_systems[-1].ReadSingle(static_txt_path))
    210 
    211 if __name__ == '__main__':
    212   unittest.main()
    213