blob: cfc3f37bbaacbb711ec7a3840126efda6776d382 [file] [log] [blame]
#!/usr/bin/env python
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Run build_server so that files needed by tests are copied to the local
# third_party directory.
import build_server
build_server.main()
import json
import optparse
import os
import posixpath
import sys
import time
import unittest
from branch_utility import BranchUtility
from chroot_file_system import ChrootFileSystem
from extensions_paths import (
CONTENT_PROVIDERS, CHROME_EXTENSIONS, PUBLIC_TEMPLATES)
from fake_fetchers import ConfigureFakeFetchers
from special_paths import SITE_VERIFICATION_FILE
from handler import Handler
from link_error_detector import LinkErrorDetector, StringifyBrokenLinks
from local_file_system import LocalFileSystem
from local_renderer import LocalRenderer
from path_util import AssertIsValid
from servlet import Request
from third_party.json_schema_compiler import json_parse
from test_util import (
ChromiumPath, DisableLogging, EnableLogging, ReadFile, Server2Path)
# Arguments set up if __main__ specifies them.
_EXPLICIT_TEST_FILES = None
_REBASE = False
_VERBOSE = False
def _ToPosixPath(os_path):
return os_path.replace(os.sep, '/')
def _FilterHidden(paths):
'''Returns a list of the non-hidden paths from |paths|.
'''
# Hidden files start with a '.' but paths like './foo' and '../foo' are not
# hidden.
return [path for path in paths if (not path.startswith('.')) or
path.startswith('./') or
path.startswith('../')]
def _GetPublicFiles():
'''Gets all public file paths mapped to their contents.
'''
def walk(path, prefix=''):
path = ChromiumPath(path)
public_files = {}
for root, dirs, files in os.walk(path, topdown=True):
relative_root = root[len(path):].lstrip(os.path.sep)
dirs[:] = _FilterHidden(dirs)
for filename in _FilterHidden(files):
with open(os.path.join(root, filename), 'r') as f:
request_path = posixpath.join(prefix, relative_root, filename)
public_files[request_path] = f.read()
return public_files
# Public file locations are defined in content_providers.json, sort of. Epic
# hack to pull them out; list all the files from the directories that
# Chromium content providers ask for.
public_files = {}
content_providers = json_parse.Parse(ReadFile(CONTENT_PROVIDERS))
for content_provider in content_providers.itervalues():
if 'chromium' in content_provider:
public_files.update(walk(content_provider['chromium']['dir'],
prefix=content_provider['serveFrom']))
return public_files
class IntegrationTest(unittest.TestCase):
def setUp(self):
ConfigureFakeFetchers()
@EnableLogging('info')
def testCronAndPublicFiles(self):
'''Runs cron then requests every public file. Cron needs to be run first
because the public file requests are offline.
'''
if _EXPLICIT_TEST_FILES is not None:
return
print('Running cron...')
start_time = time.time()
try:
response = Handler(Request.ForTest('/_cron')).Get()
if response:
self.assertEqual(200, response.status)
self.assertEqual('Success', response.content.ToString())
else:
self.fail('No response for _cron')
finally:
print('Took %s seconds' % (time.time() - start_time))
# TODO(kalman): Re-enable this, but it takes about an hour at the moment,
# presumably because every page now has a lot of links on it from the
# topnav.
#print("Checking for broken links...")
#start_time = time.time()
#link_error_detector = LinkErrorDetector(
# # TODO(kalman): Use of ChrootFileSystem here indicates a hack. Fix.
# ChrootFileSystem(LocalFileSystem.Create(), CHROME_EXTENSIONS),
# lambda path: Handler(Request.ForTest(path)).Get(),
# 'templates/public',
# ('extensions/index.html', 'apps/about_apps.html'))
#broken_links = link_error_detector.GetBrokenLinks()
#if broken_links:
# print('Found %d broken links.' % (
# len(broken_links)))
# if _VERBOSE:
# print(StringifyBrokenLinks(broken_links))
#broken_links_set = set(broken_links)
#known_broken_links_path = os.path.join(
# Server2Path('known_broken_links.json'))
#try:
# with open(known_broken_links_path, 'r') as f:
# # The JSON file converts tuples and sets into lists, and for this
# # set union/difference logic they need to be converted back.
# known_broken_links = set(tuple(item) for item in json.load(f))
#except IOError:
# known_broken_links = set()
#newly_broken_links = broken_links_set - known_broken_links
#fixed_links = known_broken_links - broken_links_set
#print('Took %s seconds.' % (time.time() - start_time))
#print('Searching for orphaned pages...')
#start_time = time.time()
#orphaned_pages = link_error_detector.GetOrphanedPages()
#if orphaned_pages:
# # TODO(jshumway): Test should fail when orphaned pages are detected.
# print('Found %d orphaned pages:' % len(orphaned_pages))
# for page in orphaned_pages:
# print(page)
#print('Took %s seconds.' % (time.time() - start_time))
public_files = _GetPublicFiles()
print('Rendering %s public files...' % len(public_files.keys()))
start_time = time.time()
try:
for path, content in public_files.iteritems():
AssertIsValid(path)
if path.endswith('redirects.json'):
continue
# The non-example html and md files are served without their file
# extensions.
path_without_ext, ext = posixpath.splitext(path)
if (ext in ('.html', '.md') and
'/examples/' not in path and
path != SITE_VERIFICATION_FILE):
path = path_without_ext
def check_result(response):
self.assertEqual(200, response.status,
'Got %s when rendering %s' % (response.status, path))
# This is reaaaaally rough since usually these will be tiny templates
# that render large files. At least it'll catch zero-length responses.
self.assertTrue(len(response.content) >= len(content),
'Rendered content length was %s vs template content length %s '
'when rendering %s' % (len(response.content), len(content), path))
check_result(Handler(Request.ForTest(path)).Get())
if path.startswith(('apps/', 'extensions/')):
# Make sure that adding the .html will temporarily redirect to
# the path without the .html for APIs and articles.
if '/examples/' not in path:
redirect_response = Handler(Request.ForTest(path + '.html')).Get()
self.assertEqual(
('/' + path, False), redirect_response.GetRedirect(),
'%s.html did not (temporarily) redirect to %s (status %s)' %
(path, path, redirect_response.status))
# Make sure including a channel will permanently redirect to the same
# path without a channel.
for channel in BranchUtility.GetAllChannelNames():
redirect_response = Handler(
Request.ForTest(posixpath.join(channel, path))).Get()
self.assertEqual(
('/' + path, True),
redirect_response.GetRedirect(),
'%s/%s did not (permanently) redirect to %s (status %s)' %
(channel, path, path, redirect_response.status))
# Samples are internationalized, test some locales.
if path.endswith('/samples'):
for lang in ('en-US', 'es', 'ar'):
check_result(Handler(Request.ForTest(
path,
headers={'Accept-Language': '%s;q=0.8' % lang})).Get())
finally:
print('Took %s seconds' % (time.time() - start_time))
#if _REBASE:
# print('Rebasing broken links with %s newly broken and %s fixed links.' %
# (len(newly_broken_links), len(fixed_links)))
# with open(known_broken_links_path, 'w') as f:
# json.dump(broken_links, f,
# indent=2, separators=(',', ': '), sort_keys=True)
#else:
# if fixed_links or newly_broken_links:
# print('**********************************************\n'
# 'CHANGE DETECTED IN BROKEN LINKS WITHOUT REBASE\n'
# '**********************************************')
# print('Found %s broken links, and some have changed. '
# 'If this is acceptable or expected then run %s with the --rebase '
# 'option.' % (len(broken_links), os.path.split(__file__)[-1]))
# elif broken_links:
# print('%s existing broken links' % len(broken_links))
# if fixed_links:
# print('%s broken links have been fixed:' % len(fixed_links))
# print(StringifyBrokenLinks(fixed_links))
# if newly_broken_links:
# print('There are %s new broken links:' % len(newly_broken_links))
# print(StringifyBrokenLinks(newly_broken_links))
# self.fail('See logging for details.')
# TODO(kalman): Move this test elsewhere, it's not an integration test.
# Perhaps like "presubmit_tests" or something.
def testExplicitFiles(self):
'''Tests just the files in _EXPLICIT_TEST_FILES.
'''
if _EXPLICIT_TEST_FILES is None:
return
for filename in _EXPLICIT_TEST_FILES:
print('Rendering %s...' % filename)
start_time = time.time()
try:
response = LocalRenderer.Render(_ToPosixPath(filename))
self.assertEqual(200, response.status)
self.assertTrue(response.content != '')
finally:
print('Took %s seconds' % (time.time() - start_time))
# TODO(jshumway): Check page for broken links (currently prohibited by the
# time it takes to render the pages).
@DisableLogging('warning')
def testFileNotFound(self):
response = LocalRenderer.Render('/extensions/notfound')
self.assertEqual(404, response.status)
def testSiteVerificationFile(self):
response = LocalRenderer.Render('/' + SITE_VERIFICATION_FILE)
self.assertEqual(200, response.status)
if __name__ == '__main__':
parser = optparse.OptionParser()
parser.add_option('-a', '--all', action='store_true', default=False,
help='Render all pages, not just the one specified')
parser.add_option('-r', '--rebase', action='store_true', default=False,
help='Rewrites the known_broken_links.json file with '
'the current set of broken links')
parser.add_option('-v', '--verbose', action='store_true', default=False,
help='Show verbose output like currently broken links')
(opts, args) = parser.parse_args()
if not opts.all:
_EXPLICIT_TEST_FILES = args
_REBASE = opts.rebase
_VERBOSE = opts.verbose
# Kill sys.argv because we have our own flags.
sys.argv = [sys.argv[0]]
unittest.main()