- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / common / extensions / docs / server2 / patch_servlet_test.py
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 from HTMLParser import HTMLParser
7 import unittest
8
9 from empty_dir_file_system import EmptyDirFileSystem
10 from fake_fetchers import ConfigureFakeFetchers
11 from github_file_system_provider import GithubFileSystemProvider
12 from host_file_system_provider import HostFileSystemProvider
13 from patch_servlet import PatchServlet
14 from render_servlet import RenderServlet
15 from server_instance import ServerInstance
16 from servlet import Request
17 from test_branch_utility import TestBranchUtility
18 from test_util import DisableLogging
19
20
21
22 _ALLOWED_HOST = 'https://chrome-apps-doc.appspot.com'
23
24
25 def _CheckURLsArePatched(content, patch_servlet_path):
26   errors = []
27   class LinkChecker(HTMLParser):
28     def handle_starttag(self, tag, attrs):
29       if tag != 'a':
30         return
31       tag_description = '<a %s .../>' % ' '.join('%s="%s"' % (key, val)
32                                                  for key, val in attrs)
33       attrs = dict(attrs)
34       if ('href' in attrs and
35            attrs['href'].startswith('/') and
36            not attrs['href'].startswith('/%s/' % patch_servlet_path)):
37         errors.append('%s has an unqualified href' % tag_description)
38   LinkChecker().feed(content)
39   return errors
40
41
42 class _RenderServletDelegate(RenderServlet.Delegate):
43   def CreateServerInstance(self):
44     return ServerInstance.ForLocal()
45
46 class _PatchServletDelegate(RenderServlet.Delegate):
47   def CreateBranchUtility(self, object_store_creator):
48     return TestBranchUtility.CreateWithCannedData()
49
50   def CreateHostFileSystemProvider(self, object_store_creator, **optargs):
51     return HostFileSystemProvider.ForLocal(object_store_creator, **optargs)
52
53   def CreateGithubFileSystemProvider(self, object_store_creator):
54     return GithubFileSystemProvider.ForEmpty()
55
56
57 class PatchServletTest(unittest.TestCase):
58   def setUp(self):
59     ConfigureFakeFetchers()
60
61   def _RenderWithPatch(self, path, issue):
62     path_with_issue = '%s/%s' % (issue, path)
63     return PatchServlet(Request.ForTest(path_with_issue, host=_ALLOWED_HOST),
64                         _PatchServletDelegate()).Get()
65
66   def _RenderWithoutPatch(self, path):
67     return RenderServlet(Request.ForTest(path, host=_ALLOWED_HOST),
68                          _RenderServletDelegate()).Get()
69
70   def _RenderAndCheck(self, path, issue, expected_equal):
71     '''Renders |path| with |issue| patched in and asserts that the result is
72     the same as |expected_equal| modulo any links that get rewritten to
73     "_patch/issue".
74     '''
75     patched_response = self._RenderWithPatch(path, issue)
76     unpatched_response = self._RenderWithoutPatch(path)
77     patched_response.headers.pop('cache-control', None)
78     unpatched_response.headers.pop('cache-control', None)
79     unpatched_content = unpatched_response.content.ToString()
80
81     # Check that all links in the patched content are qualified with
82     # the patch URL, then strip them out for checking (in)equality.
83     patched_content = patched_response.content.ToString()
84     patch_servlet_path = '_patch/%s' % issue
85     errors = _CheckURLsArePatched(patched_content, patch_servlet_path)
86     self.assertFalse(errors,
87         '%s\nFound errors:\n * %s' % (patched_content, '\n * '.join(errors)))
88     patched_content = patched_content.replace('/%s' % patch_servlet_path, '')
89
90     self.assertEqual(patched_response.status, unpatched_response.status)
91     self.assertEqual(patched_response.headers, unpatched_response.headers)
92     if expected_equal:
93       self.assertEqual(patched_content, unpatched_content)
94     else:
95       self.assertNotEqual(patched_content, unpatched_content)
96
97   def _RenderAndAssertEqual(self, path, issue):
98     self._RenderAndCheck(path, issue, True)
99
100   def _RenderAndAssertNotEqual(self, path, issue):
101     self._RenderAndCheck(path, issue, False)
102
103   @DisableLogging('warning')
104   def _AssertNotFound(self, path, issue):
105     response = self._RenderWithPatch(path, issue)
106     self.assertEqual(response.status, 404,
107         'Path %s with issue %s should have been removed for %s.' % (
108             path, issue, response))
109
110   def _AssertOk(self, path, issue):
111     response = self._RenderWithPatch(path, issue)
112     self.assertEqual(response.status, 200,
113         'Failed to render path %s with issue %s.' % (path, issue))
114     self.assertTrue(len(response.content.ToString()) > 0,
115         'Rendered result for path %s with issue %s should not be empty.' %
116         (path, issue))
117
118   def _AssertRedirect(self, path, issue, redirect_path):
119     response = self._RenderWithPatch(path, issue)
120     self.assertEqual(302, response.status)
121     self.assertEqual('/_patch/%s/%s' % (issue, redirect_path),
122                      response.headers['Location'])
123
124   def testRender(self):
125     # '_patch' is not included in paths below because it's stripped by Handler.
126     issue = '14096030'
127
128     # extensions_sidenav.json is modified in the patch.
129     self._RenderAndAssertNotEqual('extensions/index.html', issue)
130
131     # apps_sidenav.json is not patched.
132     self._RenderAndAssertEqual('apps/about_apps.html', issue)
133
134     # extensions/runtime.html is removed in the patch, should redirect to the
135     # apps version.
136     self._AssertRedirect('extensions/runtime.html', issue,
137                          'apps/runtime.html')
138
139     # apps/runtime.html is not removed.
140     self._RenderAndAssertEqual('apps/runtime.html', issue)
141
142     # test_foo.html is added in the patch.
143     self._AssertOk('extensions/test_foo.html', issue)
144
145     # Invalid issue number results in a 404.
146     self._AssertNotFound('extensions/index.html', '11111')
147
148   def testXssRedirect(self):
149     def is_redirect(from_host, from_path, to_url):
150       response = PatchServlet(Request.ForTest(from_path, host=from_host),
151                               _PatchServletDelegate()).Get()
152       redirect_url, _ = response.GetRedirect()
153       if redirect_url is None:
154         return (False, '%s/%s did not cause a redirect' % (
155             from_host, from_path))
156       if redirect_url != to_url:
157         return (False, '%s/%s redirected to %s not %s' % (
158             from_host, from_path, redirect_url, to_url))
159       return (True, '%s/%s redirected to %s' % (
160           from_host, from_path, redirect_url))
161     self.assertTrue(*is_redirect('http://developer.chrome.com', '12345',
162                                  '%s/_patch/12345' % _ALLOWED_HOST))
163     self.assertTrue(*is_redirect('http://developers.google.com', '12345',
164                                  '%s/_patch/12345' % _ALLOWED_HOST))
165     self.assertFalse(*is_redirect('http://chrome-apps-doc.appspot.com', '12345',
166                                   None))
167     self.assertFalse(*is_redirect('http://some-other-app.appspot.com', '12345',
168                                   None))
169
170 if __name__ == '__main__':
171   unittest.main()