integration_test.py revision ca12bfac764ba476d6cd062bf1dde12cc64c3f40
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# Run build_server so that files needed by tests are copied to the local
7# third_party directory.
8import build_server
9build_server.main()
10
11from itertools import groupby
12from operator import itemgetter
13import optparse
14import os
15import posixpath
16import sys
17import time
18import unittest
19
20from branch_utility import BranchUtility
21from link_error_detector import LinkErrorDetector
22from local_file_system import LocalFileSystem
23from local_renderer import LocalRenderer
24from fake_fetchers import ConfigureFakeFetchers
25from handler import Handler
26from servlet import Request
27from test_util import EnableLogging, DisableLogging
28
29# Arguments set up if __main__ specifies them.
30_EXPLICIT_TEST_FILES = None
31
32def _ToPosixPath(os_path):
33  return os_path.replace(os.sep, '/')
34
35def _GetPublicFiles():
36  '''Gets all public files mapped to their contents.
37  '''
38  public_path = os.path.join(sys.path[0], os.pardir, 'templates', 'public')
39  public_files = {}
40  for path, dirs, files in os.walk(public_path, topdown=True):
41    dirs[:] = [d for d in dirs if d != '.svn']
42    relative_posix_path = _ToPosixPath(path[len(public_path):])
43    for filename in files:
44      with open(os.path.join(path, filename), 'r') as f:
45        public_files['/'.join((relative_posix_path, filename))] = f.read()
46  return public_files
47
48def _PrintBrokenLinks(broken_links):
49  '''Prints out broken links in a more readable format.
50  '''
51  col_width = max(len(link[0]) for link in broken_links)
52  getter = itemgetter(1)
53
54  def pretty_print(prefix, message):
55    print("%s%s -> %s" % (prefix, (col_width - len(prefix)) * ' ', message))
56
57  for target, links in groupby(sorted(broken_links, key=getter), getter):
58    links = [l[0] for l in links]
59    if len(links) > 50:
60      out = "%s and %d others" % (links[0], len(links) - 1)
61      pretty_print(out, target)
62    else:
63      for link in links:
64        pretty_print(link, target)
65
66class IntegrationTest(unittest.TestCase):
67  def setUp(self):
68    ConfigureFakeFetchers()
69
70  @EnableLogging('info')
71  def testCronAndPublicFiles(self):
72    '''Runs cron then requests every public file. Cron needs to be run first
73    because the public file requests are offline.
74    '''
75    if _EXPLICIT_TEST_FILES is not None:
76      return
77
78    print('Running cron...')
79    start_time = time.time()
80    try:
81      response = Handler(Request.ForTest('/_cron/stable')).Get()
82      self.assertEqual(200, response.status)
83      self.assertEqual('Success', response.content.ToString())
84    finally:
85      print('Took %s seconds' % (time.time() - start_time))
86
87    print("Checking for broken links...")
88    start_time = time.time()
89    link_error_detector = LinkErrorDetector(
90        LocalFileSystem(os.path.join(sys.path[0], os.pardir, os.pardir)),
91        lambda path: Handler(Request.ForTest(path)).Get(),
92        'templates/public',
93        ('extensions/index.html', 'apps/about_apps.html'))
94
95    broken_links, broken_anchors = link_error_detector.GetBrokenLinks()
96    if broken_links or broken_anchors:
97      # TODO(jshumway): Test should fail when broken links are detected.
98      print('Warning: Found %d broken links:' % (
99        len(broken_links + broken_anchors)))
100      _PrintBrokenLinks(broken_links + broken_anchors)
101
102    print('Took %s seconds.' % (time.time() - start_time))
103
104    print('Searching for orphaned pages...')
105    start_time = time.time()
106    orphaned_pages = link_error_detector.GetOrphanedPages()
107    if orphaned_pages:
108      # TODO(jshumway): Test should fail when orphaned pages are detected.
109      print('Warning: Found %d orphaned pages:' % len(orphaned_pages))
110      for page in orphaned_pages:
111        print(page)
112    print('Took %s seconds.' % (time.time() - start_time))
113
114    public_files = _GetPublicFiles()
115
116    print('Rendering %s public files...' % len(public_files.keys()))
117    start_time = time.time()
118    try:
119      for path, content in public_files.iteritems():
120        if path.endswith('redirects.json'):
121          continue
122        def check_result(response):
123          self.assertEqual(200, response.status,
124              'Got %s when rendering %s' % (response.status, path))
125          # This is reaaaaally rough since usually these will be tiny templates
126          # that render large files. At least it'll catch zero-length responses.
127          self.assertTrue(len(response.content) >= len(content),
128              'Content was "%s" when rendering %s' % (response.content, path))
129
130        check_result(Handler(Request.ForTest(path)).Get())
131
132        # Make sure that leaving out the .html will temporarily redirect to the
133        # path with the .html.
134        if path != '/404.html':
135          redirect_result = Handler(
136              Request.ForTest(posixpath.splitext(path)[0])).Get()
137          self.assertEqual((path, False), redirect_result.GetRedirect())
138
139        # Make sure including a channel will permanently redirect to the same
140        # path without a channel.
141        for channel in BranchUtility.GetAllChannelNames():
142          redirect_result = Handler(
143              Request.ForTest('%s/%s' % (channel, path))).Get()
144          self.assertEqual((path, True), redirect_result.GetRedirect())
145
146        # Samples are internationalized, test some locales.
147        if path.endswith('/samples.html'):
148          for lang in ['en-US', 'es', 'ar']:
149            check_result(Handler(Request.ForTest(
150                path,
151                headers={'Accept-Language': '%s;q=0.8' % lang})).Get())
152    finally:
153      print('Took %s seconds' % (time.time() - start_time))
154
155  # TODO(kalman): Move this test elsewhere, it's not an integration test.
156  # Perhaps like "presubmit_tests" or something.
157  def testExplicitFiles(self):
158    '''Tests just the files in _EXPLICIT_TEST_FILES.
159    '''
160    if _EXPLICIT_TEST_FILES is None:
161      return
162    for filename in _EXPLICIT_TEST_FILES:
163      print('Rendering %s...' % filename)
164      start_time = time.time()
165      try:
166        response = LocalRenderer.Render(_ToPosixPath(filename))
167        self.assertEqual(200, response.status)
168        self.assertTrue(response.content != '')
169      finally:
170        print('Took %s seconds' % (time.time() - start_time))
171
172    # TODO(jshumway): Check page for broken links (currently prohibited by the
173    # time it takes to render the pages).
174
175  @DisableLogging('warning')
176  def testFileNotFound(self):
177    response = Handler(Request.ForTest('/extensions/notfound.html')).Get()
178    self.assertEqual(404, response.status)
179
180if __name__ == '__main__':
181  parser = optparse.OptionParser()
182  parser.add_option('-a', '--all', action='store_true', default=False)
183  (opts, args) = parser.parse_args()
184  if not opts.all:
185    _EXPLICIT_TEST_FILES = args
186  # Kill sys.argv because we have our own flags.
187  sys.argv = [sys.argv[0]]
188  unittest.main()
189