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
6from cStringIO import StringIO
7import json
8import unittest
9from zipfile import ZipFile
10
11from compiled_file_system import CompiledFileSystem
12from content_provider import ContentProvider
13from file_system import FileNotFoundError
14from object_store_creator import ObjectStoreCreator
15from path_canonicalizer import PathCanonicalizer
16from test_file_system import TestFileSystem
17from third_party.motemplate import Motemplate
18
19_REDIRECTS_JSON = json.dumps({
20  'oldfile.html': 'storage.html',
21  'index.html': 'https://developers.google.com/chrome',
22})
23
24
25_MARKDOWN_CONTENT = (
26  ('# Header 1 #', u'<h1 id="header-1">Header 1</h1>'),
27  ('1.  Foo\n', u'<ol>\n<li>Foo</li>\n</ol>'),
28  ('![alt text](/path/img.jpg "Title")\n',
29      '<p><img alt="alt text" src="/path/img.jpg" title="Title" /></p>'),
30  ('* Unordered item 1', u'<ul>\n<li>Unordered item 1</li>\n</ul>')
31)
32
33# Test file system data which exercises many different mimetypes.
34_TEST_DATA = {
35  'dir': {
36    'a.txt': 'a.txt content',
37    'b.txt': 'b.txt content',
38    'c': {
39      'd.txt': 'd.txt content',
40    },
41  },
42  'dir2': {
43    'dir3': {
44      'a.txt': 'a.txt content',
45      'b.txt': 'b.txt content',
46      'c': {
47        'd.txt': 'd.txt content',
48      },
49    },
50  },
51  'dir4': {
52    'index.html': 'index.html content 1'
53  },
54  'dir5': {
55    'index.html': 'index.html content 2'
56  },
57  'dir6': {
58    'notindex.html': 'notindex.html content'
59  },
60  'dir7': {
61    'index.md': '\n'.join(text[0] for text in _MARKDOWN_CONTENT)
62  },
63  'dir.txt': 'dir.txt content',
64  'dir5.html': 'dir5.html content',
65  'img.png': 'img.png content',
66  'index.html': 'index.html content',
67  'read.txt': 'read.txt content',
68  'redirects.json': _REDIRECTS_JSON,
69  'noextension': 'noextension content',
70  'run.js': 'run.js content',
71  'site.css': 'site.css content',
72  'storage.html': 'storage.html content',
73  'markdown.md': '\n'.join(text[0] for text in _MARKDOWN_CONTENT)
74}
75
76
77class ContentProviderUnittest(unittest.TestCase):
78  def setUp(self):
79    self._test_file_system = TestFileSystem(_TEST_DATA)
80    self._content_provider = self._CreateContentProvider()
81
82  def _CreateContentProvider(self, supports_zip=False):
83    object_store_creator = ObjectStoreCreator.ForTest()
84    return ContentProvider(
85        'foo',
86        CompiledFileSystem.Factory(object_store_creator),
87        self._test_file_system,
88        object_store_creator,
89        default_extensions=('.html', '.md'),
90        # TODO(kalman): Test supports_templates=False.
91        supports_templates=True,
92        supports_zip=supports_zip)
93
94  def _assertContent(self, content, content_type, content_and_type):
95    # Assert type so that str is differentiated from unicode.
96    self.assertEqual(type(content), type(content_and_type.content))
97    self.assertEqual(content, content_and_type.content)
98    self.assertEqual(content_type, content_and_type.content_type)
99
100  def _assertTemplateContent(self, content, path, version):
101    content_and_type = self._content_provider.GetContentAndType(path).Get()
102    self.assertEqual(Motemplate, type(content_and_type.content))
103    content_and_type.content = content_and_type.content.source
104    self._assertContent(content, 'text/html', content_and_type)
105    self.assertEqual(version, self._content_provider.GetVersion(path).Get())
106
107  def _assertMarkdownContent(self, content, path, version):
108    content_and_type = self._content_provider.GetContentAndType(path).Get()
109    content_and_type.content = content_and_type.content.source
110    self._assertContent(content, 'text/html', content_and_type)
111    self.assertEqual(version, self._content_provider.GetVersion(path).Get())
112
113  def testPlainText(self):
114    self._assertContent(
115        u'a.txt content', 'text/plain',
116        self._content_provider.GetContentAndType('dir/a.txt').Get())
117    self._assertContent(
118        u'd.txt content', 'text/plain',
119        self._content_provider.GetContentAndType('dir/c/d.txt').Get())
120    self._assertContent(
121        u'read.txt content', 'text/plain',
122        self._content_provider.GetContentAndType('read.txt').Get())
123    self._assertContent(
124        unicode(_REDIRECTS_JSON, 'utf-8'), 'application/json',
125        self._content_provider.GetContentAndType('redirects.json').Get())
126    self._assertContent(
127        u'run.js content', 'application/javascript',
128        self._content_provider.GetContentAndType('run.js').Get())
129    self._assertContent(
130        u'site.css content', 'text/css',
131        self._content_provider.GetContentAndType('site.css').Get())
132
133  def testTemplate(self):
134    self._assertTemplateContent(u'storage.html content', 'storage.html', '0')
135    self._test_file_system.IncrementStat('storage.html')
136    self._assertTemplateContent(u'storage.html content', 'storage.html', '1')
137
138  def testImage(self):
139    self._assertContent(
140        'img.png content', 'image/png',
141        self._content_provider.GetContentAndType('img.png').Get())
142
143  def testZipTopLevel(self):
144    zip_content_provider = self._CreateContentProvider(supports_zip=True)
145    content_and_type = zip_content_provider.GetContentAndType('dir.zip').Get()
146    zipfile = ZipFile(StringIO(content_and_type.content))
147    content_and_type.content = zipfile.namelist()
148    self._assertContent(
149        ['dir/a.txt', 'dir/b.txt', 'dir/c/d.txt'], 'application/zip',
150        content_and_type)
151
152  def testZip2ndLevel(self):
153    zip_content_provider = self._CreateContentProvider(supports_zip=True)
154    content_and_type = zip_content_provider.GetContentAndType(
155        'dir2/dir3.zip').Get()
156    zipfile = ZipFile(StringIO(content_and_type.content))
157    content_and_type.content = zipfile.namelist()
158    self._assertContent(
159        ['dir3/a.txt', 'dir3/b.txt', 'dir3/c/d.txt'], 'application/zip',
160        content_and_type)
161
162  def testCanonicalZipPaths(self):
163    # Without supports_zip the path is canonicalized as a file.
164    self.assertEqual(
165        'dir.txt',
166        self._content_provider.GetCanonicalPath('dir.zip'))
167    self.assertEqual(
168        'dir.txt',
169        self._content_provider.GetCanonicalPath('diR.zip'))
170    # With supports_zip the path is canonicalized as the zip file which
171    # corresponds to the canonical directory.
172    zip_content_provider = self._CreateContentProvider(supports_zip=True)
173    self.assertEqual(
174        'dir.zip',
175        zip_content_provider.GetCanonicalPath('dir.zip'))
176    self.assertEqual(
177        'dir.zip',
178        zip_content_provider.GetCanonicalPath('diR.zip'))
179
180  def testMarkdown(self):
181    expected_content = '\n'.join(text[1] for text in _MARKDOWN_CONTENT)
182    self._assertMarkdownContent(expected_content, 'markdown', '0')
183    self._test_file_system.IncrementStat('markdown.md')
184    self._assertMarkdownContent(expected_content, 'markdown', '1')
185
186  def testNotFound(self):
187    self.assertRaises(
188        FileNotFoundError,
189        self._content_provider.GetContentAndType('oops').Get)
190
191  def testIndexRedirect(self):
192    self._assertTemplateContent(u'index.html content', '', '0')
193    self._assertTemplateContent(u'index.html content 1', 'dir4', '0')
194    self._assertTemplateContent(u'dir5.html content', 'dir5', '0')
195    self._assertMarkdownContent(
196        '\n'.join(text[1] for text in _MARKDOWN_CONTENT),
197        'dir7',
198        '0')
199    self._assertContent(
200        'noextension content', 'text/plain',
201        self._content_provider.GetContentAndType('noextension').Get())
202    self.assertRaises(
203        FileNotFoundError,
204        self._content_provider.GetContentAndType('dir6').Get)
205
206  def testRefresh(self):
207    # Not entirely sure what to test here, but get some code coverage.
208    self._content_provider.Refresh().Get()
209
210
211if __name__ == '__main__':
212  unittest.main()
213