49
|
1 import re
|
|
2 import os.path
|
|
3 import types
|
|
4 import codecs
|
|
5 import logging
|
|
6 import StringIO
|
|
7 from wikked.page import Page
|
|
8 from wikked.fs import PageNotFoundError
|
|
9 from wikked.db import Database
|
|
10 from wikked.indexer import WikiIndex
|
|
11 from wikked.scm import SourceControl
|
|
12
|
|
13
|
|
14 class MockWikiParameters(object):
|
|
15 def __init__(self):
|
|
16 self.formatters = {
|
|
17 self._passthrough: ['txt', 'html']
|
|
18 }
|
|
19
|
|
20 self.config_text = ""
|
|
21 self.special_filenames = []
|
51
|
22 self.use_db = False
|
49
|
23
|
|
24 self.logger_factory = lambda: logging.getLogger('wikked.tests')
|
|
25 self.page_factory = lambda wiki, url: MockPage(wiki, url)
|
|
26 self.config_factory = lambda: StringIO.StringIO(self.config_text)
|
|
27 self.fs_factory = lambda cfg: MockFileSystem()
|
|
28 self.index_factory = lambda cfg: MockWikiIndex()
|
|
29 self.db_factory = lambda cfg: MockDatabase()
|
|
30 self.scm_factory = lambda cfg: MockSourceControl()
|
|
31
|
|
32 def getSpecialFilenames(self):
|
|
33 return self.special_filenames
|
|
34
|
|
35 def _passthrough(self, text):
|
|
36 return text
|
|
37
|
|
38
|
|
39 class MockPage(Page):
|
|
40 def __init__(self, wiki, url):
|
|
41 Page.__init__(self, wiki, url)
|
|
42
|
|
43
|
|
44 class MockDatabase(Database):
|
|
45 def __init__(self, content=None, logger=None):
|
|
46 Database.__init__(self, logger)
|
|
47 self.content = content
|
51
|
48 self.conn = None
|
49
|
49 self._open_count = 0
|
|
50
|
|
51 def initDb(self):
|
|
52 pass
|
|
53
|
|
54 def open(self):
|
|
55 self._open_count += 1
|
51
|
56 self.conn = 'MOCK_CONNECTION'
|
49
|
57
|
|
58 def close(self):
|
|
59 self._open_count -= 1
|
|
60 if self._open_count < 0:
|
|
61 raise Exception(
|
|
62 "The database was closed more times than it was open.")
|
51
|
63 elif self._open_count == 0:
|
|
64 self.conn = None
|
49
|
65
|
|
66 def reset(self, pages):
|
|
67 pass
|
|
68
|
|
69 def update(self, pages):
|
|
70 pass
|
|
71
|
|
72 def getPageUrls(self, subdir=None):
|
|
73 return []
|
|
74
|
|
75 def getPages(self, subdir=None):
|
|
76 return []
|
|
77
|
|
78 def getPage(self, url):
|
|
79 return None
|
|
80
|
|
81 def pageExists(self, url):
|
|
82 return False
|
|
83
|
|
84 def getLinksTo(self, url):
|
|
85 return []
|
|
86
|
|
87
|
|
88 class MockFileSystem():
|
|
89 def __init__(self, structure=None, slugify=Page.title_to_url, logger=None):
|
|
90 if not structure:
|
|
91 structure = []
|
|
92 if not slugify:
|
|
93 slugify = lambda x: x
|
|
94 self.structure = structure
|
|
95 self.slugify = slugify
|
|
96 self.logger = logger
|
|
97 self.excluded = []
|
|
98
|
|
99 def getPageInfos(self, subdir=None):
|
|
100 node = self._getNode(subdir)
|
51
|
101 if node is None:
|
|
102 raise PageNotFoundError()
|
49
|
103 for n in self._getChildren(node):
|
|
104 yield self._getPageInfo(n)
|
|
105
|
|
106 def getPageInfo(self, path):
|
|
107 node = self._getNode(path)
|
51
|
108 if node is None:
|
|
109 raise PageNotFoundError()
|
49
|
110 return self._getPageInfo(node)
|
|
111
|
|
112 def getPage(self, url):
|
|
113 path = self._getPath(url, True)
|
|
114 node = self._getNode(path)
|
51
|
115 if node is None:
|
|
116 raise PageNotFoundError()
|
49
|
117 return self._getPageInfo(node, True)
|
|
118
|
|
119 def setPage(self, path, content):
|
51
|
120 raise NotImplementedError()
|
49
|
121
|
|
122 def pageExists(self, url):
|
51
|
123 try:
|
|
124 self._getPath(url, True)
|
|
125 return True
|
|
126 except PageNotFoundError:
|
|
127 return False
|
49
|
128
|
|
129 def getPhysicalNamespacePath(self, url):
|
51
|
130 raise NotImplementedError()
|
49
|
131
|
|
132 def _getPageInfo(self, node, with_content=False):
|
|
133 path_split = os.path.splitext(node['path'])
|
|
134 url = self.slugify(path_split[0])
|
|
135 info = {
|
|
136 'url': url,
|
|
137 'path': node['path']
|
|
138 }
|
|
139 if with_content:
|
|
140 info['content'] = node['content']
|
|
141 return info
|
|
142
|
|
143 def _getNode(self, path):
|
|
144 node = self.structure
|
|
145 if path:
|
|
146 for n in path.split('/'):
|
51
|
147 if n not in node:
|
|
148 return None
|
49
|
149 node = node[n]
|
|
150 else:
|
|
151 path = ''
|
|
152 if isinstance(node, types.StringTypes):
|
|
153 return {'type': 'file', 'path': path, 'content': node}
|
|
154 return {'type': 'dir', 'path': path, 'content': node}
|
|
155
|
|
156 def _getChildren(self, node):
|
|
157 if node['type'] != 'dir':
|
|
158 raise Exception("'%s' is not a directory." % node['path'])
|
|
159 for name in node['content']:
|
|
160 child_path = os.path.join(node['path'], name)
|
|
161 child = node['content'][name]
|
|
162 if isinstance(child, types.StringTypes):
|
|
163 yield {
|
|
164 'type': 'file',
|
|
165 'path': child_path,
|
|
166 'content': child
|
|
167 }
|
|
168 else:
|
|
169 for c in self._getChildren({
|
|
170 'type': 'dir',
|
|
171 'path': child_path,
|
|
172 'content': child
|
|
173 }):
|
|
174 yield c
|
|
175
|
|
176 def _getPath(self, url, is_file):
|
|
177 path = ''
|
|
178 current = self.structure
|
|
179 parts = unicode(url).lower().split('/')
|
|
180 for i, part in enumerate(parts):
|
|
181 for name in current:
|
|
182 name_slug = self.slugify(name)
|
|
183 if is_file and i == len(parts) - 1:
|
|
184 if re.match(r"%s\.[a-z]+" % re.escape(part), name_slug):
|
|
185 current = current[name]
|
|
186 path = os.path.join(path, name)
|
|
187 break
|
|
188 else:
|
|
189 if name_slug == part:
|
|
190 current = current[name]
|
|
191 path = os.path.join(path, name)
|
|
192 break
|
|
193 else:
|
|
194 # Failed to find a part of the URL.
|
|
195 raise PageNotFoundError("No such page: " + url)
|
|
196 return path
|
|
197
|
|
198 @staticmethod
|
|
199 def save_structure(path, structure):
|
|
200 if not os.path.isdir(path):
|
|
201 os.makedirs(path)
|
|
202 for node in structure:
|
|
203 node_path = os.path.join(path, node)
|
|
204 if isinstance(structure[node], types.StringTypes):
|
|
205 with codecs.open(node_path, 'w', encoding='utf-8') as f:
|
|
206 f.write(structure[node])
|
|
207 else:
|
|
208 MockFileSystem.save_structure(node_path, structure[node])
|
|
209
|
|
210
|
|
211 class MockWikiIndex(WikiIndex):
|
|
212 def __init__(self, logger=None):
|
|
213 WikiIndex.__init__(self, logger)
|
|
214
|
|
215 def initIndex(self):
|
|
216 pass
|
|
217
|
|
218 def reset(self, pages):
|
|
219 pass
|
|
220
|
|
221 def update(self, pages):
|
|
222 pass
|
|
223
|
|
224 def search(self, query):
|
|
225 # url, title, content_highlights
|
|
226 return None
|
|
227
|
|
228
|
|
229 class MockSourceControl(SourceControl):
|
|
230 def __init__(self, logger=None):
|
|
231 SourceControl.__init__(self, logger)
|
|
232
|
|
233 def initRepo(self):
|
|
234 pass
|
|
235
|
|
236 def getSpecialFilenames(self):
|
|
237 return []
|
|
238
|
|
239 def getHistory(self, path=None):
|
|
240 return []
|
|
241
|
|
242 def getState(self, path):
|
|
243 raise NotImplementedError()
|
|
244
|
|
245 def getRevision(self, path, rev):
|
|
246 raise NotImplementedError()
|
|
247
|
|
248 def diff(self, path, rev1, rev2):
|
|
249 raise NotImplementedError()
|
|
250
|
|
251 def commit(self, paths, op_meta):
|
|
252 raise NotImplementedError()
|
|
253
|
|
254 def revert(self, paths=None):
|
|
255 raise NotImplementedError()
|