def test_find_changes_with_many_doc_creations(self):
        # Setup a controller and bind a root for user_1
        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

        # Synchronize the workspace folder
        self.wait()
        syn.loop(delay=0.010, max_loops=1)

        # Open a local client on the local workspace root
        expected_folder = os.path.join(self.local_nxdrive_folder_1,
                                       self.workspace_title)
        local_client = LocalClient(expected_folder)
        self.assertEquals(local_client.get_children_info(u'/'), [])

        # List of children names to create
        n_children = 5
        child_name_pattern = "child_%03d.txt"
        children_names = [child_name_pattern % i
                          for i in range(n_children)]

        # Launch a synchronizer thread concurrently that will stop
        # automatically as soon as all the children are synchronized
        def synchronization_loop():
            for i in range(3):
                syn.loop(delay=1, max_loops=2)

                local_children_names = [
                    c.name for c in local_client.get_children_info(u'/')]
                local_children_names.sort()
                if local_children_names == children_names:
                    # All remote children have been successfully synchronized
                    # in the local folder
                    return

        sync_thread = Thread(target=synchronization_loop)
        sync_thread.start()

        # Create the children to synchronize on the remote server concurrently
        # in a long running transaction
        remote_client.timeout = 10  # extend the timeout
        self.create_docs(self.workspace, n_children,
            name_pattern=child_name_pattern, delay=0.5)

        # Wait for the synchronizer thread to complete
        self.wait_audit_change_finder_if_needed()
        self.wait()
        sync_thread.join()

        # Check that all the children creations where detected despite the
        # creation transaction spanning longer than the individual audit
        # query time ranges.
        local_children_names = [
            c.name for c in local_client.get_children_info(u'/')]
        local_children_names.sort()
        self.assertEquals(local_children_names, children_names)
    def test_find_changes_with_many_doc_creations(self):
        raise SkipTest("WIP in https://jira.nuxeo.com/browse/NXDRIVE-170")
        # Setup a controller and bind a root for user_1
        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

        # Synchronize the workspace folder
        self.wait()
        syn.loop(delay=0.010, max_loops=1)

        # Open a local client on the local workspace root
        expected_folder = os.path.join(self.local_nxdrive_folder_1,
                                       self.workspace_title)
        local_client = LocalClient(expected_folder)
        self.assertEquals(local_client.get_children_info(u'/'), [])

        # List of children names to create
        n_children = 5
        child_name_pattern = "child_%03d.txt"
        children_names = [child_name_pattern % i
                          for i in range(n_children)]

        # Launch a synchronizer thread concurrently that will stop
        # automatically as soon as all the children are synchronized
        def synchronization_loop():
            for i in range(3):
                syn.loop(delay=1, max_loops=2)

                local_children_names = [
                    c.name for c in local_client.get_children_info(u'/')]
                local_children_names.sort()
                if local_children_names == children_names:
                    # All remote children have been successfully synchronized
                    # in the local folder
                    return

        sync_thread = Thread(target=synchronization_loop)
        sync_thread.start()

        # Create the children to synchronize on the remote server concurrently
        # in a long running transaction
        remote_client.timeout = 10  # extend the timeout
        self.create_docs(self.workspace, n_children,
            name_pattern=child_name_pattern, delay=0.5)

        # Wait for the synchronizer thread to complete
        self.wait()
        sync_thread.join()

        # Check that all the children creations where detected despite the
        # creation transaction spanning longer than the individual audit
        # query time ranges.
        local_children_names = [
            c.name for c in local_client.get_children_info(u'/')]
        local_children_names.sort()
        self.assertEquals(local_children_names, children_names)
    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_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.')
Exemple #5
0
    def test_sync_delete_root(self):
        user_workspace_uid = None
        try:
            # Get remote and local clients
            admin_remote_client = self.root_remote_client
            user_remote_client = RemoteDocumentClient(
                self.nuxeo_url,
                self.user_1,
                u'nxdrive-test-device-1',
                self.version,
                password=self.password_1,
                upload_tmp_dir=self.upload_tmp_dir)
            local_client = LocalClient(self.local_nxdrive_folder_1)

            # Make sure user workspace is created and fetch its uid
            user_workspace_uid = user_remote_client.make_file_in_user_workspace(
                'File in user workspace', filename='USFile.txt')['parentRef']

            # Create test folder in user workspace as test user
            test_folder_uid = user_remote_client.make_folder(
                user_workspace_uid, 'test_folder')
            # Create a document in the test folder
            user_remote_client.make_file(test_folder_uid, 'test_file.txt',
                                         "Some content.")

            # Register test folder as a sync root
            user_remote_client.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
            self.assertTrue(local_client.exists('/My Docs/test_folder'))
            self.assertTrue(
                local_client.exists('/My Docs/test_folder/test_file.txt'))

            # Delete test folder
            user_remote_client.delete(test_folder_uid)

            # Wait for synchronization
            self.wait_sync(wait_for_async=True)

            # Check locally synchronized content
            self.assertFalse(local_client.exists('/My Docs/test_folder'))
            self.assertEquals(len(local_client.get_children_info('/My Docs')),
                              0)
        finally:
            # Cleanup user workspace
            if user_workspace_uid is not None and admin_remote_client.exists(
                    user_workspace_uid):
                admin_remote_client.delete(user_workspace_uid, use_trash=False)
    def test_synchronize_special_filenames(self):
        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)
        self.assertEquals(ctl.list_pending(), [])

        # Create some remote documents with weird filenames
        remote = self.remote_document_client_1

        folder = remote.make_folder(self.workspace,
            u'Folder with forbidden chars: / \\ * < > ? "')

        time.sleep(self.AUDIT_CHANGE_FINDER_TIME_RESOLUTION)
        self.wait()
        syn.loop(delay=0, max_loops=1)
        self.assertEquals(ctl.list_pending(), [])
        local = LocalClient(
            os.path.join(self.local_nxdrive_folder_1, self.workspace_title))
        folder_names = [i.name for i in local.get_children_info('/')]
        self.assertEquals(folder_names,
            [u'Folder with forbidden chars- - - - - - - -'])

        # create some file on the server
        remote.make_file(folder,
            u'File with forbidden chars: / \\ * < > ? ".doc',
            content="some content")

        time.sleep(self.AUDIT_CHANGE_FINDER_TIME_RESOLUTION)
        self.wait()
        syn.loop(delay=0, max_loops=1)
        self.assertEquals(ctl.list_pending(), [])

        file_names = [i.name for i in local.get_children_info(
                      local.get_children_info('/')[0].path)]
        self.assertEquals(file_names,
            [u'File with forbidden chars- - - - - - - -.doc'])
    def test_sync_delete_root(self):
        user_workspaces_path = '/default-domain/UserWorkspaces/'
        user_workspace_title = 'nuxeoDriveTestUser-user-1'
        user_workspace_path = user_workspaces_path + user_workspace_title
        try:
            # Get remote and local clients
            admin_remote_client = self.root_remote_client
            user_remote_client = RemoteDocumentClient(
                self.nuxeo_url, self.user_1, u'nxdrive-test-device-1',
                self.version, password=self.password_1,
                upload_tmp_dir=self.upload_tmp_dir)
            local_client = LocalClient(self.local_nxdrive_folder_1)

            # Make sure user workspace is created
            user_remote_client.make_file_in_user_workspace('File in user workspace', filename='USFile.txt')

            # Create test folder in user workspace as test user
            user_remote_client.make_folder(user_workspace_path, 'test_folder')
            test_folder_path = user_workspace_path + '/test_folder'
            # Create a document in the test folder
            user_remote_client.make_file(test_folder_path, 'test_file.txt', "Some content.")

            # Register test folder as a sync root
            user_remote_client.register_as_root(test_folder_path)

            # Start engine
            self.engine_1.start()

            # Wait for synchronization
            self.wait_sync(wait_for_async=True)

            # Check locally synchronized content
            self.assertTrue(local_client.exists('/My Docs/test_folder'))
            self.assertTrue(local_client.exists('/My Docs/test_folder/test_file.txt'))

            # Delete test folder
            user_remote_client.delete(test_folder_path)

            # Wait for synchronization
            self.wait_sync(wait_for_async=True)

            # Check locally synchronized content
            self.assertFalse(local_client.exists('/My Docs/test_folder'))
            self.assertEquals(len(local_client.get_children_info('/My Docs')), 0)
        finally:
            # Cleanup user workspace
            if admin_remote_client.exists(user_workspace_path):
                admin_remote_client.delete(user_workspace_path,
                                           use_trash=False)
    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_sync_delete_root(self):
        user_workspaces_path = '/default-domain/UserWorkspaces/'
        user_workspace_title = 'nuxeoDriveTestUser-user-1'
        user_workspace_path = user_workspaces_path + user_workspace_title
        try:
            # Get remote and local clients
            admin_remote_client = self.root_remote_client
            user_remote_client = RemoteDocumentClient(
                self.nuxeo_url, self.user_1, u'nxdrive-test-device-1',
                self.version, password=self.password_1,
                upload_tmp_dir=self.upload_tmp_dir)
            local_client = LocalClient(self.local_nxdrive_folder_1)

            # Activate permission hierarchy profile as Administrator
            admin_remote_client.activate_profile('permission')

            # Make sure user workspace is created
            user_remote_client.make_file_in_user_workspace(
                                                    'File in user workspace',
                                                    filename='USFile.txt')
            # Bind server
            ctl = self.controller_1
            ctl.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url,
                            self.user_1, self.password_1)
            syn = ctl.synchronizer

            # Create test folder in user workspace as test user
            user_remote_client.make_folder(user_workspace_path, 'test_folder')
            test_folder_path = user_workspace_path + '/test_folder'
            # Create a document in the test folder
            user_remote_client.make_file(test_folder_path, 'test_file.txt',
                                        "Some content.")

            # Register test folder as a sync root
            user_remote_client.register_as_root(test_folder_path)

            # Synchronize
            self._synchronize(syn)

            # Check locally synchronized content
            self.assertTrue(local_client.exists('/My Docs/test_folder'))
            self.assertTrue(local_client.exists(
                                        '/My Docs/test_folder/test_file.txt'))

            # Delete test folder
            user_remote_client.delete(test_folder_path)

            # Synchronize
            self._synchronize(syn)

            # Check locally synchronized content
            self.assertFalse(local_client.exists('/My Docs/test_folder'))
            self.assertEquals(len(local_client.get_children_info('/My Docs')),
                              0)
        finally:
            # Cleanup user workspace
            if admin_remote_client.exists(user_workspace_path):
                admin_remote_client.delete(user_workspace_path,
                                           use_trash=False)
            # Deactivate permission hierarchy profile
            admin_remote_client.deactivate_profile('permission')
    def test_synchronize_no_space_left_on_device(self):
        raise SkipTest("WIP in https://jira.nuxeo.com/browse/NXDRIVE-170")
        # 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()
        syn = ctl.synchronizer
        syn.loop(delay=0.1, max_loops=1)

        # 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 a file in the remote root workspace
        remote.make_file('/', 'test_KO.odt', 'Some large content.')
        self.wait()

        # Synchronize simulating a "No space left on device" error
        error = IOError("No space left on device")
        ctl.make_local_raise(error)
        syn.loop(delay=0.1, max_loops=1)
        # Temporary download file (.nxpart) should be created locally but not
        # renamed and synchronization should not fail: doc pair should be
        # blacklisted and there should be 1 pending item
        self.assertTrue(local.exists('/.test_KO.odt.nxpart'))
        self.assertFalse(local.exists('/test_KO.odt'))
        self.assertEquals(len(ctl.list_pending()), 1)

        # Create another file in the remote root workspace
        remote.make_file('/', 'test_OK.odt', 'Some small content.')
        self.wait()

        # Synchronize without simulating any error
        ctl.make_local_raise(None)
        syn.loop(delay=0.1, max_loops=1)
        # Remote file should be created locally
        self.assertTrue(local.exists('/test_OK.odt'))
        # Blacklisted file should be ignored as delay (300 seconds by default)
        # is not expired, temporary download file from previously failed
        # synchronization should still be there and there should still be 1
        # pending item
        self.assertTrue(local.exists('/.test_KO.odt.nxpart'))
        self.assertFalse(local.exists('/test_KO.odt'))
        self.assertEquals(len(ctl.list_pending()), 1)

        # Retry to synchronize blacklisted file still simulating a "No space
        # left on device" error
        ctl.make_local_raise(error)
        # Reduce error skip delay to retry synchronization of pairs in error
        syn.error_skip_period = 1.0
        syn.loop(delay=0.1, max_loops=1)
        # Temporary download file (.nxpart) should be overridden but still not
        # renamed, doc pair should be blacklisted again and there should still
        # be 1 pending item
        self.assertTrue(local.exists('/.test_KO.odt.nxpart'))
        self.assertFalse(local.exists('/test_KO.odt'))
        self.assertEquals(len(ctl.list_pending()), 1)
        # In the test workspace there should be 2 files but only 1 child taken
        # into account by the local client as it ignores .nxpart suffixed files
        self.assertEquals(len(os.listdir(os.path.join(
                                            self.local_nxdrive_folder_1,
                                            self.workspace_title))), 2)
        self.assertEquals(len(local.get_children_info('/')), 1)

        # Synchronize without simulating any error, as if space had been made
        # available on device
        ctl.make_local_raise(None)
        # Wait for error skip delay to retry synchronization of pairs in error
        time.sleep(syn.error_skip_period)
        syn.loop(delay=0.1, max_loops=1)
        # Previously blacklisted file should be created locally, temporary
        # download file should not be there anymore and there should be no
        # pending items left
        self.assertTrue(local.exists('/test_KO.odt'))
        self.assertFalse(local.exists('/.test_KO.odt.nxpart'))
        self.assertEquals(len(ctl.list_pending()), 0)
        # In the test workspace there should be 2 files and 2 children taken
        # into account by the local client
        self.assertEquals(len(os.listdir(os.path.join(
                                            self.local_nxdrive_folder_1,
                                            self.workspace_title))), 2)
        self.assertEquals(len(local.get_children_info('/')), 2)
    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)
Exemple #12
0
    def test_sync_delete_root(self):
        user_workspaces_path = '/default-domain/UserWorkspaces/'
        user_workspace_title = 'nuxeoDriveTestUser-user-1'
        user_workspace_path = user_workspaces_path + user_workspace_title
        try:
            # Get remote and local clients
            admin_remote_client = self.root_remote_client
            user_remote_client = RemoteDocumentClient(
                self.nuxeo_url,
                self.user_1,
                u'nxdrive-test-device-1',
                self.version,
                password=self.password_1,
                upload_tmp_dir=self.upload_tmp_dir)
            local_client = LocalClient(self.local_nxdrive_folder_1)

            # Activate permission hierarchy profile as Administrator
            admin_remote_client.activate_profile('permission')

            # Make sure user workspace is created
            user_remote_client.make_file_in_user_workspace(
                'File in user workspace', filename='USFile.txt')
            # Bind server
            ctl = self.controller_1
            ctl.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url,
                            self.user_1, self.password_1)
            syn = ctl.synchronizer

            # Create test folder in user workspace as test user
            user_remote_client.make_folder(user_workspace_path, 'test_folder')
            test_folder_path = user_workspace_path + '/test_folder'
            # Create a document in the test folder
            user_remote_client.make_file(test_folder_path, 'test_file.txt',
                                         "Some content.")

            # Register test folder as a sync root
            user_remote_client.register_as_root(test_folder_path)

            # Synchronize
            self._synchronize(syn)

            # Check locally synchronized content
            self.assertTrue(local_client.exists('/My Docs/test_folder'))
            self.assertTrue(
                local_client.exists('/My Docs/test_folder/test_file.txt'))

            # Delete test folder
            user_remote_client.delete(test_folder_path)

            # Synchronize
            self._synchronize(syn)

            # Check locally synchronized content
            self.assertFalse(local_client.exists('/My Docs/test_folder'))
            self.assertEquals(len(local_client.get_children_info('/My Docs')),
                              0)
        finally:
            # Cleanup user workspace
            if admin_remote_client.exists(user_workspace_path):
                admin_remote_client.delete(user_workspace_path,
                                           use_trash=False)
            # Deactivate permission hierarchy profile
            admin_remote_client.deactivate_profile('permission')
    def test_move_sync_root_child_to_user_workspace(self):
        """See https://jira.nuxeo.com/browse/NXP-14870"""
        admin_remote_client = self.root_remote_client
        user1_workspace_uid = None
        try:
            # Get remote  and local clients
            remote_user1 = RemoteDocumentClient(
                self.nuxeo_url, self.user_1, u'nxdrive-test-device-1',
                self.version, password=self.password_1,
                upload_tmp_dir=self.upload_tmp_dir)
            remote_user2 = RemoteDocumentClient(
                self.nuxeo_url, self.user_2, u'nxdrive-test-device-2',
                self.version, password=self.password_2,
                upload_tmp_dir=self.upload_tmp_dir)
            local_user2 = LocalClient(self.local_nxdrive_folder_2)

            # Make sure personal workspace is created for user1 and fetch its uid
            user1_workspace_uid = remote_user1.make_file_in_user_workspace('File in user workspace',
                                                                           filename='UWFile.txt')['parentRef']

            # As user1 register personal workspace as a sync root
            remote_user1.register_as_root(user1_workspace_uid)

            # As user1 create a parent folder in user1's personal workspace
            parent_folder_uid = remote_user1.make_folder(user1_workspace_uid, 'Parent')

            # As user1 grant Everything permission to user2 on parent folder
            op_input = "doc:" + parent_folder_uid
            admin_remote_client.execute("Document.SetACE", op_input=op_input, user="******",
                                        permission="Everything", grant="true")

            # As user1 create a child folder in parent folder
            child_folder_uid = remote_user1.make_folder(parent_folder_uid, 'Child')

            # As user2 register parent folder as a sync root
            remote_user2.register_as_root(parent_folder_uid)
            remote_user2.unregister_as_root(self.workspace)
            # Start engine for user2
            self.engine_2.start()

            # Wait for synchronization
            self.wait_sync(wait_for_async=True, wait_for_engine_1=False, wait_for_engine_2=True)

            # Check locally synchronized content
            self.assertEquals(len(local_user2.get_children_info('/')), 1)
            self.assertTrue(local_user2.exists('/Parent'))
            self.assertTrue(local_user2.exists('/Parent/Child'))

            # As user1 move child folder to user1's personal workspace
            remote_user1.move(child_folder_uid, user1_workspace_uid)

            # Wait for synchronization
            self.wait_sync(wait_for_async=True, wait_for_engine_1=False, wait_for_engine_2=True)

            # Check locally synchronized content
            self.assertFalse(local_user2.exists('/Parent/Child'))

        finally:
            # Cleanup user1 personal workspace
            if user1_workspace_uid is not None and admin_remote_client.exists(user1_workspace_uid):
                admin_remote_client.delete(user1_workspace_uid,
                                           use_trash=False)
Exemple #14
0
    def test_sync_delete_shared_folder(self):
        user_workspace_uid = None
        try:
            # Get remote and local clients
            admin_remote_client = self.root_remote_client
            user1_remote_client = RemoteDocumentClient(
                self.nuxeo_url,
                self.user_1,
                u'nxdrive-test-device-1',
                self.version,
                password=self.password_1,
                upload_tmp_dir=self.upload_tmp_dir)
            user2_remote_client = RemoteDocumentClient(
                self.nuxeo_url,
                self.user_2,
                u'nxdrive-test-device-2',
                self.version,
                password=self.password_2,
                upload_tmp_dir=self.upload_tmp_dir)
            local_client_1 = LocalClient(self.local_nxdrive_folder_1)

            # Make sure user1 workspace is created and fetch its uid
            user_workspace_uid = user1_remote_client.make_file_in_user_workspace(
                'File in user workspace', filename='USFile.txt')['parentRef']

            # Register user workspace as a sync root for user1
            user1_remote_client.register_as_root(user_workspace_uid)

            # Start engine
            self.engine_1.start()

            # Wait for synchronization
            self.wait_sync(wait_for_async=True)
            # Check locally synchronized content
            self.assertTrue(local_client_1.exists('/My Docs'))

            # Create test folder in user workspace as user1
            test_folder_uid = user1_remote_client.make_folder(
                user_workspace_uid, 'test_folder')
            # Wait for synchronization
            self.wait_sync(wait_for_async=True)
            # Check locally synchronized content
            self.assertTrue(local_client_1.exists('/My Docs/test_folder'))

            # Grant ReadWrite permission to user2 on test folder
            op_input = "doc:" + test_folder_uid
            admin_remote_client.execute("Document.SetACE",
                                        op_input=op_input,
                                        user="******",
                                        permission="ReadWrite",
                                        grant="true")
            # Wait for synchronization
            self.wait_sync(wait_for_async=True)

            # Register test folder as a sync root for user2
            user2_remote_client.register_as_root(test_folder_uid)
            # Wait for synchronization
            self.wait_sync(wait_for_async=True)

            # Delete test folder
            user1_remote_client.delete(test_folder_uid)

            # Synchronize deletion
            self.wait_sync(wait_for_async=True)
            # Check locally synchronized content
            self.assertFalse(local_client_1.exists('/My Docs/test_folder'))
            self.assertEquals(
                len(local_client_1.get_children_info('/My Docs')), 1)
        finally:
            # Cleanup user workspace
            if user_workspace_uid is not None and admin_remote_client.exists(
                    user_workspace_uid):
                admin_remote_client.delete(user_workspace_uid, use_trash=False)
    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_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')
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_binding_initialization_and_first_sync():
    # Create some documents in a Nuxeo workspace and bind this server to a
    # Nuxeo Drive local folder
    make_server_tree()
    ctl.bind_server(LOCAL_NXDRIVE_FOLDER, NUXEO_URL, USER, PASSWORD)
    ctl.bind_root(LOCAL_NXDRIVE_FOLDER, TEST_WORKSPACE)

    # The binding operation creates a new local folder with the Workspace name
    # and reproduce the server side structure with folders and empty documents.
    expected_folder = os.path.join(LOCAL_NXDRIVE_FOLDER, TEST_WORKSPACE_TITLE)
    local = LocalClient(expected_folder)
    level_0 = local.get_children_info("/")

    def size(info):
        return os.stat(info.filepath).st_size

    assert_equal(len(level_0), 3)
    assert_equal(level_0[0].name, "File 5.txt")
    assert_equal(size(level_0[0]), 0)
    assert_equal(level_0[1].name, "Folder 1")
    assert_equal(level_0[2].name, "Folder 2")

    level_1 = local.get_children_info(level_0[1].path)
    assert_equal(len(level_1), 3)
    assert_equal(level_1[0].name, "File 1.txt")
    assert_equal(size(level_1[0]), 0)
    assert_equal(level_1[1].name, "Folder 1.1")
    assert_equal(level_1[2].name, "Folder 1.2")

    level_2 = local.get_children_info(level_0[2].path)
    assert_equal(len(level_2), 3)
    assert_equal(level_2[0].name, "Duplicated File.txt")
    assert_equal(size(level_2[0]), 0)
    assert_equal(level_2[1].name, "Duplicated File__1.txt")  # deduped name
    assert_equal(size(level_2[1]), 0)
    assert_equal(level_2[2].name, "File 4.txt")
    assert_equal(size(level_2[2]), 0)

    # Check the aggregate states information from the controller
    states = ctl.children_states(expected_folder)
    expected_states = [
        (u"/File 5.txt", "remotely_modified"),
        (u"/Folder 1", "children_modified"),
        (u"/Folder 2", "children_modified"),
    ]
    assert_equal(states, expected_states)

    states = ctl.children_states(expected_folder + "/Folder 1")
    expected_states = [
        (u"/Folder 1/File 1.txt", "remotely_modified"),
        (u"/Folder 1/Folder 1.1", "children_modified"),
        (u"/Folder 1/Folder 1.2", "children_modified"),
    ]
    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", "remotely_modified")]
    assert_equal(states, expected_states)

    # Check the list of files and folders with synchronization pending
    pending = ctl.list_pending()
    assert_equal(len(pending), 7)
    assert_equal(pending[0].path, "/File 5.txt")
    assert_equal(pending[1].path, "/Folder 1/File 1.txt")
    assert_equal(pending[2].path, "/Folder 1/Folder 1.1/File 2.txt")
    assert_equal(pending[3].path, "/Folder 1/Folder 1.2/File 3.txt")
    assert_equal(pending[4].path, "/Folder 2/Duplicated File.txt")
    assert_equal(pending[5].path, "/Folder 2/Duplicated File__1.txt")
    assert_equal(pending[6].path, "/Folder 2/File 4.txt")

    # It is also possible to restrict the list of pending document to a
    # specific root
    assert_equal(len(ctl.list_pending(local_root=expected_folder)), 7)

    # It is also possible to restrict the number of pending tasks
    pending = ctl.list_pending(limit=2)
    assert_equal(len(pending), 2)

    # Synchronize the first 2 documents:
    assert_equal(ctl.synchronize(limit=2), 2)
    pending = ctl.list_pending()
    assert_equal(len(pending), 5)
    assert_equal(pending[0].path, "/Folder 1/Folder 1.1/File 2.txt")
    assert_equal(pending[1].path, "/Folder 1/Folder 1.2/File 3.txt")
    assert_equal(pending[2].path, "/Folder 2/Duplicated File.txt")
    assert_equal(pending[3].path, "/Folder 2/Duplicated File__1.txt")
    assert_equal(pending[4].path, "/Folder 2/File 4.txt")

    states = ctl.children_states(expected_folder)
    expected_states = [
        (u"/File 5.txt", "synchronized"),
        (u"/Folder 1", "children_modified"),
        (u"/Folder 2", "children_modified"),
    ]
    # The actual content of the file has been updated
    assert_equal(local.get_content("/File 5.txt"), "eee")

    states = ctl.children_states(expected_folder + "/Folder 1")
    expected_states = [
        (u"/Folder 1/File 1.txt", "synchronized"),
        (u"/Folder 1/Folder 1.1", "children_modified"),
        (u"/Folder 1/Folder 1.2", "children_modified"),
    ]
    assert_equal(states, expected_states)

    # synchronize everything else
    assert_equal(ctl.synchronize(), 5)
    assert_equal(ctl.list_pending(), [])
    states = ctl.children_states(expected_folder)
    expected_states = [(u"/File 5.txt", "synchronized"), (u"/Folder 1", "synchronized"), (u"/Folder 2", "synchronized")]
    assert_equal(states, expected_states)

    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"), "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.")

    # Nothing else left to synchronize
    assert_equal(ctl.list_pending(), [])
    assert_equal(ctl.synchronize(), 0)
    assert_equal(ctl.list_pending(), [])
    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_sync_delete_root(self):
        try:
            user_workspaces_path = '/default-domain/UserWorkspaces/'
            user_workspace_title = 'nuxeoDriveTestUser_user_1'
            user_workspace_path = user_workspaces_path + user_workspace_title

            # Get remote and local clients
            admin_remote_client = self.root_remote_client
            user_remote_client = RemoteDocumentClient(
                self.nuxeo_url, self.user_1, u'nxdrive-test-device-1',
                self.version, password=self.password_1,
                upload_tmp_dir=self.upload_tmp_dir)
            local_client = LocalClient(self.local_nxdrive_folder_1)

            # Activate permission hierarchy profile as Administrator
            admin_remote_client.activate_profile('permission')

            # Bind server
            ctl = self.controller_1
            ctl.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url,
                            self.user_1, self.password_1)
            syn = ctl.synchronizer

            # Create user workspace parent as Administrator if it doesn't exist
            if not admin_remote_client.exists(user_workspaces_path):
                admin_remote_client.make_folder('/default-domain',
                                                'UserWorkspaces',
                                                doc_type='UserWorkspacesRoot')

            # Create test user workspace as Administrator
            admin_remote_client.make_folder(user_workspaces_path,
                                           user_workspace_title,
                                           doc_type='Workspace')
            # Grant ReadWrite permission to test user on its workspace
            op_input = "doc:" + user_workspace_path
            admin_remote_client.execute("Document.SetACE",
                op_input=op_input,
                user="******",
                permission="ReadWrite",
                grant="true")

            # Create test folder in user workspace as test user
            user_remote_client.make_folder(user_workspace_path, 'test_folder')
            test_folder_path = user_workspace_path + '/test_folder'
            # Create a document in the test folder
            user_remote_client.make_file(test_folder_path, 'test_file.txt',
                                        "Some content.")

            # Register test folder as a sync root
            user_remote_client.register_as_root(test_folder_path)

            # Synchronize
            self._synchronize(syn)

            # Check locally synchronized content
            self.assertTrue(local_client.exists('/My Docs/test_folder'))
            self.assertTrue(local_client.exists(
                                        '/My Docs/test_folder/test_file.txt'))

            # Delete test folder
            user_remote_client.delete(test_folder_path)

            # Synchronize
            self._synchronize(syn)

            # Check locally synchronized content
            self.assertFalse(local_client.exists('/My Docs/test_folder'))
            self.assertEquals(len(local_client.get_children_info('/My Docs')),
                              0)
        finally:
            # Cleanup user workspace
            if admin_remote_client.exists(user_workspace_path):
                admin_remote_client.delete(user_workspace_path,
                                           use_trash=False)
            # Deactivate permission hierarchy profile
            admin_remote_client.deactivate_profile('permission')
    def test_sync_delete_shared_folder(self):
        user_workspaces_path = '/default-domain/UserWorkspaces/'
        user1_workspace_title = 'nuxeoDriveTestUser-user-1'
        user1_workspace_path = user_workspaces_path + user1_workspace_title
        try:
            # Get remote and local clients
            admin_remote_client = self.root_remote_client
            user1_remote_client = RemoteDocumentClient(
                self.nuxeo_url, self.user_1, u'nxdrive-test-device-1',
                self.version, password=self.password_1,
                upload_tmp_dir=self.upload_tmp_dir)
            user2_remote_client = RemoteDocumentClient(
                self.nuxeo_url, self.user_2, u'nxdrive-test-device-2',
                self.version, password=self.password_2,
                upload_tmp_dir=self.upload_tmp_dir)
            local_client_1 = LocalClient(self.local_nxdrive_folder_1)

            # Activate permission hierarchy profile as Administrator
            admin_remote_client.activate_profile('permission')

            # Make sure user1 workspace is created
            user1_remote_client.make_file_in_user_workspace(
                                                    'File in user workspace',
                                                    filename='USFile.txt')

            # Register user workspace as a sync root for user1
            user1_remote_client.register_as_root(user1_workspace_path)

            # Bind server for user1
            ctl_1 = self.controller_1
            ctl_1.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url,
                            self.user_1, self.password_1)
            syn_1 = ctl_1.synchronizer

            # Synchronize
            self._synchronize(syn_1)
            # Check locally synchronized content
            self.assertTrue(local_client_1.exists('/My Docs'))

            # Create test folder in user workspace as user1
            user1_remote_client.make_folder(user1_workspace_path,
                                            'test_folder')
            # Synchronize
            self._synchronize(syn_1)
            # Check locally synchronized content
            self.assertTrue(local_client_1.exists('/My Docs/test_folder'))

            # Grant ReadWrite permission to user2 on test folder
            test_folder_path = user1_workspace_path + '/test_folder'
            op_input = "doc:" + test_folder_path
            admin_remote_client.execute("Document.SetACE",
                op_input=op_input,
                user="******",
                permission="ReadWrite",
                grant="true")

            # Register test folder as a sync root for user2
            user2_remote_client.register_as_root(test_folder_path)

            # Wait for a while:
            time.sleep(2.0)

            # Delete test folder
            user1_remote_client.delete(test_folder_path)

            # Synchronize
            self._synchronize(syn_1)
            # Check locally synchronized content
            self.assertFalse(local_client_1.exists('/My Docs/test_folder'))
            self.assertEquals(len(local_client_1.get_children_info(
                                                            '/My Docs')), 1)
        finally:
            # Cleanup user workspace
            if admin_remote_client.exists(user1_workspace_path):
                admin_remote_client.delete(user1_workspace_path,
                                           use_trash=False)
            # Deactivate permission hierarchy profile
            admin_remote_client.deactivate_profile('permission')
    def test_rebind_without_duplication(self):
        raise SkipTest("WIP in https://jira.nuxeo.com/browse/NXDRIVE-170")
        """Check rebinding an existing folder won't duplicate everything"""
        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
        self.assertEquals(ctl.list_pending(), [])

        # Let's create some document on the client and the server
        local = LocalClient(self.local_nxdrive_folder_1)
        local.make_folder('/', self.workspace_title)
        local.make_folder('/' + self.workspace_title, 'Folder 3')
        self.make_server_tree()
        self.wait()

        syn.loop(delay=0, max_loops=3)
        self.assertEquals(ctl.list_pending(), [])

        self.assertEquals(self.get_all_states(), [
            (u'/',
             u'synchronized', u'synchronized'),
            (u'/Nuxeo Drive Test Workspace',
             u'synchronized', u'synchronized'),
            (u'/Nuxeo Drive Test Workspace/File 5.txt',
             u'synchronized', u'synchronized'),
            (u'/Nuxeo Drive Test Workspace/Folder 1',
             u'synchronized', u'synchronized'),
            (u'/Nuxeo Drive Test Workspace/Folder 1/File 1.txt',
             u'synchronized', u'synchronized'),
            (u'/Nuxeo Drive Test Workspace/Folder 1/Folder 1.1',
             u'synchronized', u'synchronized'),
            (u'/Nuxeo Drive Test Workspace/Folder 1/Folder 1.1/File 2.txt',
             u'synchronized', u'synchronized'),
            (u'/Nuxeo Drive Test Workspace/Folder 1/Folder 1.2',
             u'synchronized', u'synchronized'),
            (u'/Nuxeo Drive Test Workspace/Folder 1/Folder 1.2/File 3.txt',
             u'synchronized', u'synchronized'),
            (u'/Nuxeo Drive Test Workspace/Folder 2',
             u'synchronized', u'synchronized'),
            (u'/Nuxeo Drive Test Workspace/Folder 2/Duplicated File.txt',
             u'synchronized', u'synchronized'),
            (u'/Nuxeo Drive Test Workspace/Folder 2/Duplicated File__1.txt',
             u'synchronized', u'synchronized'),
            (u'/Nuxeo Drive Test Workspace/Folder 2/File 4.txt',
             u'synchronized', u'synchronized'),
            (u'/Nuxeo Drive Test Workspace/Folder 3',
             u'synchronized', u'synchronized')
        ])
        self.assertEquals(
            len(local.get_children_info('/Nuxeo Drive Test Workspace')), 4)

        # Unbind: the state database is emptied
        ctl.unbind_server(self.local_nxdrive_folder_1)
        self.assertEquals(self.get_all_states(), [])

        # Previously synchronized files are still there, untouched
        self.assertEquals(
            len(local.get_children_info('/Nuxeo Drive Test Workspace')), 4)

        # Lets rebind the same folder to the same workspace
        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.loop(delay=0, max_loops=3)
        self.assertEquals(ctl.list_pending(), [])

        # Check that the sync that occurs right after the bind automatically
        # detects the file alignments and hence everything is synchronized
        # without duplication
        self.assertEquals(self.get_all_states(), [
            (u'/',
             u'synchronized', u'synchronized'),
            (u'/Nuxeo Drive Test Workspace',
             u'synchronized', u'synchronized'),
            (u'/Nuxeo Drive Test Workspace/File 5.txt',
             u'synchronized', u'synchronized'),
            (u'/Nuxeo Drive Test Workspace/Folder 1',
             u'synchronized', u'synchronized'),
            (u'/Nuxeo Drive Test Workspace/Folder 1/File 1.txt',
             u'synchronized', u'synchronized'),
            (u'/Nuxeo Drive Test Workspace/Folder 1/Folder 1.1',
             u'synchronized', u'synchronized'),
            (u'/Nuxeo Drive Test Workspace/Folder 1/Folder 1.1/File 2.txt',
             u'synchronized', u'synchronized'),
            (u'/Nuxeo Drive Test Workspace/Folder 1/Folder 1.2',
             u'synchronized', u'synchronized'),
            (u'/Nuxeo Drive Test Workspace/Folder 1/Folder 1.2/File 3.txt',
             u'synchronized', u'synchronized'),
            (u'/Nuxeo Drive Test Workspace/Folder 2',
             u'synchronized', u'synchronized'),
            (u'/Nuxeo Drive Test Workspace/Folder 2/Duplicated File.txt',
             u'synchronized', u'synchronized'),
            (u'/Nuxeo Drive Test Workspace/Folder 2/Duplicated File__1.txt',
             u'synchronized', u'synchronized'),
            (u'/Nuxeo Drive Test Workspace/Folder 2/File 4.txt',
             u'synchronized', u'synchronized'),
            (u'/Nuxeo Drive Test Workspace/Folder 3',
             u'synchronized', u'synchronized')
        ])
        self.assertEquals(ctl.list_pending(), [])
        # Previously synchronized files are still there, untouched
        self.assertEquals(
            len(local.get_children_info('/Nuxeo Drive Test Workspace')), 4)
Exemple #23
0
    def test_move_sync_root_child_to_user_workspace(self):
        """See https://jira.nuxeo.com/browse/NXP-14870"""
        admin_remote_client = self.root_remote_client
        user1_workspace_uid = None
        try:
            # Get remote  and local clients
            remote_user1 = RemoteDocumentClientForTests(
                self.nuxeo_url, self.user_1, u'nxdrive-test-device-1',
                self.version, password=self.password_1,
                upload_tmp_dir=self.upload_tmp_dir)
            remote_user2 = RemoteDocumentClientForTests(
                self.nuxeo_url, self.user_2, u'nxdrive-test-device-2',
                self.version, password=self.password_2,
                upload_tmp_dir=self.upload_tmp_dir)
            local_user2 = LocalClient(self.local_nxdrive_folder_2)

            # Make sure personal workspace is created for user1 and fetch its uid
            user1_workspace_uid = remote_user1.make_file_in_user_workspace('File in user workspace',
                                                                           filename='UWFile.txt')['parentRef']

            # As user1 register personal workspace as a sync root
            remote_user1.register_as_root(user1_workspace_uid)

            # As user1 create a parent folder in user1's personal workspace
            parent_folder_uid = remote_user1.make_folder(user1_workspace_uid, 'Parent')

            # As user1 grant Everything permission to user2 on parent folder
            op_input = "doc:" + parent_folder_uid
            admin_remote_client.execute("Document.SetACE", op_input=op_input, user=self.user_2,
                                        permission="Everything", grant="true")

            # As user1 create a child folder in parent folder
            child_folder_uid = remote_user1.make_folder(parent_folder_uid, 'Child')

            # As user2 register parent folder as a sync root
            remote_user2.register_as_root(parent_folder_uid)
            remote_user2.unregister_as_root(self.workspace)
            # Start engine for user2
            self.engine_2.start()

            # Wait for synchronization
            self.wait_sync(wait_for_async=True, wait_for_engine_1=False, wait_for_engine_2=True)

            # Check locally synchronized content
            self.assertEqual(len(local_user2.get_children_info('/')), 1)
            self.assertTrue(local_user2.exists('/Parent'))
            self.assertTrue(local_user2.exists('/Parent/Child'))

            # As user1 move child folder to user1's personal workspace
            remote_user1.move(child_folder_uid, user1_workspace_uid)

            # Wait for synchronization
            self.wait_sync(wait_for_async=True, wait_for_engine_1=False, wait_for_engine_2=True)

            # Check locally synchronized content
            self.assertFalse(local_user2.exists('/Parent/Child'))

        finally:
            # Cleanup user1 personal workspace
            if user1_workspace_uid is not None and admin_remote_client.exists(user1_workspace_uid):
                admin_remote_client.delete(user1_workspace_uid,
                                           use_trash=False)
    def test_move_sync_root_child_to_user_workspace(self):
        """See https://jira.nuxeo.com/browse/NXP-14870"""

        admin_remote_client = self.root_remote_client
        user1_workspace_path = ('/default-domain/UserWorkspaces/'
                                'nuxeoDriveTestUser-user-1')
        try:
            # Get remote  and local clients
            remote_user1 = RemoteDocumentClient(
                self.nuxeo_url,
                self.user_1,
                u'nxdrive-test-device-1',
                self.version,
                password=self.password_1,
                upload_tmp_dir=self.upload_tmp_dir)
            remote_user2 = RemoteDocumentClient(
                self.nuxeo_url,
                self.user_2,
                u'nxdrive-test-device-2',
                self.version,
                password=self.password_2,
                upload_tmp_dir=self.upload_tmp_dir)
            local_user2 = LocalClient(self.local_nxdrive_folder_2)

            # Make sure personal workspace is created for user1
            remote_user1.make_file_in_user_workspace('File in user workspace',
                                                     filename='UWFile.txt')

            # As user1 register personal workspace as a sync root
            remote_user1.register_as_root(user1_workspace_path)

            # As user1 create a parent folder in user1's personal workspace
            remote_user1.make_folder(user1_workspace_path, 'Parent')

            # As user1 grant Everything permission to user2 on parent folder
            parent_folder_path = user1_workspace_path + '/Parent'
            op_input = "doc:" + parent_folder_path
            admin_remote_client.execute("Document.SetACE",
                                        op_input=op_input,
                                        user="******",
                                        permission="Everything",
                                        grant="true")

            # As user1 create a child folder in parent folder
            remote_user1.make_folder(parent_folder_path, 'Child')

            # As user2 register parent folder as a sync root
            remote_user2.register_as_root(parent_folder_path)

            # Bind server for user2
            ctl_2 = self.controller_2
            ctl_2.bind_server(self.local_nxdrive_folder_2, self.nuxeo_url,
                              self.user_2, self.password_2)

            # Launch first synchronization
            syn_2 = ctl_2.synchronizer
            self._synchronize(syn_2)

            # Check locally synchronized content
            self.assertEquals(len(local_user2.get_children_info('/')), 1)
            self.assertTrue(local_user2.exists('/Parent'))
            self.assertTrue(local_user2.exists('/Parent/Child'))

            # As user1 move child folder to user1's personal workspace
            remote_user1.move(parent_folder_path + '/Child',
                              user1_workspace_path)

            # Synchronize
            self._synchronize(syn_2)

            # Check locally synchronized content
            self.assertFalse(local_user2.exists('/Parent/Child'))

        finally:
            # Cleanup user1 personal workspace
            if admin_remote_client.exists(user1_workspace_path):
                admin_remote_client.delete(user1_workspace_path,
                                           use_trash=False)
    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)
    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'))
Exemple #27
0
    def test_sync_delete_shared_folder(self):
        user_workspaces_path = '/default-domain/UserWorkspaces/'
        user1_workspace_title = 'nuxeoDriveTestUser-user-1'
        user1_workspace_path = user_workspaces_path + user1_workspace_title
        try:
            # Get remote and local clients
            admin_remote_client = self.root_remote_client
            user1_remote_client = RemoteDocumentClient(
                self.nuxeo_url,
                self.user_1,
                u'nxdrive-test-device-1',
                self.version,
                password=self.password_1,
                upload_tmp_dir=self.upload_tmp_dir)
            user2_remote_client = RemoteDocumentClient(
                self.nuxeo_url,
                self.user_2,
                u'nxdrive-test-device-2',
                self.version,
                password=self.password_2,
                upload_tmp_dir=self.upload_tmp_dir)
            local_client_1 = LocalClient(self.local_nxdrive_folder_1)

            # Activate permission hierarchy profile as Administrator
            admin_remote_client.activate_profile('permission')

            # Make sure user1 workspace is created
            user1_remote_client.make_file_in_user_workspace(
                'File in user workspace', filename='USFile.txt')

            # Register user workspace as a sync root for user1
            user1_remote_client.register_as_root(user1_workspace_path)

            # Bind server for user1
            ctl_1 = self.controller_1
            ctl_1.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url,
                              self.user_1, self.password_1)
            syn_1 = ctl_1.synchronizer

            # Synchronize
            self._synchronize(syn_1)
            # Check locally synchronized content
            self.assertTrue(local_client_1.exists('/My Docs'))

            # Create test folder in user workspace as user1
            user1_remote_client.make_folder(user1_workspace_path,
                                            'test_folder')
            # Synchronize
            self._synchronize(syn_1)
            # Check locally synchronized content
            self.assertTrue(local_client_1.exists('/My Docs/test_folder'))

            # Grant ReadWrite permission to user2 on test folder
            test_folder_path = user1_workspace_path + '/test_folder'
            op_input = "doc:" + test_folder_path
            admin_remote_client.execute("Document.SetACE",
                                        op_input=op_input,
                                        user="******",
                                        permission="ReadWrite",
                                        grant="true")

            # Register test folder as a sync root for user2
            user2_remote_client.register_as_root(test_folder_path)

            # Wait for a while:
            time.sleep(2.0)

            # Delete test folder
            user1_remote_client.delete(test_folder_path)

            # Synchronize
            self._synchronize(syn_1)
            # Check locally synchronized content
            self.assertFalse(local_client_1.exists('/My Docs/test_folder'))
            self.assertEquals(
                len(local_client_1.get_children_info('/My Docs')), 1)
        finally:
            # Cleanup user workspace
            if admin_remote_client.exists(user1_workspace_path):
                admin_remote_client.delete(user1_workspace_path,
                                           use_trash=False)
            # Deactivate permission hierarchy profile
            admin_remote_client.deactivate_profile('permission')
    def test_move_sync_root_child_to_user_workspace(self):
        """See https://jira.nuxeo.com/browse/NXP-14870"""

        admin_remote_client = self.root_remote_client
        user1_workspace_path = ('/default-domain/UserWorkspaces/'
                                'nuxeoDriveTestUser-user-1')
        try:
            # Get remote  and local clients
            remote_user1 = RemoteDocumentClient(
                self.nuxeo_url, self.user_1, u'nxdrive-test-device-1',
                self.version, password=self.password_1,
                upload_tmp_dir=self.upload_tmp_dir)
            remote_user2 = RemoteDocumentClient(
                self.nuxeo_url, self.user_2, u'nxdrive-test-device-2',
                self.version, password=self.password_2,
                upload_tmp_dir=self.upload_tmp_dir)
            local_user2 = LocalClient(self.local_nxdrive_folder_2)

            # Make sure personal workspace is created for user1
            remote_user1.make_file_in_user_workspace('File in user workspace',
                                                     filename='UWFile.txt')

            # As user1 register personal workspace as a sync root
            remote_user1.register_as_root(user1_workspace_path)

            # As user1 create a parent folder in user1's personal workspace
            remote_user1.make_folder(user1_workspace_path, 'Parent')

            # As user1 grant Everything permission to user2 on parent folder
            parent_folder_path = user1_workspace_path + '/Parent'
            op_input = "doc:" + parent_folder_path
            admin_remote_client.execute("Document.SetACE",
                op_input=op_input,
                user="******",
                permission="Everything",
                grant="true")

            # As user1 create a child folder in parent folder
            remote_user1.make_folder(parent_folder_path, 'Child')

            # As user2 register parent folder as a sync root
            remote_user2.register_as_root(parent_folder_path)

            # Bind server for user2
            ctl_2 = self.controller_2
            ctl_2.bind_server(self.local_nxdrive_folder_2, self.nuxeo_url,
                            self.user_2, self.password_2)

            # Launch first synchronization
            syn_2 = ctl_2.synchronizer
            self._synchronize(syn_2)

            # Check locally synchronized content
            self.assertEquals(len(local_user2.get_children_info('/')), 1)
            self.assertTrue(local_user2.exists('/Parent'))
            self.assertTrue(local_user2.exists('/Parent/Child'))

            # As user1 move child folder to user1's personal workspace
            remote_user1.move(parent_folder_path + '/Child',
                              user1_workspace_path)

            # Synchronize
            self._synchronize(syn_2)

            # Check locally synchronized content
            self.assertFalse(local_user2.exists('/Parent/Child'))

        finally:
            # Cleanup user1 personal workspace
            if admin_remote_client.exists(user1_workspace_path):
                admin_remote_client.delete(user1_workspace_path,
                                           use_trash=False)
    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'))
Exemple #30
0
    def test_synchronize_no_space_left_on_device(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(
            os.path.join(self.local_nxdrive_folder_1, self.workspace_title))
        remote = self.remote_document_client_1

        # Create a file in the remote root workspace
        remote.make_file('/', 'test_KO.odt', 'Some large content.')
        time.sleep(self.AUDIT_CHANGE_FINDER_TIME_RESOLUTION)
        self.wait()

        # Synchronize simulating a "No space left on device" error
        error = IOError("No space left on device")
        ctl.make_local_raise(error)
        syn.loop(delay=0.1, max_loops=1)
        # Temporary download file (.part) should be created locally but not
        # renamed and synchronization should not fail: doc pair should be
        # blacklisted and there should be 1 pending item
        self.assertTrue(local.exists('/.test_KO.odt.part'))
        self.assertFalse(local.exists('/test_KO.odt'))
        self.assertEquals(len(ctl.list_pending()), 1)

        # Create another file in the remote root workspace
        remote.make_file('/', 'test_OK.odt', 'Some small content.')
        time.sleep(self.AUDIT_CHANGE_FINDER_TIME_RESOLUTION)
        self.wait()

        # Synchronize without simulating any error
        ctl.make_local_raise(None)
        syn.loop(delay=0.1, max_loops=1)
        # Remote file should be created locally
        self.assertTrue(local.exists('/test_OK.odt'))
        # Blacklisted file should be ignored as delay (300 seconds by default)
        # is not expired, temporary download file from previously failed
        # synchronization should still be there and there should still be 1
        # pending item
        self.assertTrue(local.exists('/.test_KO.odt.part'))
        self.assertFalse(local.exists('/test_KO.odt'))
        self.assertEquals(len(ctl.list_pending()), 1)

        # Retry to synchronize blacklisted file still simulating a "No space
        # left on device" error
        ctl.make_local_raise(error)
        # Reduce error skip delay to retry synchronization of pairs in error
        syn.error_skip_period = 1.0
        syn.loop(delay=0.1, max_loops=1)
        # Temporary download file (.part) should be overridden but still not
        # renamed, doc pair should be blacklisted again and there should still
        # be 1 pending item
        self.assertTrue(local.exists('/.test_KO.odt.part'))
        self.assertFalse(local.exists('/test_KO.odt'))
        self.assertEquals(len(ctl.list_pending()), 1)
        # In the test workspace there should be 2 files but only 1 child taken
        # into account by the local client as it ignores .part suffixed files
        self.assertEquals(
            len(
                os.listdir(
                    os.path.join(self.local_nxdrive_folder_1,
                                 self.workspace_title))), 2)
        self.assertEquals(len(local.get_children_info('/')), 1)

        # Synchronize without simulating any error, as if space had been made
        # available on device
        ctl.make_local_raise(None)
        # Wait for error skip delay to retry synchronization of pairs in error
        time.sleep(syn.error_skip_period)
        syn.loop(delay=0.1, max_loops=1)
        # Previously blacklisted file should be created locally, temporary
        # download file should not be there anymore and there should be no
        # pending items left
        self.assertTrue(local.exists('/test_KO.odt'))
        self.assertFalse(local.exists('/.test_KO.odt.part'))
        self.assertEquals(len(ctl.list_pending()), 0)
        # In the test workspace there should be 2 files and 2 children taken
        # into account by the local client
        self.assertEquals(
            len(
                os.listdir(
                    os.path.join(self.local_nxdrive_folder_1,
                                 self.workspace_title))), 2)
        self.assertEquals(len(local.get_children_info('/')), 2)
    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'))
    def test_sync_delete_shared_folder(self):
        user_workspace_uid = None
        try:
            # Get remote and local clients
            admin_remote_client = self.root_remote_client
            user1_remote_client = RemoteDocumentClient(
                self.nuxeo_url, self.user_1, u'nxdrive-test-device-1',
                self.version, password=self.password_1,
                upload_tmp_dir=self.upload_tmp_dir)
            user2_remote_client = RemoteDocumentClient(
                self.nuxeo_url, self.user_2, u'nxdrive-test-device-2',
                self.version, password=self.password_2,
                upload_tmp_dir=self.upload_tmp_dir)
            local_client_1 = LocalClient(self.local_nxdrive_folder_1)

            # Make sure user1 workspace is created and fetch its uid
            user_workspace_uid = user1_remote_client.make_file_in_user_workspace('File in user workspace',
                                                                                 filename='USFile.txt')['parentRef']

            # Register user workspace as a sync root for user1
            user1_remote_client.register_as_root(user_workspace_uid)

            # Start engine
            self.engine_1.start()

            # Wait for synchronization
            self.wait_sync(wait_for_async=True)
            # Check locally synchronized content
            self.assertTrue(local_client_1.exists('/My Docs'))

            # Create test folder in user workspace as user1
            test_folder_uid = user1_remote_client.make_folder(user_workspace_uid, 'test_folder')
            # Wait for synchronization
            self.wait_sync(wait_for_async=True)
            # Check locally synchronized content
            self.assertTrue(local_client_1.exists('/My Docs/test_folder'))

            # Grant ReadWrite permission to user2 on test folder
            op_input = "doc:" + test_folder_uid
            admin_remote_client.execute("Document.SetACE", op_input=op_input, user=self.user_2,
                                        permission="ReadWrite", grant="true")
            # Wait for synchronization
            self.wait_sync(wait_for_async=True)

            # Register test folder as a sync root for user2
            user2_remote_client.register_as_root(test_folder_uid)
            # Wait for synchronization
            self.wait_sync(wait_for_async=True)

            # Delete test folder
            user1_remote_client.delete(test_folder_uid)

            # Synchronize deletion
            self.wait_sync(wait_for_async=True)
            # Check locally synchronized content
            self.assertFalse(local_client_1.exists('/My Docs/test_folder'))
            self.assertEquals(len(local_client_1.get_children_info('/My Docs')), 1)
        finally:
            # Cleanup user workspace
            if user_workspace_uid is not None and admin_remote_client.exists(user_workspace_uid):
                admin_remote_client.delete(user_workspace_uid,
                                           use_trash=False)