15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#!/usr/bin/env python
2c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)# Copyright 2013 The Chromium Authors. All rights reserved.
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file.
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Run build_server so that files needed by tests are copied to the local
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# third_party directory.
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import build_server
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)build_server.main()
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)import optparse
12c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)import os
13ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochimport posixpath
14c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)import sys
15c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)import time
16c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)import unittest
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochfrom branch_utility import BranchUtility
19bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdochfrom link_error_detector import LinkErrorDetector, StringifyBrokenLinks
20ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochfrom local_file_system import LocalFileSystem
21c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)from local_renderer import LocalRenderer
22b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)from fake_fetchers import ConfigureFakeFetchers
23b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)from handler import Handler
24b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)from servlet import Request
25b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)from test_util import EnableLogging, DisableLogging
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
27c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)# Arguments set up if __main__ specifies them.
28c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)_EXPLICIT_TEST_FILES = None
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
30a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)def _ToPosixPath(os_path):
31a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  return os_path.replace(os.sep, '/')
32a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)
33c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)def _GetPublicFiles():
34c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  '''Gets all public files mapped to their contents.
35c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  '''
36b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  public_path = os.path.join(sys.path[0], os.pardir, 'templates', 'public')
37c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  public_files = {}
38c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for path, dirs, files in os.walk(public_path, topdown=True):
39c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    dirs[:] = [d for d in dirs if d != '.svn']
40a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    relative_posix_path = _ToPosixPath(path[len(public_path):])
41c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    for filename in files:
42c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      with open(os.path.join(path, filename), 'r') as f:
43a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        public_files['/'.join((relative_posix_path, filename))] = f.read()
44c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return public_files
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class IntegrationTest(unittest.TestCase):
47c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  def setUp(self):
48b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    ConfigureFakeFetchers()
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
50b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  @EnableLogging('info')
51c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  def testCronAndPublicFiles(self):
52c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    '''Runs cron then requests every public file. Cron needs to be run first
53c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    because the public file requests are offline.
54c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    '''
55c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if _EXPLICIT_TEST_FILES is not None:
56c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
58c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    print('Running cron...')
59c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    start_time = time.time()
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
61b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      response = Handler(Request.ForTest('/_cron/stable')).Get()
62c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      self.assertEqual(200, response.status)
63c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      self.assertEqual('Success', response.content.ToString())
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    finally:
65c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      print('Took %s seconds' % (time.time() - start_time))
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
67ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    print("Checking for broken links...")
68ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    start_time = time.time()
69ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    link_error_detector = LinkErrorDetector(
70ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        LocalFileSystem(os.path.join(sys.path[0], os.pardir, os.pardir)),
71ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        lambda path: Handler(Request.ForTest(path)).Get(),
72ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        'templates/public',
73ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        ('extensions/index.html', 'apps/about_apps.html'))
74ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
75558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    broken_links = link_error_detector.GetBrokenLinks()
76558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    if broken_links:
77ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      # TODO(jshumway): Test should fail when broken links are detected.
78ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      print('Warning: Found %d broken links:' % (
79558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch        len(broken_links)))
80bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch      print(StringifyBrokenLinks(broken_links))
81ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
82ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    print('Took %s seconds.' % (time.time() - start_time))
83ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
84ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    print('Searching for orphaned pages...')
85ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    start_time = time.time()
86ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    orphaned_pages = link_error_detector.GetOrphanedPages()
87ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    if orphaned_pages:
88ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      # TODO(jshumway): Test should fail when orphaned pages are detected.
89ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      print('Warning: Found %d orphaned pages:' % len(orphaned_pages))
90ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      for page in orphaned_pages:
91ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        print(page)
92ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    print('Took %s seconds.' % (time.time() - start_time))
93ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
94c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    public_files = _GetPublicFiles()
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
96c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    print('Rendering %s public files...' % len(public_files.keys()))
97c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    start_time = time.time()
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
99a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      for path, content in public_files.iteritems():
100eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        if path.endswith('redirects.json'):
101eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          continue
102c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        def check_result(response):
103c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          self.assertEqual(200, response.status,
104c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)              'Got %s when rendering %s' % (response.status, path))
105c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          # This is reaaaaally rough since usually these will be tiny templates
106c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          # that render large files. At least it'll catch zero-length responses.
107c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          self.assertTrue(len(response.content) >= len(content),
108c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)              'Content was "%s" when rendering %s' % (response.content, path))
109ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
110b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        check_result(Handler(Request.ForTest(path)).Get())
111ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
112ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        # Make sure that leaving out the .html will temporarily redirect to the
113ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        # path with the .html.
114ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        if path != '/404.html':
115ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch          redirect_result = Handler(
116ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch              Request.ForTest(posixpath.splitext(path)[0])).Get()
117ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch          self.assertEqual((path, False), redirect_result.GetRedirect())
118ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
119ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        # Make sure including a channel will permanently redirect to the same
120ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        # path without a channel.
121ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        for channel in BranchUtility.GetAllChannelNames():
122ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch          redirect_result = Handler(
123ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch              Request.ForTest('%s/%s' % (channel, path))).Get()
124ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch          self.assertEqual((path, True), redirect_result.GetRedirect())
125ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
126c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        # Samples are internationalized, test some locales.
127c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        if path.endswith('/samples.html'):
128c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          for lang in ['en-US', 'es', 'ar']:
129b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)            check_result(Handler(Request.ForTest(
130b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)                path,
131b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)                headers={'Accept-Language': '%s;q=0.8' % lang})).Get())
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    finally:
133c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      print('Took %s seconds' % (time.time() - start_time))
134c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
135b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  # TODO(kalman): Move this test elsewhere, it's not an integration test.
136b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  # Perhaps like "presubmit_tests" or something.
137c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  def testExplicitFiles(self):
138c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    '''Tests just the files in _EXPLICIT_TEST_FILES.
139c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    '''
140c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if _EXPLICIT_TEST_FILES is None:
141c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return
142c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    for filename in _EXPLICIT_TEST_FILES:
143c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      print('Rendering %s...' % filename)
144c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      start_time = time.time()
145c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      try:
146a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        response = LocalRenderer.Render(_ToPosixPath(filename))
147c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        self.assertEqual(200, response.status)
148c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        self.assertTrue(response.content != '')
149c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      finally:
150c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        print('Took %s seconds' % (time.time() - start_time))
151c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
152ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    # TODO(jshumway): Check page for broken links (currently prohibited by the
153ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    # time it takes to render the pages).
154ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
155c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  @DisableLogging('warning')
156c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  def testFileNotFound(self):
157b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    response = Handler(Request.ForTest('/extensions/notfound.html')).Get()
158c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    self.assertEqual(404, response.status)
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if __name__ == '__main__':
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  parser = optparse.OptionParser()
162c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  parser.add_option('-a', '--all', action='store_true', default=False)
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  (opts, args) = parser.parse_args()
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if not opts.all:
165c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    _EXPLICIT_TEST_FILES = args
166c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  # Kill sys.argv because we have our own flags.
167c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  sys.argv = [sys.argv[0]]
168c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  unittest.main()
169