def test_local_replace(self): local = LocalClient(self.local_test_folder_1) remote = self.remote_document_client_1 self.engine_1.start() self.wait_sync(wait_for_async=True) # Create 2 files with the same name but different content # in separate folders local.make_file('/', 'test.odt', 'Some content.') local.make_folder('/', 'folder') shutil.copyfile( os.path.join(self.local_test_folder_1, 'test.odt'), os.path.join(self.local_test_folder_1, 'folder', 'test.odt')) local.update_content('/folder/test.odt', 'Updated content.') # Copy the newest file to the root workspace and synchronize it sync_root = os.path.join(self.local_nxdrive_folder_1, self.workspace_title) test_file = os.path.join(self.local_test_folder_1, 'folder', 'test.odt') shutil.copyfile(test_file, os.path.join(sync_root, 'test.odt')) self.wait_sync() self.assertTrue(remote.exists('/test.odt')) self.assertEqual(remote.get_content('/test.odt'), 'Updated content.') # Copy the oldest file to the root workspace and synchronize it. # First wait a bit for file time stamps to increase enough. time.sleep(OS_STAT_MTIME_RESOLUTION) shutil.copyfile(os.path.join(self.local_test_folder_1, 'test.odt'), os.path.join(sync_root, 'test.odt')) self.wait_sync() self.assertTrue(remote.exists('/test.odt')) self.assertEqual(remote.get_content('/test.odt'), 'Some content.')
def test_local_replace(self): local = LocalClient(self.local_test_folder_1) remote = self.remote_document_client_1 self.engine_1.start() self.wait_sync(wait_for_async=True) # Create 2 files with the same name but different content # in separate folders local.make_file('/', 'test.odt', 'Some content.') local.make_folder('/', 'folder') copyfile(os.path.join(self.local_test_folder_1, 'test.odt'), os.path.join(self.local_test_folder_1, 'folder', 'test.odt')) local.update_content('/folder/test.odt', 'Updated content.') # Copy the newest file to the root workspace and synchronize it sync_root = os.path.join(self.local_nxdrive_folder_1, self.workspace_title) test_file = os.path.join(self.local_test_folder_1, 'folder', 'test.odt') copyfile(test_file, os.path.join(sync_root, 'test.odt')) self.wait_sync() self.assertTrue(remote.exists('/test.odt')) self.assertEquals(remote.get_content('/test.odt'), 'Updated content.') # Copy the oldest file to the root workspace and synchronize it. # First wait a bit for file time stamps to increase enough. time.sleep(OS_STAT_MTIME_RESOLUTION) copyfile(os.path.join(self.local_test_folder_1, 'test.odt'), os.path.join(sync_root, 'test.odt')) self.wait_sync() self.assertTrue(remote.exists('/test.odt')) self.assertEquals(remote.get_content('/test.odt'), 'Some content.')
def test_drive_edit_doc_becoming_synced(self): raise SkipTest("WIP in https://jira.nuxeo.com/browse/NXDRIVE-170") ctl = self.controller_1 ctl.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url, self.user_1, self.password_1) local = LocalClient(self.local_nxdrive_folder_1) remote = self.remote_document_client_1 syn = ctl.synchronizer # Create file in test workspace (non sync root) doc_id = remote.make_file('/', 'test.odt', 'Some content.') # Drive edit file ctl.download_edit(self.nuxeo_url, 'default', doc_id, 'test.odt', open_file=False) # Check file is downloaded to the Locally Edited folder self.assertTrue(local.exists('/%s/test.odt' % LOCALLY_EDITED_FOLDER_NAME)) # Register test workspace as a sync root ctl.bind_root(self.local_nxdrive_folder_1, self.workspace) self._sync(syn) self.assertTrue(local.exists('/%s/test.odt' % self.workspace_title)) # Update file in local sync root time.sleep(OS_STAT_MTIME_RESOLUTION) local.update_content('/%s/test.odt' % self.workspace_title, 'Content updated from local sync root.') self._sync(syn, wait_for_async=False) self.assertEquals(remote.get_content('/test.odt'), 'Content updated from local sync root.') self._sync(syn) self.assertEquals(local.get_content('/%s/test.odt' % LOCALLY_EDITED_FOLDER_NAME), 'Content updated from local sync root.') # Update locally edited file time.sleep(OS_STAT_MTIME_RESOLUTION) local.update_content('/%s/test.odt' % LOCALLY_EDITED_FOLDER_NAME, 'Content updated from Locally Edited.') self._sync(syn, wait_for_async=False) self.assertEquals(remote.get_content('/test.odt'), 'Content updated from Locally Edited.') self._sync(syn) self.assertEquals(local.get_content('/%s/test.odt' % self.workspace_title), 'Content updated from Locally Edited.') # Update file in remote sync root remote.update_content('/test.odt', 'Content updated from remote sync root.') self._sync(syn) self.assertEquals(local.get_content('/%s/test.odt' % self.workspace_title), 'Content updated from remote sync root.') self.assertEquals(local.get_content('/%s/test.odt' % LOCALLY_EDITED_FOLDER_NAME), 'Content updated from remote sync root.')
def test_synchronization_modification_on_created_file(self): raise SkipTest("WIP in https://jira.nuxeo.com/browse/NXDRIVE-170") ctl = self.controller_1 # Regression test: a file is created locally, then modification is # detected before first upload ctl.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url, self.user_1, self.password_1) ctl.bind_root(self.local_nxdrive_folder_1, self.workspace) syn = ctl.synchronizer expected_folder = os.path.join(self.local_nxdrive_folder_1, self.workspace_title) self.assertEquals(ctl.list_pending(), []) self.wait() syn.loop(delay=0.010, max_loops=1) # Let's create some document on the client and the server local = LocalClient(expected_folder) local.make_folder('/', 'Folder') local.make_file('/Folder', 'File.txt', content='Some content.') # First local scan (assuming the network is offline): syn.scan_local(self.local_nxdrive_folder_1) self.assertEquals(len(ctl.list_pending()), 2) self.assertEquals(ctl.children_states(expected_folder), [ (u'Folder', 'children_modified'), ]) self.assertEquals(ctl.children_states(expected_folder + '/Folder'), [ (u'File.txt', u'unknown'), ]) # Wait a bit for file time stamps to increase enough: on most OS # the file modification time resolution is 1s time.sleep(OS_STAT_MTIME_RESOLUTION) # Let's modify it offline and rescan locally local.update_content('/Folder/File.txt', content='Some content.') syn.scan_local(self.local_nxdrive_folder_1) self.assertEquals(len(ctl.list_pending()), 2) self.assertEquals(ctl.children_states(expected_folder), [ (u'Folder', u'children_modified'), ]) self.assertEquals(ctl.children_states(expected_folder + '/Folder'), [ (u'File.txt', u'locally_modified'), ]) # Assume the computer is back online, the synchronization should occur # as if the document was just created and not trigger an update self.wait() syn.loop(delay=0.010, max_loops=1) self.assertEquals(ctl.list_pending(), []) self.assertEquals(ctl.children_states(expected_folder), [ (u'Folder', u'synchronized'), ]) self.assertEquals(ctl.children_states(expected_folder + '/Folder'), [ (u'File.txt', u'synchronized'), ])
def test_drive_edit_non_synced_doc(self): raise SkipTest("WIP in https://jira.nuxeo.com/browse/NXDRIVE-170") ctl = self.controller_1 ctl.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url, self.user_1, self.password_1) local = LocalClient(self.local_nxdrive_folder_1) remote = self.remote_document_client_1 syn = ctl.synchronizer # Create file in test workspace (non sync root) doc_id = remote.make_file('/', 'test.odt', 'Some content.') # Drive edit file ctl.download_edit(self.nuxeo_url, 'default', doc_id, 'test.odt', open_file=False) # Check file is downloaded to the Locally Edited folder self.assertTrue(local.exists('/%s/test.odt' % LOCALLY_EDITED_FOLDER_NAME)) self.assertEquals(local.get_content('/%s/test.odt' % LOCALLY_EDITED_FOLDER_NAME), 'Some content.') # Check Locally Edited collection exists, is registered as a sync root # for test user and file is member of it self.assertTrue(self.root_remote_client.exists( self.locally_edited_path)) sync_roots = remote.get_roots() self.assertEquals(len(sync_roots), 1) self.assertEquals(sync_roots[0].path, self.locally_edited_path) self.assertTrue(doc_id in self.root_remote_client.get_collection_members( self.locally_edited_path)) # Update locally edited file # Let's first sync because of https://jira.nuxeo.com/browse/NXDRIVE-144 self._sync(syn) time.sleep(OS_STAT_MTIME_RESOLUTION) local.update_content('/%s/test.odt' % LOCALLY_EDITED_FOLDER_NAME, 'Updated content.') self._sync(syn, wait_for_async=False) self.assertEquals(remote.get_content('/test.odt'), 'Updated content.') # Drive edit file a second time (should not download a new file but # detect the existing one) ctl.download_edit(self.nuxeo_url, 'default', doc_id, 'test.odt', open_file=False) self.assertEquals(len(local.get_children_info('/%s' % LOCALLY_EDITED_FOLDER_NAME)), 1) # Update locally edited file time.sleep(OS_STAT_MTIME_RESOLUTION) local.update_content('/%s/test.odt' % LOCALLY_EDITED_FOLDER_NAME, 'Twice updated content.') self._sync(syn, wait_for_async=False) self.assertEquals(remote.get_content('/test.odt'), 'Twice updated content.')
def test_self_conflict(self): raise SkipTest("WIP in https://jira.nuxeo.com/browse/NXDRIVE-170") ctl = self.controller_1 sb = ctl.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url, self.user_1, self.password_1) if not ctl.get_top_level_state(self.local_nxdrive_folder_1).last_remote_modifier: raise SkipTest("Self-conflict automatic resolution not implemented in Nuxeo Platform %s" % sb.server_version) local = LocalClient(os.path.join(self.local_nxdrive_folder_1, self.workspace_title)) remote = self.remote_file_system_client_1 syn = ctl.synchronizer # Launch first synchronization self._sync(syn) self.assertTrue(local.exists('/test.txt')) # Update content on both sides by the same user, remote last time.sleep(OS_STAT_MTIME_RESOLUTION) local.update_content('/test.txt', 'Local update') time.sleep(1.0) remote.update_content(self.file_id, 'Remote update') self._sync(syn, max_loops=2) self.assertEquals(len(local.get_children_info('/')), 1) self.assertTrue(local.exists('/test.txt')) self.assertEquals(local.get_content('/test.txt'), 'Remote update') remote_children = remote.get_children_info(self.workspace_id) self.assertEquals(len(remote_children), 1) self.assertEquals(remote_children[0].uid, self.file_id) self.assertEquals(remote_children[0].name, 'test.txt') self.assertEquals(remote.get_content(remote_children[0].uid), 'Remote update') # Update content on both sides by the same user, local last remote.update_content(self.file_id, 'Remote update 2') time.sleep(OS_STAT_MTIME_RESOLUTION) local.update_content('/test.txt', 'Local update 2') self._sync(syn, max_loops=2) self.assertEquals(len(local.get_children_info('/')), 1) self.assertTrue(local.exists('/test.txt')) self.assertEquals(local.get_content('/test.txt'), 'Local update 2') remote_children = remote.get_children_info(self.workspace_id) self.assertEquals(len(remote_children), 1) self.assertEquals(remote_children[0].uid, self.file_id) self.assertEquals(remote_children[0].name, 'test.txt') self.assertEquals(remote.get_content(remote_children[0].uid), 'Local update 2')
def test_drive_edit_remote_move_sync_root_to_non_sync_root(self): raise SkipTest("WIP in https://jira.nuxeo.com/browse/NXDRIVE-170") ctl = self.controller_1 ctl.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url, self.user_1, self.password_1) local = LocalClient(self.local_nxdrive_folder_1) remote = self.remote_document_client_1 syn = ctl.synchronizer # Create folder, register it as a sync root and create file inside it sync_root_id = remote.make_folder('/', 'syncRoot') ctl.bind_root(self.local_nxdrive_folder_1, sync_root_id) doc_id = remote.make_file(sync_root_id, 'test.odt', 'Some content.') # Launch first synchronization self._sync(syn) self.assertTrue(local.exists('/syncRoot/test.odt')) # Drive edit file ctl.download_edit(self.nuxeo_url, 'default', doc_id, 'test.odt', open_file=False) # Check file is downloaded to the Locally Edited folder self.assertTrue(local.exists('/%s/test.odt' % LOCALLY_EDITED_FOLDER_NAME)) # Update locally edited file # Let's first sync because of https://jira.nuxeo.com/browse/NXDRIVE-144 self._sync(syn) time.sleep(OS_STAT_MTIME_RESOLUTION) local.update_content('/%s/test.odt' % LOCALLY_EDITED_FOLDER_NAME, 'Content updated from Locally Edited.') self._sync(syn, wait_for_async=False) self.assertEquals(remote.get_content('/syncRoot/test.odt'), 'Content updated from Locally Edited.') self._sync(syn) self.assertEquals(local.get_content('/syncRoot/test.odt'), 'Content updated from Locally Edited.') # Move file to non sync root workspace remote.move('/syncRoot/test.odt', self.workspace) self._sync(syn) self.assertFalse(local.exists('/syncRoot/test.odt')) self.assertTrue(local.exists('/%s/test.odt' % LOCALLY_EDITED_FOLDER_NAME)) self.assertEquals(len(local.get_children_info('/%s' % LOCALLY_EDITED_FOLDER_NAME)), 1)
def test_local_replace(self): # Bind the server and root workspace ctl = self.controller_1 ctl.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url, self.user_1, self.password_1) ctl.bind_root(self.local_nxdrive_folder_1, self.workspace) # Launch first synchronization self.wait_audit_change_finder_if_needed() self.wait() syn = ctl.synchronizer syn.loop(delay=0.1, max_loops=1) # Get local and remote clients local = LocalClient(self.local_test_folder_1) remote = self.remote_document_client_1 # Create 2 files with the same name but different content # in separate folders local.make_file('/', 'test.odt', 'Some content.') local.make_folder('/', 'folder') copyfile(os.path.join(self.local_test_folder_1, 'test.odt'), os.path.join(self.local_test_folder_1, 'folder', 'test.odt')) local.update_content('/folder/test.odt', 'Updated content.') # Copy the newest file to the root workspace and synchronize it sync_root = os.path.join(self.local_nxdrive_folder_1, self.workspace_title) test_file = os.path.join(self.local_test_folder_1, 'folder', 'test.odt') copyfile(test_file, os.path.join(sync_root, 'test.odt')) syn.loop(delay=0.1, max_loops=1) self.assertTrue(remote.exists('/test.odt')) self.assertEquals(remote.get_content('/test.odt'), 'Updated content.') # Copy the oldest file to the root workspace and synchronize it. # First wait a bit for file time stamps to increase enough. time.sleep(self.OS_STAT_MTIME_RESOLUTION) copyfile(os.path.join(self.local_test_folder_1, 'test.odt'), os.path.join(sync_root, 'test.odt')) syn.loop(delay=0.1, max_loops=1) self.assertTrue(remote.exists('/test.odt')) self.assertEquals(remote.get_content('/test.odt'), 'Some content.')
def test_local_replace(self): # Bind the server and root workspace ctl = self.controller_1 ctl.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url, self.user_1, self.password_1) ctl.bind_root(self.local_nxdrive_folder_1, self.workspace) # Launch first synchronization time.sleep(self.AUDIT_CHANGE_FINDER_TIME_RESOLUTION) self.wait() syn = ctl.synchronizer syn.loop(delay=0.1, max_loops=1) # Get local and remote clients local = LocalClient(self.local_test_folder_1) remote = self.remote_document_client_1 # Create 2 files with the same name but different content # in separate folders local.make_file('/', 'test.odt', 'Some content.') local.make_folder('/', 'folder') copyfile(os.path.join(self.local_test_folder_1, 'test.odt'), os.path.join(self.local_test_folder_1, 'folder', 'test.odt')) local.update_content('/folder/test.odt', 'Updated content.') # Copy the newest file to the root workspace and synchronize it sync_root = os.path.join(self.local_nxdrive_folder_1, self.workspace_title) copyfile(os.path.join(self.local_test_folder_1, 'folder', 'test.odt'), os.path.join(sync_root, 'test.odt')) syn.loop(delay=0.1, max_loops=1) self.assertTrue(remote.exists('/test.odt')) self.assertEquals(remote.get_content('/test.odt'), 'Updated content.') # Copy the oldest file to the root workspace and synchronize it. # First wait a bit for file time stamps to increase enough. time.sleep(self.OS_STAT_MTIME_RESOLUTION) copyfile(os.path.join(self.local_test_folder_1, 'test.odt'), os.path.join(sync_root, 'test.odt')) syn.loop(delay=0.1, max_loops=1) self.assertTrue(remote.exists('/test.odt')) self.assertEquals(remote.get_content('/test.odt'), 'Some content.')
def test_local_replace(self): # Bind the server and root workspace ctl = self.controller_1 ctl.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url, self.user_1, self.password_1) ctl.bind_root(self.local_nxdrive_folder_1, self.workspace) # Launch first synchronization time.sleep(self.AUDIT_CHANGE_FINDER_TIME_RESOLUTION) self.wait() syn = ctl.synchronizer syn.loop(delay=0.1, max_loops=1) # Get local and remote clients local = LocalClient(self.local_test_folder_1) remote = self.remote_document_client_1 # Create 2 files with the same name but different content # in separate folders local.make_file("/", "test.odt", "Some content.") local.make_folder("/", "folder") copyfile( os.path.join(self.local_test_folder_1, "test.odt"), os.path.join(self.local_test_folder_1, "folder", "test.odt"), ) local.update_content("/folder/test.odt", "Updated content.") # Copy the newest file to the root workspace and synchronize it sync_root = os.path.join(self.local_nxdrive_folder_1, self.workspace_title) copyfile(os.path.join(self.local_test_folder_1, "folder", "test.odt"), os.path.join(sync_root, "test.odt")) syn.loop(delay=0.1, max_loops=1) self.assertTrue(remote.exists("/test.odt")) self.assertEquals(remote.get_content("/test.odt"), "Updated content.") # Copy the oldest file to the root workspace and synchronize it. # First wait a bit for file time stamps to increase enough. time.sleep(self.OS_STAT_MTIME_RESOLUTION) copyfile(os.path.join(self.local_test_folder_1, "test.odt"), os.path.join(sync_root, "test.odt")) syn.loop(delay=0.1, max_loops=1) self.assertTrue(remote.exists("/test.odt")) self.assertEquals(remote.get_content("/test.odt"), "Some content.")
def test_synchronization_modification_on_created_file(): # Regression test: a file is created locally, then modification is detected # before first upload ctl.bind_server(LOCAL_NXDRIVE_FOLDER, NUXEO_URL, USER, PASSWORD) ctl.bind_root(LOCAL_NXDRIVE_FOLDER, TEST_WORKSPACE) expected_folder = os.path.join(LOCAL_NXDRIVE_FOLDER, TEST_WORKSPACE_TITLE) assert_equal(ctl.list_pending(), []) # Let's create some document on the client and the server local = LocalClient(expected_folder) local.make_folder("/", "Folder") local.make_file("/Folder", "File.txt", content="Some content.") # First local scan (assuming the network is offline): ctl.scan_local(expected_folder) assert_equal(len(ctl.list_pending()), 2) assert_equal(ctl.children_states(expected_folder), [(u"/Folder", "children_modified")]) assert_equal(ctl.children_states(expected_folder + "/Folder"), [(u"/Folder/File.txt", u"unknown")]) # Wait a bit for file time stamps to increase enough: on most OS the file # modification time resolution is 1s time.sleep(1.0) # Let's modify it offline and rescan locally local.update_content("/Folder/File.txt", content="Some content.") ctl.scan_local(expected_folder) assert_equal(len(ctl.list_pending()), 2) assert_equal(ctl.children_states(expected_folder), [(u"/Folder", u"children_modified")]) assert_equal(ctl.children_states(expected_folder + "/Folder"), [(u"/Folder/File.txt", u"locally_modified")]) # Assume the computer is back online, the synchronization should occur as if # the document was just created and not trigger an update ctl.loop(full_local_scan=True, full_remote_scan=True, delay=0.010, max_loops=1, fault_tolerant=False) assert_equal(len(ctl.list_pending()), 0) assert_equal(ctl.children_states(expected_folder), [(u"/Folder", u"synchronized")]) assert_equal(ctl.children_states(expected_folder + "/Folder"), [(u"/Folder/File.txt", u"synchronized")])
class TestPermissionHierarchy(UnitTestCase): def setUpApp(self, **kwargs): super(TestPermissionHierarchy, self).setUpApp( server_profile='permission') def setUp(self): self.admin = self.root_remote_client self.user1 = RemoteDocumentClientForTests( self.nuxeo_url, self.user_1, 'nxdrive-test-device-1', self.version, password=self.password_1, upload_tmp_dir=self.upload_tmp_dir) self.user2 = RemoteDocumentClientForTests( self.nuxeo_url, self.user_2, 'nxdrive-test-device-2', self.version, password=self.password_2, upload_tmp_dir=self.upload_tmp_dir) self.local_client_1 = LocalClient(self.local_nxdrive_folder_1) self.local_client_2 = LocalClient(self.local_nxdrive_folder_2) # Make sure user workspace is created and fetch its UID self.workspace_uid = self.user1.make_file_in_user_workspace( 'File in user workspace', filename='USFile.txt')['parentRef'] self.addCleanup(self.delete_wspace) def delete_wspace(self): # Cleanup user workspace if self.workspace_uid and self.admin.exists(self.workspace_uid): self.admin.delete(self.workspace_uid, use_trash=False) def set_readonly(self, user, doc_path, grant=True): """ Mark one folder as RO or RW. :param bool grant: Set RO if True else RW. """ op_input = 'doc:' + doc_path if grant: self.root_remote_client.execute('Document.SetACE', op_input=op_input, user=user, permission='Read') self.root_remote_client.block_inheritance( doc_path, overwrite=False) else: self.root_remote_client.execute('Document.SetACE', op_input=op_input, user=user, permission='ReadWrite', grant='true') def test_sync_delete_root(self): # Create test folder in user workspace as test user test_folder_uid = self.user1.make_folder( self.workspace_uid, 'test_folder') # Create a document in the test folder self.user1.make_file( test_folder_uid, 'test_file.txt', 'Some content.') # Register test folder as a sync root self.user1.register_as_root(test_folder_uid) # Start engine self.engine_1.start() # Wait for synchronization self.wait_sync(wait_for_async=True) # Check locally synchronized content root = '/My Docs/test_folder' self.assertTrue(self.local_client_1.exists(root)) self.assertTrue(self.local_client_1.exists(root + '/test_file.txt')) # Delete test folder self.user1.delete(test_folder_uid) # Wait for synchronization self.wait_sync(wait_for_async=True) # Check locally synchronized content self.assertFalse(self.local_client_1.exists(root)) self.assertEqual( len(self.local_client_1.get_children_info('/My Docs')), 0) def test_sync_delete_shared_folder(self): self.engine_1.start() # Register user workspace as a sync root for user1 self.user1.register_as_root(self.workspace_uid) # Create test folder in user workspace as user1 test_folder_uid = self.user1.make_folder( self.workspace_uid, 'test_folder') self.wait_sync(wait_for_async=True) assert self.local_client_1.exists('/My Docs') assert self.local_client_1.exists('/My Docs/test_folder') # Grant ReadWrite permission to user2 on test folder self.set_readonly(self.user_2, test_folder_uid, grant=False) self.wait_sync(wait_for_async=True) # Register test folder as a sync root for user2 self.user2.register_as_root(test_folder_uid) self.wait_sync(wait_for_async=True) # Delete test folder self.user1.delete(test_folder_uid) self.wait_sync(wait_for_async=True) # Check locally synchronized content assert not self.local_client_1.exists('/My Docs/test_folder') children = self.local_client_1.get_children_info('/My Docs') assert len(children) == 1 def test_sync_unshared_folder(self): # Register user workspace as a sync root for user1 self.user1.register_as_root(self.workspace_uid) # Start engine self.engine_2.start() # Wait for synchronization self.wait_sync(wait_for_async=True, wait_for_engine_2=True, wait_for_engine_1=False) # Check locally synchronized content self.assertTrue(self.local_client_2.exists('/My Docs')) self.assertTrue(self.local_client_2.exists('/Other Docs')) # Create test folder in user workspace as user1 test_folder_uid = self.user1.make_folder(self.workspace_uid, 'Folder A') folder_b = self.user1.make_folder(test_folder_uid, 'Folder B') folder_c = self.user1.make_folder(folder_b, 'Folder C') folder_d = self.user1.make_folder(folder_c, 'Folder D') self.user1.make_folder(folder_d, 'Folder E') # Grant ReadWrite permission to user2 on test folder self.set_readonly(self.user_2, test_folder_uid, grant=False) # Register test folder as a sync root for user2 self.user2.register_as_root(test_folder_uid) # Wait for synchronization self.wait_sync(wait_for_async=True, wait_for_engine_2=True, wait_for_engine_1=False) self.assertTrue(self.local_client_2.exists('/Other Docs/Folder A')) self.assertTrue(self.local_client_2.exists( '/Other Docs/Folder A/Folder B/Folder C/Folder D/Folder E')) # Use for later get_fs_item checks folder_b_fs = self.local_client_2.get_remote_id( '/Other Docs/Folder A/Folder B') folder_a_fs = self.local_client_2.get_remote_id('/Other Docs/Folder A') # Unshare Folder A and share Folder C self.admin.execute('Document.RemoveACL', op_input='doc:' + test_folder_uid, acl='local') self.set_readonly(self.user_2, folder_c) self.user2.register_as_root(folder_c) self.wait_sync(wait_for_async=True, wait_for_engine_2=True, wait_for_engine_1=False) self.assertFalse(self.local_client_2.exists('/Other Docs/Folder A')) self.assertTrue(self.local_client_2.exists('/Other Docs/Folder C')) self.assertTrue(self.local_client_2.exists( '/Other Docs/Folder C/Folder D/Folder E')) # Verify that we dont have any 403 errors self.assertIsNone(self.remote_file_system_client_2.get_fs_item( folder_a_fs)) self.assertIsNone(self.remote_file_system_client_2.get_fs_item( folder_b_fs)) def test_sync_move_permission_removal(self): root = self.user1.make_folder(self.workspace_uid, 'testing') readonly = self.user1.make_folder(root, 'ReadFolder') readwrite = self.user1.make_folder(root, 'WriteFolder') # Register user workspace as a sync root for user1 self.user1.register_as_root(self.workspace_uid) # Register root folder as a sync root for user2 self.set_readonly(self.user_2, root, grant=False) self.user2.register_as_root(root) # Make one read-only document self.user1.make_file(readonly, 'file_ro.txt', content='Read-only doc.') # Read only folder for user 2 self.set_readonly(self.user_2, readonly) # Basic test to be sure we are in RO mode with self.assertRaises(HTTPError): self.user2.make_file(readonly, 'test.txt', content='test') # ReadWrite folder for user 2 self.set_readonly(self.user_2, readwrite, grant=False) # Start'n sync self.engine_2.start() self.wait_sync(wait_for_async=True, wait_for_engine_1=False, wait_for_engine_2=True) # Checks root = '/Other Docs/testing/' self.assertTrue(self.local_client_2.exists(root + 'ReadFolder')) self.assertTrue(self.local_client_2.exists( root + 'ReadFolder/file_ro.txt')) self.assertTrue(self.local_client_2.exists(root + 'WriteFolder')) self.assertEqual( self.local_client_2.get_content(root + 'ReadFolder/file_ro.txt'), 'Read-only doc.') # Move the read-only file self.local_client_2.move(root + 'ReadFolder/file_ro.txt', root + 'WriteFolder', name='file_rw.txt') # Remove RO on ReadFolder folder self.set_readonly(self.user_2, readonly, grant=False) # Edit the new writable file self.local_client_2.update_content(root + 'WriteFolder/file_rw.txt', 'Now a fresh read-write doc.') # Sync self.wait_sync(wait_for_async=True, wait_for_engine_1=False, wait_for_engine_2=True) # Local checks self.assertFalse(self.local_client_2.exists( root + 'ReadFolder/file_ro.txt')) self.assertFalse(self.local_client_2.exists( root + 'WriteFolder/file_ro.txt')) self.assertTrue(self.local_client_2.exists( root + 'WriteFolder/file_rw.txt')) self.assertEqual( self.local_client_2.get_content(root + 'WriteFolder/file_rw.txt'), 'Now a fresh read-write doc.') # Remote checks self.assertEqual(len(self.user1.get_children_info(readonly)), 0) children = self.user1.get_children_info(readwrite) self.assertEqual(len(children), 1) self.assertEqual(children[0].filename, 'file_rw.txt') good_digest = hashlib.md5('Now a fresh read-write doc.').hexdigest() self.assertEqual(children[0].digest, good_digest) # No errors check self.assertEqual(len(self.engine_2.get_dao().get_errors()), 0)
def test_update_local_file_content_update_remote_file_property(self): raise SkipTest("WIP in https://jira.nuxeo.com/browse/NXDRIVE-170") # Get local and remote clients local = LocalClient(self.local_nxdrive_folder_1) remote = self.remote_document_client_1 # Bind server and test workspace for nuxeoDriveTestUser_user_1 ctl = self.controller_1 ctl.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url, self.user_1, self.password_1) ctl.bind_root(self.local_nxdrive_folder_1, self.workspace) # Launch first synchronization self.wait() sync = ctl.synchronizer sync.loop(delay=0.1, max_loops=1) # Test workspace should be created locally self.assertTrue(local.exists('/Nuxeo Drive Test Workspace')) # Create a local file in the test workspace then synchronize local.make_file('/Nuxeo Drive Test Workspace', 'test.odt', 'Some content.') sync.loop(delay=0.1, max_loops=1) # Test file should be created remotely in the test workspace self.assertTrue(remote.exists('/test.odt')) # Locally update the file content and remotely update one of its # properties concurrently, then synchronize time.sleep(OS_STAT_MTIME_RESOLUTION) local.update_content('/Nuxeo Drive Test Workspace/test.odt', 'Updated content.') self.assertEquals(local.get_content( '/Nuxeo Drive Test Workspace/test.odt'), 'Updated content.') test_file_ref = remote._check_ref('/test.odt') # Wait for 1 second to make sure the file's last modification time # will be different from the pair state's last remote update time time.sleep(REMOTE_MODIFICATION_TIME_RESOLUTION) remote.update(test_file_ref, properties={'dc:description': 'Some description.'}) test_file = remote.fetch(test_file_ref) self.assertEqual(test_file['properties']['dc:description'], 'Some description.') self.wait() sync.loop(delay=0.1, max_loops=2) # Test file should be updated remotely in the test workspace, # and no conflict should be detected. # Even though fetching the remote changes will send a # 'documentModified' event for the test file as a result of its # dc:description property update, since the file will not have been # renamed nor moved and its content not modified since last # synchronization, its remote pair state will not be marked as # 'modified', see Model.update_remote(). # Thus the pair state will be ('modified', 'synchronized'), resolved as # 'locally_modified'. self.assertTrue(remote.exists('/test.odt')) self.assertEquals(remote.get_content('/test.odt'), 'Updated content.') test_file = remote.fetch(test_file_ref) self.assertEqual(test_file['properties']['dc:description'], 'Some description.') self.assertEqual(len(remote.get_children_info(self.workspace)), 1) # Check that the content of the test file has not changed self.assertTrue(local.exists('/Nuxeo Drive Test Workspace/test.odt')) self.assertEquals(local.get_content( '/Nuxeo Drive Test Workspace/test.odt'), 'Updated content.') self.assertEqual(len(local.get_children_info( '/Nuxeo Drive Test Workspace')), 1)
class TestIntegrationVersioning(IntegrationTestCase): def setUp(self): super(TestIntegrationVersioning, self).setUp() self.controller_1.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url, self.user_1, self.password_1) self.controller_2.bind_server(self.local_nxdrive_folder_2, self.nuxeo_url, self.user_2, self.password_2) self.controller_1.bind_root(self.local_nxdrive_folder_1, self.workspace) self.controller_2.bind_root(self.local_nxdrive_folder_2, self.workspace) self.syn_1 = self.controller_1.synchronizer self.syn_2 = self.controller_2.synchronizer self.syn_1.loop(delay=0.010, max_loops=1, no_event_init=True) self.syn_2.loop(delay=0.010, max_loops=1, no_event_init=True) # Fetch server bindings after sync loop as it closes the Session self.sb_1 = self.controller_1.get_server_binding( self.local_nxdrive_folder_1) self.sb_2 = self.controller_2.get_server_binding( self.local_nxdrive_folder_2) self.remote_client_1 = self.remote_document_client_1 self.remote_client_2 = self.remote_document_client_2 sync_root_folder_1 = os.path.join(self.local_nxdrive_folder_1, self.workspace_title) sync_root_folder_2 = os.path.join(self.local_nxdrive_folder_2, self.workspace_title) self.local_client_1 = LocalClient(sync_root_folder_1) self.local_client_2 = LocalClient(sync_root_folder_2) # Call the Nuxeo operation to set the versioning delay to 10 seconds self.versioning_delay = self.OS_STAT_MTIME_RESOLUTION * 10 self.root_remote_client.execute( "NuxeoDrive.SetVersioningOptions", delay=str(self.versioning_delay)) def test_versioning(self): # Create a file as user 1 self.local_client_1.make_file('/', 'Test versioning.txt', "This is version 0") self._synchronize_and_assert(self.syn_1, self.sb_1, 1) doc = self.root_remote_client.fetch( self.TEST_WORKSPACE_PATH + '/Test versioning.txt') self._assert_version(doc, 0, 0) # Synchronize it for user 2 self.assertTrue(self.remote_client_2.exists('/Test versioning.txt')) self._synchronize_and_assert(self.syn_2, self.sb_2, 1, wait=True) self.assertTrue(self.local_client_2.exists('/Test versioning.txt')) # Update it as user 2 => should be versioned time.sleep(self.OS_STAT_MTIME_RESOLUTION) self.local_client_2.update_content('/Test versioning.txt', "Modified content") self._synchronize_and_assert(self.syn_2, self.sb_2, 1) doc = self.root_remote_client.fetch( self.TEST_WORKSPACE_PATH + '/Test versioning.txt') self._assert_version(doc, 0, 1) # Update it as user 2 => should NOT be versioned # since the versioning delay (10s) is not passed by time.sleep(self.OS_STAT_MTIME_RESOLUTION) self.local_client_2.update_content('/Test versioning.txt', "Content twice modified") self._synchronize_and_assert(self.syn_2, self.sb_2, 1) doc = self.root_remote_client.fetch( self.TEST_WORKSPACE_PATH + '/Test versioning.txt') self._assert_version(doc, 0, 1) # Update it as user 2 after 10s => should be versioned # since the versioning delay is passed by time.sleep(self.versioning_delay + 0.1) self.local_client_2.update_content('/Test versioning.txt', "Updated again!!") self._synchronize_and_assert(self.syn_2, self.sb_2, 1) doc = self.root_remote_client.fetch( self.TEST_WORKSPACE_PATH + '/Test versioning.txt') self._assert_version(doc, 0, 2) def test_version_restore(self): remote_client = self.remote_client_1 local_client = self.local_client_1 # Create a remote doc doc = remote_client.make_file(self.workspace, 'Document to restore.txt', content="Initial content.") time.sleep(self.AUDIT_CHANGE_FINDER_TIME_RESOLUTION) self.wait() self._synchronize_and_assert(self.syn_1, self.sb_1, 1) self.assertTrue(local_client.exists('/Document to restore.txt')) self.assertEquals(local_client.get_content('/Document to restore.txt'), "Initial content.") # Create version 1.0, update content, then restore version 1.0 remote_client.create_version(doc, 'Major') # Ensure that modification time is different between the version # and the updated live document, otherwise the synchronizer won't # consider the restored document (with the modification date of # the version) as to be updated time.sleep(1.0) remote_client.update_content(doc, "Updated content.") time.sleep(self.AUDIT_CHANGE_FINDER_TIME_RESOLUTION) self.wait() self._synchronize_and_assert(self.syn_1, self.sb_1, 1) self.assertEquals(local_client.get_content('/Document to restore.txt'), "Updated content.") version_uid = remote_client.get_versions(doc)[0][0] remote_client.restore_version(version_uid) time.sleep(self.AUDIT_CHANGE_FINDER_TIME_RESOLUTION) self.wait() self._synchronize_and_assert(self.syn_1, self.sb_1, 1) self.assertEquals(local_client.get_content('/Document to restore.txt'), "Initial content.") def _synchronize_and_assert(self, synchronizer, server_binding, expected_synchronized, wait=False): if wait: # Wait for audit changes to be detected after the 1 second step time.sleep(self.AUDIT_CHANGE_FINDER_TIME_RESOLUTION) n_synchronized = synchronizer.update_synchronize_server(server_binding) self.assertEqual(n_synchronized, expected_synchronized) def _assert_version(self, doc, major, minor): self.assertEquals(doc['properties']['uid:major_version'], major) self.assertEquals(doc['properties']['uid:minor_version'], minor)
class TestDriveEdit(UnitTestCase): locally_edited_path = ( "/default-domain/UserWorkspaces/" + "nuxeoDriveTestUser-user-1/Collections/" + LOCALLY_EDITED_FOLDER_NAME ) def setUpApp(self): super(TestDriveEdit, self).setUpApp() self.drive_edit = self.manager_1.get_drive_edit() self.drive_edit.driveEditUploadCompleted.connect(self.app.sync_completed) self.drive_edit.start() self.remote = self.remote_document_client_1 self.local = LocalClient(os.path.join(self.nxdrive_conf_folder_1, "edit")) def tearDownApp(self): self.drive_edit.stop() super(TestDriveEdit, self).tearDownApp() def test_note_edit(self): remote_fs_client = self.remote_file_system_client_1 toplevel_folder_info = remote_fs_client.get_filesystem_root_info() workspace_id = remote_fs_client.get_children_info(toplevel_folder_info.uid)[0].uid file_1_id = remote_fs_client.make_file( workspace_id, u"Mode op\xe9ratoire.txt", "Content of file 1 Avec des accents h\xe9h\xe9." ).uid doc_id = file_1_id.split("#")[-1] self._drive_edit_update(doc_id, u"Mode op\xe9ratoire.txt", "Atol de PomPom Gali") def test_filename_encoding(self): filename = u"Mode op\xe9ratoire.txt" doc_id = self.remote.make_file("/", filename, "Some content.") self._drive_edit_update(doc_id, filename, "Test") def _office_locker(self, path): return os.path.join(os.path.dirname(path), "~$" + os.path.basename(path)[2:]) def _openoffice_locker(self, path): return os.path.join(os.path.dirname(path), ".~lock." + os.path.basename(path)[2:]) # def test_autolock_office(self): # self._autolock(self._office_locker) # def test_autolock_openoffice(self): # LibreOffice as well # self._autolock(self._openoffice_locker) def _autolock(self, locker): global called_open, lock_file called_open = False filename = u"Document.docx" doc_id = self.remote.make_file("/", filename, "Some content.") def open_local_file(path): global called_open, lock_file called_open = True # Lock file lock_file = locker(path) with open(lock_file, "w") as f: f.write("plop") self.manager_1.open_local_file = open_local_file self.manager_1.set_drive_edit_auto_lock(1) self.drive_edit._manager.open_local_file = open_local_file self.drive_edit.edit(self.nuxeo_url, doc_id, filename=filename, user=self.user_1) self.wait_sync(timeout=2, fail_if_timeout=False) self.assertTrue(called_open, "Should have called open_local_file") # Should be able to test lock self.assertTrue(self.remote_restapi_client_1.is_locked(doc_id)) os.remove(lock_file) self.wait_sync(timeout=2, fail_if_timeout=False) # Should be unlock self.assertFalse(self.remote_restapi_client_1.is_locked(doc_id)) self.manager_1.set_drive_edit_auto_lock(0) with open(lock_file, "w") as f: f.write("plop") self.wait_sync(timeout=2, fail_if_timeout=False) self.assertFalse(self.remote_restapi_client_1.is_locked(doc_id)) def _drive_edit_update(self, doc_id, filename, content, url=None): # Download file local_path = u"/%s/%s" % (doc_id, filename) def open_local_file(path): pass self.manager_1.open_local_file = open_local_file if url is None: self.drive_edit._prepare_edit(self.nuxeo_url, doc_id) else: self.drive_edit.handle_url(url) self.assertTrue(self.local.exists(local_path)) self.wait_sync(timeout=2, fail_if_timeout=False) self.local.delete_final(local_path) # Update file content self.local.update_content(local_path, content) self.wait_sync() self.assertEquals(self.remote.get_blob(self.remote.get_info(doc_id)), content) # Update file content twice update_content = content + " updated" self.local.update_content(local_path, update_content) self.wait_sync() self.assertEquals(self.remote.get_blob(self.remote.get_info(doc_id)), update_content)
def test_update_local_file_content_update_remote_file_property(self): # Get local and remote clients local = LocalClient(self.local_nxdrive_folder_1) remote = self.remote_document_client_1 # Bind server and test workspace for nuxeoDriveTestUser_user_1 ctl = self.controller_1 ctl.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url, self.user_1, self.password_1) ctl.bind_root(self.local_nxdrive_folder_1, self.workspace) # Launch first synchronization self.wait_audit_change_finder_if_needed() self.wait() sync = ctl.synchronizer sync.loop(delay=0.1, max_loops=1) # Test workspace should be created locally self.assertTrue(local.exists('/Nuxeo Drive Test Workspace')) # Create a local file in the test workspace then synchronize local.make_file('/Nuxeo Drive Test Workspace', 'test.odt', 'Some content.') sync.loop(delay=0.1, max_loops=1) # Test file should be created remotely in the test workspace self.assertTrue(remote.exists('/test.odt')) # Locally update the file content and remotely update one of its # properties concurrently, then synchronize time.sleep(self.OS_STAT_MTIME_RESOLUTION) local.update_content('/Nuxeo Drive Test Workspace/test.odt', 'Updated content.') self.assertEquals(local.get_content( '/Nuxeo Drive Test Workspace/test.odt'), 'Updated content.') test_file_ref = remote._check_ref('/test.odt') # Wait for 1 second to make sure the file's last modification time # will be different from the pair state's last remote update time time.sleep(self.REMOTE_MODIFICATION_TIME_RESOLUTION) remote.update(test_file_ref, properties={'dc:description': 'Some description.'}) test_file = remote.fetch(test_file_ref) self.assertEqual(test_file['properties']['dc:description'], 'Some description.') self.wait_audit_change_finder_if_needed() self.wait() sync.loop(delay=0.1, max_loops=2) # Test file should be updated remotely in the test workspace, # and no conflict should be detected. # Even though fetching the remote changes will send a # 'documentModified' event for the test file as a result of its # dc:description property update, since the file will not have been # renamed nor moved and its content not modified since last # synchronization, its remote pair state will not be marked as # 'modified', see Model.update_remote(). # Thus the pair state will be ('modified', 'synchronized'), resolved as # 'locally_modified'. self.assertTrue(remote.exists('/test.odt')) self.assertEquals(remote.get_content('/test.odt'), 'Updated content.') test_file = remote.fetch(test_file_ref) self.assertEqual(test_file['properties']['dc:description'], 'Some description.') self.assertEqual(len(remote.get_children_info(self.workspace)), 1) # Check that the content of the test file has not changed self.assertTrue(local.exists('/Nuxeo Drive Test Workspace/test.odt')) self.assertEquals(local.get_content( '/Nuxeo Drive Test Workspace/test.odt'), 'Updated content.') self.assertEqual(len(local.get_children_info( '/Nuxeo Drive Test Workspace')), 1)
def test_synchronize_denying_read_access_local_modification(self): """Test denying Read access with concurrent local modification Use cases: - Deny Read access on a regular folder and make some local and remote changes concurrently. => Only locally modified content should be kept and should be marked as 'unsynchronized', other content should be deleted. Remote changes should not be impacted client side. Local changes should not be impacted server side. - Grant Read access back. => Remote documents should be merged with locally modified content which should be unmarked as 'unsynchronized' and therefore synchronized upstream. See TestIntegrationRemoteDeletion .test_synchronize_remote_deletion_local_modification as the same uses cases are tested. Note that we use the .odt extension for test files to make sure that they are created as File and not Note documents on the server when synchronized upstream, as the current implementation of RemoteDocumentClient is File oriented. """ # Bind the server and root workspace ctl = self.controller_1 ctl.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url, self.user_1, self.password_1) ctl.bind_root(self.local_nxdrive_folder_1, self.workspace) # Get local and remote clients local = LocalClient(os.path.join(self.local_nxdrive_folder_1, self.workspace_title)) remote = self.remote_document_client_1 root_remote = self.root_remote_client # Create documents in the remote root workspace # then synchronize remote.make_folder('/', 'Test folder') remote.make_file('/Test folder', 'joe.odt', 'Some content') remote.make_file('/Test folder', 'jack.odt', 'Some content') remote.make_folder('/Test folder', 'Sub folder 1') remote.make_file('/Test folder/Sub folder 1', 'sub file 1.txt', 'Content') syn = ctl.synchronizer self._synchronize(syn) self.assertTrue(local.exists('/Test folder')) self.assertTrue(local.exists('/Test folder/joe.odt')) self.assertTrue(local.exists('/Test folder/jack.odt')) self.assertTrue(local.exists('/Test folder/Sub folder 1')) self.assertTrue(local.exists( '/Test folder/Sub folder 1/sub file 1.txt')) # Remove Read permission for test user on a regular folder # and make some local and remote changes concurrently then synchronize test_folder_path = self.TEST_WORKSPACE_PATH + '/Test folder' self._set_read_permission("nuxeoDriveTestUser_user_1", test_folder_path, False) # Local changes time.sleep(self.OS_STAT_MTIME_RESOLUTION) # Create new file local.make_file('/Test folder', 'local.odt', 'New local content') # Create new folder with files local.make_folder('/Test folder', 'Local sub folder 2') local.make_file('/Test folder/Local sub folder 2', 'local sub file 2.txt', 'Other local content') # Update file local.update_content('/Test folder/joe.odt', 'Some locally updated content') # Remote changes # Create new file root_remote.make_file(test_folder_path, 'remote.odt', 'New remote content') # Create new folder with files root_remote.make_folder(test_folder_path, 'Remote sub folder 2') root_remote.make_file(test_folder_path + '/Remote sub folder 2', 'remote sub file 2.txt', 'Other remote content') # Update file root_remote.update_content(test_folder_path + '/joe.odt', 'Some remotely updated content') self._synchronize(syn) # Only locally modified content should exist # and should be marked as 'unsynchronized', other content should # have been deleted. # Remote changes should not be impacted client side. # Local changes should not be impacted server side. # Local check self.assertTrue(local.exists('/Test folder')) self.assertEquals(len(local.get_children_info('/Test folder')), 3) self.assertTrue(local.exists('/Test folder/joe.odt')) self.assertEquals(local.get_content('/Test folder/joe.odt'), 'Some locally updated content') self.assertTrue(local.exists('/Test folder/local.odt')) self.assertTrue(local.exists('/Test folder/Local sub folder 2')) self.assertTrue(local.exists( '/Test folder/Local sub folder 2/local sub file 2.txt')) self.assertFalse(local.exists('/Test folder/jack.odt')) self.assertFalse(local.exists('/Test folder/remote.odt')) self.assertFalse(local.exists('/Test folder/Sub folder 1')) self.assertFalse(local.exists( '/Test folder/Sub folder 1/sub file 1.txt')) self.assertFalse(local.exists('/Test folder/Remote sub folder 1')) self.assertFalse(local.exists( '/Test folder/Remote sub folder 1/remote sub file 1.txt')) # State check session = ctl.get_session() self._check_pair_state(session, '/Test folder', 'unsynchronized') self._check_pair_state(session, '/Test folder/joe.odt', 'unsynchronized') self._check_pair_state(session, '/Test folder/local.odt', 'unsynchronized') self._check_pair_state(session, '/Test folder/Local sub folder 2', 'unsynchronized') self._check_pair_state(session, '/Test folder/Local sub folder 2/local sub file 2.txt', 'unsynchronized') # Remote check test_folder_uid = root_remote.get_info(test_folder_path).uid self.assertEquals(len(root_remote.get_children_info( test_folder_uid)), 5) self.assertTrue(root_remote.exists(test_folder_path + '/joe.odt')) self.assertEquals(root_remote.get_content( test_folder_path + '/joe.odt'), 'Some remotely updated content') self.assertTrue(root_remote.exists(test_folder_path + '/jack.odt')) self.assertTrue(root_remote.exists(test_folder_path + '/remote.odt')) self.assertTrue(root_remote.exists(test_folder_path + '/Sub folder 1')) self.assertTrue(root_remote.exists( test_folder_path + '/Sub folder 1/sub file 1.txt')) self.assertTrue(root_remote.exists( test_folder_path + '/Remote sub folder 2')) self.assertTrue(root_remote.exists( test_folder_path + '/Remote sub folder 2/remote sub file 2.txt')) self.assertFalse(root_remote.exists(test_folder_path + '/local.odt')) self.assertFalse(root_remote.exists( test_folder_path + '/Local sub folder 2')) self.assertFalse(root_remote.exists( test_folder_path + '/Local sub folder 1/local sub file 2.txt')) # Add Read permission back for test user then synchronize self._set_read_permission("nuxeoDriveTestUser_user_1", self.TEST_WORKSPACE_PATH + '/Test folder', True) self._synchronize(syn) # Remote documents should be merged with locally modified content # which should be unmarked as 'unsynchronized' and therefore # synchronized upstream. # Local check self.assertTrue(local.exists('/Test folder')) children_info = local.get_children_info('/Test folder') self.assertEquals(len(children_info), 8) for info in children_info: if info.name == 'joe.odt': remote_version = info elif info.name.startswith('joe (') and info.name.endswith(').odt'): local_version = info self.assertTrue(remote_version is not None) self.assertTrue(local_version is not None) self.assertTrue(local.exists(remote_version.path)) self.assertEquals(local.get_content(remote_version.path), 'Some remotely updated content') self.assertTrue(local.exists(local_version.path)) self.assertEquals(local.get_content(local_version.path), 'Some locally updated content') self.assertTrue(local.exists('/Test folder/jack.odt')) self.assertTrue(local.exists('/Test folder/local.odt')) self.assertTrue(local.exists('/Test folder/remote.odt')) self.assertTrue(local.exists('/Test folder/Sub folder 1')) self.assertTrue(local.exists( '/Test folder/Sub folder 1/sub file 1.txt')) self.assertTrue(local.exists('/Test folder/Local sub folder 2')) self.assertTrue(local.exists( '/Test folder/Local sub folder 2/local sub file 2.txt')) self.assertTrue(local.exists('/Test folder/Remote sub folder 2')) self.assertTrue(local.exists( '/Test folder/Remote sub folder 2/remote sub file 2.txt')) # State check self._check_pair_state(session, '/Test folder', 'synchronized') self._check_pair_state(session, '/Test folder/joe.odt', 'synchronized') self._check_pair_state(session, '/Test folder/local.odt', 'synchronized') self._check_pair_state(session, '/Test folder/Local sub folder 2', 'synchronized') self._check_pair_state(session, '/Test folder/Local sub folder 2/local sub file 2.txt', 'synchronized') # Remote check self.assertTrue(remote.exists('/Test folder')) children_info = remote.get_children_info(test_folder_uid) self.assertEquals(len(children_info), 8) for info in children_info: if info.name == 'joe.odt': remote_version = info elif info.name.startswith('joe (') and info.name.endswith(').odt'): local_version = info self.assertTrue(remote_version is not None) self.assertTrue(local_version is not None) remote_version_ref_length = (len(remote_version.path) - len(self.TEST_WORKSPACE_PATH)) remote_version_ref = remote_version.path[-remote_version_ref_length:] self.assertTrue(remote.exists(remote_version_ref)) self.assertEquals(remote.get_content(remote_version_ref), 'Some remotely updated content') local_version_ref_length = (len(local_version.path) - len(self.TEST_WORKSPACE_PATH)) local_version_ref = local_version.path[-local_version_ref_length:] self.assertTrue(remote.exists(local_version_ref)) self.assertEquals(remote.get_content(local_version_ref), 'Some locally updated content') self.assertTrue(remote.exists('/Test folder/jack.odt')) self.assertTrue(remote.exists('/Test folder/local.odt')) self.assertTrue(remote.exists('/Test folder/remote.odt')) self.assertTrue(remote.exists('/Test folder/Sub folder 1')) self.assertTrue(remote.exists( '/Test folder/Sub folder 1/sub file 1.txt')) self.assertTrue(remote.exists('/Test folder/Local sub folder 2')) self.assertTrue(remote.exists( '/Test folder/Local sub folder 2/local sub file 2.txt')) self.assertTrue(remote.exists('/Test folder/Remote sub folder 2')) self.assertTrue(remote.exists( '/Test folder/Remote sub folder 2/remote sub file 2.txt'))
class TestDriveEdit(UnitTestCase): locally_edited_path = ('/default-domain/UserWorkspaces/' + 'nuxeoDriveTestUser-user-1/Collections/' + LOCALLY_EDITED_FOLDER_NAME) def setUpApp(self): super(TestDriveEdit, self).setUpApp() self.drive_edit = self.manager_1.get_drive_edit() self.drive_edit.driveEditUploadCompleted.connect(self.app.sync_completed) self.drive_edit.start() self.remote = self.remote_document_client_1 self.local = LocalClient(os.path.join(self.nxdrive_conf_folder_1, 'edit')) def tearDownApp(self): self.drive_edit.stop() super(TestDriveEdit, self).tearDownApp() def test_filename_encoding(self): filename = u'Mode op\xe9ratoire.txt' doc_id = self.remote.make_file('/', filename, 'Some content.') # Linux / Win + Chrome: quoted utf-8 encoded browser_filename = 'Mode%20op%C3%A9ratoire.txt' self._drive_edit_update(doc_id, filename, browser_filename, 'Win + Chrome') # Win + IE: unquoted utf-8 encoded browser_filename = 'Mode op\xc3\xa9ratoire.txt' self._drive_edit_update(doc_id, filename, browser_filename, 'Win + IE') # Win + FF: quoted string containing unicode browser_filename = 'Mode%20op\xe9ratoire.txt' self._drive_edit_update(doc_id, filename, browser_filename, 'Win + FF') # OSX + Chrome / OSX + FF: quoted utf-8 encoded, except for white spaces! browser_filename = 'Mode op%C3%A9ratoire.txt' self._drive_edit_update(doc_id, filename, browser_filename, 'OS X + Chrome or FF') def _office_locker(self, path): return os.path.join(os.path.dirname(path), "~$" + os.path.basename(path)[2:]) def _openoffice_locker(self, path): return os.path.join(os.path.dirname(path), ".~lock." + os.path.basename(path)[2:]) #def test_autolock_office(self): # self._autolock(self._office_locker) #def test_autolock_openoffice(self): # LibreOffice as well # self._autolock(self._openoffice_locker) def _autolock(self, locker): global called_open, lock_file called_open = False filename = u'Document.docx' doc_id = self.remote.make_file('/', filename, 'Some content.') def open_local_file (path): global called_open, lock_file called_open = True # Lock file lock_file = locker(path) with open(lock_file, 'w') as f: f.write("plop") self.manager_1.open_local_file = open_local_file self.manager_1.set_drive_edit_auto_lock(1) self.drive_edit._manager.open_local_file = open_local_file self.drive_edit.edit(self.nuxeo_url, doc_id, filename=filename, user=self.user_1) self.wait_sync(timeout=2, fail_if_timeout=False) self.assertTrue(called_open, "Should have called open_local_file") # Should be able to test lock self.assertTrue(self.remote_restapi_client_1.is_locked(doc_id)) os.remove(lock_file) self.wait_sync(timeout=2, fail_if_timeout=False) # Should be unlock self.assertFalse(self.remote_restapi_client_1.is_locked(doc_id)) self.manager_1.set_drive_edit_auto_lock(0) with open(lock_file, 'w') as f: f.write("plop") self.wait_sync(timeout=2, fail_if_timeout=False) self.assertFalse(self.remote_restapi_client_1.is_locked(doc_id)) def _drive_edit_update(self, doc_id, filename, browser_filename, content): # Download file local_path = '/%s/%s' % (doc_id, filename) self.drive_edit._prepare_edit(self.nuxeo_url, doc_id, browser_filename) self.assertTrue(self.local.exists(local_path)) self.wait_sync(timeout=2, fail_if_timeout=False) self.local.delete_final(local_path) # Update file content self.local.update_content(local_path, content) self.wait_sync() self.assertEquals(self.remote.get_content('/' + filename), content)
def test_real_conflict(self): raise SkipTest("WIP in https://jira.nuxeo.com/browse/NXDRIVE-170") ctl = self.controller_1 ctl.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url, self.user_1, self.password_1) local = LocalClient(os.path.join(self.local_nxdrive_folder_1, self.workspace_title)) remote = self.remote_file_system_client_2 syn = ctl.synchronizer # Mark workspace as sync root for user2 self.remote_document_client_2.register_as_root(self.workspace) # Launch first synchronization self._sync(syn) self.assertTrue(local.exists('/test.txt')) # Update content on both sides by different users, remote last time.sleep(OS_STAT_MTIME_RESOLUTION) local.update_content('/test.txt', 'Local update') remote.update_content(self.file_id, 'Remote update') self._sync(syn, max_loops=2) local_children = local.get_children_info('/') self.assertEquals(len(local_children), 2) child1 = local_children[0] child2 = local_children[1] self.assertTrue(child1.path.startswith('/test (%s' % self.user_1)) self.assertEquals(local.get_content(child1.path), 'Local update') self.assertEquals(child2.path, '/test.txt') self.assertEquals(local.get_content(child2.path), 'Remote update') remote_children = remote.get_children_info(self.workspace_id) self.assertEquals(len(remote_children), 2) child1 = remote_children[0] child2 = remote_children[1] self.assertEquals(child1.name, 'test.txt') self.assertEquals(remote.get_content(child1.uid), 'Remote update') self.assertTrue(child2.name.startswith('test (%s' % self.user_1)) self.assertEquals(remote.get_content(child2.uid), 'Local update') # Update content on both sides by different users, local last remote.update_content(self.file_id, 'Remote update 2') time.sleep(OS_STAT_MTIME_RESOLUTION) local.update_content('/test.txt', 'Local update 2') self._sync(syn, max_loops=2) local_children = local.get_children_info('/') self.assertEquals(len(local_children), 3) child1 = local_children[0] child2 = local_children[1] child3 = local_children[2] self.assertTrue(child1.path.startswith('/test (%s' % self.user_1)) self.assertTrue(local.get_content(child1.path).startswith('Local update')) self.assertTrue(child2.path.startswith('/test (%s' % self.user_1)) self.assertTrue(local.get_content(child2.path).startswith('Local update')) self.assertEquals(child3.path, '/test.txt') self.assertEquals(local.get_content(child3.path), 'Remote update 2') remote_children = remote.get_children_info(self.workspace_id) self.assertEquals(len(remote_children), 3) child1 = remote_children[0] child2 = remote_children[1] child3 = remote_children[2] self.assertEquals(child1.name, 'test.txt') self.assertEquals(remote.get_content(child1.uid), 'Remote update 2') self.assertTrue(child2.name.startswith('test (%s' % self.user_1)) self.assertTrue(remote.get_content(child2.uid).startswith('Local update')) self.assertTrue(child3.name.startswith('test (%s' % self.user_1)) self.assertTrue(remote.get_content(child3.uid).startswith('Local update'))
def test_local_scan(): ctl.bind_server(TEST_SYNCED_FOLDER, 'http://example.com/nuxeo', 'username', 'secret') ctl.bind_root(TEST_SYNCED_FOLDER, 'folder_1-nuxeo-ref') ctl.bind_root(TEST_SYNCED_FOLDER, 'folder_2-nuxeo-ref') root_1 = join(TEST_SYNCED_FOLDER, 'Folder 1') root_2 = join(TEST_SYNCED_FOLDER, 'Folder 2') client_1 = LocalClient(root_1) # Folder are registered but empty for now assert_equal(ctl.children_states(root_1), []) assert_equal(ctl.children_states(root_2), []) # Put some content under the first root client_1.make_file('/', 'File 1.txt', content="Initial 'File 1.txt' content") folder_3 = client_1.make_folder('/', 'Folder 3') client_1.make_file(folder_3, 'File 2.txt', content="Initial 'File 2.txt' content") # The states have not been updated assert_equal(ctl.children_states(root_1), []) assert_equal(ctl.children_states(root_2), []) # Scanning the other root will not updated the first root states. session = ctl.get_session() ctl.scan_local(root_2, session) assert_equal(ctl.children_states(root_1), []) # Scanning root_1 will find the changes ctl.scan_local(root_1, session) assert_equal(ctl.children_states(root_1), [ (u'/File 1.txt', u'unknown'), (u'/Folder 3', 'children_modified'), ]) folder_3_abs = os.path.join(root_1, 'Folder 3') assert_equal(ctl.children_states(folder_3_abs), [ (u'/Folder 3/File 2.txt', u'unknown'), ]) # Wait a bit for file time stamps to increase enough: on most OS the file # modification time resolution is 1s time.sleep(1.0) # let's do some changes client_1.delete('/File 1.txt') client_1.make_folder('/Folder 3', 'Folder 3.1') client_1.make_file('/Folder 3', 'File 3.txt', content="Initial 'File 3.txt' content") client_1.update_content('/Folder 3/File 2.txt', "Updated content for 'File 2.txt'") # If we don't do a rescan, the controller is not aware of the changes assert_equal(ctl.children_states(root_1), [ (u'/File 1.txt', u'unknown'), (u'/Folder 3', 'children_modified'), ]) folder_3_abs = os.path.join(root_1, 'Folder 3') assert_equal(ctl.children_states(folder_3_abs), [ (u'/Folder 3/File 2.txt', u'unknown'), ]) # Let's scan again ctl.scan_local(root_1, session) assert_equal(ctl.children_states(root_1), [ (u'/Folder 3', 'children_modified'), ]) assert_equal(ctl.children_states(folder_3_abs), [ (u'/Folder 3/File 2.txt', u'locally_modified'), (u'/Folder 3/File 3.txt', u'unknown'), (u'/Folder 3/Folder 3.1', u'unknown') ]) # Delete the toplevel folder that has not been synchronised to the # server client_1.delete('/Folder 3') ctl.scan_local(root_1, session) assert_equal(ctl.children_states(root_1), []) assert_equal(ctl.children_states(folder_3_abs), [])
def test_binding_synchronization_empty_start(): ctl.bind_server(LOCAL_NXDRIVE_FOLDER, NUXEO_URL, USER, PASSWORD) ctl.bind_root(LOCAL_NXDRIVE_FOLDER, TEST_WORKSPACE) expected_folder = os.path.join(LOCAL_NXDRIVE_FOLDER, TEST_WORKSPACE_TITLE) # Nothing to synchronize by default assert_equal(ctl.list_pending(), []) assert_equal(ctl.synchronize(), 0) # Let's create some document on the server make_server_tree() # By default nothing is detected assert_equal(ctl.list_pending(), []) # assert_equal(ctl.children_states(expected_folder), []) # Let's scan manually session = ctl.get_session() ctl.scan_remote(expected_folder, session) # Changes on the remote server have been detected... assert_equal(len(ctl.list_pending()), 11) # ...but nothing is yet visible locally as those files don't exist there # yet. # assert_equal(ctl.children_states(expected_folder), []) # Let's perform the synchronization assert_equal(ctl.synchronize(limit=100), 11) # We should now be fully synchronized assert_equal(len(ctl.list_pending()), 0) assert_equal( ctl.children_states(expected_folder), [(u"/File 5.txt", u"synchronized"), (u"/Folder 1", u"synchronized"), (u"/Folder 2", u"synchronized")], ) local = LocalClient(expected_folder) assert_equal(local.get_content("/Folder 1/File 1.txt"), "aaa") assert_equal(local.get_content("/Folder 1/Folder 1.1/File 2.txt"), "bbb") assert_equal(local.get_content("/Folder 1/Folder 1.2/File 3.txt"), "ccc") assert_equal(local.get_content("/Folder 2/File 4.txt"), "ddd") assert_equal(local.get_content("/Folder 2/Duplicated File.txt"), "Some content.") assert_equal(local.get_content("/Folder 2/Duplicated File__1.txt"), "Other content.") # Wait a bit for file time stamps to increase enough: on most OS the file # modification time resolution is 1s time.sleep(1.0) # Let do some local and remote changes concurrently local.delete("/File 5.txt") local.update_content("/Folder 1/File 1.txt", "aaaa") remote_client.update_content("/Folder 1/Folder 1.1/File 2.txt", "bbbb") remote_client.delete("/Folder 2") f3 = remote_client.make_folder(TEST_WORKSPACE, "Folder 3") remote_client.make_file(f3, "File 6.txt", content="ffff") local.make_folder("/", "Folder 4") # Rescan ctl.scan_local(expected_folder, session) ctl.scan_remote(expected_folder, session) assert_equal( ctl.children_states(expected_folder), [ (u"/File 5.txt", u"locally_deleted"), (u"/Folder 1", u"children_modified"), (u"/Folder 2", u"children_modified"), # what do we want for this? # Folder 3 is not yet visible has not sync has happen to give it a # local path yet (u"/Folder 4", u"unknown"), ], ) # It is possible to fetch the full children states of the root though: full_states = ctl.children_states(expected_folder, full_states=True) assert_equal(len(full_states), 5) assert_equal(full_states[0][0].remote_name, "Folder 3") assert_equal(full_states[0][1], "children_modified") states = ctl.children_states(expected_folder + "/Folder 1") expected_states = [ (u"/Folder 1/File 1.txt", "locally_modified"), (u"/Folder 1/Folder 1.1", "children_modified"), (u"/Folder 1/Folder 1.2", "synchronized"), ] assert_equal(states, expected_states) states = ctl.children_states(expected_folder + "/Folder 1/Folder 1.1") expected_states = [(u"/Folder 1/Folder 1.1/File 2.txt", u"remotely_modified")] assert_equal(states, expected_states) states = ctl.children_states(expected_folder + "/Folder 2") expected_states = [ (u"/Folder 2/Duplicated File.txt", u"remotely_deleted"), (u"/Folder 2/Duplicated File__1.txt", u"remotely_deleted"), (u"/Folder 2/File 4.txt", u"remotely_deleted"), ] assert_equal(states, expected_states) # Perform synchronization assert_equal(ctl.synchronize(limit=100), 10) # We should now be fully synchronized again assert_equal(len(ctl.list_pending()), 0) assert_equal( ctl.children_states(expected_folder), [(u"/Folder 1", "synchronized"), (u"/Folder 3", "synchronized"), (u"/Folder 4", "synchronized")], ) states = ctl.children_states(expected_folder + "/Folder 1") expected_states = [ (u"/Folder 1/File 1.txt", "synchronized"), (u"/Folder 1/Folder 1.1", "synchronized"), (u"/Folder 1/Folder 1.2", "synchronized"), ] assert_equal(states, expected_states) assert_equal(local.get_content("/Folder 1/File 1.txt"), "aaaa") assert_equal(local.get_content("/Folder 1/Folder 1.1/File 2.txt"), "bbbb") assert_equal(local.get_content("/Folder 3/File 6.txt"), "ffff") assert_equal(remote_client.get_content("/Folder 1/File 1.txt"), "aaaa") assert_equal(remote_client.get_content("/Folder 1/Folder 1.1/File 2.txt"), "bbbb") assert_equal(remote_client.get_content("/Folder 3/File 6.txt"), "ffff") # Rescan: no change to detect we should reach a fixpoint ctl.scan_local(expected_folder, session) ctl.scan_remote(expected_folder, session) assert_equal(len(ctl.list_pending()), 0) assert_equal( ctl.children_states(expected_folder), [(u"/Folder 1", "synchronized"), (u"/Folder 3", "synchronized"), (u"/Folder 4", "synchronized")], ) # Send some binary data that is not valid in utf-8 or ascii (to test the # HTTP / Multipart transform layer). time.sleep(1.0) local.update_content("/Folder 1/File 1.txt", "\x80") remote_client.update_content("/Folder 1/Folder 1.1/File 2.txt", "\x80") ctl.scan_local(expected_folder, session) ctl.scan_remote(expected_folder, session) assert_equal(ctl.synchronize(limit=100), 2) assert_equal(remote_client.get_content("/Folder 1/File 1.txt"), "\x80") assert_equal(local.get_content("/Folder 1/Folder 1.1/File 2.txt"), "\x80")
def test_synchronize_remote_deletion_local_modification(self): """Test remote deletion with concurrent local modification Use cases: - Remotely delete a regular folder and make some local changes concurrently. => Only locally modified content should be kept and should be marked as 'unsynchronized', other content should be deleted. - Remotely restore folder from the trash. => Remote documents should be merged with locally modified content which should be unmarked as 'unsynchronized'. - Remotely delete a file and locally update its content concurrently. => File should be kept locally and be marked as 'unsynchronized'. - Remotely restore file from the trash. => Remote file should be merged with locally modified file with a conflict detection and both files should be marked as 'synchronized'. - Remotely delete a file and locally rename it concurrently. => File should be kept locally and be marked as 'synchronized'. - Remotely restore file from the trash. => Remote file should be merged with locally renamed file and both files should be marked as 'synchronized'. See TestIntegrationSecurityUpdates .test_synchronize_denying_read_access_local_modification as the same uses cases are tested. Note that we use the .odt extension for test files to make sure that they are created as File and not Note documents on the server when synchronized upstream, as the current implementation of RemoteDocumentClient is File oriented. """ # Bind the server and root workspace ctl = self.controller_1 ctl.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url, self.user_1, self.password_1) ctl.bind_root(self.local_nxdrive_folder_1, self.workspace) # Get local and remote clients local = LocalClient(os.path.join(self.local_nxdrive_folder_1, self.workspace_title)) remote = self.remote_document_client_1 # Create documents in the remote root workspace # then synchronize remote.make_folder('/', 'Test folder') remote.make_file('/Test folder', 'joe.odt', 'Some content') remote.make_file('/Test folder', 'jack.odt', 'Some content') remote.make_folder('/Test folder', 'Sub folder 1') remote.make_file('/Test folder/Sub folder 1', 'sub file 1.txt', 'Content') syn = ctl.synchronizer self._synchronize(syn) self.assertTrue(local.exists('/Test folder')) self.assertTrue(local.exists('/Test folder/joe.odt')) self.assertTrue(local.exists('/Test folder/jack.odt')) self.assertTrue(local.exists('/Test folder/Sub folder 1')) self.assertTrue(local.exists( '/Test folder/Sub folder 1/sub file 1.txt')) # Delete remote folder and make some local changes # concurrently then synchronize remote.delete('/Test folder') time.sleep(self.OS_STAT_MTIME_RESOLUTION) # Create new file local.make_file('/Test folder', 'new.odt', "New content") # Create new folder with files local.make_folder('/Test folder', 'Sub folder 2') local.make_file('/Test folder/Sub folder 2', 'sub file 2.txt', 'Other content') # Update file local.update_content('/Test folder/joe.odt', 'Some updated content') self._synchronize(syn) # Only locally modified content should exist # and should be marked as 'unsynchronized', other content should # have been deleted # Local check self.assertTrue(local.exists('/Test folder')) self.assertTrue(local.exists('/Test folder/joe.odt')) self.assertEquals(local.get_content('/Test folder/joe.odt'), 'Some updated content') self.assertTrue(local.exists('/Test folder/new.odt')) self.assertTrue(local.exists('/Test folder/Sub folder 2')) self.assertTrue(local.exists( '/Test folder/Sub folder 2/sub file 2.txt')) self.assertFalse(local.exists('/Test folder/jack.odt')) self.assertFalse(local.exists('/Test folder/Sub folder 1')) self.assertFalse(local.exists( '/Test folder/Sub folder 1/sub file 1.txt')) # State check session = ctl.get_session() self._check_pair_state(session, '/Test folder', 'unsynchronized') self._check_pair_state(session, '/Test folder/joe.odt', 'unsynchronized') self._check_pair_state(session, '/Test folder/new.odt', 'unsynchronized') self._check_pair_state(session, '/Test folder/Sub folder 2', 'unsynchronized') self._check_pair_state(session, '/Test folder/Sub folder 2/sub file 2.txt', 'unsynchronized') # Remote check self.assertFalse(remote.exists('/Test folder')) # Restore remote folder and its children from trash then synchronize remote.undelete('/Test folder') remote.undelete('/Test folder/joe.odt') remote.undelete('/Test folder/jack.odt') remote.undelete('/Test folder/Sub folder 1') remote.undelete('/Test folder/Sub folder 1/sub file 1.txt') self._synchronize(syn) # Remotely restored documents should be merged with # locally modified content which should be unmarked # as 'unsynchronized' and therefore synchronized upstream # Local check self.assertTrue(local.exists('/Test folder')) children_info = local.get_children_info('/Test folder') self.assertEquals(len(children_info), 6) for info in children_info: if info.name == 'joe.odt': remote_version = info elif info.name.startswith('joe (') and info.name.endswith(').odt'): local_version = info self.assertTrue(remote_version is not None) self.assertTrue(local_version is not None) self.assertTrue(local.exists(remote_version.path)) self.assertEquals(local.get_content(remote_version.path), 'Some content') self.assertTrue(local.exists(local_version.path)) self.assertEquals(local.get_content(local_version.path), 'Some updated content') self.assertTrue(local.exists('/Test folder/jack.odt')) self.assertTrue(local.exists('/Test folder/new.odt')) self.assertTrue(local.exists('/Test folder/Sub folder 1')) self.assertTrue(local.exists( '/Test folder/Sub folder 1/sub file 1.txt')) self.assertTrue(local.exists('/Test folder/Sub folder 2')) self.assertTrue(local.exists( '/Test folder/Sub folder 2/sub file 2.txt')) # State check self._check_pair_state(session, '/Test folder', 'synchronized') self._check_pair_state(session, '/Test folder/joe.odt', 'synchronized') self._check_pair_state(session, '/Test folder/new.odt', 'synchronized') self._check_pair_state(session, '/Test folder/Sub folder 2', 'synchronized') self._check_pair_state(session, '/Test folder/Sub folder 2/sub file 2.txt', 'synchronized') # Remote check self.assertTrue(remote.exists('/Test folder')) test_folder_uid = remote.get_info('/Test folder').uid children_info = remote.get_children_info(test_folder_uid) self.assertEquals(len(children_info), 6) for info in children_info: if info.name == 'joe.odt': remote_version = info elif info.name.startswith('joe (') and info.name.endswith(').odt'): local_version = info self.assertTrue(remote_version is not None) self.assertTrue(local_version is not None) remote_version_ref_length = (len(remote_version.path) - len(self.TEST_WORKSPACE_PATH)) remote_version_ref = remote_version.path[-remote_version_ref_length:] self.assertTrue(remote.exists(remote_version_ref)) self.assertEquals(remote.get_content(remote_version_ref), 'Some content') local_version_ref_length = (len(local_version.path) - len(self.TEST_WORKSPACE_PATH)) local_version_ref = local_version.path[-local_version_ref_length:] self.assertTrue(remote.exists(local_version_ref)) self.assertEquals(remote.get_content(local_version_ref), 'Some updated content') self.assertTrue(remote.exists('/Test folder/jack.odt')) self.assertTrue(remote.exists('/Test folder/new.odt')) self.assertTrue(remote.exists('/Test folder/Sub folder 1')) self.assertTrue(remote.exists( '/Test folder/Sub folder 1/sub file 1.txt')) self.assertTrue(remote.exists('/Test folder/Sub folder 2')) self.assertTrue(remote.exists( '/Test folder/Sub folder 2/sub file 2.txt')) # Delete remote file and update its local content # concurrently then synchronize remote.delete('/Test folder/jack.odt') time.sleep(self.OS_STAT_MTIME_RESOLUTION) local.update_content('/Test folder/jack.odt', 'Some updated content') self._synchronize(syn) # File should be kept locally and be marked as 'unsynchronized'. # Local check self.assertTrue(local.exists('/Test folder/jack.odt')) self.assertEquals(local.get_content('/Test folder/jack.odt'), 'Some updated content') # Remote check self.assertFalse(remote.exists('/Test folder/jack.odt')) # State check session = ctl.get_session() self._check_pair_state(session, '/Test folder', 'synchronized') self._check_pair_state(session, '/Test folder/jack.odt', 'unsynchronized') # Remotely restore file from the trash then synchronize remote.undelete('/Test folder/jack.odt') self._synchronize(syn) # Remotely restored file should be merged with locally modified file # with a conflict detection and both files should be marked # as 'synchronized' # Local check children_info = local.get_children_info('/Test folder') for info in children_info: if info.name == 'jack.odt': remote_version = info elif (info.name.startswith('jack (') and info.name.endswith(').odt')): local_version = info self.assertTrue(remote_version is not None) self.assertTrue(local_version is not None) self.assertTrue(local.exists(remote_version.path)) self.assertEquals(local.get_content(remote_version.path), 'Some content') self.assertTrue(local.exists(local_version.path)) self.assertEquals(local.get_content(local_version.path), 'Some updated content') # Remote check self.assertTrue(remote.exists(remote_version.path)) self.assertEquals(remote.get_content(remote_version.path), 'Some content') local_version_path = self._truncate_remote_path(local_version.path) self.assertTrue(remote.exists(local_version_path)) self.assertEquals(remote.get_content(local_version_path), 'Some updated content') # State check self._check_pair_state(session, remote_version.path, 'synchronized') self._check_pair_state(session, local_version.path, 'synchronized') # Delete remote file and rename it locally # concurrently then synchronize remote.delete('/Test folder/jack.odt') time.sleep(self.OS_STAT_MTIME_RESOLUTION) local.rename('/Test folder/jack.odt', 'jack renamed.odt') self._synchronize(syn) # File should be kept locally and be marked as 'synchronized' # Local check self.assertFalse(local.exists('/Test folder/jack.odt')) self.assertTrue(local.exists('/Test folder/jack renamed.odt')) self.assertEquals(local.get_content('/Test folder/jack renamed.odt'), 'Some content') # Remote check self.assertFalse(remote.exists('/Test folder/jack.odt')) # State check session = ctl.get_session() self._check_pair_state(session, '/Test folder', 'synchronized') self._check_pair_state(session, '/Test folder/jack renamed.odt', 'synchronized') # Remotely restore file from the trash then synchronize remote.undelete('/Test folder/jack.odt') self._synchronize(syn) # Remotely restored file should be merged with locally renamed file # and both files should be marked as 'synchronized' # Local check self.assertTrue(local.exists('/Test folder/jack.odt')) self.assertEquals(local.get_content('/Test folder/jack.odt'), 'Some content') self.assertTrue(local.exists('/Test folder/jack renamed.odt')) self.assertEquals(local.get_content('/Test folder/jack renamed.odt'), 'Some content') # Remote check self.assertTrue(remote.exists('/Test folder/jack.odt')) self.assertEquals(remote.get_content('/Test folder/jack.odt'), 'Some content') self.assertTrue(remote.exists('/Test folder/jack renamed.odt')) self.assertEquals(remote.get_content('/Test folder/jack renamed.odt'), 'Some content') # State check self._check_pair_state(session, '/Test folder/jack.odt', 'synchronized') self._check_pair_state(session, '/Test folder/jack renamed.odt', 'synchronized')
def test_binding_synchronization_empty_start(self): ctl = self.controller_1 remote_client = self.remote_document_client_1 ctl.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url, self.user_1, self.password_1) ctl.bind_root(self.local_nxdrive_folder_1, self.workspace) syn = ctl.synchronizer expected_folder = os.path.join(self.local_nxdrive_folder_1, self.workspace_title) # Nothing to synchronize by default self.assertEquals(ctl.list_pending(), []) self.assertEquals(syn.synchronize(), 0) # Let's create some document on the server self.make_server_tree() # By default nothing is detected self.assertEquals(ctl.list_pending(), []) self.assertEquals(ctl.children_states(expected_folder), []) # Let's scan manually syn.scan_remote(self.local_nxdrive_folder_1) # Changes on the remote server have been detected... self.assertEquals(len(ctl.list_pending()), 12) # ...but nothing is yet visible locally as those files don't exist # there yet. self.assertEquals(ctl.children_states(expected_folder), []) # Let's perform the synchronization self.assertEquals(syn.synchronize(limit=100), 12) # We should now be fully synchronized self.assertEquals(ctl.list_pending(), []) self.assertEquals(ctl.children_states(expected_folder), [ (u'File 5.txt', u'synchronized'), (u'Folder 1', u'synchronized'), (u'Folder 2', u'synchronized'), ]) local_client = LocalClient(self.local_nxdrive_folder_1) self.assertEquals(local_client.get_content( '/Nuxeo Drive Test Workspace/Folder 1/File 1.txt'), "aaa") self.assertEquals(local_client.get_content( '/Nuxeo Drive Test Workspace/Folder 1/Folder 1.1/File 2.txt'), "bbb") self.assertEquals(local_client.get_content( '/Nuxeo Drive Test Workspace/Folder 1/Folder 1.2/File 3.txt'), "ccc") self.assertEquals(local_client.get_content( '/Nuxeo Drive Test Workspace/Folder 2/File 4.txt'), "ddd") c1 = local_client.get_content( '/Nuxeo Drive Test Workspace/Folder 2/Duplicated File.txt') c2 = local_client.get_content( '/Nuxeo Drive Test Workspace/Folder 2/Duplicated File__1.txt') self.assertEquals(tuple(sorted((c1, c2))), ("Other content.", "Some content.")) # Wait a bit for file time stamps to increase enough: on OSX HFS+ the # file modification time resolution is 1s for instance time.sleep(self.OS_STAT_MTIME_RESOLUTION) # Let do some local and remote changes concurrently local_client.delete('/Nuxeo Drive Test Workspace/File 5.txt') local_client.update_content( '/Nuxeo Drive Test Workspace/Folder 1/File 1.txt', 'aaaa') # The remote client use in this test is handling paths relative to # the 'Nuxeo Drive Test Workspace' remote_client.update_content('/Folder 1/Folder 1.1/File 2.txt', 'bbbb') remote_client.delete('/Folder 2') f3 = remote_client.make_folder(self.workspace, 'Folder 3') remote_client.make_file(f3, 'File 6.txt', content='ffff') local_client.make_folder('/Nuxeo Drive Test Workspace', 'Folder 4') # Rescan syn.scan_local(self.local_nxdrive_folder_1) syn.scan_remote(self.local_nxdrive_folder_1) self.assertEquals(ctl.children_states(expected_folder), [ (u'File 5.txt', u'locally_deleted'), (u'Folder 1', u'children_modified'), (u'Folder 2', u'children_modified'), # what do we want for this? # Folder 3 is not yet visible has not sync has happen to give it a # local path yet (u'Folder 4', u'unknown'), ]) # The information on the remote state of Folder 3 has been stored in # the database though session = ctl.get_session() f3_state = session.query(LastKnownState).filter_by( remote_name='Folder 3').one() self.assertEquals(f3_state.local_path, None) states = ctl.children_states(expected_folder + '/Folder 1') expected_states = [ (u'File 1.txt', 'locally_modified'), (u'Folder 1.1', 'children_modified'), (u'Folder 1.2', 'synchronized'), ] self.assertEquals(states, expected_states) states = ctl.children_states(expected_folder + '/Folder 1/Folder 1.1') expected_states = [ (u'File 2.txt', u'remotely_modified'), ] self.assertEquals(states, expected_states) states = ctl.children_states(expected_folder + '/Folder 2') expected_states = [ (u'Duplicated File.txt', u'remotely_deleted'), (u'Duplicated File__1.txt', u'remotely_deleted'), (u'File 4.txt', u'remotely_deleted'), ] self.assertEquals(states, expected_states) # Perform synchronization: deleted folder content are not # counted in the summary self.assertEquals(syn.synchronize(limit=100), 7) # We should now be fully synchronized again self.assertEquals(ctl.list_pending(), []) self.assertEquals(ctl.children_states(expected_folder), [ (u'Folder 1', 'synchronized'), (u'Folder 3', 'synchronized'), (u'Folder 4', 'synchronized'), ]) states = ctl.children_states(expected_folder + '/Folder 1') expected_states = [ (u'File 1.txt', 'synchronized'), (u'Folder 1.1', 'synchronized'), (u'Folder 1.2', 'synchronized'), ] self.assertEquals(states, expected_states) local = LocalClient(expected_folder) self.assertEquals(local.get_content( '/Folder 1/File 1.txt'), "aaaa") self.assertEquals(local.get_content( '/Folder 1/Folder 1.1/File 2.txt'), "bbbb") self.assertEquals(local.get_content( '/Folder 3/File 6.txt'), "ffff") self.assertEquals(remote_client.get_content( '/Folder 1/File 1.txt'), "aaaa") self.assertEquals(remote_client.get_content( '/Folder 1/Folder 1.1/File 2.txt'), "bbbb") self.assertEquals(remote_client.get_content( '/Folder 3/File 6.txt'), "ffff") # Rescan: no change to detect we should reach a fixpoint syn.scan_local(self.local_nxdrive_folder_1) syn.scan_remote(self.local_nxdrive_folder_1) self.assertEquals(ctl.list_pending(), []) self.assertEquals(ctl.children_states(expected_folder), [ (u'Folder 1', 'synchronized'), (u'Folder 3', 'synchronized'), (u'Folder 4', 'synchronized'), ]) # Send some binary data that is not valid in utf-8 or ascii # (to test the HTTP / Multipart transform layer). time.sleep(self.OS_STAT_MTIME_RESOLUTION) local.update_content('/Folder 1/File 1.txt', "\x80") remote_client.update_content('/Folder 1/Folder 1.1/File 2.txt', '\x80') syn.scan_local(self.local_nxdrive_folder_1) syn.scan_remote(self.local_nxdrive_folder_1) self.assertEquals(syn.synchronize(limit=100), 2) self.assertEquals(remote_client.get_content('/Folder 1/File 1.txt'), "\x80") self.assertEquals(local.get_content('/Folder 1/Folder 1.1/File 2.txt'), "\x80")
class TestIntegrationVersioning(IntegrationTestCase): def setUp(self): super(TestIntegrationVersioning, self).setUp() self.controller_1.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url, self.user_1, self.password_1) self.controller_2.bind_server(self.local_nxdrive_folder_2, self.nuxeo_url, self.user_2, self.password_2) self.sb_1 = self.controller_1.get_server_binding( self.local_nxdrive_folder_1) self.sb_2 = self.controller_2.get_server_binding( self.local_nxdrive_folder_2) self.controller_1.bind_root(self.local_nxdrive_folder_1, self.workspace) self.controller_2.bind_root(self.local_nxdrive_folder_2, self.workspace) self.syn_1 = self.controller_1.synchronizer self.syn_2 = self.controller_2.synchronizer self.syn_1.loop(delay=0.010, max_loops=1) self.syn_2.loop(delay=0.010, max_loops=1) self.remote_client_1 = self.remote_document_client_1 self.remote_client_2 = self.remote_document_client_2 sync_root_folder_1 = os.path.join(self.local_nxdrive_folder_1, self.workspace_title) sync_root_folder_2 = os.path.join(self.local_nxdrive_folder_2, self.workspace_title) self.local_client_1 = LocalClient(sync_root_folder_1) self.local_client_2 = LocalClient(sync_root_folder_2) # Call the Nuxeo operation to set the versioning delay to 3 seconds self.versioning_delay = self.OS_STAT_MTIME_RESOLUTION * 3 self.root_remote_client.execute( "NuxeoDrive.SetVersioningOptions", delay=str(self.versioning_delay)) def test_versioning(self): # Create a file as user 1 self.local_client_1.make_file('/', 'Test versioning.txt', "This is version 0") self._synchronize_and_assert(self.syn_1, self.sb_1, 1) doc = self.root_remote_client.fetch( self.TEST_WORKSPACE_PATH + '/Test versioning.txt') self._assert_version(doc, '0', '0') # Synchronize it for user 2 self.assertTrue(self.remote_client_2.exists('/Test versioning.txt')) self._synchronize_and_assert(self.syn_2, self.sb_2, 1, wait=True) self.assertTrue(self.local_client_2.exists('/Test versioning.txt')) # Update it as user 2 => should be versioned time.sleep(self.OS_STAT_MTIME_RESOLUTION) self.local_client_2.update_content('/Test versioning.txt', "Modified content") self._synchronize_and_assert(self.syn_2, self.sb_2, 1) doc = self.root_remote_client.fetch( self.TEST_WORKSPACE_PATH + '/Test versioning.txt') self._assert_version(doc, '0', '1') # Update it as user 2 => should NOT be versioned # since the versioning delay (3s) is not passed by time.sleep(self.OS_STAT_MTIME_RESOLUTION) self.local_client_2.update_content('/Test versioning.txt', "Content twice modified") self._synchronize_and_assert(self.syn_2, self.sb_2, 1) doc = self.root_remote_client.fetch( self.TEST_WORKSPACE_PATH + '/Test versioning.txt') self._assert_version(doc, '0', '1') # Update it as user 2 after 3s => should be versioned # since the versioning delay is passed by time.sleep(self.versioning_delay + 0.1) self.local_client_2.update_content('/Test versioning.txt', "Updated again!!") self._synchronize_and_assert(self.syn_2, self.sb_2, 1) doc = self.root_remote_client.fetch( self.TEST_WORKSPACE_PATH + '/Test versioning.txt') self._assert_version(doc, '0', '2') def _synchronize_and_assert(self, synchronizer, server_binding, expected_synchronized, wait=False): if wait: # Wait for audit changes to be detected after the 1 second step time.sleep(self.AUDIT_CHANGE_FINDER_TIME_RESOLUTION) n_synchronized = synchronizer.update_synchronize_server(server_binding) self.assertEqual(n_synchronized, expected_synchronized) def _assert_version(self, doc, major, minor): self.assertEquals(doc['properties']['uid:major_version'], major) self.assertEquals(doc['properties']['uid:minor_version'], minor)
class TestDriveEdit(UnitTestCase): locally_edited_path = ('/default-domain/UserWorkspaces/' + 'nuxeoDriveTestUser-user-1/Collections/' + LOCALLY_EDITED_FOLDER_NAME) def setUpApp(self): super(TestDriveEdit, self).setUpApp() self.drive_edit = self.manager_1.get_drive_edit() self.drive_edit.driveEditUploadCompleted.connect(self.app.sync_completed) self.drive_edit.start() self.remote = self.remote_document_client_1 self.local = LocalClient(os.path.join(self.nxdrive_conf_folder_1, 'edit')) def tearDownApp(self): self.drive_edit.stop() super(TestDriveEdit, self).tearDownApp() def test_filename_encoding(self): filename = u'Mode op\xe9ratoire.txt' doc_id = self.remote.make_file('/', filename, 'Some content.') # Linux / Win + Chrome: quoted utf-8 encoded browser_filename = 'Mode%20op%C3%A9ratoire.txt' self._drive_edit_update(doc_id, filename, browser_filename, 'Win + Chrome') # Win + IE: unquoted utf-8 encoded browser_filename = 'Mode op\xc3\xa9ratoire.txt' self._drive_edit_update(doc_id, filename, browser_filename, 'Win + IE') # Win + FF: quoted string containing unicode browser_filename = 'Mode%20op\xe9ratoire.txt' self._drive_edit_update(doc_id, filename, browser_filename, 'Win + FF') # OSX + Chrome / OSX + FF: quoted utf-8 encoded, except for white spaces! browser_filename = 'Mode op%C3%A9ratoire.txt' self._drive_edit_update(doc_id, filename, browser_filename, 'OS X + Chrome or FF') def _drive_edit_update(self, doc_id, filename, browser_filename, content): # Download file local_path = '/%s/%s' % (doc_id, filename) self.drive_edit._prepare_edit(self.nuxeo_url, doc_id, browser_filename) self.assertTrue(self.local.exists(local_path)) self.wait_sync(timeout=2, fail_if_timeout=False) # Update file content self.local.update_content(local_path, content) self.wait_sync() self.assertEquals(self.remote.get_content('/' + filename), content) def test_drive_edit_non_synced_doc(self): raise SkipTest("WIP in https://jira.nuxeo.com/browse/NXDRIVE-170") ctl = self.controller_1 ctl.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url, self.user_1, self.password_1) local = LocalClient(self.local_nxdrive_folder_1) remote = self.remote_document_client_1 syn = ctl.synchronizer # Create file in test workspace (non sync root) doc_id = remote.make_file('/', 'test.odt', 'Some content.') # Drive edit file ctl.download_edit(self.nuxeo_url, 'default', doc_id, 'test.odt', open_file=False) # Check file is downloaded to the Locally Edited folder self.assertTrue(local.exists('/%s/test.odt' % LOCALLY_EDITED_FOLDER_NAME)) self.assertEquals(local.get_content('/%s/test.odt' % LOCALLY_EDITED_FOLDER_NAME), 'Some content.') # Check Locally Edited collection exists, is registered as a sync root # for test user and file is member of it self.assertTrue(self.root_remote_client.exists( self.locally_edited_path)) sync_roots = remote.get_roots() self.assertEquals(len(sync_roots), 1) self.assertEquals(sync_roots[0].path, self.locally_edited_path) self.assertTrue(doc_id in self.root_remote_client.get_collection_members( self.locally_edited_path)) # Update locally edited file # Let's first sync because of https://jira.nuxeo.com/browse/NXDRIVE-144 self._sync(syn) time.sleep(OS_STAT_MTIME_RESOLUTION) local.update_content('/%s/test.odt' % LOCALLY_EDITED_FOLDER_NAME, 'Updated content.') self._sync(syn, wait_for_async=False) self.assertEquals(remote.get_content('/test.odt'), 'Updated content.') # Drive edit file a second time (should not download a new file but # detect the existing one) ctl.download_edit(self.nuxeo_url, 'default', doc_id, 'test.odt', open_file=False) self.assertEquals(len(local.get_children_info('/%s' % LOCALLY_EDITED_FOLDER_NAME)), 1) # Update locally edited file time.sleep(OS_STAT_MTIME_RESOLUTION) local.update_content('/%s/test.odt' % LOCALLY_EDITED_FOLDER_NAME, 'Twice updated content.') self._sync(syn, wait_for_async=False) self.assertEquals(remote.get_content('/test.odt'), 'Twice updated content.') def test_drive_edit_synced_doc(self): raise SkipTest("WIP in https://jira.nuxeo.com/browse/NXDRIVE-170") ctl = self.controller_1 ctl.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url, self.user_1, self.password_1) ctl.bind_root(self.local_nxdrive_folder_1, self.workspace) local = LocalClient(self.local_nxdrive_folder_1) remote = self.remote_document_client_1 syn = ctl.synchronizer # Create file in test workspace (sync root) doc_id = remote.make_file('/', 'test.odt', 'Some content.') # Launch first synchronization self._sync(syn) self.assertTrue(local.exists('/%s/test.odt' % self.workspace_title)) # Drive edit file ctl.download_edit(self.nuxeo_url, 'default', doc_id, 'test.odt', open_file=False) # Check file is downloaded to the Locally Edited folder self.assertTrue(local.exists('/%s/test.odt' % LOCALLY_EDITED_FOLDER_NAME)) self.assertEquals(local.get_content('/%s/test.odt' % LOCALLY_EDITED_FOLDER_NAME), 'Some content.') # Update locally edited file # Let's first sync because of https://jira.nuxeo.com/browse/NXDRIVE-144 self._sync(syn) time.sleep(OS_STAT_MTIME_RESOLUTION) local.update_content('/%s/test.odt' % LOCALLY_EDITED_FOLDER_NAME, 'Content updated from Locally Edited.') self._sync(syn, wait_for_async=False) self.assertEquals(remote.get_content('/test.odt'), 'Content updated from Locally Edited.') self._sync(syn) self.assertEquals(local.get_content('/%s/test.odt' % self.workspace_title), 'Content updated from Locally Edited.') # Update file in local sync root time.sleep(OS_STAT_MTIME_RESOLUTION) local.update_content('/%s/test.odt' % self.workspace_title, 'Content updated from local sync root.') self._sync(syn, wait_for_async=False) self.assertEquals(remote.get_content('/test.odt'), 'Content updated from local sync root.') self._sync(syn) self.assertEquals(local.get_content('/%s/test.odt' % LOCALLY_EDITED_FOLDER_NAME), 'Content updated from local sync root.') # Update file in remote sync root remote.update_content('/test.odt', 'Content updated from remote sync root.') self._sync(syn) self.assertEquals(local.get_content('/%s/test.odt' % self.workspace_title), 'Content updated from remote sync root.') self.assertEquals(local.get_content('/%s/test.odt' % LOCALLY_EDITED_FOLDER_NAME), 'Content updated from remote sync root.') def test_drive_edit_doc_becoming_synced(self): raise SkipTest("WIP in https://jira.nuxeo.com/browse/NXDRIVE-170") ctl = self.controller_1 ctl.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url, self.user_1, self.password_1) local = LocalClient(self.local_nxdrive_folder_1) remote = self.remote_document_client_1 syn = ctl.synchronizer # Create file in test workspace (non sync root) doc_id = remote.make_file('/', 'test.odt', 'Some content.') # Drive edit file ctl.download_edit(self.nuxeo_url, 'default', doc_id, 'test.odt', open_file=False) # Check file is downloaded to the Locally Edited folder self.assertTrue(local.exists('/%s/test.odt' % LOCALLY_EDITED_FOLDER_NAME)) # Register test workspace as a sync root ctl.bind_root(self.local_nxdrive_folder_1, self.workspace) self._sync(syn) self.assertTrue(local.exists('/%s/test.odt' % self.workspace_title)) # Update file in local sync root time.sleep(OS_STAT_MTIME_RESOLUTION) local.update_content('/%s/test.odt' % self.workspace_title, 'Content updated from local sync root.') self._sync(syn, wait_for_async=False) self.assertEquals(remote.get_content('/test.odt'), 'Content updated from local sync root.') self._sync(syn) self.assertEquals(local.get_content('/%s/test.odt' % LOCALLY_EDITED_FOLDER_NAME), 'Content updated from local sync root.') # Update locally edited file time.sleep(OS_STAT_MTIME_RESOLUTION) local.update_content('/%s/test.odt' % LOCALLY_EDITED_FOLDER_NAME, 'Content updated from Locally Edited.') self._sync(syn, wait_for_async=False) self.assertEquals(remote.get_content('/test.odt'), 'Content updated from Locally Edited.') self._sync(syn) self.assertEquals(local.get_content('/%s/test.odt' % self.workspace_title), 'Content updated from Locally Edited.') # Update file in remote sync root remote.update_content('/test.odt', 'Content updated from remote sync root.') self._sync(syn) self.assertEquals(local.get_content('/%s/test.odt' % self.workspace_title), 'Content updated from remote sync root.') self.assertEquals(local.get_content('/%s/test.odt' % LOCALLY_EDITED_FOLDER_NAME), 'Content updated from remote sync root.') def test_drive_edit_remote_move_non_sync_root_to_sync_root(self): raise SkipTest("WIP in https://jira.nuxeo.com/browse/NXDRIVE-184") ctl = self.controller_1 ctl.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url, self.user_1, self.password_1) local = LocalClient(self.local_nxdrive_folder_1) remote = self.remote_document_client_1 syn = ctl.synchronizer # Create file in test workspace (non sync root) doc_id = remote.make_file('/', 'test.odt', 'Some content.') # Drive edit file ctl.download_edit(self.nuxeo_url, 'default', doc_id, 'test.odt', open_file=False) # Check file is downloaded to the Locally Edited folder self.assertTrue(local.exists('/%s/test.odt' % LOCALLY_EDITED_FOLDER_NAME)) # Update locally edited file # Let's first sync because of https://jira.nuxeo.com/browse/NXDRIVE-144 self._sync(syn) time.sleep(OS_STAT_MTIME_RESOLUTION) local.update_content('/%s/test.odt' % LOCALLY_EDITED_FOLDER_NAME, 'Updated content.') self._sync(syn, wait_for_async=False) self.assertEquals(remote.get_content('/test.odt'), 'Updated content.') # Register a folder as sync root and remotely move file to it sync_root_id = remote.make_folder('/', 'syncRoot') ctl.bind_root(self.local_nxdrive_folder_1, sync_root_id) self._sync(syn) self.assertTrue(local.exists('/syncRoot')) remote.move('/test.odt', '/syncRoot') self._sync(syn) self.assertTrue(local.exists('/%s/test.odt' % LOCALLY_EDITED_FOLDER_NAME)) self.assertTrue(local.exists('/syncRoot/test.odt')) def test_drive_edit_remote_move_sync_root_to_non_sync_root(self): raise SkipTest("WIP in https://jira.nuxeo.com/browse/NXDRIVE-170") ctl = self.controller_1 ctl.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url, self.user_1, self.password_1) local = LocalClient(self.local_nxdrive_folder_1) remote = self.remote_document_client_1 syn = ctl.synchronizer # Create folder, register it as a sync root and create file inside it sync_root_id = remote.make_folder('/', 'syncRoot') ctl.bind_root(self.local_nxdrive_folder_1, sync_root_id) doc_id = remote.make_file(sync_root_id, 'test.odt', 'Some content.') # Launch first synchronization self._sync(syn) self.assertTrue(local.exists('/syncRoot/test.odt')) # Drive edit file ctl.download_edit(self.nuxeo_url, 'default', doc_id, 'test.odt', open_file=False) # Check file is downloaded to the Locally Edited folder self.assertTrue(local.exists('/%s/test.odt' % LOCALLY_EDITED_FOLDER_NAME)) # Update locally edited file # Let's first sync because of https://jira.nuxeo.com/browse/NXDRIVE-144 self._sync(syn) time.sleep(OS_STAT_MTIME_RESOLUTION) local.update_content('/%s/test.odt' % LOCALLY_EDITED_FOLDER_NAME, 'Content updated from Locally Edited.') self._sync(syn, wait_for_async=False) self.assertEquals(remote.get_content('/syncRoot/test.odt'), 'Content updated from Locally Edited.') self._sync(syn) self.assertEquals(local.get_content('/syncRoot/test.odt'), 'Content updated from Locally Edited.') # Move file to non sync root workspace remote.move('/syncRoot/test.odt', self.workspace) self._sync(syn) self.assertFalse(local.exists('/syncRoot/test.odt')) self.assertTrue(local.exists('/%s/test.odt' % LOCALLY_EDITED_FOLDER_NAME)) self.assertEquals(len(local.get_children_info('/%s' % LOCALLY_EDITED_FOLDER_NAME)), 1) def test_drive_edit_move_sync_root_to_sync_root(self): raise SkipTest("WIP in https://jira.nuxeo.com/browse/NXDRIVE-170") ctl = self.controller_1 ctl.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url, self.user_1, self.password_1) local = LocalClient(self.local_nxdrive_folder_1) remote = self.remote_document_client_1 syn = ctl.synchronizer # Create 2 folders, register them as sync roots and create file inside first folder sync_root_id1 = remote.make_folder('/', 'syncRoot1') sync_root_id2 = remote.make_folder('/', 'syncRoot2') ctl.bind_root(self.local_nxdrive_folder_1, sync_root_id1) ctl.bind_root(self.local_nxdrive_folder_1, sync_root_id2) doc_id = remote.make_file(sync_root_id1, 'test.odt', 'Some content.') # Launch first synchronization self._sync(syn) self.assertTrue(local.exists('/syncRoot1/test.odt')) self.assertTrue(local.exists('/syncRoot2')) # Drive edit file ctl.download_edit(self.nuxeo_url, 'default', doc_id, 'test.odt', open_file=False) # Check file is downloaded to the Locally Edited folder self.assertTrue(local.exists('/%s/test.odt' % LOCALLY_EDITED_FOLDER_NAME)) # Update locally edited file # Let's first sync because of https://jira.nuxeo.com/browse/NXDRIVE-144 self._sync(syn) time.sleep(OS_STAT_MTIME_RESOLUTION) local.update_content('/%s/test.odt' % LOCALLY_EDITED_FOLDER_NAME, 'Content updated from Locally Edited.') self._sync(syn, wait_for_async=False) self.assertEquals(remote.get_content('/syncRoot1/test.odt'), 'Content updated from Locally Edited.') self._sync(syn) self.assertEquals(local.get_content('/syncRoot1/test.odt'), 'Content updated from Locally Edited.') # Remotely move file to other sync root remote.move('/syncRoot1/test.odt', '/syncRoot2') self._sync(syn) self.assertFalse(local.exists('/syncRoot1/test.odt')) self.assertTrue(local.exists('/syncRoot2/test.odt')) self.assertTrue(local.exists('/%s/test.odt' % LOCALLY_EDITED_FOLDER_NAME)) self.assertEquals(len(local.get_children_info('/%s' % LOCALLY_EDITED_FOLDER_NAME)), 1) # Locally move back file to other sync root local.move('/syncRoot2/test.odt', '/syncRoot1') self._sync(syn, wait_for_async=False) self.assertFalse(local.exists('/syncRoot2/test.odt')) self.assertTrue(local.exists('/syncRoot1/test.odt')) self.assertTrue(local.exists('/%s/test.odt' % LOCALLY_EDITED_FOLDER_NAME)) self.assertEquals(len(local.get_children_info('/%s' % LOCALLY_EDITED_FOLDER_NAME)), 1) def _sync(self, syn, wait_for_async=True): if wait_for_async: self.wait() syn.loop(delay=0, max_loops=1)
class TestDirectEdit(UnitTestCase): locally_edited_path = ('/default-domain/UserWorkspaces/' + 'nuxeoDriveTestUser-user-1/Collections/' + LOCALLY_EDITED_FOLDER_NAME) def setUpApp(self): super(TestDirectEdit, self).setUpApp() self.direct_edit = self.manager_1.direct_edit self.direct_edit._test = True self.direct_edit.directEditUploadCompleted.connect(self.app.sync_completed) self.direct_edit.start() self.remote = self.remote_document_client_1 self.local = LocalClient(os.path.join(self.nxdrive_conf_folder_1, 'edit')) def tearDownApp(self): self.direct_edit.stop() super(TestDirectEdit, self).tearDownApp() def test_url_resolver(self): self.assertIsNotNone(self.direct_edit._get_engine(self.nuxeo_url, self.user_1)) self.assertIsNone(self.direct_edit._get_engine(self.nuxeo_url, u'Administrator')) self.manager_1._engine_types['NXDRIVETESTURL'] = MockUrlTestEngine # HTTP EXPLICIT self.manager_1._engines['0'] = MockUrlTestEngine('http://localhost:80/nuxeo') self.assertIsNone(self.direct_edit._get_engine("http://localhost:8080/nuxeo", u'Administrator')) self.assertIsNotNone(self.direct_edit._get_engine("http://localhost:80/nuxeo", u'Administrator')) self.assertIsNotNone(self.direct_edit._get_engine("http://localhost/nuxeo/", u'Administrator')) # HTTP IMPLICIT self.manager_1._engines['0'] = MockUrlTestEngine('http://localhost/nuxeo') self.assertIsNone(self.direct_edit._get_engine("http://localhost:8080/nuxeo", u'Administrator')) self.assertIsNotNone(self.direct_edit._get_engine("http://localhost:80/nuxeo/", u'Administrator')) self.assertIsNotNone(self.direct_edit._get_engine("http://localhost/nuxeo", u'Administrator')) # HTTPS EXPLICIT self.manager_1._engines['0'] = MockUrlTestEngine('https://localhost:443/nuxeo') self.assertIsNone(self.direct_edit._get_engine("http://localhost:8080/nuxeo", u'Administrator')) self.assertIsNotNone(self.direct_edit._get_engine("https://localhost:443/nuxeo", u'Administrator')) self.assertIsNotNone(self.direct_edit._get_engine("https://localhost/nuxeo/", u'Administrator')) # HTTPS IMPLICIT self.manager_1._engines['0'] = MockUrlTestEngine('https://localhost/nuxeo') self.assertIsNone(self.direct_edit._get_engine("http://localhost:8080/nuxeo", u'Administrator')) self.assertIsNotNone(self.direct_edit._get_engine("https://localhost:443/nuxeo/", u'Administrator')) self.assertIsNotNone(self.direct_edit._get_engine("https://localhost/nuxeo", u'Administrator')) def test_note_edit(self): remote_fs_client = self.remote_file_system_client_1 toplevel_folder_info = remote_fs_client.get_filesystem_root_info() workspace_id = remote_fs_client.get_children_info( toplevel_folder_info.uid)[0].uid file_1_id = remote_fs_client.make_file(workspace_id, u'Mode op\xe9ratoire.txt', "Content of file 1 Avec des accents h\xe9h\xe9.").uid doc_id = file_1_id.split('#')[-1] self._direct_edit_update(doc_id, u'Mode op\xe9ratoire.txt', 'Atol de PomPom Gali') def test_filename_encoding(self): filename = u'Mode op\xe9ratoire.txt' doc_id = self.remote.make_file('/', filename, 'Some content.') self._direct_edit_update(doc_id, filename, 'Test') def _test_locked_file_signal(self): self._received = True def test_locked_file(self): self._received = False filename = u'Mode operatoire.txt' doc_id = self.remote.make_file('/', filename, 'Some content.') self.remote_document_client_2.lock(doc_id) self.direct_edit.directEditLocked.connect(self._test_locked_file_signal) self.direct_edit._prepare_edit(self.nuxeo_url, doc_id) self.assertTrue(self._received) def test_self_locked_file(self): filename = u'Mode operatoire.txt' doc_id = self.remote.make_file('/', filename, 'Some content.') self.remote.lock(doc_id) self._direct_edit_update(doc_id, filename, 'Test') def _office_locker(self, path): return os.path.join(os.path.dirname(path), "~$" + os.path.basename(path)[2:]) def _openoffice_locker(self, path): return os.path.join(os.path.dirname(path), ".~lock." + os.path.basename(path)[2:]) # def test_autolock_office(self): # self._autolock(self._office_locker) # def test_autolock_openoffice(self): # LibreOffice as well # self._autolock(self._openoffice_locker) def _autolock(self, locker): global called_open, lock_file called_open = False filename = u'Document.docx' doc_id = self.remote.make_file('/', filename, 'Some content.') def open_local_file(path): global called_open, lock_file called_open = True # Lock file lock_file = locker(path) with open(lock_file, 'w') as f: f.write("plop") self.manager_1.open_local_file = open_local_file self.manager_1.set_direct_edit_auto_lock(1) self.direct_edit._manager.open_local_file = open_local_file self.direct_edit.edit(self.nuxeo_url, doc_id, filename=filename, user=self.user_1) self.wait_sync(timeout=2, fail_if_timeout=False) self.assertTrue(called_open, "Should have called open_local_file") # Should be able to test lock self.assertTrue(self.remote_document_client_1.is_locked(doc_id)) os.remove(lock_file) self.wait_sync(timeout=2, fail_if_timeout=False) # Should be unlock self.assertFalse(self.remote_document_client_1.is_locked(doc_id)) self.manager_1.set_direct_edit_auto_lock(0) with open(lock_file, 'w') as f: f.write("plop") self.wait_sync(timeout=2, fail_if_timeout=False) self.assertFalse(self.remote_document_client_1.is_locked(doc_id)) def _direct_edit_update(self, doc_id, filename, content, url=None): # Download file local_path = u'/%s/%s' % (doc_id, filename) def open_local_file(path): pass self.manager_1.open_local_file = open_local_file if url is None: self.direct_edit._prepare_edit(self.nuxeo_url, doc_id) else: self.direct_edit.handle_url(url) self.assertTrue(self.local.exists(local_path)) self.wait_sync(timeout=2, fail_if_timeout=False) self.local.delete_final(local_path) # Update file content self.local.update_content(local_path, content) self.wait_sync() self.assertEqual(self.remote.get_blob(self.remote.get_info(doc_id)), content) # Update file content twice update_content = content + ' updated' self.local.update_content(local_path, update_content) self.wait_sync() self.assertEqual(self.remote.get_blob(self.remote.get_info(doc_id)), update_content) def test_direct_edit_cleanup(self): filename = u'Mode op\xe9ratoire.txt' doc_id = self.remote.make_file('/', filename, 'Some content.') # Download file local_path = u'/%s/%s' % (doc_id, filename) def open_local_file(_): pass self.manager_1.open_local_file = open_local_file self.direct_edit._prepare_edit(self.nuxeo_url, doc_id) self.assertTrue(self.local.exists(local_path)) self.wait_sync(timeout=2, fail_if_timeout=False) self.direct_edit.stop() # Update file content self.local.update_content(local_path, 'Test') # Create empty folder (NXDRIVE-598) self.local.make_folder('/', 'emptyfolder') # Verify the cleanup dont delete document self.direct_edit._cleanup() self.assertTrue(self.local.exists(local_path)) self.assertNotEquals(self.remote.get_blob(self.remote.get_info(doc_id)), 'Test') # Verify it reupload it self.direct_edit.start() self.wait_sync(timeout=2, fail_if_timeout=False) self.assertTrue(self.local.exists(local_path)) self.assertEqual(self.remote.get_blob(self.remote.get_info(doc_id)), 'Test') # Verify it is cleanup if sync self.direct_edit.stop() self.direct_edit._cleanup() self.assertFalse(self.local.exists(local_path)) def test_user_name(self): # user_1 is drive_user_1, no more informations user = self.engine_1.get_user_full_name(self.user_1) self.assertEqual(user, self.user_1) # Create a complete user remote = self.root_remote_client remote.create_user('john', firstName='John', lastName='Doe') user = self.engine_1.get_user_full_name('john') self.assertEqual(user, 'John Doe') # Unknown user user = self.engine_1.get_user_full_name('unknown') self.assertEqual(user, 'unknown')
class TestIntegrationVersioning(IntegrationTestCase): def setUp(self): super(TestIntegrationVersioning, self).setUp() self.controller_1.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url, self.user_1, self.password_1) self.controller_2.bind_server(self.local_nxdrive_folder_2, self.nuxeo_url, self.user_2, self.password_2) self.controller_1.bind_root(self.local_nxdrive_folder_1, self.workspace) self.controller_2.bind_root(self.local_nxdrive_folder_2, self.workspace) self.syn_1 = self.controller_1.synchronizer self.syn_2 = self.controller_2.synchronizer self.syn_1.loop(delay=0.010, max_loops=1, no_event_init=True) self.syn_2.loop(delay=0.010, max_loops=1, no_event_init=True) # Fetch server bindings after sync loop as it closes the Session self.sb_1 = self.controller_1.get_server_binding( self.local_nxdrive_folder_1) self.sb_2 = self.controller_2.get_server_binding( self.local_nxdrive_folder_2) self.remote_client_1 = self.remote_document_client_1 self.remote_client_2 = self.remote_document_client_2 sync_root_folder_1 = os.path.join(self.local_nxdrive_folder_1, self.workspace_title) sync_root_folder_2 = os.path.join(self.local_nxdrive_folder_2, self.workspace_title) self.local_client_1 = LocalClient(sync_root_folder_1) self.local_client_2 = LocalClient(sync_root_folder_2) # Call the Nuxeo operation to set the versioning delay to 10 seconds self.versioning_delay = self.OS_STAT_MTIME_RESOLUTION * 10 self.root_remote_client.execute( "NuxeoDrive.SetVersioningOptions", delay=str(self.versioning_delay)) def test_versioning(self): # Create a file as user 1 self.local_client_1.make_file('/', 'Test versioning.txt', "This is version 0") self._synchronize_and_assert(self.syn_1, self.sb_1, 1) doc = self.root_remote_client.fetch( self.TEST_WORKSPACE_PATH + '/Test versioning.txt') self._assert_version(doc, 0, 0) # Synchronize it for user 2 self.assertTrue(self.remote_client_2.exists('/Test versioning.txt')) self._synchronize_and_assert(self.syn_2, self.sb_2, 1, wait=True) self.assertTrue(self.local_client_2.exists('/Test versioning.txt')) # Update it as user 2 => should be versioned time.sleep(self.OS_STAT_MTIME_RESOLUTION) self.local_client_2.update_content('/Test versioning.txt', "Modified content") self._synchronize_and_assert(self.syn_2, self.sb_2, 1) doc = self.root_remote_client.fetch( self.TEST_WORKSPACE_PATH + '/Test versioning.txt') self._assert_version(doc, 0, 1) # Update it as user 2 => should NOT be versioned # since the versioning delay (10s) is not passed by time.sleep(self.OS_STAT_MTIME_RESOLUTION) self.local_client_2.update_content('/Test versioning.txt', "Content twice modified") self._synchronize_and_assert(self.syn_2, self.sb_2, 1) doc = self.root_remote_client.fetch( self.TEST_WORKSPACE_PATH + '/Test versioning.txt') self._assert_version(doc, 0, 1) # Update it as user 2 after 10s => should be versioned # since the versioning delay is passed by time.sleep(self.versioning_delay + 0.1) self.local_client_2.update_content('/Test versioning.txt', "Updated again!!") self._synchronize_and_assert(self.syn_2, self.sb_2, 1) doc = self.root_remote_client.fetch( self.TEST_WORKSPACE_PATH + '/Test versioning.txt') self._assert_version(doc, 0, 2) def test_version_restore(self): remote_client = self.remote_client_1 local_client = self.local_client_1 # Create a remote doc doc = remote_client.make_file(self.workspace, 'Document to restore.txt', content="Initial content.") self.wait_audit_change_finder_if_needed() self.wait() self._synchronize_and_assert(self.syn_1, self.sb_1, 1) self.assertTrue(local_client.exists('/Document to restore.txt')) self.assertEquals(local_client.get_content('/Document to restore.txt'), "Initial content.") # Create version 1.0, update content, then restore version 1.0 remote_client.create_version(doc, 'Major') # Ensure that modification time is different between the version # and the updated live document, otherwise the synchronizer won't # consider the restored document (with the modification date of # the version) as to be updated time.sleep(1.0) remote_client.update_content(doc, "Updated content.") self.wait_audit_change_finder_if_needed() self.wait() self._synchronize_and_assert(self.syn_1, self.sb_1, 1) self.assertEquals(local_client.get_content('/Document to restore.txt'), "Updated content.") version_uid = remote_client.get_versions(doc)[0][0] remote_client.restore_version(version_uid) self.wait_audit_change_finder_if_needed() self.wait() self._synchronize_and_assert(self.syn_1, self.sb_1, 1) self.assertEquals(local_client.get_content('/Document to restore.txt'), "Initial content.") def _synchronize_and_assert(self, synchronizer, server_binding, expected_synchronized, wait=False): if wait: # Wait for audit changes to be detected after the 1 second step self.wait_audit_change_finder_if_needed() self.wait() n_synchronized = synchronizer.update_synchronize_server(server_binding) self.assertEqual(n_synchronized, expected_synchronized) def _assert_version(self, doc, major, minor): self.assertEquals(doc['properties']['uid:major_version'], major) self.assertEquals(doc['properties']['uid:minor_version'], minor)
def test_conflict_detection_and_renaming(self): raise SkipTest("WIP in https://jira.nuxeo.com/browse/NXDRIVE-170") ctl = self.controller_1 ctl.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url, self.user_1, self.password_1) ctl.bind_root(self.local_nxdrive_folder_1, self.workspace) syn = ctl.synchronizer # Fetch the workspace sync root syn.loop(delay=0, max_loops=1, no_event_init=True) self.assertEquals(ctl.list_pending(), []) # Let's create some document on the client and synchronize it. local = LocalClient(self.local_nxdrive_folder_1) local_path = local.make_file('/' + self.workspace_title, 'Some File.doc', content="Original content.") syn.loop(delay=0, max_loops=1, no_event_init=True) # Let's modify it concurrently but with the same content (digest) time.sleep(OS_STAT_MTIME_RESOLUTION) local.update_content(local_path, 'Same new content.') remote_2 = self.remote_document_client_2 remote_2.update_content('/Some File.doc', 'Same new content.') # Let's synchronize and check the conflict handling: automatic # resolution will work for this case/ self.wait() syn.loop(delay=0, max_loops=1, no_event_init=True) item_infos = local.get_children_info('/' + self.workspace_title) self.assertEquals(len(item_infos), 1) self.assertEquals(item_infos[0].name, 'Some File.doc') self.assertEquals(local.get_content(local_path), 'Same new content.') # Let's trigger another conflict that cannot be resolved # automatically: time.sleep(OS_STAT_MTIME_RESOLUTION) local.update_content(local_path, 'Local new content.') remote_2 = self.remote_document_client_2 remote_2.update_content('/Some File.doc', 'Remote new content.') self.wait() # 2 loops are necessary for full conflict handling syn.loop(delay=0, max_loops=2, no_event_init=True) item_infos = local.get_children_info('/' + self.workspace_title) self.assertEquals(len(item_infos), 2) first, second = item_infos if first.name == 'Some File.doc': version_from_remote, version_from_local = first, second else: version_from_local, version_from_remote = first, second self.assertEquals(version_from_remote.name, 'Some File.doc') self.assertEquals(local.get_content(version_from_remote.path), 'Remote new content.') self.assertTrue(version_from_local.name.startswith('Some File ('), msg="'%s' was expected to start with 'Some File ('" % version_from_local.name) self.assertTrue(version_from_local.name.endswith(').doc'), msg="'%s' was expected to end with ').doc'" % version_from_local.name) self.assertEquals(local.get_content(version_from_local.path), 'Local new content.') # Everything is synchronized all_states = self.get_all_states() self.assertEquals(all_states[:2], [ (u'/', u'synchronized', u'synchronized'), (u'/Nuxeo Drive Test Workspace', u'synchronized', u'synchronized'), ]) # The filename changes with the date self.assertEquals(all_states[2][1:], (u'synchronized', u'synchronized')) self.assertEquals(all_states[3], (u'/Nuxeo Drive Test Workspace/Some File.doc', u'synchronized', u'synchronized'))