Merge lp:~diegosarmentero/ubuntuone-client/search-filter into lp:ubuntuone-client
- search-filter
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Diego Sarmentero |
Approved revision: | 1352 |
Merged at revision: | 1350 |
Proposed branch: | lp:~diegosarmentero/ubuntuone-client/search-filter |
Merge into: | lp:ubuntuone-client |
Diff against target: |
507 lines (+324/-1) 14 files modified
contrib/testing/testcase.py (+2/-0) tests/platform/ipc/test_external_interface.py (+9/-0) tests/platform/test_tools.py (+20/-0) tests/syncdaemon/test_files_search.py (+87/-0) tests/syncdaemon/test_fsm.py (+107/-0) ubuntuone/platform/ipc/ipc_client.py (+4/-0) ubuntuone/platform/ipc/linux.py (+6/-0) ubuntuone/platform/ipc/perspective_broker.py (+5/-0) ubuntuone/platform/tools/__init__.py (+5/-0) ubuntuone/syncdaemon/files_search.py (+48/-0) ubuntuone/syncdaemon/filesystem_manager.py (+17/-1) ubuntuone/syncdaemon/interaction_interfaces.py (+6/-0) ubuntuone/syncdaemon/main.py (+3/-0) ubuntuone/syncdaemon/volume_manager.py (+5/-0) |
To merge this branch: | bzr merge lp:~diegosarmentero/ubuntuone-client/search-filter |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Mike McCracken (community) | Approve | ||
Roberto Alsina (community) | Approve | ||
Review via email: mp+130862@code.launchpad.net |
Description of the change
This will be used from control panel, instead of having the data of the files inside u1 duplicated there, and this will be fix the problems to keep that data up to date, and check if the files are already in the server (as the two bug reports describe).
- 1348. By Diego Sarmentero
-
adding missing files
- 1349. By Diego Sarmentero
-
delete unnecesary fake
- 1350. By Diego Sarmentero
-
removing line for testing purposes
- 1351. By Diego Sarmentero
-
docstring fixed
Roberto Alsina (ralsina) : | # |
- 1352. By Diego Sarmentero
-
fix string
Mike McCracken (mikemc) wrote : | # |
Hmm, my example test is kind of dumb, since it's pointing out something in the test, not in the method it's testing… I should probably have put it in test_files_
Mike McCracken (mikemc) wrote : | # |
I've changed my mind.
For reference: I'm OK with matching fuzzy things, and I was mostly concerned with the order in which they appear, I wanted to be sure the least-surprising match showed up first.
However the only example I found where that might happen was if we have a search pattern with a space, and the files list has a pattern with no space that matches that pattern because of our fuzzy changes. In this case, though, when we sort the matching paths at the end of get_paths_
So even though using multiple regexes to control the order of matches is only a little slower in real terms, I can't think of a case in which it's necessary.
Preview Diff
1 | === modified file 'contrib/testing/testcase.py' |
2 | --- contrib/testing/testcase.py 2012-10-02 19:52:03 +0000 |
3 | +++ contrib/testing/testcase.py 2012-10-23 17:07:21 +0000 |
4 | @@ -50,6 +50,7 @@ |
5 | config, |
6 | action_queue, |
7 | event_queue, |
8 | + files_search, |
9 | filesystem_manager as fs_manager, |
10 | interaction_interfaces, |
11 | interfaces, |
12 | @@ -254,6 +255,7 @@ |
13 | |
14 | self.eventlog_listener = None |
15 | self.status_listener = FakeStatusListener() |
16 | + self.search = files_search.SearchFiles(self.fs) |
17 | |
18 | def _connect_aq(self, _): |
19 | """Connect the fake action queue.""" |
20 | |
21 | === modified file 'tests/platform/ipc/test_external_interface.py' |
22 | --- tests/platform/ipc/test_external_interface.py 2012-08-10 12:49:46 +0000 |
23 | +++ tests/platform/ipc/test_external_interface.py 2012-10-23 17:07:21 +0000 |
24 | @@ -275,6 +275,15 @@ |
25 | in_signature='s', out_signature='a{ss}') |
26 | |
27 | @defer.inlineCallbacks |
28 | + def test_search_files(self): |
29 | + """Test get_metadata.""" |
30 | + result = ['path'] |
31 | + yield self.assert_method_called(self.service.file_system, |
32 | + 'search_files', result, 'file') |
33 | + self.assert_remote_method('search_files', |
34 | + in_signature='s', out_signature='as') |
35 | + |
36 | + @defer.inlineCallbacks |
37 | def test_get_metadata_by_node(self): |
38 | """Test get_metadata_by_node.""" |
39 | result = {'node_id': 'test'} |
40 | |
41 | === modified file 'tests/platform/test_tools.py' |
42 | --- tests/platform/test_tools.py 2012-08-10 12:49:46 +0000 |
43 | +++ tests/platform/test_tools.py 2012-10-23 17:07:21 +0000 |
44 | @@ -203,6 +203,26 @@ |
45 | self.assertEqual('node_id', result['node_id']) |
46 | |
47 | @defer.inlineCallbacks |
48 | + def test_search_files(self): |
49 | + """Check that get_metadata works as expected.""" |
50 | + mdid = 'id' |
51 | + path = os.path.join(self.root_dir, u'path/to/file_test') |
52 | + mdobj = {'server_hash': 'asdqwe123'} |
53 | + mdid2 = 'id2' |
54 | + path2 = os.path.join(self.root_dir, u'path/to/my_files') |
55 | + mdobj2 = {'server_hash': 'asdqwe456'} |
56 | + mdid3 = 'id3' |
57 | + path3 = u'/home2/to/my_files' |
58 | + mdobj3 = {'server_hash': 'asdqwe456'} |
59 | + self.fs._idx_path = {path: mdid, path2: mdid2, path3: mdid3} |
60 | + self.fs.fs = {mdid: mdobj, mdid2: mdobj2, mdid3: mdobj3} |
61 | + |
62 | + result = yield self.tool.search_files('file') |
63 | + expected = [os.path.join(self.root_dir, 'path/to/file_test'), |
64 | + os.path.join(self.root_dir, 'path/to/my_files')] |
65 | + self.assertEqual(result, expected) |
66 | + |
67 | + @defer.inlineCallbacks |
68 | def test_quit_when_running(self): |
69 | """Test the quit method when the daemon is running.""" |
70 | self.patch(tools, 'is_already_running', |
71 | |
72 | === added file 'tests/syncdaemon/test_files_search.py' |
73 | --- tests/syncdaemon/test_files_search.py 1970-01-01 00:00:00 +0000 |
74 | +++ tests/syncdaemon/test_files_search.py 2012-10-23 17:07:21 +0000 |
75 | @@ -0,0 +1,87 @@ |
76 | +# -*- coding: utf-8 *-* |
77 | +# |
78 | +# Copyright 2012 Canonical Ltd. |
79 | +# |
80 | +# This program is free software: you can redistribute it and/or modify it |
81 | +# under the terms of the GNU General Public License version 3, as published |
82 | +# by the Free Software Foundation. |
83 | +# |
84 | +# This program is distributed in the hope that it will be useful, but |
85 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
86 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
87 | +# PURPOSE. See the GNU General Public License for more details. |
88 | +# |
89 | +# You should have received a copy of the GNU General Public License along |
90 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
91 | +# |
92 | +# In addition, as a special exception, the copyright holders give |
93 | +# permission to link the code of portions of this program with the |
94 | +# OpenSSL library under certain conditions as described in each |
95 | +# individual source file, and distribute linked combinations |
96 | +# including the two. |
97 | +# You must obey the GNU General Public License in all respects |
98 | +# for all of the code used other than OpenSSL. If you modify |
99 | +# file(s) with this exception, you may extend this exception to your |
100 | +# version of the file(s), but you are not obligated to do so. If you |
101 | +# do not wish to do so, delete this exception statement from your |
102 | +# version. If you delete this exception statement from all source |
103 | +# files in the program, then also delete it here. |
104 | +"""Test the files search module.""" |
105 | + |
106 | +from twisted.internet import defer |
107 | +from twisted.trial.unittest import TestCase |
108 | + |
109 | +from ubuntuone.syncdaemon import files_search |
110 | + |
111 | + |
112 | +class FakeVolumeManager(object): |
113 | + """Fake VolumeManager.""" |
114 | + |
115 | + def get_folders(self): |
116 | + """Return the u1 folders.""" |
117 | + return ('/home/u1/', '/home/.local/udfs/', '/home/.local/shared', |
118 | + '/home/.local/shares') |
119 | + |
120 | + |
121 | +class FakeFilesystemManager(object): |
122 | + """Fake FilesystemManager.""" |
123 | + |
124 | + def __init__(self): |
125 | + self.pattern = '' |
126 | + self.vm = FakeVolumeManager() |
127 | + |
128 | + def get_paths_by_pattern(self, pattern): |
129 | + """Store the pattern received.""" |
130 | + self.pattern = pattern |
131 | + |
132 | + |
133 | +class FilesystemManagerTestCase(TestCase): |
134 | + """Test the SyncMenu.""" |
135 | + |
136 | + @defer.inlineCallbacks |
137 | + def setUp(self): |
138 | + yield super(FilesystemManagerTestCase, self).setUp() |
139 | + self.fsm = FakeFilesystemManager() |
140 | + self.search = files_search.SearchFiles(self.fsm) |
141 | + root, udf_dir, shared_dir, _ = self.fsm.vm.get_folders() |
142 | + paths = "(%s|%s|%s)" % (root, udf_dir, shared_dir) |
143 | + self.basic_pattern = paths + ".*/.*%s.*$" |
144 | + |
145 | + def test_simple_string(self): |
146 | + """Check the pattern is the same string.""" |
147 | + pattern = 'filename' |
148 | + self.search.search(pattern) |
149 | + expected = self.basic_pattern % pattern |
150 | + self.assertEqual(expected, self.fsm.pattern) |
151 | + |
152 | + def test_escaped_string(self): |
153 | + """Check the pattern is the same string.""" |
154 | + self.search.search('file_name') |
155 | + expected = self.basic_pattern % 'file\\_name' |
156 | + self.assertEqual(expected, self.fsm.pattern) |
157 | + |
158 | + def test_string_with_space(self): |
159 | + """Check the pattern is the same string.""" |
160 | + self.search.search('file name') |
161 | + expected = self.basic_pattern % 'file.+name' |
162 | + self.assertEqual(expected, self.fsm.pattern) |
163 | |
164 | === modified file 'tests/syncdaemon/test_fsm.py' |
165 | --- tests/syncdaemon/test_fsm.py 2012-08-08 13:21:13 +0000 |
166 | +++ tests/syncdaemon/test_fsm.py 2012-10-23 17:07:21 +0000 |
167 | @@ -32,6 +32,7 @@ |
168 | |
169 | import errno |
170 | import os |
171 | +import re |
172 | import time |
173 | |
174 | from mocker import MockerTestCase, ANY |
175 | @@ -4299,3 +4300,109 @@ |
176 | self.mocker.result(path) |
177 | self.mocker.replay() |
178 | self.assertRaises(KeyError, self.fsm.dir_content, path) |
179 | + |
180 | + |
181 | +class FSMSearchTestCase(BaseTwistedTestCase): |
182 | + """Base test case for FSM.""" |
183 | + |
184 | + @defer.inlineCallbacks |
185 | + def setUp(self): |
186 | + """Setup the test.""" |
187 | + yield super(FSMSearchTestCase, self).setUp() |
188 | + self.shares_dir = self.mktemp('shares') |
189 | + self.root_dir = self.mktemp('root') |
190 | + self.fsmdir = self.mktemp("fsmdir") |
191 | + self.partials_dir = self.mktemp("partials") |
192 | + self.tritcask_path = self.mktemp("tritcask") |
193 | + |
194 | + self.db = Tritcask(self.tritcask_path) |
195 | + self.addCleanup(self.db.shutdown) |
196 | + self.fsm = FileSystemManager(self.fsmdir, self.partials_dir, |
197 | + FakeVolumeManager(self.root_dir), self.db) |
198 | + |
199 | + def test_get_paths_by_pattern(self): |
200 | + """Check that we obtain the files that correspond to the filter.""" |
201 | + mdid = 'id' |
202 | + path = '/my/path/to/file_test' |
203 | + mdobj = {'server_hash': 'asdqwe123'} |
204 | + mdid2 = 'id2' |
205 | + path2 = '/my/path/to/my_photos' |
206 | + mdobj2 = {'server_hash': 'asdqwe456'} |
207 | + self.fsm._idx_path = {path: mdid, path2: mdid2} |
208 | + self.fsm.fs = {mdid: mdobj, mdid2: mdobj2} |
209 | + expected = ['/my/path/to/file_test'] |
210 | + # expectations |
211 | + paths = "(%s|%s)" % ('/my/path/', '/home/') |
212 | + pattern = paths + ".*/.*%s.*$" |
213 | + keywords = '.+'.join(re.escape('file').split('\\ ')) |
214 | + search = pattern % keywords |
215 | + result = self.fsm.get_paths_by_pattern(search) |
216 | + self.assertEqual(result, expected) |
217 | + |
218 | + def test_get_paths_by_pattern_valid_folders(self): |
219 | + """Search for the pattern in the valid folders.""" |
220 | + mdid = 'id' |
221 | + path = '/my/path/to/file_test' |
222 | + mdobj = {'server_hash': 'asdqwe123'} |
223 | + mdid2 = 'id2' |
224 | + path2 = '/home/to/my_files' |
225 | + mdobj2 = {'server_hash': 'asdqwe456'} |
226 | + mdid3 = 'id3' |
227 | + path3 = '/home2/to/my_files' |
228 | + mdobj3 = {'server_hash': 'asdqwe456'} |
229 | + self.fsm._idx_path = {path: mdid, path2: mdid2, path3: mdid3} |
230 | + self.fsm.fs = {mdid: mdobj, mdid2: mdobj2, mdid3: mdobj3} |
231 | + expected = ['/home/to/my_files', '/my/path/to/file_test'] |
232 | + # expectations |
233 | + paths = "(%s|%s)" % ('/my/path/', '/home/') |
234 | + pattern = paths + ".*/.*%s.*$" |
235 | + keywords = '.+'.join(re.escape('file').split('\\ ')) |
236 | + search = pattern % keywords |
237 | + result = self.fsm.get_paths_by_pattern(search) |
238 | + self.assertEqual(result, expected) |
239 | + |
240 | + def test_get_paths_by_pattern_not_in_server(self): |
241 | + """Check that we ignore the files that are not still in the server..""" |
242 | + mdid = 'id' |
243 | + path = '/my/path/to/file_test' |
244 | + mdobj = {'server_hash': 'asdqwe123'} |
245 | + mdid2 = 'id2' |
246 | + path2 = '/home/to/my_files' |
247 | + mdobj2 = {'server_hash': ''} |
248 | + mdid3 = 'id3' |
249 | + path3 = '/home2/to/my_files' |
250 | + mdobj3 = {'server_hash': 'asdqwe456'} |
251 | + self.fsm._idx_path = {path: mdid, path2: mdid2, path3: mdid3} |
252 | + self.fsm.fs = {mdid: mdobj, mdid2: mdobj2, mdid3: mdobj3} |
253 | + expected = ['/my/path/to/file_test'] |
254 | + # expectations |
255 | + paths = "(%s|%s)" % ('/my/path/', '/home/') |
256 | + pattern = paths + ".*/.*%s.*$" |
257 | + keywords = '.+'.join(re.escape('file').split('\\ ')) |
258 | + search = pattern % keywords |
259 | + result = self.fsm.get_paths_by_pattern(search) |
260 | + self.assertEqual(result, expected) |
261 | + |
262 | + def test_get_paths_by_pattern_sorted_result(self): |
263 | + """Search that we obtained the paths sorted..""" |
264 | + mdid = 'id' |
265 | + path = '/my/path/to/file_test' |
266 | + mdobj = {'server_hash': 'asdqwe123'} |
267 | + mdid2 = 'id2' |
268 | + path2 = '/home/to/my_files' |
269 | + mdobj2 = {'server_hash': 'asdqwe456'} |
270 | + mdid3 = 'id3' |
271 | + path3 = '/home2/to/my_files' |
272 | + mdobj3 = {'server_hash': 'asdqwe456'} |
273 | + self.fsm._idx_path = {path: mdid, path2: mdid2, path3: mdid3} |
274 | + self.fsm.fs = {mdid: mdobj, mdid2: mdobj2, mdid3: mdobj3} |
275 | + expected = ['/my/path/to/file_test', '/home/to/my_files'] |
276 | + # expectations |
277 | + paths = "(%s|%s)" % ('/my/path/', '/home/') |
278 | + pattern = paths + ".*/.*%s.*$" |
279 | + keywords = '.+'.join(re.escape('file').split('\\ ')) |
280 | + search = pattern % keywords |
281 | + result = self.fsm.get_paths_by_pattern(search) |
282 | + self.assertNotEqual(result, expected) |
283 | + expected = sorted(expected) |
284 | + self.assertEqual(result, expected) |
285 | \ No newline at end of file |
286 | |
287 | === modified file 'ubuntuone/platform/ipc/ipc_client.py' |
288 | --- ubuntuone/platform/ipc/ipc_client.py 2012-09-06 22:19:37 +0000 |
289 | +++ ubuntuone/platform/ipc/ipc_client.py 2012-10-23 17:07:21 +0000 |
290 | @@ -213,6 +213,10 @@ |
291 | * int: bytes written |
292 | """ |
293 | |
294 | + @remote |
295 | + def search_files(self, name): |
296 | + """Returns a list of the files that contain name in the path.""" |
297 | + |
298 | @signal |
299 | def on_content_queue_changed(self): |
300 | """Emit ContentQueueChanged.""" |
301 | |
302 | === modified file 'ubuntuone/platform/ipc/linux.py' |
303 | --- ubuntuone/platform/ipc/linux.py 2012-08-13 14:34:36 +0000 |
304 | +++ ubuntuone/platform/ipc/linux.py 2012-10-23 17:07:21 +0000 |
305 | @@ -411,6 +411,12 @@ |
306 | """Return a list of dirty nodes.""" |
307 | return self.service.file_system.get_dirty_nodes() |
308 | |
309 | + @dbus.service.method(DBUS_IFACE_FS_NAME, |
310 | + in_signature='s', out_signature='as') |
311 | + def search_files(self, pattern): |
312 | + """Return the files (as a list) that contain pattern in the path.""" |
313 | + return self.service.file_system.search_files(pattern) |
314 | + |
315 | |
316 | class Shares(DBusExposedObject): |
317 | """An interface to interact with shares.""" |
318 | |
319 | === modified file 'ubuntuone/platform/ipc/perspective_broker.py' |
320 | --- ubuntuone/platform/ipc/perspective_broker.py 2012-09-06 22:19:37 +0000 |
321 | +++ ubuntuone/platform/ipc/perspective_broker.py 2012-10-23 17:07:21 +0000 |
322 | @@ -491,6 +491,7 @@ |
323 | 'get_metadata_by_node', |
324 | 'get_metadata_and_quick_tree_synced', |
325 | 'get_dirty_nodes', |
326 | + 'search_files', |
327 | ] |
328 | |
329 | def get_metadata(self, path): |
330 | @@ -515,6 +516,10 @@ |
331 | """Return a list of dirty nodes.""" |
332 | return self.service.file_system.get_dirty_nodes() |
333 | |
334 | + def search_files(self, pattern): |
335 | + """Search for the occurrence of pattern in the files names.""" |
336 | + return self.service.file_system.search_files(pattern) |
337 | + |
338 | |
339 | class Shares(IPCExposedObject): |
340 | """An interface to interact with shares.""" |
341 | |
342 | === modified file 'ubuntuone/platform/tools/__init__.py' |
343 | --- ubuntuone/platform/tools/__init__.py 2012-10-05 12:44:28 +0000 |
344 | +++ ubuntuone/platform/tools/__init__.py 2012-10-23 17:07:21 +0000 |
345 | @@ -482,6 +482,11 @@ |
346 | """Get metadata for 'path'.""" |
347 | return self.proxy.call_method('file_system', 'get_metadata', path) |
348 | |
349 | + @log_call(logger.debug) |
350 | + def search_files(self, pattern): |
351 | + """Get the files that matches the pattern.""" |
352 | + return self.proxy.call_method('file_system', 'search_files', pattern) |
353 | + |
354 | @defer.inlineCallbacks |
355 | @log_call(logger.debug) |
356 | def change_public_access(self, path, is_public): |
357 | |
358 | === added file 'ubuntuone/syncdaemon/files_search.py' |
359 | --- ubuntuone/syncdaemon/files_search.py 1970-01-01 00:00:00 +0000 |
360 | +++ ubuntuone/syncdaemon/files_search.py 2012-10-23 17:07:21 +0000 |
361 | @@ -0,0 +1,48 @@ |
362 | +# -*- coding: utf-8 -*- |
363 | +# |
364 | +# Copyright 2011-2012 Canonical Ltd. |
365 | +# |
366 | +# This program is free software: you can redistribute it and/or modify it |
367 | +# under the terms of the GNU General Public License version 3, as published |
368 | +# by the Free Software Foundation. |
369 | +# |
370 | +# This program is distributed in the hope that it will be useful, but |
371 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
372 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
373 | +# PURPOSE. See the GNU General Public License for more details. |
374 | +# |
375 | +# You should have received a copy of the GNU General Public License along |
376 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
377 | +# |
378 | +# In addition, as a special exception, the copyright holders give |
379 | +# permission to link the code of portions of this program with the |
380 | +# OpenSSL library under certain conditions as described in each |
381 | +# individual source file, and distribute linked combinations |
382 | +# including the two. |
383 | +# You must obey the GNU General Public License in all respects |
384 | +# for all of the code used other than OpenSSL. If you modify |
385 | +# file(s) with this exception, you may extend this exception to your |
386 | +# version of the file(s), but you are not obligated to do so. If you |
387 | +# do not wish to do so, delete this exception statement from your |
388 | +# version. If you delete this exception statement from all source |
389 | +# files in the program, then also delete it here. |
390 | +"""Searchs for files inside the U1 folder.""" |
391 | + |
392 | +import re |
393 | + |
394 | + |
395 | +class SearchFiles(object): |
396 | + """Seearch for specific patterns in the U1 files names.""" |
397 | + |
398 | + def __init__(self, fs): |
399 | + self.fs = fs |
400 | + root, udf_dir, shared_dir, _ = self.fs.vm.get_folders() |
401 | + paths = "(%s|%s|%s)" % (root, udf_dir, shared_dir) |
402 | + self.pattern = paths + ".*/.*%s.*$" |
403 | + |
404 | + def search(self, pattern=''): |
405 | + """Search for the files that contains name.""" |
406 | + keywords = '.+'.join(re.escape(pattern).split('\\ ')) |
407 | + search = self.pattern % keywords |
408 | + results = self.fs.get_paths_by_pattern(search) |
409 | + return results |
410 | \ No newline at end of file |
411 | |
412 | === modified file 'ubuntuone/syncdaemon/filesystem_manager.py' |
413 | --- ubuntuone/syncdaemon/filesystem_manager.py 2012-08-08 13:21:13 +0000 |
414 | +++ ubuntuone/syncdaemon/filesystem_manager.py 2012-10-23 17:07:21 +0000 |
415 | @@ -33,6 +33,7 @@ |
416 | from __future__ import with_statement |
417 | |
418 | import os |
419 | +import re |
420 | import time |
421 | import functools |
422 | import itertools |
423 | @@ -324,7 +325,7 @@ |
424 | |
425 | def __init__(self, data_dir, partials_dir, vm, db): |
426 | if not isinstance(data_dir, basestring): |
427 | - raise TypeError("data_dir should be a string instead of %s" % \ |
428 | + raise TypeError("data_dir should be a string instead of %s" % |
429 | type(data_dir)) |
430 | fsmdir = os.path.join(data_dir, 'fsm') |
431 | self._trash_dir = os.path.join(data_dir, 'trash') |
432 | @@ -1363,6 +1364,21 @@ |
433 | |
434 | return all_paths |
435 | |
436 | + def get_paths_by_pattern(self, search): |
437 | + """Return a list of paths that match the pattern.""" |
438 | + pattern = re.compile(search) |
439 | + |
440 | + def _get_matching(): |
441 | + """Find the paths that match""" |
442 | + for p, m in self._idx_path.iteritems(): |
443 | + #basename = os.path.basename(p) |
444 | + if pattern.match(p): |
445 | + mdobj = self.fs[m] |
446 | + if mdobj["server_hash"]: |
447 | + yield p |
448 | + |
449 | + return sorted(_get_matching()) |
450 | + |
451 | def delete_to_trash(self, mdid, parent_id): |
452 | """Move the node to the trash.""" |
453 | mdobj = self.fs[mdid] |
454 | |
455 | === modified file 'ubuntuone/syncdaemon/interaction_interfaces.py' |
456 | --- ubuntuone/syncdaemon/interaction_interfaces.py 2012-09-14 22:46:21 +0000 |
457 | +++ ubuntuone/syncdaemon/interaction_interfaces.py 2012-10-23 17:07:21 +0000 |
458 | @@ -401,6 +401,12 @@ |
459 | dirty_nodes.append(self._mdobj_dict(mdobj)) |
460 | return dirty_nodes |
461 | |
462 | + @unicode_to_bytes |
463 | + @log_call(logger.debug) |
464 | + def search_files(self, pattern): |
465 | + """Search for the occurrence of pattern in the files names.""" |
466 | + return self.main.search.search(pattern) |
467 | + |
468 | |
469 | class SyncdaemonShares(SyncdaemonObject): |
470 | """An interface to interact with shares.""" |
471 | |
472 | === modified file 'ubuntuone/syncdaemon/main.py' |
473 | --- ubuntuone/syncdaemon/main.py 2012-09-20 12:56:32 +0000 |
474 | +++ ubuntuone/syncdaemon/main.py 2012-10-23 17:07:21 +0000 |
475 | @@ -39,6 +39,7 @@ |
476 | action_queue, |
477 | config, |
478 | event_queue, |
479 | + files_search, |
480 | filesystem_manager, |
481 | hash_queue, |
482 | events_nanny, |
483 | @@ -156,6 +157,8 @@ |
484 | self.mark = task.LoopingCall(self.log_mark) |
485 | self.mark.start(mark_interval) |
486 | |
487 | + self.search = files_search.SearchFiles(self.fs) |
488 | + |
489 | def start_event_logger(self): |
490 | """Start the event logger if it's available for this platform.""" |
491 | self.eventlog_listener = event_logging.get_listener(self.fs, self.vm) |
492 | |
493 | === modified file 'ubuntuone/syncdaemon/volume_manager.py' |
494 | --- ubuntuone/syncdaemon/volume_manager.py 2012-08-06 19:18:57 +0000 |
495 | +++ ubuntuone/syncdaemon/volume_manager.py 2012-10-23 17:07:21 +0000 |
496 | @@ -506,6 +506,11 @@ |
497 | """Request the list of volumes to the server.""" |
498 | self.m.action_q.list_volumes() |
499 | |
500 | + def get_folders(self): |
501 | + """Get the list of folders where the data is stored.""" |
502 | + return (self.m.root_dir, self._udfs_dir, self._shared_dir, |
503 | + self._shares_dir) |
504 | + |
505 | def handle_AQ_LIST_VOLUMES(self, volumes): |
506 | """Handle AQ_LIST_VOLUMES event.""" |
507 | self.log.debug('handle_AQ_LIST_VOLUMES: handling volumes list.') |
I don't really agree with replacing spaces with '.+'.
The old share_links_search does a substring search in just the
basename, while this matches the whole path, which I think is a good
idea.
However, I was expecting this to also be substring search, but you are
replacing spaces in the search string with '.+', which means the
behavior is a little unexpected wrt. spaces in the search text.
Here's a test to be added to test_fsm.py in FSMSearchTestCase that shows what I mean:
def test_get_ paths_by_ pattern_ with_spaces( self): to/a/file/ deep/in/ the/woods/ called/ a file' to/anythingyouw ant-to- defile'
self.fsm. _idx_path = {path: mdid, path2: mdid2} re.escape( 'a file').split('\\ ')) get_paths_ by_pattern( search) by_pattern: ", result
self.assertEqu al(result, expected)
"""Test that spaces in a pattern are handled as expected."""
mdid = 'id'
path = '/my/path/
mdobj = {'server_hash': 'asdqwe123'}
mdid2 = 'id2'
path2 = '/my/path/
mdobj2 = {'server_hash': 'asdqwe456'}
self.fsm.fs = {mdid: mdobj, mdid2: mdobj2}
expected = [path]
# expectations
paths = "(%s|%s)" % ('/my/path/', '/home/')
pattern = paths + ".*/.*%s.*$"
keywords = '.+'.join(
print "keywords:", keywords
search = pattern % keywords
result = self.fsm.
print "result from get_paths_
I would expect "a file" to not match "anythingyouwan t-to-defile" . It
matches both:
Here's what that test prints: to/a/file/ deep/in/ the/woods/ called/ a file id to/a/file/ deep/in/ the/woods/ called/ a file to/anythingyouw ant-to- defile id2 to/anythingyouw ant-to- defile by_pattern: ['/my/path/ to/a/file/ deep/in/ the/woods/ called/ a file', '/my/path/ to/anythingyouw ant-to- defile' ] mmccrack/ Documents/ Canonical/ Source/ test-improve- buildout/ scripts/ devsetup/ parts/ubuntuone -client/ tests/syncdaemo n/test_ fsm.py" , line 4361, in test_get_ paths_by_ pattern_ with_spaces assertEqual( result, expected) mmccrack/ Documents/ Canonical/ Source/ test-improve- buildout/ scripts/ devsetup/ eggs/Twisted- 11.1.0- py2.7-macosx- 10.7-x86_ 64.egg/ twisted/ trial/unittest. py", line 270, in assertEqual trial.unittest. FailTest: not equal: to/a/file/ deep/in/ the/woods/ called/ a file', to/anythingyouw ant-to- defile' ] to/a/file/ deep/in/ the/woods/ called/ a file']
keywords: a.+file
searching through /my/path/
match with p= /my/path/
searching through /my/path/
match with p= /my/path/
result from get_paths_
Traceback (most recent call last):
File "/Users/
self.
File "/Users/
% (msg, pformat(first), pformat(second)))
twisted.
a = ['/my/path/
'/my/path/
b = ['/my/path/