def test_delete_local_folder_delay_remote_changes_fetch(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 folder in the test workspace and a file inside
        # this folder, then synchronize
        local.make_folder('/Nuxeo Drive Test Workspace', 'Test folder')
        local.make_file('/Nuxeo Drive Test Workspace/Test folder',
                        'test.odt', 'Some content.')

        sync.loop(delay=0.1, max_loops=1)

        # Test folder should be created remotely in the test workspace
        self.assertTrue(remote.exists('/Test folder'))
        self.assertTrue(remote.exists('/Test folder/test.odt'))

        # Delete Test folder locally before fetching remote changes,
        # then synchronize
        local.delete('/Nuxeo Drive Test Workspace/Test folder')
        self.assertFalse(local.exists(
                                    '/Nuxeo Drive Test Workspace/Test folder'))

        self.wait_audit_change_finder_if_needed()
        self.wait()
        sync.loop(delay=0.1, max_loops=1)

        # Test folder should be deleted remotely in the test workspace.
        # Even though fetching the remote changes will send
        # 'documentCreated' events for Test folder and its child file
        # as a result of the previous synchronization loop, since the folder
        # will not have been renamed nor moved since last synchronization,
        # its remote pair state will not be marked as 'modified',
        # see Model.update_remote().
        # Thus the pair state will be ('deleted', 'synchronized'), resolved as
        # 'locally_deleted'.
        self.assertFalse(remote.exists('Test folder'))

        # Check Test folder has not been re-created locally
        self.assertFalse(local.exists(
                                    '/Nuxeo Drive Test Workspace/Test folder'))
    def test_local_delete_top_level_folder(self):
        sb, ctl = self.sb_1, self.controller_1
        local_client = LocalClient(self.local_test_folder_1)
        session = ctl.get_session()

        # Check top level folder
        self.assertTrue(local_client.exists(u'/Nuxeo Drive'))

        # Delete top level folder
        local_client.delete(u'/Nuxeo Drive')
        self.assertRaises(NotFound, local_client.get_info, u'/Nuxeo Drive')

        self.assertEquals(ctl.synchronizer.update_synchronize_server(sb), 1)

        # Check deleted server binding
        self.assertRaises(RuntimeError,
                          ctl.get_server_binding,
                          self.local_nxdrive_folder_1,
                          raise_if_missing=True)
        # Check deleted pair state
        self.assertEquals(len(session.query(LastKnownState).all()), 0)
    def test_local_delete_top_level_folder(self):
        sb, ctl = self.sb_1, self.controller_1
        local_client = LocalClient(self.local_test_folder_1)
        session = ctl.get_session()

        # Check top level folder
        self.assertTrue(local_client.exists(u'/Nuxeo Drive'))

        # Delete top level folder
        local_client.delete(u'/Nuxeo Drive')
        self.assertRaises(NotFound,
                          local_client.get_info, u'/Nuxeo Drive')

        self.assertEquals(ctl.synchronizer.update_synchronize_server(sb), 1)

        # Check deleted server binding
        self.assertRaises(RuntimeError,
                          ctl.get_server_binding, self.local_nxdrive_folder_1,
                          raise_if_missing=True)
        # Check deleted pair state
        self.assertEquals(len(session.query(LastKnownState).all()), 0)
    def test_synchronize_paged_delete_detection(self):
        raise SkipTest("WIP in https://jira.nuxeo.com/browse/NXDRIVE-170")
        # Initialize a controller with page size = 1 for deleted items
        # detection query
        # TODO NXDRIVE-170: refactor
        #ctl = Controller(self.nxdrive_conf_folder_1, page_size=1)
        ctl = None
        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 remote folder with 2 children then synchronize
        remote.make_folder('/', 'Remote folder',)
        remote.make_file('/Remote folder', 'Remote file 1.odt',
                         'Some content.')
        remote.make_file('/Remote folder', 'Remote file 2.odt',
                         'Other content.')

        self.wait()
        syn.loop(delay=0.1, max_loops=1)
        self.assertTrue(local.exists('/Remote folder'))
        self.assertTrue(local.exists('/Remote folder/Remote file 1.odt'))
        self.assertTrue(local.exists('/Remote folder/Remote file 2.odt'))

        # Delete remote folder then synchronize
        remote.delete('/Remote folder')

        self.wait()
        syn.loop(delay=0.1, max_loops=1)
        self.assertFalse(local.exists('/Remote folder'))
        self.assertFalse(local.exists('/Remote folder/Remote file 1.odt'))
        self.assertFalse(local.exists('/Remote folder/Remote file 2.odt'))

        # Create a local folder with 2 children then synchronize
        local.make_folder('/', 'Local folder')
        local.make_file('/Local folder', 'Local file 1.odt', 'Some content.')
        local.make_file('/Local folder', 'Local file 2.odt', 'Other content.')

        syn.loop(delay=0.1, max_loops=1)
        self.assertTrue(remote.exists('/Local folder'))
        self.assertTrue(remote.exists('/Local folder/Local file 1.odt'))
        self.assertTrue(remote.exists('/Local folder/Local file 2.odt'))

        # Delete local folder then synchronize
        time.sleep(OS_STAT_MTIME_RESOLUTION)
        local.delete('/Local folder')

        syn.loop(delay=0.1, max_loops=1)
        self.assertFalse(remote.exists('/Local folder'))
        # Wait for async completion as recursive deletion of children is done
        # by the BulkLifeCycleChangeListener which is asynchronous
        self.wait()
        self.assertFalse(remote.exists('/Local folder/Local file 1.odt'))
        self.assertFalse(remote.exists('/Local folder/Local file 2.odt'))

        # Dispose dedicated Controller instantiated for this test
        ctl.dispose()
    def test_delete_root_folder(self):
        raise SkipTest("WIP in https://jira.nuxeo.com/browse/NXDRIVE-170")
        """Check that local delete of root maps to unbind_root on the server"""
        ctl = self.controller_1
        sb = 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

        # Let's synchronize the new root
        self.assertEquals(syn.update_synchronize_server(sb), 1)
        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'),
        ])

        # Refetching the changes in the server audit log does not see any
        # change
        self.wait()
        self.assertEquals(syn.update_synchronize_server(sb), 0)
        self.assertEquals(ctl.list_pending(), [])

        # The workspace has been synced
        local = LocalClient(self.local_nxdrive_folder_1)
        self.assertTrue(local.exists('/' + self.workspace_title))

        # Let's create a subfolder and synchronize it
        local.make_folder('/' + self.workspace_title, 'Folder 3')
        self.assertEquals(syn.update_synchronize_server(sb), 1)
        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/Folder 3',
             u'synchronized', u'synchronized'),
        ])

        # Let's delete the root locally
        local.delete('/' + self.workspace_title)
        self.assertFalse(local.exists('/' + self.workspace_title))
        self.assertEquals(syn.update_synchronize_server(sb), 1)

        self.assertEquals(self.get_all_states(), [
            (u'/',
             u'synchronized', u'synchronized'),
        ])

        # On the server this has been mapped to a root unregistration:
        # the workspace is still there
        self.assertTrue(self.remote_document_client_1.exists('/'))

        # The subfolder has not been deleted on the server
        self.assertTrue(self.remote_document_client_1.exists('/Folder 3'))

        # But the workspace folder is still not there on the client:
        self.assertFalse(local.exists('/' + self.workspace_title))
        self.assertEquals(ctl.list_pending(), [])

        # Synchronizing later does not refetch the workspace as it's not
        # mapped as a sync root.
        self.wait()
        self.assertEquals(syn.update_synchronize_server(sb), 0)
        self.assertEquals(self.get_all_states(), [
            (u'/',
             u'synchronized', u'synchronized'),
        ])
        self.assertFalse(local.exists('/' + self.workspace_title))

        # We can rebind the root and fetch back its content
        ctl.bind_root(self.local_nxdrive_folder_1, self.workspace)
        self.wait()

        self.wait()
        syn.loop(delay=0, max_loops=1, no_event_init=True)
        self.assertEquals(ctl.list_pending(), [])
        self.assertTrue(local.exists('/' + self.workspace_title))
        self.assertTrue(local.exists('/' + self.workspace_title + '/Folder 3'))
        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/Folder 3',
             u'synchronized', u'synchronized'),
        ])
    def test_binding_synchronization_empty_start(self):
        ctl = self.controller_1
        remote_client = self.remote_document_client_1
        ctl.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url,
                        self.user_1, self.password_1)
        ctl.bind_root(self.local_nxdrive_folder_1, self.workspace)
        syn = ctl.synchronizer
        expected_folder = os.path.join(self.local_nxdrive_folder_1,
                                       self.workspace_title)

        # Nothing to synchronize by default
        self.assertEquals(ctl.list_pending(), [])
        self.assertEquals(syn.synchronize(), 0)

        # Let's create some document on the server
        self.make_server_tree()

        # By default nothing is detected
        self.assertEquals(ctl.list_pending(), [])
        self.assertEquals(ctl.children_states(expected_folder), [])

        # Let's scan manually
        syn.scan_remote(self.local_nxdrive_folder_1)

        # Changes on the remote server have been detected...
        self.assertEquals(len(ctl.list_pending()), 12)

        # ...but nothing is yet visible locally as those files don't exist
        # there yet.
        self.assertEquals(ctl.children_states(expected_folder), [])

        # Let's perform the synchronization
        self.assertEquals(syn.synchronize(limit=100), 12)

        # We should now be fully synchronized
        self.assertEquals(ctl.list_pending(), [])
        self.assertEquals(ctl.children_states(expected_folder), [
            (u'File 5.txt', u'synchronized'),
            (u'Folder 1', u'synchronized'),
            (u'Folder 2', u'synchronized'),
        ])
        local_client = LocalClient(self.local_nxdrive_folder_1)
        self.assertEquals(local_client.get_content(
            '/Nuxeo Drive Test Workspace/Folder 1/File 1.txt'),
            "aaa")

        self.assertEquals(local_client.get_content(
            '/Nuxeo Drive Test Workspace/Folder 1/Folder 1.1/File 2.txt'),
            "bbb")

        self.assertEquals(local_client.get_content(
            '/Nuxeo Drive Test Workspace/Folder 1/Folder 1.2/File 3.txt'),
            "ccc")

        self.assertEquals(local_client.get_content(
            '/Nuxeo Drive Test Workspace/Folder 2/File 4.txt'),
            "ddd")

        c1 = local_client.get_content(
            '/Nuxeo Drive Test Workspace/Folder 2/Duplicated File.txt')

        c2 = local_client.get_content(
            '/Nuxeo Drive Test Workspace/Folder 2/Duplicated File__1.txt')

        self.assertEquals(tuple(sorted((c1, c2))),
                          ("Other content.", "Some content."))

        # Wait a bit for file time stamps to increase enough: on OSX HFS+ the
        # file modification time resolution is 1s for instance
        time.sleep(self.OS_STAT_MTIME_RESOLUTION)

        # Let do some local and remote changes concurrently
        local_client.delete('/Nuxeo Drive Test Workspace/File 5.txt')
        local_client.update_content(
            '/Nuxeo Drive Test Workspace/Folder 1/File 1.txt', 'aaaa')

        # The remote client use in this test is handling paths relative to
        # the 'Nuxeo Drive Test Workspace'
        remote_client.update_content('/Folder 1/Folder 1.1/File 2.txt',
                                     'bbbb')
        remote_client.delete('/Folder 2')
        f3 = remote_client.make_folder(self.workspace, 'Folder 3')
        remote_client.make_file(f3, 'File 6.txt', content='ffff')
        local_client.make_folder('/Nuxeo Drive Test Workspace', 'Folder 4')

        # Rescan
        syn.scan_local(self.local_nxdrive_folder_1)
        syn.scan_remote(self.local_nxdrive_folder_1)
        self.assertEquals(ctl.children_states(expected_folder), [
            (u'File 5.txt', u'locally_deleted'),
            (u'Folder 1', u'children_modified'),
            (u'Folder 2', u'children_modified'),  # what do we want for this?
            # Folder 3 is not yet visible has not sync has happen to give it a
            # local path yet
            (u'Folder 4', u'unknown'),
        ])
        # The information on the remote state of Folder 3 has been stored in
        # the database though
        session = ctl.get_session()
        f3_state = session.query(LastKnownState).filter_by(
            remote_name='Folder 3').one()
        self.assertEquals(f3_state.local_path, None)

        states = ctl.children_states(expected_folder + '/Folder 1')
        expected_states = [
            (u'File 1.txt', 'locally_modified'),
            (u'Folder 1.1', 'children_modified'),
            (u'Folder 1.2', 'synchronized'),
        ]
        self.assertEquals(states, expected_states)
        states = ctl.children_states(expected_folder + '/Folder 1/Folder 1.1')
        expected_states = [
            (u'File 2.txt', u'remotely_modified'),
        ]
        self.assertEquals(states, expected_states)
        states = ctl.children_states(expected_folder + '/Folder 2')
        expected_states = [
            (u'Duplicated File.txt', u'remotely_deleted'),
            (u'Duplicated File__1.txt', u'remotely_deleted'),
            (u'File 4.txt', u'remotely_deleted'),
        ]
        self.assertEquals(states, expected_states)

        # Perform synchronization: deleted folder content are not
        # counted in the summary
        self.assertEquals(syn.synchronize(limit=100), 7)

        # We should now be fully synchronized again
        self.assertEquals(ctl.list_pending(), [])
        self.assertEquals(ctl.children_states(expected_folder), [
            (u'Folder 1', 'synchronized'),
            (u'Folder 3', 'synchronized'),
            (u'Folder 4', 'synchronized'),
        ])
        states = ctl.children_states(expected_folder + '/Folder 1')
        expected_states = [
            (u'File 1.txt', 'synchronized'),
            (u'Folder 1.1', 'synchronized'),
            (u'Folder 1.2', 'synchronized'),
        ]
        self.assertEquals(states, expected_states)
        local = LocalClient(expected_folder)
        self.assertEquals(local.get_content(
            '/Folder 1/File 1.txt'),
            "aaaa")
        self.assertEquals(local.get_content(
            '/Folder 1/Folder 1.1/File 2.txt'),
            "bbbb")
        self.assertEquals(local.get_content(
            '/Folder 3/File 6.txt'),
            "ffff")
        self.assertEquals(remote_client.get_content(
            '/Folder 1/File 1.txt'),
            "aaaa")
        self.assertEquals(remote_client.get_content(
            '/Folder 1/Folder 1.1/File 2.txt'),
            "bbbb")
        self.assertEquals(remote_client.get_content(
            '/Folder 3/File 6.txt'),
            "ffff")

        # Rescan: no change to detect we should reach a fixpoint
        syn.scan_local(self.local_nxdrive_folder_1)
        syn.scan_remote(self.local_nxdrive_folder_1)
        self.assertEquals(ctl.list_pending(), [])
        self.assertEquals(ctl.children_states(expected_folder), [
            (u'Folder 1', 'synchronized'),
            (u'Folder 3', 'synchronized'),
            (u'Folder 4', 'synchronized'),
        ])

        # Send some binary data that is not valid in utf-8 or ascii
        # (to test the HTTP / Multipart transform layer).
        time.sleep(self.OS_STAT_MTIME_RESOLUTION)
        local.update_content('/Folder 1/File 1.txt', "\x80")
        remote_client.update_content('/Folder 1/Folder 1.1/File 2.txt', '\x80')
        syn.scan_local(self.local_nxdrive_folder_1)
        syn.scan_remote(self.local_nxdrive_folder_1)
        self.assertEquals(syn.synchronize(limit=100), 2)
        self.assertEquals(remote_client.get_content('/Folder 1/File 1.txt'),
                          "\x80")
        self.assertEquals(local.get_content('/Folder 1/Folder 1.1/File 2.txt'),
                          "\x80")
    def test_synchronize_paged_delete_detection(self):
        # Initialize a controller with page size = 1 for deleted items
        # detection query
        ctl = Controller(self.nxdrive_conf_folder_1, page_size=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 remote folder with 2 children then synchronize
        remote.make_folder('/', 'Remote folder',)
        remote.make_file('/Remote folder', 'Remote file 1.odt',
                         'Some content.')
        remote.make_file('/Remote folder', 'Remote file 2.odt',
                         'Other content.')

        time.sleep(self.AUDIT_CHANGE_FINDER_TIME_RESOLUTION)
        self.wait()
        syn.loop(delay=0.1, max_loops=1)
        self.assertTrue(local.exists('/Remote folder'))
        self.assertTrue(local.exists('/Remote folder/Remote file 1.odt'))
        self.assertTrue(local.exists('/Remote folder/Remote file 2.odt'))

        # Delete remote folder then synchronize
        remote.delete('/Remote folder')

        time.sleep(self.AUDIT_CHANGE_FINDER_TIME_RESOLUTION)
        self.wait()
        syn.loop(delay=0.1, max_loops=1)
        self.assertFalse(local.exists('/Remote folder'))
        self.assertFalse(local.exists('/Remote folder/Remote file 1.odt'))
        self.assertFalse(local.exists('/Remote folder/Remote file 2.odt'))

        # Create a local folder with 2 children then synchronize
        local.make_folder('/', 'Local folder')
        local.make_file('/Local folder', 'Local file 1.odt', 'Some content.')
        local.make_file('/Local folder', 'Local file 2.odt', 'Other content.')

        syn.loop(delay=0.1, max_loops=1)
        self.assertTrue(remote.exists('/Local folder'))
        self.assertTrue(remote.exists('/Local folder/Local file 1.odt'))
        self.assertTrue(remote.exists('/Local folder/Local file 2.odt'))

        # Delete local folder then synchronize
        time.sleep(self.OS_STAT_MTIME_RESOLUTION)
        local.delete('/Local folder')

        syn.loop(delay=0.1, max_loops=1)
        self.assertFalse(remote.exists('/Local folder'))
        self.assertFalse(remote.exists('/Local folder/Local file 1.odt'))
        self.assertFalse(remote.exists('/Local folder/Local file 2.odt'))

        # Dispose dedicated Controller instantiated for this test
        ctl.dispose()
    def test_delete_local_folder_2_clients(self):
        raise SkipTest("WIP in https://jira.nuxeo.com/browse/NXDRIVE-170")

        # Define 2 controllers, one for each device
        ctl1 = self.controller_1
        ctl2 = self.controller_2

        # Get local clients for each device and remote client
        local1 = LocalClient(self.local_nxdrive_folder_1)
        local2 = LocalClient(self.local_nxdrive_folder_2)
        remote = self.remote_document_client_1

        # Bind each device to the server with the same account:
        # nuxeoDriveTestUser_user_1 and bind test workspace
        ctl1.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url,
                        self.user_1, self.password_1)
        ctl2.bind_server(self.local_nxdrive_folder_2, self.nuxeo_url,
                        self.user_1, self.password_1)
        ctl1.bind_root(self.local_nxdrive_folder_1, self.workspace)

        # Check synchronization roots for nuxeoDriveTestUser_user_1,
        # there should be 1, the test workspace
        sync_roots = remote.get_roots()
        self.assertEquals(len(sync_roots), 1)
        self.assertEquals(sync_roots[0].name, self.workspace_title)

        # Launch first synchronization on both devices
        self.wait()
        sync1 = ctl1.synchronizer
        sync2 = ctl2.synchronizer
        sync1.loop(delay=0.1, max_loops=1)
        sync2.loop(delay=0.1, max_loops=1)

        # Test workspace should be created locally on both devices
        self.assertTrue(local1.exists('/Nuxeo Drive Test Workspace'))
        self.assertTrue(local2.exists('/Nuxeo Drive Test Workspace'))

        # Make nuxeoDriveTestUser_user_1 create a remote folder in the
        # test workspace and a file inside this folder,
        # then synchronize both devices
        test_folder = remote.make_folder(self.workspace, 'Test folder')
        remote.make_file(test_folder, 'test.odt', 'Some content.')

        self.wait()
        sync1.loop(delay=0.1, max_loops=1)
        sync2.loop(delay=0.1, max_loops=1)

        # Test folder should be created locally on both devices
        self.assertTrue(local1.exists(
                        '/Nuxeo Drive Test Workspace/Test folder'))
        self.assertTrue(local1.exists(
                        '/Nuxeo Drive Test Workspace/Test folder/test.odt'))
        self.assertTrue(local2.exists(
                        '/Nuxeo Drive Test Workspace/Test folder'))
        self.assertTrue(local2.exists(
                        '/Nuxeo Drive Test Workspace/Test folder/test.odt'))

        # Delete Test folder locally on one of the devices
        local1.delete('/Nuxeo Drive Test Workspace/Test folder')
        self.assertFalse(local1.exists(
                                    '/Nuxeo Drive Test Workspace/Test folder'))

        # Launch synchronization on both devices in separate threads
        def sync1_loop():
            sync1.loop(delay=1.0, max_loops=3)

        def sync2_loop():
            sync2.loop(delay=1.0, max_loops=3)

        sync1_thread = Thread(target=sync1_loop)
        sync2_thread = Thread(target=sync2_loop)
        sync1_thread.start()
        sync2_thread.start()

        # Wait for synchronization threads to complete
        sync1_thread.join()
        sync2_thread.join()

        # Test folder should be deleted on the server and on both devices
        self.assertFalse(remote.exists(test_folder))
        self.assertFalse(local1.exists(
                                    '/Nuxeo Drive Test Workspace/Test folder'))
        self.assertFalse(local2.exists(
                                    '/Nuxeo Drive Test Workspace/Test folder'))
    def test_delete_local_folder_update_remote_folder_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 folder in the test workspace and a file inside
        # this folder, then synchronize
        local.make_folder('/Nuxeo Drive Test Workspace', 'Test folder')
        local.make_file('/Nuxeo Drive Test Workspace/Test folder',
                        'test.odt', 'Some content.')

        sync.loop(delay=0.1, max_loops=1)

        # Test folder should be created remotely in the test workspace
        self.assertTrue(remote.exists('/Test folder'))
        self.assertTrue(remote.exists('/Test folder/test.odt'))

        # Delete Test folder locally and remotely update one of its properties
        # concurrently, then synchronize
        local.delete('/Nuxeo Drive Test Workspace/Test folder')
        self.assertFalse(local.exists(
                                    '/Nuxeo Drive Test Workspace/Test folder'))
        test_folder_ref = remote._check_ref('/Test folder')
        # Wait for 1 second to make sure the folder'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_folder_ref,
                      properties={'dc:description': 'Some description.'})
        test_folder = remote.fetch(test_folder_ref)
        self.assertEqual(test_folder['properties']['dc:description'],
                         'Some description.')

        self.wait()
        sync.loop(delay=0.1, max_loops=1)

        # Test folder should be deleted remotely in the test workspace.
        # Even though fetching the remote changes will send a
        # 'documentModified' event for Test folder as a result of its
        # dc:description property update, since the folder will not have been
        # renamed nor moved since last synchronization, its remote pair state
        # will not be marked as 'modified', see Model.update_remote().
        # Thus the pair state will be ('deleted', 'synchronized'), resolved as
        # 'locally_deleted'.
        self.assertFalse(remote.exists('/Test folder'))

        # Check Test folder has not been re-created locally
        self.assertFalse(local.exists(
                                    '/Nuxeo Drive Test Workspace/Test folder'))
    def test_delete_local_folder_2_clients(self):

        # Define 2 controllers, one for each device
        ctl1 = self.controller_1
        ctl2 = self.controller_2

        # Get local clients for each device and remote client
        local1 = LocalClient(self.local_nxdrive_folder_1)
        local2 = LocalClient(self.local_nxdrive_folder_2)
        remote = self.remote_document_client_1

        # Bind each device to the server with the same account:
        # nuxeoDriveTestUser_user_1 and bind test workspace
        ctl1.bind_server(self.local_nxdrive_folder_1, self.nuxeo_url,
                        self.user_1, self.password_1)
        ctl2.bind_server(self.local_nxdrive_folder_2, self.nuxeo_url,
                        self.user_1, self.password_1)
        ctl1.bind_root(self.local_nxdrive_folder_1, self.workspace)

        # Check synchronization roots for nuxeoDriveTestUser_user_1,
        # there should be 1, the test workspace
        sync_roots = remote.get_roots()
        self.assertEquals(len(sync_roots), 1)
        self.assertEquals(sync_roots[0].name, self.workspace_title)

        # Launch first synchronization on both devices
        self.wait_audit_change_finder_if_needed()
        self.wait()
        sync1 = ctl1.synchronizer
        sync2 = ctl2.synchronizer
        sync1.loop(delay=0.1, max_loops=1)
        sync2.loop(delay=0.1, max_loops=1)

        # Test workspace should be created locally on both devices
        self.assertTrue(local1.exists('/Nuxeo Drive Test Workspace'))
        self.assertTrue(local2.exists('/Nuxeo Drive Test Workspace'))

        # Make nuxeoDriveTestUser_user_1 create a remote folder in the
        # test workspace and a file inside this folder,
        # then synchronize both devices
        test_folder = remote.make_folder(self.workspace, 'Test folder')
        remote.make_file(test_folder, 'test.odt', 'Some content.')

        self.wait_audit_change_finder_if_needed()
        self.wait()
        sync1.loop(delay=0.1, max_loops=1)
        sync2.loop(delay=0.1, max_loops=1)

        # Test folder should be created locally on both devices
        self.assertTrue(local1.exists(
                        '/Nuxeo Drive Test Workspace/Test folder'))
        self.assertTrue(local1.exists(
                        '/Nuxeo Drive Test Workspace/Test folder/test.odt'))
        self.assertTrue(local2.exists(
                        '/Nuxeo Drive Test Workspace/Test folder'))
        self.assertTrue(local2.exists(
                        '/Nuxeo Drive Test Workspace/Test folder/test.odt'))

        # Delete Test folder locally on one of the devices
        local1.delete('/Nuxeo Drive Test Workspace/Test folder')
        self.assertFalse(local1.exists(
                                    '/Nuxeo Drive Test Workspace/Test folder'))

        # Launch synchronization on both devices in separate threads
        def sync1_loop():
            sync1.loop(delay=1.0, max_loops=3)

        def sync2_loop():
            sync2.loop(delay=1.0, max_loops=3)

        sync1_thread = Thread(target=sync1_loop)
        sync2_thread = Thread(target=sync2_loop)
        sync1_thread.start()
        sync2_thread.start()

        # Wait for synchronization threads to complete
        sync1_thread.join()
        sync2_thread.join()

        # Test folder should be deleted on the server and on both devices
        self.assertFalse(remote.exists(test_folder))
        self.assertFalse(local1.exists(
                                    '/Nuxeo Drive Test Workspace/Test folder'))
        self.assertFalse(local2.exists(
                                    '/Nuxeo Drive Test Workspace/Test folder'))
    def test_delete_local_folder_update_remote_folder_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 folder in the test workspace and a file inside
        # this folder, then synchronize
        local.make_folder('/Nuxeo Drive Test Workspace', 'Test folder')
        local.make_file('/Nuxeo Drive Test Workspace/Test folder',
                        'test.odt', 'Some content.')

        sync.loop(delay=0.1, max_loops=1)

        # Test folder should be created remotely in the test workspace
        self.assertTrue(remote.exists('/Test folder'))
        self.assertTrue(remote.exists('/Test folder/test.odt'))

        # Delete Test folder locally and remotely update one of its properties
        # concurrently, then synchronize
        local.delete('/Nuxeo Drive Test Workspace/Test folder')
        self.assertFalse(local.exists(
                                    '/Nuxeo Drive Test Workspace/Test folder'))
        test_folder_ref = remote._check_ref('/Test folder')
        # Wait for 1 second to make sure the folder'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_folder_ref,
                      properties={'dc:description': 'Some description.'})
        test_folder = remote.fetch(test_folder_ref)
        self.assertEqual(test_folder['properties']['dc:description'],
                         'Some description.')

        self.wait_audit_change_finder_if_needed()
        self.wait()
        sync.loop(delay=0.1, max_loops=1)

        # Test folder should be deleted remotely in the test workspace.
        # Even though fetching the remote changes will send a
        # 'documentModified' event for Test folder as a result of its
        # dc:description property update, since the folder will not have been
        # renamed nor moved since last synchronization, its remote pair state
        # will not be marked as 'modified', see Model.update_remote().
        # Thus the pair state will be ('deleted', 'synchronized'), resolved as
        # 'locally_deleted'.
        self.assertFalse(remote.exists('/Test folder'))

        # Check Test folder has not been re-created locally
        self.assertFalse(local.exists(
                                    '/Nuxeo Drive Test Workspace/Test folder'))
def test_binding_synchronization_empty_start():
    ctl.bind_server(LOCAL_NXDRIVE_FOLDER, NUXEO_URL, USER, PASSWORD)
    ctl.bind_root(LOCAL_NXDRIVE_FOLDER, TEST_WORKSPACE)
    expected_folder = os.path.join(LOCAL_NXDRIVE_FOLDER, TEST_WORKSPACE_TITLE)

    # Nothing to synchronize by default
    assert_equal(ctl.list_pending(), [])
    assert_equal(ctl.synchronize(), 0)

    # Let's create some document on the server
    make_server_tree()

    # By default nothing is detected
    assert_equal(ctl.list_pending(), [])
    # assert_equal(ctl.children_states(expected_folder), [])

    # Let's scan manually
    session = ctl.get_session()
    ctl.scan_remote(expected_folder, session)

    # Changes on the remote server have been detected...
    assert_equal(len(ctl.list_pending()), 11)

    # ...but nothing is yet visible locally as those files don't exist there
    # yet.
    # assert_equal(ctl.children_states(expected_folder), [])

    # Let's perform the synchronization
    assert_equal(ctl.synchronize(limit=100), 11)

    # We should now be fully synchronized
    assert_equal(len(ctl.list_pending()), 0)
    assert_equal(
        ctl.children_states(expected_folder),
        [(u"/File 5.txt", u"synchronized"), (u"/Folder 1", u"synchronized"), (u"/Folder 2", u"synchronized")],
    )
    local = LocalClient(expected_folder)
    assert_equal(local.get_content("/Folder 1/File 1.txt"), "aaa")
    assert_equal(local.get_content("/Folder 1/Folder 1.1/File 2.txt"), "bbb")
    assert_equal(local.get_content("/Folder 1/Folder 1.2/File 3.txt"), "ccc")
    assert_equal(local.get_content("/Folder 2/File 4.txt"), "ddd")
    assert_equal(local.get_content("/Folder 2/Duplicated File.txt"), "Some content.")
    assert_equal(local.get_content("/Folder 2/Duplicated File__1.txt"), "Other content.")

    # Wait a bit for file time stamps to increase enough: on most OS the file
    # modification time resolution is 1s
    time.sleep(1.0)

    # Let do some local and remote changes concurrently
    local.delete("/File 5.txt")
    local.update_content("/Folder 1/File 1.txt", "aaaa")
    remote_client.update_content("/Folder 1/Folder 1.1/File 2.txt", "bbbb")
    remote_client.delete("/Folder 2")
    f3 = remote_client.make_folder(TEST_WORKSPACE, "Folder 3")
    remote_client.make_file(f3, "File 6.txt", content="ffff")
    local.make_folder("/", "Folder 4")

    # Rescan
    ctl.scan_local(expected_folder, session)
    ctl.scan_remote(expected_folder, session)
    assert_equal(
        ctl.children_states(expected_folder),
        [
            (u"/File 5.txt", u"locally_deleted"),
            (u"/Folder 1", u"children_modified"),
            (u"/Folder 2", u"children_modified"),  # what do we want for this?
            # Folder 3 is not yet visible has not sync has happen to give it a
            # local path yet
            (u"/Folder 4", u"unknown"),
        ],
    )
    # It is possible to fetch the full children states of the root though:
    full_states = ctl.children_states(expected_folder, full_states=True)
    assert_equal(len(full_states), 5)
    assert_equal(full_states[0][0].remote_name, "Folder 3")
    assert_equal(full_states[0][1], "children_modified")

    states = ctl.children_states(expected_folder + "/Folder 1")
    expected_states = [
        (u"/Folder 1/File 1.txt", "locally_modified"),
        (u"/Folder 1/Folder 1.1", "children_modified"),
        (u"/Folder 1/Folder 1.2", "synchronized"),
    ]
    assert_equal(states, expected_states)
    states = ctl.children_states(expected_folder + "/Folder 1/Folder 1.1")
    expected_states = [(u"/Folder 1/Folder 1.1/File 2.txt", u"remotely_modified")]
    assert_equal(states, expected_states)
    states = ctl.children_states(expected_folder + "/Folder 2")
    expected_states = [
        (u"/Folder 2/Duplicated File.txt", u"remotely_deleted"),
        (u"/Folder 2/Duplicated File__1.txt", u"remotely_deleted"),
        (u"/Folder 2/File 4.txt", u"remotely_deleted"),
    ]
    assert_equal(states, expected_states)

    # Perform synchronization
    assert_equal(ctl.synchronize(limit=100), 10)

    # We should now be fully synchronized again
    assert_equal(len(ctl.list_pending()), 0)
    assert_equal(
        ctl.children_states(expected_folder),
        [(u"/Folder 1", "synchronized"), (u"/Folder 3", "synchronized"), (u"/Folder 4", "synchronized")],
    )
    states = ctl.children_states(expected_folder + "/Folder 1")
    expected_states = [
        (u"/Folder 1/File 1.txt", "synchronized"),
        (u"/Folder 1/Folder 1.1", "synchronized"),
        (u"/Folder 1/Folder 1.2", "synchronized"),
    ]
    assert_equal(states, expected_states)
    assert_equal(local.get_content("/Folder 1/File 1.txt"), "aaaa")
    assert_equal(local.get_content("/Folder 1/Folder 1.1/File 2.txt"), "bbbb")
    assert_equal(local.get_content("/Folder 3/File 6.txt"), "ffff")
    assert_equal(remote_client.get_content("/Folder 1/File 1.txt"), "aaaa")
    assert_equal(remote_client.get_content("/Folder 1/Folder 1.1/File 2.txt"), "bbbb")
    assert_equal(remote_client.get_content("/Folder 3/File 6.txt"), "ffff")

    # Rescan: no change to detect we should reach a fixpoint
    ctl.scan_local(expected_folder, session)
    ctl.scan_remote(expected_folder, session)
    assert_equal(len(ctl.list_pending()), 0)
    assert_equal(
        ctl.children_states(expected_folder),
        [(u"/Folder 1", "synchronized"), (u"/Folder 3", "synchronized"), (u"/Folder 4", "synchronized")],
    )

    # Send some binary data that is not valid in utf-8 or ascii (to test the
    # HTTP / Multipart transform layer).
    time.sleep(1.0)
    local.update_content("/Folder 1/File 1.txt", "\x80")
    remote_client.update_content("/Folder 1/Folder 1.1/File 2.txt", "\x80")
    ctl.scan_local(expected_folder, session)
    ctl.scan_remote(expected_folder, session)
    assert_equal(ctl.synchronize(limit=100), 2)
    assert_equal(remote_client.get_content("/Folder 1/File 1.txt"), "\x80")
    assert_equal(local.get_content("/Folder 1/Folder 1.1/File 2.txt"), "\x80")
Exemple #13
0
def test_local_scan():
    ctl.bind_server(TEST_SYNCED_FOLDER, 'http://example.com/nuxeo',
                    'username', 'secret')
    ctl.bind_root(TEST_SYNCED_FOLDER, 'folder_1-nuxeo-ref')
    ctl.bind_root(TEST_SYNCED_FOLDER, 'folder_2-nuxeo-ref')
    root_1 = join(TEST_SYNCED_FOLDER, 'Folder 1')
    root_2 = join(TEST_SYNCED_FOLDER, 'Folder 2')

    client_1 = LocalClient(root_1)

    # Folder are registered but empty for now
    assert_equal(ctl.children_states(root_1), [])
    assert_equal(ctl.children_states(root_2), [])

    # Put some content under the first root
    client_1.make_file('/', 'File 1.txt',
                       content="Initial 'File 1.txt' content")
    folder_3 = client_1.make_folder('/', 'Folder 3')
    client_1.make_file(folder_3, 'File 2.txt',
                       content="Initial 'File 2.txt' content")

    # The states have not been updated
    assert_equal(ctl.children_states(root_1), [])
    assert_equal(ctl.children_states(root_2), [])

    # Scanning the other root will not updated the first root states.
    session = ctl.get_session()
    ctl.scan_local(root_2, session)
    assert_equal(ctl.children_states(root_1), [])

    # Scanning root_1 will find the changes
    ctl.scan_local(root_1, session)
    assert_equal(ctl.children_states(root_1), [
        (u'/File 1.txt', u'unknown'),
        (u'/Folder 3', 'children_modified'),
    ])
    folder_3_abs = os.path.join(root_1, 'Folder 3')
    assert_equal(ctl.children_states(folder_3_abs), [
        (u'/Folder 3/File 2.txt', u'unknown'),
    ])

    # Wait a bit for file time stamps to increase enough: on most OS the file
    # modification time resolution is 1s
    time.sleep(1.0)

    # let's do some changes
    client_1.delete('/File 1.txt')
    client_1.make_folder('/Folder 3', 'Folder 3.1')
    client_1.make_file('/Folder 3', 'File 3.txt',
                      content="Initial 'File 3.txt' content")
    client_1.update_content('/Folder 3/File 2.txt',
                            "Updated content for 'File 2.txt'")

    # If we don't do a rescan, the controller is not aware of the changes
    assert_equal(ctl.children_states(root_1), [
        (u'/File 1.txt', u'unknown'),
        (u'/Folder 3', 'children_modified'),
    ])
    folder_3_abs = os.path.join(root_1, 'Folder 3')
    assert_equal(ctl.children_states(folder_3_abs), [
        (u'/Folder 3/File 2.txt', u'unknown'),
    ])

    # Let's scan again
    ctl.scan_local(root_1, session)
    assert_equal(ctl.children_states(root_1), [
        (u'/Folder 3', 'children_modified'),
    ])
    assert_equal(ctl.children_states(folder_3_abs), [
        (u'/Folder 3/File 2.txt', u'locally_modified'),
        (u'/Folder 3/File 3.txt', u'unknown'),
        (u'/Folder 3/Folder 3.1', u'unknown')
    ])

    # Delete the toplevel folder that has not been synchronised to the
    # server
    client_1.delete('/Folder 3')
    ctl.scan_local(root_1, session)
    assert_equal(ctl.children_states(root_1), [])
    assert_equal(ctl.children_states(folder_3_abs), [])