Merge lp:~diegosarmentero/ubuntuone-client/darwin4-fsevents into lp:ubuntuone-client

Proposed by Diego Sarmentero
Status: Merged
Approved by: Alejandro J. Cura
Approved revision: 1299
Merged at revision: 1274
Proposed branch: lp:~diegosarmentero/ubuntuone-client/darwin4-fsevents
Merge into: lp:ubuntuone-client
Prerequisite: lp:~mandel/ubuntuone-client/clean-fsevents
Diff against target: 783 lines (+647/-9)
4 files modified
tests/platform/filesystem_notifications/test_darwin.py (+637/-0)
tests/platform/filesystem_notifications/test_filesystem_notifications.py (+5/-0)
tests/platform/filesystem_notifications/test_windows.py (+2/-2)
ubuntuone/platform/filesystem_notifications/darwin.py (+3/-7)
To merge this branch: bzr merge lp:~diegosarmentero/ubuntuone-client/darwin4-fsevents
Reviewer Review Type Date Requested Status
Alejandro J. Cura (community) Approve
Manuel de la Peña (community) Approve
Review via email: mp+114405@code.launchpad.net

This proposal supersedes a proposal from 2012-06-25.

Commit message

- Adding filesystem notifications feature for darwin (LP: #1013323)

Description of the change

Now you can execute:
./run-mac-tests tests/platform/filesystem_notifications/

To post a comment you must log in.
Revision history for this message
Alejandro J. Cura (alecu) wrote : Posted in a previous version of this proposal

The branch looks mostly fine.

A few questions, though:

 * Why is test_file_moved_from_partial skipped? I think this is a critical operation that should be tested.

 * Why is there a test that still uses time.sleep? As mentioned in previous branches we should not be using sleep at all in tests.

And some fixes needed, too:

It looks wrong to import fsevents in /platform/filesystem_notifications/__init__.py, so please, move each platform specific definition of EVENT_CODES to ubuntuone.platform.filesystem_notifications.windows and .darwin

---

Lines 827 and 828 are duplicated.

---

Please move the logic in these few lines to a function, add some unit tests for it, and use it from both places:

837 + index = 0
838 + if not self._path.endswith(os.path.sep):
839 + index = 1
840 + path = path[len(self._path) + index:]

review: Needs Information
Revision history for this message
Manuel de la Peña (mandel) wrote : Posted in a previous version of this proposal

The diff is misleading because it should depend on lp:~mandel/ubuntuone-client/clean-fsevents. Diego can you do a resummit with the correct dependency so that we are not scared regading the size :)

Revision history for this message
Diego Sarmentero (diegosarmentero) wrote :

Answering alecu:
* Why is test_file_moved_from_partial skipped? I think this is a critical operation that should be tested.
This is marked for fix in

There is a bug for this, in which i'm working on:
https://bugs.launchpad.net/ubuntuone-client/+bug/1012706

The rest of the things has been fixed.

Revision history for this message
Manuel de la Peña (mandel) wrote :

Looks like there is a thread somewhere that is not killed in the tests. I'm running the tests via u1trial and once they are done the process does not stop.

review: Needs Fixing
1296. By Diego Sarmentero

removing time.sleep

Revision history for this message
Diego Sarmentero (diegosarmentero) wrote :

> Looks like there is a thread somewhere that is not killed in the tests. I'm
> running the tests via u1trial and once they are done the process does not
> stop.

Fixed!

Revision history for this message
Alejandro J. Cura (alecu) wrote :

The tearDown in TestWatchManager should not be calling self.patch.
In the same tearDown, the deferred returned by self.manager.stop() should be yielded on.

---
typos:

"""Test that we can get a Watch using is wd.""" ->
"""Test that we can get a Watch using its wd."""

---

This line should not be part of the tests; it should be part of the stopping of the watcher thread:

self.manager.manager.observer.join()

That part of the code (stopping the watcher thread) is particularly dirty: I just found that there's a __del__ in WatchManager that we missed on the review of the previous branch, and that should not be there, since the use of __del__ is discouraged in python, and much more so when it's being used to work around a thread not being correctly finished.

I think that the stop method should be returning a deferred that's fired when the join is done; but please, let's discuss together a better solution to this on IRC or Mumble.
---

Revision history for this message
Alejandro J. Cura (alecu) :
review: Needs Fixing
1297. By Diego Sarmentero

adding join on stop

1298. By Diego Sarmentero

moving patch to setUp

1299. By Diego Sarmentero

reverting

Revision history for this message
Manuel de la Peña (mandel) wrote :

Looks goo to me knowing that we will merge this tests later.

review: Approve
Revision history for this message
Alejandro J. Cura (alecu) wrote :

I still would like better handling of the watches being stopped, so for that I'm creating a different bug: #1024102

Otherwise the code here looks ok.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'tests/platform/filesystem_notifications/test_darwin.py'
--- tests/platform/filesystem_notifications/test_darwin.py 2012-07-10 18:43:56 +0000
+++ tests/platform/filesystem_notifications/test_darwin.py 2012-07-12 13:53:19 +0000
@@ -32,7 +32,9 @@
32import os32import os
33import tempfile33import tempfile
34import thread34import thread
35import itertools
3536
37import fsevents
36from twisted.internet import defer38from twisted.internet import defer
3739
38from contrib.testing.testcase import BaseTwistedTestCase40from contrib.testing.testcase import BaseTwistedTestCase
@@ -41,15 +43,20 @@
41 darwin as filesystem_notifications,43 darwin as filesystem_notifications,
42)44)
43from ubuntuone.platform.filesystem_notifications.common import (45from ubuntuone.platform.filesystem_notifications.common import (
46 NotifyProcessor,
44 Watch,47 Watch,
45 WatchManager,48 WatchManager,
46)49)
47from ubuntuone.platform.filesystem_notifications.pyinotify_agnostic import (50from ubuntuone.platform.filesystem_notifications.pyinotify_agnostic import (
48 EventsCodes,51 EventsCodes,
49 ProcessEvent,52 ProcessEvent,
53 IN_CLOSE_WRITE,
50 IN_CREATE,54 IN_CREATE,
51 IN_DELETE,55 IN_DELETE,
56 IN_OPEN,
52)57)
58from tests.platform.filesystem_notifications import BaseFSMonitorTestCase
59
5360
54# A reverse mapping for the tests61# A reverse mapping for the tests
55REVERSE_MACOS_ACTIONS = {}62REVERSE_MACOS_ACTIONS = {}
@@ -135,6 +142,7 @@
135 yield super(TestWatch, self).setUp()142 yield super(TestWatch, self).setUp()
136 self.basedir = self.mktemp('test_root')143 self.basedir = self.mktemp('test_root')
137 self.mask = None144 self.mask = None
145 self.stream = None
138 self.memento = MementoHandler()146 self.memento = MementoHandler()
139 self.memento.setLevel(logging.DEBUG)147 self.memento.setLevel(logging.DEBUG)
140 self.raw_events = []148 self.raw_events = []
@@ -747,3 +755,632 @@
747 watch._subdirs.add(path)755 watch._subdirs.add(path)
748 watch._update_subdirs(path, REVERSE_MACOS_ACTIONS[IN_DELETE])756 watch._update_subdirs(path, REVERSE_MACOS_ACTIONS[IN_DELETE])
749 self.assertTrue(path not in watch._subdirs)757 self.assertTrue(path not in watch._subdirs)
758
759
760class TestWatchManager(BaseTwistedTestCase):
761 """Test the watch manager."""
762
763 @defer.inlineCallbacks
764 def setUp(self):
765 """Set each of the tests."""
766 yield super(TestWatchManager, self).setUp()
767 self.parent_path = '/Users/username/'
768 self.path = self.parent_path + 'path'
769 self.watch = Watch(1, self.path, None)
770 self.manager = WatchManager(None)
771 self.manager._wdm = {1: self.watch}
772
773 @defer.inlineCallbacks
774 def test_stop(self):
775 """Test that the different watches are stopped."""
776 self.was_called = False
777
778 def fake_stop_watching(watch):
779 """Fake stop watch."""
780 self.was_called = True
781 return defer.succeed(True)
782
783 self.patch(Watch, "stop_watching", fake_stop_watching)
784 self.patch(self.manager.manager.observer, "unschedule", lambda x: None)
785 yield self.manager.stop()
786 self.assertTrue(self.was_called, 'The watch stop should be called.')
787
788 def test_stop_multiple(self):
789 """The watches should became watching=False and the observer stopped."""
790 self.patch(self.manager.manager.observer, "unschedule", lambda x: None)
791 second_path = self.parent_path + "second_path"
792 second_watch = Watch(2, second_path, None)
793 second_watch._watching = True
794 self.manager._wdm[2] = second_watch
795 self.manager.stop()
796 self.assertFalse(second_watch.platform_watch.watching)
797 self.assertEqual(second_watch._subdirs, set())
798 # Give time to the thread to be finished.
799 self.manager.manager.observer.join()
800 self.assertFalse(self.manager.manager.observer.is_alive())
801
802 def test_get_present_watch(self):
803 """Test that we can get a Watch using is wd."""
804 self.assertEqual(self.watch, self.manager.get_watch(1))
805
806 def test_get_missing_watch(self):
807 """Test that we get an error when trying to get a missing wd."""
808 self.assertRaises(KeyError, self.manager.get_watch, (1,))
809
810 @defer.inlineCallbacks
811 def test_delete_present_watch(self):
812 """Test that we can remove a present watch."""
813 self.was_called = False
814
815 def stop_watching():
816 """Fake stop watch."""
817 self.was_called = True
818 return defer.succeed(True)
819
820 def fake_unschedule(s):
821 """Fake function that should receive a Stream object."""
822 self.stream = s
823
824 self.patch(self.manager.manager.observer, "unschedule",
825 fake_unschedule)
826
827 self.watch.stop_watching = stop_watching
828 yield self.manager.del_watch(1)
829 self.assertIsInstance(self.stream, fsevents.Stream)
830 self.assertRaises(KeyError, self.manager.get_watch, (1,))
831
832 def test_add_single_watch(self):
833 """Test the addition of a new single watch."""
834 self.was_called = False
835
836 def fake_start_watching(*args):
837 """Fake start watch."""
838 self.was_called = True
839
840 self.patch(Watch, "start_watching", fake_start_watching)
841 self.manager._wdm = {}
842
843 mask = 'bit_mask'
844 self.manager.add_watch(self.path, mask)
845 self.assertEqual(1, len(self.manager._wdm))
846 self.assertTrue(self.was_called, 'The watch start was not called.')
847 self.assertEqual(self.path + os.path.sep, self.manager._wdm[0].path)
848
849 def test_get_watch_present_wd(self):
850 """Test that the correct path is returned."""
851 self.assertEqual(self.path + os.path.sep, self.manager.get_path(1))
852
853 def test_get_watch_missing_wd(self):
854 """Test that the correct path is returned."""
855 self.manager._wdm = {}
856 self.assertEqual(None, self.manager.get_path(1))
857
858 def test_get_wd_exact_path(self):
859 """Test the wd is returned when there is a watch for the path."""
860 self.assertEqual(1, self.manager.get_wd(self.path))
861
862 def test_get_wd_child_path(self):
863 """Test the wd is returned when we have a child path."""
864 child = os.path.join(self.path, 'child')
865 self.assertEqual(1, self.manager.get_wd(child))
866
867 def test_get_wd_unwatched(self):
868 """A watch on an unwatched path returns None."""
869 self.assertEqual(None, self.manager.get_wd(self.parent_path))
870
871 def test_rm_present_wd(self):
872 """Test the removal of a present watch."""
873 self.patch(self.watch, "stop_watching", lambda: None)
874 self.patch(self.manager.manager.observer, "unschedule", lambda x: None)
875 self.manager.rm_watch(1)
876 self.assertEqual(None, self.manager._wdm.get(1))
877
878 def test_rm_root_path(self):
879 """Test the removal of a root path."""
880 events = []
881
882 def fake_processor(event):
883 """Memorize the processed events."""
884 events.append(event.pathname)
885
886 self.watch._processor = fake_processor
887 self.manager.rm_path(self.path)
888 self.assertEqual(self.watch, self.manager._wdm.get(1))
889 self.watch._watching = True
890 event = FakeFileEvent(256, None, os.path.join(self.path, 'test'))
891 self.watch.platform_watch._process_events(event)
892 self.assertEqual(0, len(events))
893
894 def test_rm_child_path(self):
895 """Test the removal of a child path."""
896 events = []
897
898 def fake_processor(event):
899 """Memorize the processed events."""
900 events.append(event.pathname)
901
902 self.patch(filesystem_notifications.reactor, 'callFromThread',
903 lambda x, e: x(e))
904
905 self.watch._processor = fake_processor
906 child = os.path.join(self.path, 'child')
907 self.manager.rm_path(child)
908 self.assertEqual(self.watch, self.manager._wdm[1])
909 # assert that the correct event is ignored
910 self.watch.platform_watch.watching = True
911 event = FakeFileEvent(256, None, os.path.join('child', 'test'))
912 self.watch.platform_watch._process_events(event)
913 self.assertEqual(0, len(events))
914 # assert that other events are not ignored
915 event2 = FakeFileEvent(256, None, 'test')
916 self.watch.platform_watch._process_events(event2)
917 self.assertEqual(1, len(events))
918
919
920class TestWatchManagerAddWatches(BaseTwistedTestCase):
921 """Test the watch manager."""
922 timeout = 5
923
924 def test_add_watch_twice(self):
925 """Adding a watch twice succeeds when the watch is running."""
926 self.patch(Watch, "start_watching", lambda self: None)
927 manager = WatchManager(None)
928 # no need to stop watching because start_watching is fake
929
930 path = '/Users/username/path'
931 mask = 'fake bit mask'
932 d1 = manager.add_watch(path, mask)
933 d2 = manager.add_watch(path, mask)
934
935 self.assertTrue(d1.result, "Should not be called yet.")
936 self.assertFalse(d2.result, "Should not be called yet.")
937
938
939class FakeEvent(object):
940 """Fake event."""
941
942 def __init__(self, wd=0, dir=True, name=None, path=None, pathname=None,
943 cookie=None):
944 """Create fake event."""
945 self.dir = dir
946 self.wd = wd
947 self.name = name
948 self.path = path
949 self.pathname = pathname
950 self.cookie = cookie
951
952
953class FakeLog(object):
954 """A fake log that is used by the general processor."""
955
956 def __init__(self):
957 """Create the fake."""
958 self.called_methods = []
959
960 def info(self, *args):
961 """Fake the info call."""
962 self.called_methods.append(('info', args))
963
964
965class FakeGeneralProcessor(object):
966 """Fake implementation of the general processor."""
967
968 def __init__(self):
969 """Create the fake."""
970 self.called_methods = []
971 self.paths_to_return = []
972 self.log = FakeLog()
973 self.share_id = None
974 self.ignore = False
975
976 def rm_from_mute_filter(self, event, paths):
977 """Fake rm_from_mute_filter."""
978 self.called_methods.append(('rm_from_mute_filter', event, paths))
979
980 def add_to_mute_filter(self, event, paths):
981 """Fake add_to_move_filter."""
982 self.called_methods.append(('add_to_mute_filter', event, paths))
983
984 def is_ignored(self, path):
985 """Fake is_ignored."""
986 self.called_methods.append(('is_ignored', path))
987 return self.ignore
988
989 def push_event(self, event):
990 """Fake push event."""
991 self.called_methods.append(('push_event', event))
992
993 def eq_push(self, event, path=None, path_to=None, path_from=None):
994 """Fake event to push event."""
995 self.called_methods.append(('eq_push', event, path, path_to,
996 path_from))
997
998 def get_paths_starting_with(self, fullpath, include_base=False):
999 """Fake get_paths_starting_with."""
1000 self.called_methods.append(('get_paths_starting_with', fullpath,
1001 include_base))
1002 return self.paths_to_return
1003
1004 def get_path_share_id(self, path):
1005 """Fake get_path_share_id."""
1006 self.called_methods.append(('get_path_share_id', path))
1007 return self.share_id
1008
1009 def rm_watch(self, path):
1010 """Fake the remove watch."""
1011 self.called_methods.append(('rm_watch', path))
1012
1013 def freeze_begin(self, path):
1014 """Fake freeze_begin"""
1015 self.called_methods.append(('freeze_begin', path))
1016
1017 def freeze_rollback(self):
1018 """Fake rollback."""
1019 self.called_methods.append(('freeze_rollback',))
1020
1021 def freeze_commit(self, path):
1022 """Fake freeze commit."""
1023 self.called_methods.append(('freeze_commit', path))
1024
1025
1026class TestNotifyProcessor(BaseTwistedTestCase):
1027 """Test the notify processor."""
1028
1029 @defer.inlineCallbacks
1030 def setUp(self):
1031 """set up the diffeent tests."""
1032 yield super(TestNotifyProcessor, self).setUp()
1033 self.processor = NotifyProcessor(None)
1034 self.general = FakeGeneralProcessor()
1035 self.processor.general_processor = self.general
1036
1037 def test_rm_from_mute_filter(self):
1038 """Test that we remove the event from the mute filter."""
1039 event = 'event'
1040 paths = 'paths'
1041 self.processor.rm_from_mute_filter(event, paths)
1042 self.assertEqual(1, len(self.general.called_methods))
1043 self.assertEqual('rm_from_mute_filter',
1044 self.general.called_methods[0][0])
1045 self.assertEqual(event, self.general.called_methods[0][1])
1046 self.assertEqual(paths, self.general.called_methods[0][2])
1047
1048 def test_add_to_mute_filter(self):
1049 """Test that we add the path to the mute filter."""
1050 event = 'event'
1051 paths = 'paths'
1052 self.processor.add_to_mute_filter(event, paths)
1053 self.assertEqual(1, len(self.general.called_methods))
1054 self.assertEqual('add_to_mute_filter',
1055 self.general.called_methods[0][0])
1056 self.assertEqual(event, self.general.called_methods[0][1])
1057 self.assertEqual(paths, self.general.called_methods[0][2])
1058
1059 def test_is_ignored(self):
1060 """Test that we do ensure that the path is ignored."""
1061 path = 'path'
1062 self.processor.is_ignored(path)
1063 self.assertEqual(1, len(self.general.called_methods))
1064 self.assertEqual('is_ignored',
1065 self.general.called_methods[0][0])
1066 self.assertEqual(path, self.general.called_methods[0][1])
1067
1068 def test_release_held_event(self):
1069 """Test that we do release the held event."""
1070 event = 'event'
1071 # set the held event to assert that is pushed
1072 self.processor.held_event = event
1073 self.processor.release_held_event()
1074 self.assertEqual('push_event',
1075 self.general.called_methods[0][0])
1076 self.assertEqual(event, self.general.called_methods[0][1])
1077
1078 def test_process_IN_MODIFY_dir(self):
1079 """Test that the modify works as exepcted with dirs."""
1080 event = FakeEvent(dir=True)
1081 self.processor.process_IN_MODIFY(event)
1082 # no method should be called
1083 self.assertEqual(0, len(self.general.called_methods))
1084
1085 def test_process_IN_MODIFY_file(self):
1086 """Test that the modify works as expected with files."""
1087 event = FakeEvent(dir=False, wd=0, name='name',
1088 path='path', pathname='pathname')
1089 self.processor.process_IN_MODIFY(event)
1090 # we should be getting two different method, and open and a close
1091 self.assertEqual(2, len(self.general.called_methods))
1092 self.assertEqual('push_event',
1093 self.general.called_methods[0][0])
1094 self.assertEqual('push_event',
1095 self.general.called_methods[1][0])
1096 self.assertEqual(event.dir, self.general.called_methods[0][1].dir)
1097 self.assertEqual(event.wd, self.general.called_methods[0][1].wd)
1098 self.assertEqual(event.name, self.general.called_methods[0][1].name)
1099 self.assertEqual(event.path, self.general.called_methods[0][1].path)
1100 self.assertEqual(event.pathname,
1101 self.general.called_methods[0][1].pathname)
1102 self.assertEqual(IN_OPEN,
1103 self.general.called_methods[0][1].mask)
1104 self.assertEqual(event.dir, self.general.called_methods[1][1].dir)
1105 self.assertEqual(event.wd, self.general.called_methods[1][1].wd)
1106 self.assertEqual(event.name, self.general.called_methods[1][1].name)
1107 self.assertEqual(event.path, self.general.called_methods[1][1].path)
1108 self.assertEqual(event.pathname,
1109 self.general.called_methods[1][1].pathname)
1110 self.assertEqual(IN_CLOSE_WRITE,
1111 self.general.called_methods[1][1].mask)
1112
1113 def test_process_IN_MOVED_FROM(self):
1114 """Test that the in moved from works as expected."""
1115 event = FakeEvent(dir=False, wd=0, name='name',
1116 path='path', pathname='pathname')
1117 self.processor.process_IN_MOVED_FROM(event)
1118 self.assertEqual(event, self.processor.held_event)
1119
1120 def test_process_IN_MOVED_TO_dir(self):
1121 """Test that the in moved to works as expected."""
1122 event = FakeEvent(wd=0, dir=True, name='name', path='path',
1123 pathname=os.path.join('test', 'pathname'),
1124 cookie='cookie')
1125 held_event = FakeEvent(wd=0, dir=True, name='hname', path='hpath',
1126 pathname=os.path.join('test', 'hpathname'),
1127 cookie='cookie')
1128 self.general.share_id = 'my_share_id'
1129 self.processor.held_event = held_event
1130 self.processor.process_IN_MOVED_TO(event)
1131 self.assertEqual(5, len(self.general.called_methods))
1132 # assert that the ignores are called
1133 self.assertEqual('is_ignored', self.general.called_methods[0][0])
1134 self.assertEqual(held_event.pathname,
1135 self.general.called_methods[0][1])
1136 self.assertEqual('is_ignored', self.general.called_methods[1][0])
1137 self.assertEqual(event.pathname, self.general.called_methods[1][1])
1138 # assert that we do request the share_id
1139 self.assertEqual('get_path_share_id',
1140 self.general.called_methods[2][0])
1141 self.assertEqual(os.path.split(event.pathname)[0],
1142 self.general.called_methods[2][1],
1143 'Get the share_id for event')
1144 self.assertEqual('get_path_share_id',
1145 self.general.called_methods[3][0])
1146 self.assertEqual(os.path.split(held_event.pathname)[0],
1147 self.general.called_methods[3][1],
1148 'Get the share_id for held event.')
1149
1150 self.assertEqual('eq_push', self.general.called_methods[4][0])
1151 self.assertEqual('FS_DIR_MOVE', self.general.called_methods[4][1])
1152 self.assertEqual(event.pathname, self.general.called_methods[4][3])
1153 self.assertEqual(held_event.pathname,
1154 self.general.called_methods[4][4])
1155
1156 def test_process_IN_MOVED_TO_file(self):
1157 """Test that the in moved to works as expected."""
1158 event = FakeEvent(wd=0, dir=False, name='name', path='path',
1159 pathname=os.path.join('test', 'pathname'),
1160 cookie='cookie')
1161 held_event = FakeEvent(wd=0, dir=False, name='hname', path='hpath',
1162 pathname=os.path.join('test', 'hpathname'),
1163 cookie='cookie')
1164 self.general.share_id = 'my_share_id'
1165 self.processor.held_event = held_event
1166 self.processor.process_IN_MOVED_TO(event)
1167 self.assertEqual(5, len(self.general.called_methods))
1168 # assert that the ignores are called
1169 self.assertEqual('is_ignored', self.general.called_methods[0][0])
1170 self.assertEqual(held_event.pathname,
1171 self.general.called_methods[0][1])
1172 self.assertEqual('is_ignored', self.general.called_methods[1][0])
1173 self.assertEqual(event.pathname, self.general.called_methods[1][1])
1174 # assert that we do request the share_id
1175 self.assertEqual('get_path_share_id',
1176 self.general.called_methods[2][0])
1177 self.assertEqual(os.path.split(event.pathname)[0],
1178 self.general.called_methods[2][1],
1179 'Get the share_id for event')
1180 self.assertEqual('get_path_share_id',
1181 self.general.called_methods[3][0])
1182 self.assertEqual(os.path.split(held_event.pathname)[0],
1183 self.general.called_methods[3][1],
1184 'Get the share_id for held event.')
1185
1186 self.assertEqual('eq_push', self.general.called_methods[4][0])
1187 self.assertEqual('FS_FILE_MOVE', self.general.called_methods[4][1])
1188 self.assertEqual(event.pathname, self.general.called_methods[4][3])
1189 self.assertEqual(held_event.pathname,
1190 self.general.called_methods[4][4])
1191
1192 def test_fake_create_event_dir(self):
1193 """Test that the in moved to works as expected."""
1194 event = FakeEvent(wd=0, dir=True, name='name', path='path',
1195 pathname='pathname')
1196 self.processor._fake_create_event(event)
1197 self.assertEqual(1, len(self.general.called_methods))
1198 self.assertEqual('eq_push', self.general.called_methods[0][0])
1199 self.assertEqual('FS_DIR_CREATE', self.general.called_methods[0][1])
1200 self.assertEqual(event.pathname, self.general.called_methods[0][2])
1201
1202 def test_fake_create_event_file(self):
1203 """Test that the in moved to works as expected."""
1204 event = FakeEvent(wd=0, dir=False, name='name', path='path',
1205 pathname='pathname')
1206 self.processor._fake_create_event(event)
1207 self.assertEqual(2, len(self.general.called_methods))
1208 self.assertEqual('eq_push', self.general.called_methods[0][0])
1209 self.assertEqual('FS_FILE_CREATE', self.general.called_methods[0][1])
1210 self.assertEqual(event.pathname, self.general.called_methods[0][2])
1211 self.assertEqual('eq_push', self.general.called_methods[1][0])
1212 self.assertEqual('FS_FILE_CLOSE_WRITE',
1213 self.general.called_methods[1][1])
1214 self.assertEqual(event.pathname, self.general.called_methods[1][2])
1215
1216 def test_fake_delete_create_event_dir(self):
1217 """Test that we do fake a delete and a later delete."""
1218 event = FakeEvent(wd=0, dir=True, name='name', path='path',
1219 pathname='pathname')
1220 held_event = FakeEvent(wd=0, dir=True, name='hname', path='hpath',
1221 pathname='hpathname')
1222 self.processor.held_event = held_event
1223 self.processor._fake_delete_create_event(event)
1224 self.assertEqual(2, len(self.general.called_methods))
1225 self.assertEqual('eq_push', self.general.called_methods[0][0])
1226 self.assertEqual('FS_DIR_DELETE', self.general.called_methods[0][1])
1227 self.assertEqual(held_event.pathname,
1228 self.general.called_methods[0][2])
1229 self.assertEqual('eq_push', self.general.called_methods[1][0])
1230 self.assertEqual('FS_DIR_CREATE', self.general.called_methods[1][1])
1231 self.assertEqual(event.pathname, self.general.called_methods[1][2])
1232
1233 def test_fake_delete_create_event_file(self):
1234 """Test that we do fake a delete and a later delete."""
1235 event = FakeEvent(wd=0, dir=False, name='name', path='path',
1236 pathname='pathname')
1237 held_event = FakeEvent(wd=0, dir=False, name='hname', path='hpath',
1238 pathname='hpathname')
1239 self.processor.held_event = held_event
1240 self.processor._fake_delete_create_event(event)
1241 self.assertEqual(3, len(self.general.called_methods))
1242 self.assertEqual('eq_push', self.general.called_methods[0][0])
1243 self.assertEqual('FS_FILE_DELETE', self.general.called_methods[0][1])
1244 self.assertEqual(held_event.pathname,
1245 self.general.called_methods[0][2])
1246 self.assertEqual('eq_push', self.general.called_methods[1][0])
1247 self.assertEqual('FS_FILE_CREATE', self.general.called_methods[1][1])
1248 self.assertEqual(event.pathname, self.general.called_methods[1][2])
1249 self.assertEqual('eq_push', self.general.called_methods[2][0])
1250 self.assertEqual('FS_FILE_CLOSE_WRITE',
1251 self.general.called_methods[2][1])
1252 self.assertEqual(event.pathname, self.general.called_methods[2][2])
1253
1254 def test_process_default_no_held(self):
1255 """Test that the process default works as expected."""
1256 event = 'event'
1257 self.processor.process_default(event)
1258 self.assertEqual(1, len(self.general.called_methods))
1259 self.assertEqual('push_event',
1260 self.general.called_methods[0][0])
1261 self.assertEqual(event,
1262 self.general.called_methods[0][1])
1263
1264 def test_process_default_with_held(self):
1265 """Test that the process default works as expected."""
1266 event = 'event'
1267 held_event = 'held_event'
1268 self.processor.held_event = held_event
1269 self.processor.process_default(event)
1270 self.assertEqual(2, len(self.general.called_methods))
1271 self.assertEqual('push_event',
1272 self.general.called_methods[0][0])
1273 self.assertEqual(held_event,
1274 self.general.called_methods[0][1])
1275 self.assertEqual('push_event',
1276 self.general.called_methods[1][0])
1277 self.assertEqual(event,
1278 self.general.called_methods[1][1])
1279
1280 def test_handle_dir_delete_files(self):
1281 """Test that the handle dir delete works as expected."""
1282 path = 'path'
1283 present_files = 'abcde'
1284 # create files and dirs to be returned from the get paths
1285 for file_name in present_files:
1286 self.general.paths_to_return.append((file_name, False))
1287 self.processor.handle_dir_delete(path)
1288 # there are calls for, rm the watch, get paths and then one per file
1289 self.assertEqual(len(present_files) + 2,
1290 len(self.general.called_methods))
1291 rm_call = self.general.called_methods.pop(0)
1292 self.assertEqual('rm_watch', rm_call[0])
1293 self.assertEqual(path, rm_call[1])
1294 paths_call = self.general.called_methods.pop(0)
1295 self.assertEqual('get_paths_starting_with', paths_call[0])
1296 self.assertEqual(path, paths_call[1])
1297 self.assertFalse(paths_call[2])
1298 # we need to push the delete events in reverse order because we want
1299 # to delete children before we delete parents
1300 present_files = present_files[::-1]
1301 for i, called_method in enumerate(self.general.called_methods):
1302 self.assertEqual('eq_push', called_method[0])
1303 self.assertEqual('FS_FILE_DELETE', called_method[1])
1304 self.assertEqual(present_files[i], called_method[2])
1305
1306 def test_handle_dir_delete_dirs(self):
1307 """Test that the handle dir delete works as expected."""
1308 path = 'path'
1309 present_files = 'abcde'
1310 # create files and dirs to be returned from the get paths
1311 for file_name in present_files:
1312 self.general.paths_to_return.append((file_name, True))
1313 self.processor.handle_dir_delete(path)
1314 # there are calls for, rm the watch, get paths and then one per file
1315 self.assertEqual(2 * len(present_files) + 2,
1316 len(self.general.called_methods))
1317 rm_call = self.general.called_methods.pop(0)
1318 self.assertEqual('rm_watch', rm_call[0])
1319 self.assertEqual(path, rm_call[1])
1320 paths_call = self.general.called_methods.pop(0)
1321 self.assertEqual('get_paths_starting_with', paths_call[0])
1322 self.assertEqual(path, paths_call[1])
1323 self.assertFalse(paths_call[2])
1324 # we need to push the delete events in reverse order because we want
1325 # to delete children before we delete parents
1326 present_files = present_files[::-1]
1327
1328 # from http://docs.python.org/library/itertools.html#recipes
1329 def grouper(n, iterable, fillvalue=None):
1330 "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
1331 args = [iter(iterable)] * n
1332 return itertools.izip_longest(fillvalue=fillvalue, *args)
1333
1334 for i, called_methods in enumerate(grouper(2,
1335 self.general.called_methods)):
1336 rm_call = called_methods[0]
1337 self.assertEqual('rm_watch', rm_call[0])
1338 self.assertEqual(present_files[i], rm_call[1])
1339 push_call = called_methods[1]
1340 self.assertEqual('eq_push', push_call[0])
1341 self.assertEqual('FS_DIR_DELETE', push_call[1])
1342 self.assertEqual(present_files[i], push_call[2])
1343
1344 def test_freeze_begin(self):
1345 """Test that the freeze being works as expected."""
1346 path = 'path'
1347 self.processor.freeze_begin(path)
1348 self.assertEqual(1, len(self.general.called_methods))
1349 self.assertEqual('freeze_begin',
1350 self.general.called_methods[0][0])
1351 self.assertEqual(path, self.general.called_methods[0][1])
1352
1353 def test_freeze_rollback(self):
1354 """Test that the freeze rollback works as expected."""
1355 self.processor.freeze_rollback()
1356 self.assertEqual(1, len(self.general.called_methods))
1357 self.assertEqual('freeze_rollback',
1358 self.general.called_methods[0][0])
1359
1360 def test_freeze_commit(self):
1361 """Test that the freeze commit works as expected."""
1362 path = 'path'
1363 self.processor.freeze_commit(path)
1364 self.assertEqual(1, len(self.general.called_methods))
1365 self.assertEqual('freeze_commit',
1366 self.general.called_methods[0][0])
1367 self.assertEqual(path, self.general.called_methods[0][1])
1368
1369
1370class FilesystemMonitorTestCase(BaseFSMonitorTestCase):
1371 """Tests for the FilesystemMonitor."""
1372
1373 def test_add_watch_twice(self):
1374 """Check the deferred returned by a second add_watch."""
1375 self.patch(Watch, "start_watching", lambda self: None)
1376 self.patch(Watch, "started", lambda self: True)
1377 manager = WatchManager(None)
1378 # no need to stop watching because start_watching is fake
1379
1380 path = '/Users/username/path'
1381 mask = 'fake bit mask'
1382 d1 = manager.add_watch(path, mask)
1383 d2 = manager.add_watch(path, mask)
1384
1385 self.assertTrue(d1.result, "Should not be called yet.")
1386 self.assertTrue(d2, "Should not be called yet.")
7501387
=== modified file 'tests/platform/filesystem_notifications/test_filesystem_notifications.py'
--- tests/platform/filesystem_notifications/test_filesystem_notifications.py 2012-06-28 10:26:50 +0000
+++ tests/platform/filesystem_notifications/test_filesystem_notifications.py 2012-07-12 13:53:19 +0000
@@ -39,6 +39,7 @@
39 BaseTwistedTestCase,39 BaseTwistedTestCase,
40 FakeVolumeManager,40 FakeVolumeManager,
41 skip_if_win32_missing_fs_event,41 skip_if_win32_missing_fs_event,
42 skip_if_darwin_missing_fs_event,
42)43)
43from ubuntuone.platform import (44from ubuntuone.platform import (
44 remove_file,45 remove_file,
@@ -264,6 +265,7 @@
264 mute_filter = self.monitor._processor.mute_filter265 mute_filter = self.monitor._processor.mute_filter
265 return sum(len(x) for x in mute_filter._cnt.values())266 return sum(len(x) for x in mute_filter._cnt.values())
266267
268 @skip_if_darwin_missing_fs_event
267 @skip_if_win32_missing_fs_event269 @skip_if_win32_missing_fs_event
268 @defer.inlineCallbacks270 @defer.inlineCallbacks
269 def test_file_open(self):271 def test_file_open(self):
@@ -281,6 +283,7 @@
281 test_result = yield self._deferred283 test_result = yield self._deferred
282 defer.returnValue(test_result)284 defer.returnValue(test_result)
283285
286 @skip_if_darwin_missing_fs_event
284 @skip_if_win32_missing_fs_event287 @skip_if_win32_missing_fs_event
285 @defer.inlineCallbacks288 @defer.inlineCallbacks
286 def test_file_close_nowrite(self):289 def test_file_close_nowrite(self):
@@ -298,6 +301,7 @@
298 test_result = yield self._deferred301 test_result = yield self._deferred
299 defer.returnValue(test_result)302 defer.returnValue(test_result)
300303
304 @skip_if_darwin_missing_fs_event
301 @defer.inlineCallbacks305 @defer.inlineCallbacks
302 def test_file_create_close_write(self):306 def test_file_create_close_write(self):
303 """Test receiving the create and close_write signals on files."""307 """Test receiving the create and close_write signals on files."""
@@ -423,6 +427,7 @@
423 test_result = yield self._deferred427 test_result = yield self._deferred
424 defer.returnValue(test_result)428 defer.returnValue(test_result)
425429
430 @skip_if_darwin_missing_fs_event
426 @defer.inlineCallbacks431 @defer.inlineCallbacks
427 def test_file_moved_from_partial(self):432 def test_file_moved_from_partial(self):
428 """Test the handling of the FILE_MOVE event when source is partial."""433 """Test the handling of the FILE_MOVE event when source is partial."""
429434
=== modified file 'tests/platform/filesystem_notifications/test_windows.py'
--- tests/platform/filesystem_notifications/test_windows.py 2012-07-10 20:24:04 +0000
+++ tests/platform/filesystem_notifications/test_windows.py 2012-07-12 13:53:19 +0000
@@ -1321,7 +1321,7 @@
1321 for file_name in present_files:1321 for file_name in present_files:
1322 self.general.paths_to_return.append((file_name, False))1322 self.general.paths_to_return.append((file_name, False))
1323 self.processor.handle_dir_delete(path)1323 self.processor.handle_dir_delete(path)
1324 # there are calls for, rm the wathc, get paths and then one per file1324 # there are calls for, rm the watch, get paths and then one per file
1325 self.assertEqual(len(present_files) + 2,1325 self.assertEqual(len(present_files) + 2,
1326 len(self.general.called_methods))1326 len(self.general.called_methods))
1327 rm_call = self.general.called_methods.pop(0)1327 rm_call = self.general.called_methods.pop(0)
@@ -1347,7 +1347,7 @@
1347 for file_name in present_files:1347 for file_name in present_files:
1348 self.general.paths_to_return.append((file_name, True))1348 self.general.paths_to_return.append((file_name, True))
1349 self.processor.handle_dir_delete(path)1349 self.processor.handle_dir_delete(path)
1350 # there are calls for, rm the wathc, get paths and then one per file1350 # there are calls for, rm the watch, get paths and then one per file
1351 self.assertEqual(2 * len(present_files) + 2,1351 self.assertEqual(2 * len(present_files) + 2,
1352 len(self.general.called_methods))1352 len(self.general.called_methods))
1353 rm_call = self.general.called_methods.pop(0)1353 rm_call = self.general.called_methods.pop(0)
13541354
=== modified file 'ubuntuone/platform/filesystem_notifications/darwin.py'
--- ubuntuone/platform/filesystem_notifications/darwin.py 2012-07-11 09:48:03 +0000
+++ ubuntuone/platform/filesystem_notifications/darwin.py 2012-07-12 13:53:19 +0000
@@ -61,7 +61,7 @@
61}61}
6262
6363
64def path_is_ignored(self, path):64def path_is_ignored(path):
65 """Should we ignore this path in the current platform.?"""65 """Should we ignore this path in the current platform.?"""
66 # don't support links yet66 # don't support links yet
67 if os.path.islink(path):67 if os.path.islink(path):
@@ -135,10 +135,6 @@
135 self.observer = fsevents.Observer()135 self.observer = fsevents.Observer()
136 self.observer.start()136 self.observer.start()
137137
138 def __del__(self):
139 """Stop the observer."""
140 self.observer.stop()
141
142 def stop_watch(self, watch):138 def stop_watch(self, watch):
143 """Stop a given watch."""139 """Stop a given watch."""
144 watch.stop_watching()140 watch.stop_watching()
@@ -148,10 +144,11 @@
148 def stop(self):144 def stop(self):
149 """Stop the manager."""145 """Stop the manager."""
150 self.observer.stop()146 self.observer.stop()
147 self.observer.join()
151148
152 def del_watch(self, watch):149 def del_watch(self, watch):
153 """Delete the watch and clean resources."""150 """Delete the watch and clean resources."""
154 self.observer.unschedule(watch.stream)151 self.observer.unschedule(watch.platform_watch.stream)
155152
156 def add_watch(self, watch):153 def add_watch(self, watch):
157 """This method perform actually the action of registering the watch."""154 """This method perform actually the action of registering the watch."""
@@ -160,4 +157,3 @@
160157
161 def rm_watch(self, watch):158 def rm_watch(self, watch):
162 """Remove the the watch with the given wd."""159 """Remove the the watch with the given wd."""
163 self.observer.unschedule(watch.platform_watch.stream)

Subscribers

People subscribed via source and target branches